Fixed sync loading with dojo 1.8. dojo_1.8
authorJoseph Lewis <joehms22@gmail.com>
Mon, 20 Aug 2012 01:08:58 +0000 (19:08 -0600)
committerJoseph Lewis <joehms22@gmail.com>
Mon, 20 Aug 2012 01:08:58 +0000 (19:08 -0600)
Signed-off-by: Joseph Lewis <joehms22@gmail.com>
100 files changed:
Open-ILS/web/js/dojo/openils/BibTemplate.js
Open-ILS/web/js/dojo/openils/CGI.js
Open-ILS/web/js/dojo/openils/DojoPatch.js
Open-ILS/web/js/dojo/openils/tests/functions/AuthorityControlSet.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/AutoSuggestStore.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/CGI.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/CopyLocation.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/Event.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/Event.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/Util.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/editor.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/editors.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/functions/editors.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/module.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/module.js~ [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/tests/runTests.html [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/widget/tests/module.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/widget/tests/runTests.html [new file with mode: 0644]
Open-ILS/web/js/ui/base.js
Open-ILS/web/js/ui/default/acq/common/base64.js
Open-ILS/web/js/ui/default/acq/common/jubgrid.js
Open-ILS/web/js/ui/default/acq/common/li_table.js
Open-ILS/web/js/ui/default/acq/common/tag_manager.js
Open-ILS/web/js/ui/default/acq/common/vlagent.js
Open-ILS/web/js/ui/default/acq/financial/claim_eligible.js
Open-ILS/web/js/ui/default/acq/financial/list_funding_sources.js
Open-ILS/web/js/ui/default/acq/financial/list_funds.js
Open-ILS/web/js/ui/default/acq/financial/view_fund.js
Open-ILS/web/js/ui/default/acq/financial/view_funding_source.js
Open-ILS/web/js/ui/default/acq/financial/view_provider.js
Open-ILS/web/js/ui/default/acq/invoice/common.js
Open-ILS/web/js/ui/default/acq/invoice/receive.js
Open-ILS/web/js/ui/default/acq/invoice/view.js
Open-ILS/web/js/ui/default/acq/lineitem/findbib.js
Open-ILS/web/js/ui/default/acq/lineitem/related.js
Open-ILS/web/js/ui/default/acq/lineitem/search.js
Open-ILS/web/js/ui/default/acq/lineitem/worksheet.js
Open-ILS/web/js/ui/default/acq/picklist/bib_search.js
Open-ILS/web/js/ui/default/acq/picklist/brief_record.js
Open-ILS/web/js/ui/default/acq/picklist/from_bib.js
Open-ILS/web/js/ui/default/acq/picklist/upload.js
Open-ILS/web/js/ui/default/acq/picklist/user_request.js
Open-ILS/web/js/ui/default/acq/picklist/view.js
Open-ILS/web/js/ui/default/acq/picklist/view_list.js
Open-ILS/web/js/ui/default/acq/po/create.js
Open-ILS/web/js/ui/default/acq/po/events.js
Open-ILS/web/js/ui/default/acq/po/search.js
Open-ILS/web/js/ui/default/acq/po/view_po.js
Open-ILS/web/js/ui/default/acq/receiving/process.js
Open-ILS/web/js/ui/default/acq/search/invoice.js
Open-ILS/web/js/ui/default/acq/search/picklist.js
Open-ILS/web/js/ui/default/acq/search/unified.js
Open-ILS/web/js/ui/default/actor/user/register.js
Open-ILS/web/js/ui/default/actor/user/trigger_events.js
Open-ILS/web/js/ui/default/booking/capture.js
Open-ILS/web/js/ui/default/booking/common.js
Open-ILS/web/js/ui/default/booking/populator.js
Open-ILS/web/js/ui/default/booking/pull_list.js
Open-ILS/web/js/ui/default/booking/reservation.js
Open-ILS/web/js/ui/default/cat/authority/list.js
Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
Open-ILS/web/js/ui/default/conify/global/acq/cancel_reason.js
Open-ILS/web/js/ui/default/conify/global/acq/claim_grid.js
Open-ILS/web/js/ui/default/conify/global/acq/distribution_formula.js
Open-ILS/web/js/ui/default/conify/global/acq/edi_account.js
Open-ILS/web/js/ui/default/conify/global/acq/fund_tag.js
Open-ILS/web/js/ui/default/conify/global/acq/lineitem_alert.js
Open-ILS/web/js/ui/default/conify/global/acq/lineitem_marc_attr_def.js
Open-ILS/web/js/ui/default/conify/global/acq/provider.js
Open-ILS/web/js/ui/default/conify/global/action/survey.js
Open-ILS/web/js/ui/default/conify/global/action/survey/edit.js
Open-ILS/web/js/ui/default/conify/global/action_trigger/event_definition.js
Open-ILS/web/js/ui/default/conify/global/asset/copy_location_group.js
Open-ILS/web/js/ui/default/conify/global/asset/copy_location_order.js
Open-ILS/web/js/ui/default/conify/global/asset/copy_template.js
Open-ILS/web/js/ui/default/conify/global/config/acn_prefix.js
Open-ILS/web/js/ui/default/conify/global/config/acn_suffix.js
Open-ILS/web/js/ui/default/conify/global/config/barcode_completion.js
Open-ILS/web/js/ui/default/conify/global/config/billing_type.js
Open-ILS/web/js/ui/default/conify/global/config/circ_limit_set.js
Open-ILS/web/js/ui/default/conify/global/config/circ_matrix_matchpoint.js
Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js
Open-ILS/web/js/ui/default/conify/global/config/sms_carrier.js
Open-ILS/web/js/ui/default/conify/global/config/standing_penalty.js
Open-ILS/web/js/ui/default/conify/global/config/z3950_source.js
Open-ILS/web/js/ui/default/conify/global/permission/grp_penalty_threshold.js
Open-ILS/web/js/ui/default/conify/global/vandelay/match_set.js
Open-ILS/web/js/ui/default/serial/list_item.js
Open-ILS/web/js/ui/default/serial/list_stream.js
Open-ILS/web/js/ui/default/serial/list_subscription.js
Open-ILS/web/js/ui/default/serial/print_routing_list_users.js
Open-ILS/web/js/ui/default/serial/subscription.js
Open-ILS/web/js/ui/default/serial/subscription/issuance.js
Open-ILS/web/js/ui/default/vandelay/vandelay.js

index 8c81447..156f74f 100644 (file)
@@ -236,6 +236,9 @@ if(!dojo._hasResource["openils.BibTemplate"]) {
         }
     });
 
+
+       // 2012-07-26 - Joseph Lewis, Is this needed anymore? It isn't referenced
+       // anywhere that I see.
     dojo._hasResource["openils.FeedTemplate"] = true;
     dojo.provide("openils.FeedTemplate");
     dojo.declare('openils.FeedTemplate', null, {
index d63576e..ca383d1 100644 (file)
@@ -14,6 +14,8 @@
  * ---------------------------------------------------------------------------
  */
 
+// 2012-07-26 Joseph Lewis -- Can or should this be replaced with dojo's builtin
+// functions? dojo.queryToObject?
 if(!dojo._hasResource["openils.CGI"]) {
 
     dojo._hasResource["openils.CGI"] = true;
index 54b4413..2e3eebe 100644 (file)
@@ -25,4 +25,4 @@ if(!dojo._hasResource["openils.DojoPatch"]) {
 
        
 
-        
\ No newline at end of file
+        
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/AuthorityControlSet.js b/Open-ILS/web/js/dojo/openils/tests/functions/AuthorityControlSet.js
new file mode 100644 (file)
index 0000000..0ef9e0d
--- /dev/null
@@ -0,0 +1,39 @@
+dojo.provide("openils.tests.functions.AuthorityControlSet");
+
+//Import in the code being tested.
+dojo.require("openils.AuthorityControlSet");
+
+doh.register("openils.tests.functions.AuthorityControlSet", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               
+               var acs = new openils.AuthorityControlSet();
+               doh.assertTrue(acs != null);
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/AutoSuggestStore.js b/Open-ILS/web/js/dojo/openils/tests/functions/AutoSuggestStore.js
new file mode 100644 (file)
index 0000000..4bfd1a9
--- /dev/null
@@ -0,0 +1,39 @@
+dojo.provide("openils.tests.functions.AutoSuggestStore");
+
+//Import in the code being tested.
+dojo.require("openils.AutoSuggestStore");
+
+doh.register("openils.tests.functions.AutoSuggestStore", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               
+               var acs = new openils.AutoSuggestStore();
+               doh.assertTrue(acs != null);
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js b/Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js
new file mode 100644 (file)
index 0000000..0b4d17e
--- /dev/null
@@ -0,0 +1,47 @@
+dojo.provide("openils.tests.functions.BibTemplate");
+
+//Import in the code being tested.
+dojo.require("openils.BibTemplate");
+
+doh.register("openils.tests.functions.BibTemplate", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+       },
+       
+       function test_default_constructor(doh) {
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+               doh.assertTrue(acs.org_unit == '-');
+               doh.assertTrue(acs.sync == false);
+               doh.assertTrue(acs.nodelay == false);
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/BibTemplate.js~
new file mode 100644 (file)
index 0000000..300464f
--- /dev/null
@@ -0,0 +1,46 @@
+dojo.provide("openils.tests.functions.BibTemplate");
+
+//Import in the code being tested.
+dojo.require("openils.BibTemplate");
+
+doh.register("openils.tests.functions.BibTemplate", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+       },
+       
+       function test_default_constructor(doh) {
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+               doh.assertTrue(acs.org_unit == '-');
+               doh.assertTrue(
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/CGI.js b/Open-ILS/web/js/dojo/openils/tests/functions/CGI.js
new file mode 100644 (file)
index 0000000..a626d15
--- /dev/null
@@ -0,0 +1,89 @@
+dojo.provide("openils.tests.functions.CGI");
+
+//Import in the code being tested.
+dojo.require("openils.CGI");
+
+doh.register("openils.tests.functions.CGI", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               //      description:
+               //              
+               
+               var acs = new openils.CGI();
+               doh.assertTrue(acs != null);
+       },
+       
+       function test_keys(doh) {
+               //      summary:
+               //              Tests the key fetching mechanism of CGI
+               //      description:
+               //              CGI.js will return the key part of the query string: ?key=value&
+               
+               var acs = new openils.CGI();
+               
+               // Being we're using dojo's test framework this should be true.
+               var dojoKeys = dojo.queryToObject(location.search.replace(/^\?/,""));
+               
+               
+               doh.assertTrue(acs.keys().length == Object.keys(dojoKeys).length);
+       },
+       
+       function test_params(doh) {
+               //      summary:
+               //              Tests all of the params in the CGI script
+               //      description:
+               //              CGI.js will return the values part of the query string
+               
+               var acs = new openils.CGI();
+               var keys = acs.keys();
+               
+               var dojoVersion = dojo.queryToObject(location.search.replace(/^\?/,""));
+               
+               for(var i = 0; i < keys.length; i++)
+               {
+                       doh.assertTrue(acs.param(keys[i]) == dojoVersion[keys[i]]);
+               }
+       },
+       
+       function test_toString(doh) {
+               //      summary:
+               //              Tests to make sure the toString actually reports something,
+               //              anything.
+               //      description:
+               //              The tostring method is supposed to only be for debugging, but
+               //              shoudld still pass.
+               
+               var acs = new openils.CGI();
+               
+               if(acs.keys().length == 0)
+                       doh.assertTrue(acs.toString() == "");
+               else
+                       doh.assertTrue(acs.toString() != "");
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function
+               //     description:
+               //          A simple test of the doh assertFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/CopyLocation.js b/Open-ILS/web/js/dojo/openils/tests/functions/CopyLocation.js
new file mode 100644 (file)
index 0000000..eaca6de
--- /dev/null
@@ -0,0 +1,42 @@
+dojo.provide("openils.tests.functions.CopyLocation");
+
+//Import in the code being tested.
+dojo.require("openils.CopyLocation");
+
+
+doh.register("openils.tests.functions.CopyLocation", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.CopyLocation.createStore != null);
+               doh.assertTrue(openils.CopyLocation.retrieve != null);
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js b/Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js
new file mode 100644 (file)
index 0000000..e852eec
--- /dev/null
@@ -0,0 +1,31 @@
+dojo.provide("openils.tests.functions.DojoPatch");
+
+//Import in the code being tested.
+dojo.require("openils.DojoPatch");
+
+doh.register("openils.tests.functions.DojoPatch", [
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/DojoPatch.js~
new file mode 100644 (file)
index 0000000..5bae5f6
--- /dev/null
@@ -0,0 +1,47 @@
+dojo.provide("openils.tests.functions.DojoPatch");
+
+//Import in the code being tested.
+dojo.require("openils.DojoPatch");
+
+doh.register("openils.tests.functions.DojoPatch", [
+
+       function test_constructor(doh) {
+               //      summary:
+               //              Simply tests that the Constructor works, and the file has loaded
+               
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+       },
+       
+       function test_default_constructor(doh) {
+               var acs = new openils.BibTemplate();
+               doh.assertTrue(acs != null);
+               doh.assertTrue(acs.org_unit == '-');
+               doh.assertTrue(acs.sync == false);
+               doh.assertTrue(acs.nodelay == false);
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/Event.js b/Open-ILS/web/js/dojo/openils/tests/functions/Event.js
new file mode 100644 (file)
index 0000000..3eeac45
--- /dev/null
@@ -0,0 +1,75 @@
+dojo.provide("openils.tests.functions.Event");
+
+// Import in the code being tested.
+dojo.require("openils.Event");
+
+
+doh.register("openils.tests.functions.Event", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.Event != null);
+       },
+       
+       function test_parse(doh) {
+               //      summary:
+               //              Tests the parse function of the event
+               //      description:
+               //              "parse" - Parses a proposed event object.  If this object is an
+       //              event, a new openils.Event is returned.  Otherwise,
+       //              null is returned 
+       
+       // Test a blank object
+       var obj = {};
+       doh.assertTrue(openils.Event.parse(obj) == null);
+       
+       // Test nothing
+       doh.assertTrue(openils.Event.parse() == null);
+       
+       // Test something that looks like an event
+       var evt = {
+               ilsevent:"Who is John Galt?",
+               textcode:"I would tell him to shrug."
+       };
+       
+       doh.assertTrue(openils.Event.parse(evt) != null);
+       },
+       
+       function test_parse_and_raise(doh) {
+               //      summary:
+               //              Tests the parse and raise function of Event
+               //      description:
+               //              If the provided object is a non-success event, the
+       //              event is thrown as an exception.
+               
+               
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/Event.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/Event.js~
new file mode 100644 (file)
index 0000000..f57cb19
--- /dev/null
@@ -0,0 +1,75 @@
+dojo.provide("openils.tests.functions.Event");
+
+//Import in the code being tested.
+dojo.require("openils.Event");
+
+
+doh.register("openils.tests.functions.Event", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.Event != null);
+       },
+       
+       function test_parse(doh) {
+               //      summary:
+               //              Tests the parse function of the event
+               //      description:
+               //              "parse" - Parses a proposed event object.  If this object is an
+       //              event, a new openils.Event is returned.  Otherwise,
+       //              null is returned 
+       
+       // Test a blank object
+       var obj = {};
+       doh.assertTrue(openils.Event.parse(obj) == null);
+       
+       // Test nothing
+       doh.assertTrue(openils.Event.parse() == null);
+       
+       // Test something that looks like an event
+       var evt = {
+               ilsevent:"Who is John Galt?",
+               textcode:"I would tell him to shrug."
+       };
+       
+       doh.assertTrue(openils.Event.parse(evt) != null);
+       },
+       
+       function test_parse_and_raise(doh) {
+               //      summary:
+               //              Tests the parse and raise function of Event
+               //      description:
+               //              If the provided object is a non-success event, the
+       //              event is thrown as an exception.
+               
+               
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js b/Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js
new file mode 100644 (file)
index 0000000..2f855fb
--- /dev/null
@@ -0,0 +1,346 @@
+dojo.provide("openils.tests.functions.FlattenerStore");
+
+// Import in the code being tested.
+dojo.require("openils.FlattenerStore");
+
+
+doh.register("openils.tests.functions.FlattenerStore", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.FlattenerStore != null);
+       },
+       
+       function test_FlattenerStoreError(doh){
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               doh.assertTrue(openils.FlattenerStoreError != null);
+       },
+       
+       function test_FlattenerStoreError_toString(doh){
+               //      summary:
+               //              Tests toString of FlattenerStoreError
+               //      description:
+               //              "openils.FlattenerStore: " + this.message
+               
+               var msg = "Walden Pond";
+               var fse = new FlattenerStoreError(msg);
+               
+               doh.assertTrue(fse.toString().indexOf(msg) != -1);
+       },
+       
+       function test_vars(doh) {
+               //      summary:
+               //              Simplest test, checks all vars have been overwritten on init.
+               //      description:
+               //
+               
+               var testobj = "JUST A TEST";
+                               
+               var init = {
+                   "fmClass": testobj,
+                   "mapClause": testobj,
+                   "sloClause": testobj,
+                   "limit": -100,
+                   "offset": -10,
+                   "baseSort": testobj,
+                   "defaultSort": testobj
+        };
+        
+        var fs = new openils.FlattenerStore(init);
+        
+               for(var x in Object.keys(fs))
+                       doh.assertTrue(fs[x] === init[x]);
+       },
+       
+       function test_getValue(doh)
+       {
+               //      summary:
+               //              Performs a getValue against a null
+               //      description:
+               //
+               
+               try     {
+                       
+               } catch(err) {
+                       
+               }
+       },
+       
+       
+
+        /* *** Begin dojo.data.api.Read methods *** */
+
+        "getValue": function(
+            /* object */ item,
+            /* string */ attribute,
+            /* anything */ defaultValue) {
+            //console.log("getValue(" + lazy(item) + ", " + attribute + ", " + defaultValue + ")")
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("getValue(): bad item " + item);
+            else if (typeof attribute != "string")
+                throw new FlattenerStoreError("getValue(): bad attribute");
+
+            var value = item[attribute];
+            return (typeof value == "undefined") ? defaultValue : value;
+        },
+
+        "getValues": function(/* object */ item, /* string */ attribute) {
+            //console.log("getValues(" + item + ", " + attribute + ")");
+            if (!this.isItem(item) || typeof attribute != "string")
+                throw new FlattenerStoreError("bad arguments");
+
+            var result = this.getValue(item, attribute, []);
+            return dojo.isArray(result) ? result : [result];
+        },
+
+        "getAttributes": function(/* object */ item) {
+            //console.log("getAttributes(" + item + ")");
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("getAttributes(): bad args");
+            else
+                return this._display_attributes();
+        },
+
+        "hasAttribute": function(/* object */ item, /* string */ attribute) {
+            //console.log("hasAttribute(" + item + ", " + attribute + ")");
+            if (!this.isItem(item) || typeof attribute != "string") {
+                throw new FlattenerStoreError("hasAttribute(): bad args");
+            } else {
+                return dojo.indexOf(this._display_attributes(), attribute) > -1;
+            }
+        },
+
+        "containsValue": function(
+            /* object */ item,
+            /* string */ attribute,
+            /* anything */ value) {
+            //console.log("containsValue(" + item + ", " + attribute + ", " + value + ")");
+            if (!this.isItem(item) || typeof attribute != "string")
+                throw new FlattenerStoreError("bad data");
+            else
+                return (
+                    dojo.indexOf(this.getValues(item, attribute), value) >= -1
+                );
+        },
+        
+        
+        function test_isItem(doh)
+        {
+                       //      summary:
+                       //              Tests to see if the thing is an item or not.
+                       //      description:
+                       //              
+                       
+                       var testobj = "JUST A TEST";
+                               
+                       var init = {
+                               "fmClass": testobj,
+                               "mapClause": ["field"],
+                               "sloClause": testobj,
+                               "limit": -100,
+                               "offset": -10,
+                               "baseSort": testobj,
+                               "defaultSort": testobj
+                   };
+                   
+                   var fs = new openils.FlattenerStore(init);
+        
+               doh.assertFalse(fs.isItem());
+               doh.assertFalse(fs.isItem(1));
+               doh.assertFalse(fs.isItem({}));
+               doh.assertTrue(fs.isItem({"field":"value"}));
+               },
+               
+               
+               
+               function test_isItemLoaded(doh)
+        {
+                       //      summary:
+                       //              Tests to see if the thing is an item or not.
+                       //      description:
+                       //              
+                       
+                       var testobj = "JUST A TEST";
+                               
+                       var init = {
+                               "fmClass": testobj,
+                               "mapClause": ["field"],
+                               "sloClause": testobj,
+                               "limit": -100,
+                               "offset": -10,
+                               "baseSort": testobj,
+                               "defaultSort": testobj
+                   };
+                   
+                   var fs = new openils.FlattenerStore(init);
+               var obj = {"field":"value"};
+               //fs.fmIdentifier = "field";
+               console.log("Identifier is: " + fs.fmIdentifier);
+               
+               doh.assertFalse(fs.isItemLoaded(null));
+               doh.assertFalse(fs.isItemLoaded(obj));
+
+               fs._current_items = [obj];
+               doh.assertTrue(fs.isItemLoaded(obj));
+               },
+               
+               
+
+        "loadItem": function(/* object */ keywordArgs) {
+            if (!keywordArgs.force && this.isItemLoaded(keywordArgs.item))
+                return;
+
+            keywordArgs.identity = this.getIdentity(keywordArgs.item);
+            return this.fetchItemByIdentity(keywordArgs);
+        },
+
+        /* *** Begin dojo.data.api.Identity methods *** */
+
+        "getIdentity": function(/* object */ item) {
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("not an item");
+
+            return item[this.fmIdentifier];
+        },
+
+        "getIdentityAttributes": function(/* object */ item) {
+            // console.log("getIdentityAttributes(" + item + ")");
+            return [this.fmIdentifier];
+        },
+
+        
+
+        /* dojo.data.api.Write - only very partially implemented, because
+         * for FlattenerGrid, the intended client of this store, we don't
+         * need most of the methods. */
+
+        "deleteItem": function(item) {
+            //console.log("deleteItem()");
+
+            var identity = this.getIdentity(item);
+            delete this._current_items[identity];   /* safe even if missing */
+
+            this.onDelete(item);
+        },
+
+        "setValue": function(item, attribute, value) {
+            /* Silently do nothing when setValue()'s caller wants to change
+             * the identifier.  They must be confused anyway. */
+            if (attribute == this.fmIdentifier)
+                return;
+
+            var old_value = dojo.clone(item[attribute]);
+
+            item[attribute] = dojo.clone(value);
+            this.onSet(item, attribute, old_value, value);
+        },
+
+        "setValues": function(item, attribute, values) {
+            console.warn("[unimplemented] setValues()");    /* unneeded */
+        },
+
+        "newItem": function(keywordArgs, parentInfo) {
+            console.warn("[unimplemented] newItem()");    /* unneeded */
+        },
+
+        "unsetAttribute": function() {
+            console.warn("[unimplemented] unsetAttribute()");   /* unneeded */
+        },
+
+        "save": function() {
+            console.warn("[unimplemented] save()"); /* unneeded */
+        },
+
+        "revert": function() {
+            console.warn("[unimplemented] revert()");   /* unneeded */
+        },
+
+        "isDirty": function() { /* I /think/ this will be ok for our purposes */
+            console.info("[stub] isDirty() will always return false");
+
+            return false;
+        },
+
+        /* dojo.data.api.Notification - Keep these no-op methods because
+         * clients will dojo.connect() to them.  */
+
+        "onNew" : function(item) { /* no-op */ },
+        "onDelete" : function(item) { /* no-op */ },
+        "onSet": function(item, attr, oldval, newval) { /* no-op */ },
+
+        /* *** Classes implementing any Dojo APIs do this to list which
+         *     APIs they're implementing. *** */
+
+        "getFeatures": function() {
+            return {
+                "dojo.data.api.Read": true,
+                "dojo.data.api.Identity": true,
+                "dojo.data.api.Notification": true,
+                "dojo.data.api.Write": true     /* well, only partly */
+            };
+        }
+    });
+}
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/FlattenerStore.js~
new file mode 100644 (file)
index 0000000..2b3fe39
--- /dev/null
@@ -0,0 +1,412 @@
+dojo.provide("openils.tests.functions.FlattenerStore");
+
+// Import in the code being tested.
+dojo.require("openils.FlattenerStore");
+
+
+doh.register("openils.tests.functions.FlattenerStore", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.FlattenerStore != null);
+       },
+       
+       function test_FlattenerStoreError(doh){
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               doh.assertTrue(openils.FlattenerStoreError != null);
+       },
+       
+       function test_FlattenerStoreError_toString(doh){
+               //      summary:
+               //              Tests toString of FlattenerStoreError
+               //      description:
+               //              "openils.FlattenerStore: " + this.message
+               
+               var msg = "Walden Pond";
+               var fse = new FlattenerStoreError(msg);
+               
+               doh.assertTrue(fse.toString().indexOf(msg) != -1);
+       },
+       
+       function test_vars(doh) {
+               //      summary:
+               //              Simplest test, checks all vars have been overwritten on init.
+               //      description:
+               //
+               
+               var testobj = "JUST A TEST";
+                               
+               var init = {
+                   "fmClass": testobj,
+                   "mapClause": testobj,
+                   "sloClause": testobj,
+                   "limit": -100,
+                   "offset": -10,
+                   "baseSort": testobj,
+                   "defaultSort": testobj
+        };
+        
+        var fs = new openils.FlattenerStore(init);
+        
+               for(var x in Object.keys(fs))
+                       doh.assertTrue(fs[x] === init[x]);
+       },
+       
+       function test_getValue(doh)
+       {
+               //      summary:
+               //              Performs a getValue against a null
+               //      description:
+               //
+               
+               try     {
+                       
+               } catch(err) {
+                       
+               }
+       },
+       
+       
+
+        /* *** Begin dojo.data.api.Read methods *** */
+
+        "getValue": function(
+            /* object */ item,
+            /* string */ attribute,
+            /* anything */ defaultValue) {
+            //console.log("getValue(" + lazy(item) + ", " + attribute + ", " + defaultValue + ")")
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("getValue(): bad item " + item);
+            else if (typeof attribute != "string")
+                throw new FlattenerStoreError("getValue(): bad attribute");
+
+            var value = item[attribute];
+            return (typeof value == "undefined") ? defaultValue : value;
+        },
+
+        "getValues": function(/* object */ item, /* string */ attribute) {
+            //console.log("getValues(" + item + ", " + attribute + ")");
+            if (!this.isItem(item) || typeof attribute != "string")
+                throw new FlattenerStoreError("bad arguments");
+
+            var result = this.getValue(item, attribute, []);
+            return dojo.isArray(result) ? result : [result];
+        },
+
+        "getAttributes": function(/* object */ item) {
+            //console.log("getAttributes(" + item + ")");
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("getAttributes(): bad args");
+            else
+                return this._display_attributes();
+        },
+
+        "hasAttribute": function(/* object */ item, /* string */ attribute) {
+            //console.log("hasAttribute(" + item + ", " + attribute + ")");
+            if (!this.isItem(item) || typeof attribute != "string") {
+                throw new FlattenerStoreError("hasAttribute(): bad args");
+            } else {
+                return dojo.indexOf(this._display_attributes(), attribute) > -1;
+            }
+        },
+
+        "containsValue": function(
+            /* object */ item,
+            /* string */ attribute,
+            /* anything */ value) {
+            //console.log("containsValue(" + item + ", " + attribute + ", " + value + ")");
+            if (!this.isItem(item) || typeof attribute != "string")
+                throw new FlattenerStoreError("bad data");
+            else
+                return (
+                    dojo.indexOf(this.getValues(item, attribute), value) >= -1
+                );
+        },
+        
+        
+        function test_isItem(doh)
+        {
+                       //      summary:
+                       //              Tests to see if the thing is an item or not.
+                       //      description:
+                       //              
+                       
+                       var testobj = "JUST A TEST";
+                               
+                       var init = {
+                               "fmClass": testobj,
+                               "mapClause": ["field"],
+                               "sloClause": testobj,
+                               "limit": -100,
+                               "offset": -10,
+                               "baseSort": testobj,
+                               "defaultSort": testobj
+                   };
+                   
+                   var fs = new openils.FlattenerStore(init);
+        
+               doh.assertFalse(fs.isItem());
+               doh.assertFalse(fs.isItem(1));
+               doh.assertFalse(fs.isItem({}));
+               doh.assertTrue(fs.isItem({"field":"value"}));
+               },
+               
+               
+               
+               function test_isItemLoaded(doh)
+        {
+                       //      summary:
+                       //              Tests to see if the thing is an item or not.
+                       //      description:
+                       //              
+                       
+                       var testobj = "JUST A TEST";
+                               
+                       var init = {
+                               "fmClass": testobj,
+                               "mapClause": ["field"],
+                               "sloClause": testobj,
+                               "limit": -100,
+                               "offset": -10,
+                               "baseSort": testobj,
+                               "defaultSort": testobj
+                   };
+                   
+                   var fs = new openils.FlattenerStore(init);
+               var obj = {"field":"value"};
+               //fs.fmIdentifier = "field";
+               console.log(fs.fmIdentifier);
+               
+               doh.assertFalse(fs.isItemLoaded(null));
+               doh.assertFalse(fs.isItemLoaded(obj));
+
+               fs._current_items = [obj];
+               doh.assertTrue(fs.isItemLoaded(obj));
+               },
+               
+               
+
+        "loadItem": function(/* object */ keywordArgs) {
+            if (!keywordArgs.force && this.isItemLoaded(keywordArgs.item))
+                return;
+
+            keywordArgs.identity = this.getIdentity(keywordArgs.item);
+            return this.fetchItemByIdentity(keywordArgs);
+        },
+
+        /* *** Begin dojo.data.api.Identity methods *** */
+
+        "getIdentity": function(/* object */ item) {
+            if (!this.isItem(item))
+                throw new FlattenerStoreError("not an item");
+
+            return item[this.fmIdentifier];
+        },
+
+        "getIdentityAttributes": function(/* object */ item) {
+            // console.log("getIdentityAttributes(" + item + ")");
+            return [this.fmIdentifier];
+        },
+
+        "fetchItemByIdentity": function(/* object */ keywordArgs) {
+            var callback_scope = keywordArgs.scope || dojo.global;
+            var identity = keywordArgs.identity;
+
+            if (typeof identity == "undefined")
+                throw new FlattenerStoreError(
+                    "fetchItemByIdentity() needs identity in keywordArgs"
+                );
+
+            /* First of force's two implications:
+             * fetch even if already loaded. */
+            if (this._current_items[identity] && !keywordArgs.force) {
+                keywordArgs.onItem.call(
+                    callback_scope, this._current_items[identity]
+                );
+
+                return;
+            }
+
+            var post_params = this._prepare_flattener_params(keywordArgs);
+
+            var process_fetch_one = dojo.hitch(
+                this, function(obj, when) {
+                    if (when < this._last_fetch) /* Stale response. Discard. */
+                        return;
+
+                    if (dojo.isArray(obj)) {
+                        if (obj.length <= 1) {
+                            obj = obj.pop() || null;    /* safe enough */
+                            /* Second of force's two implications: call setValue
+                             * ourselves.  Makes a DataGrid update. */
+                            if (keywordArgs.force && obj &&
+                                (origitem = this._current_items[identity])) {
+                                for (var prop in origitem)
+                                    this.setValue(origitem, prop, obj[prop]);
+                            }
+                            if (keywordArgs.onItem)
+                                keywordArgs.onItem.call(callback_scope, obj);
+                        } else {
+                            var e = new FlattenerStoreError("Too many results");
+                            if (keywordArgs.onError)
+                                keywordArgs.onError.call(callback_scope, e);
+                            else
+                                throw e;
+                        }
+                    } else {
+                        var e = new FlattenerStoreError("Bad response");
+                        if (keywordArgs.onError)
+                            keywordArgs.onError.call(callback_scope, e);
+                        else
+                            throw e;
+                    }
+                }
+            );
+
+            var fetch_time = this._last_fetch = (new Date().getTime());
+
+            dojo.xhrPost({
+                "url": this._flattener_url,
+                "content": post_params,
+                "handleAs": "json",
+                "sync": false,
+                "preventCache": true,
+                "headers": {"Accept": "application/json"},
+                "load": function(obj){ process_fetch_one(obj, fetch_time); }
+            });
+        },
+
+        /* dojo.data.api.Write - only very partially implemented, because
+         * for FlattenerGrid, the intended client of this store, we don't
+         * need most of the methods. */
+
+        "deleteItem": function(item) {
+            //console.log("deleteItem()");
+
+            var identity = this.getIdentity(item);
+            delete this._current_items[identity];   /* safe even if missing */
+
+            this.onDelete(item);
+        },
+
+        "setValue": function(item, attribute, value) {
+            /* Silently do nothing when setValue()'s caller wants to change
+             * the identifier.  They must be confused anyway. */
+            if (attribute == this.fmIdentifier)
+                return;
+
+            var old_value = dojo.clone(item[attribute]);
+
+            item[attribute] = dojo.clone(value);
+            this.onSet(item, attribute, old_value, value);
+        },
+
+        "setValues": function(item, attribute, values) {
+            console.warn("[unimplemented] setValues()");    /* unneeded */
+        },
+
+        "newItem": function(keywordArgs, parentInfo) {
+            console.warn("[unimplemented] newItem()");    /* unneeded */
+        },
+
+        "unsetAttribute": function() {
+            console.warn("[unimplemented] unsetAttribute()");   /* unneeded */
+        },
+
+        "save": function() {
+            console.warn("[unimplemented] save()"); /* unneeded */
+        },
+
+        "revert": function() {
+            console.warn("[unimplemented] revert()");   /* unneeded */
+        },
+
+        "isDirty": function() { /* I /think/ this will be ok for our purposes */
+            console.info("[stub] isDirty() will always return false");
+
+            return false;
+        },
+
+        /* dojo.data.api.Notification - Keep these no-op methods because
+         * clients will dojo.connect() to them.  */
+
+        "onNew" : function(item) { /* no-op */ },
+        "onDelete" : function(item) { /* no-op */ },
+        "onSet": function(item, attr, oldval, newval) { /* no-op */ },
+
+        /* *** Classes implementing any Dojo APIs do this to list which
+         *     APIs they're implementing. *** */
+
+        "getFeatures": function() {
+            return {
+                "dojo.data.api.Read": true,
+                "dojo.data.api.Identity": true,
+                "dojo.data.api.Notification": true,
+                "dojo.data.api.Write": true     /* well, only partly */
+            };
+        }
+    });
+}
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/Util.js b/Open-ILS/web/js/dojo/openils/tests/functions/Util.js
new file mode 100644 (file)
index 0000000..f7eda6b
--- /dev/null
@@ -0,0 +1,482 @@
+dojo.provide("openils.tests.functions.Util");
+
+//Import in the code being tested.
+dojo.require("openils.Util");
+
+doh.register("openils.tests.functions.Util", [
+
+function test_timestampAsDateObj(doh) {
+       //     summary:
+       //          Tests the ability to convert timestamps to date objects.
+       //     description:
+       //          This functions 
+       var res = openils.Util.timeStampAsDateObj("2012-06-04T12:01:02-0700");
+       var res2 = openils.Util.timeStampAsDateObj("2012-06-04T12:01:02-0800");
+
+       doh.assertTrue(res.getFullYear() == 2012);
+       doh.assertTrue(res.getMonth() == 5);
+       doh.assertTrue(res.getDate() == 4);
+       //Will fail depending on where you are, as JS translates to local
+       //time.
+       //doh.assertTrue(res.getHours() == 13);
+       doh.assertTrue(res.getMinutes() == 1);
+       doh.assertTrue(res.getSeconds() == 2);
+       doh.assertTrue(res.getHours() - res2.getHours() == -1);
+},
+
+function test_userFullName(doh) {
+       /** The full name fields
+      
+      openils.Util._userFullNameFields = [
+        "prefix", "first_given_name", "second_given_name",
+        "family_name", "suffix", "alias", "usrname"
+               ];**/
+       
+       var test_user = {
+               prefix: function(){return "Mr.";},
+               first_given_name: function(){return "DENT";},
+               second_given_name: function(){return "Arthur";},
+               family_name: function(){return "dent";},
+               suffix: function(){return "jr";},
+               alias: function(){return "The Sandwich Maker";},
+               usrname: function(){return "dentarthurdent";}
+       };
+
+       var names = openils.Util.userFullName(test_user);
+       
+       console.log(names);
+
+       doh.assertTrue(names.indexOf(test_user.prefix()) != -1);
+       doh.assertTrue(names.indexOf(test_user.first_given_name()) != -1);
+       doh.assertTrue(names.indexOf(test_user.second_given_name()) != -1);
+       doh.assertTrue(names.indexOf(test_user.family_name()) != -1);
+       doh.assertTrue(names.indexOf(test_user.suffix()) != -1);
+       doh.assertTrue(names.indexOf(test_user.alias()) != -1);
+       doh.assertTrue(names.indexOf(test_user.usrname()) != -1);
+       
+       doh.assertTrue(names.length == Object.keys(test_user).length);
+
+},
+
+
+function test_userFullNameHash(doh) {
+
+       var test_user = {
+               prefix: function(){return "Mr.";},
+               first_given_name: function(){return "DENT";},
+               second_given_name: function(){return "Arthur";},
+               family_name: function(){return "dent";},
+               suffix: function(){return "jr";},
+               alias: function(){return "The Sandwich Maker";},
+               usrname: function(){return "dentarthurdent";}
+       };
+
+       var names = openils.Util.userFullNameHash(test_user);
+               
+       console.log(names);
+
+       doh.assertTrue(names.prefix == test_user.prefix());
+       doh.assertTrue(names.first_given_name == test_user.first_given_name());
+       doh.assertTrue(names.second_given_name == test_user.second_given_name());
+       doh.assertTrue(names.family_name == test_user.family_name());
+       doh.assertTrue(names.suffix == test_user.suffix());
+       doh.assertTrue(names.alias == test_user.alias());
+       doh.assertTrue(names.usrname == test_user.usrname());
+
+
+       doh.assertTrue(Object.keys(names).length == Object.keys(test_user).length);
+
+},
+
+
+function test_addOnLoad(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+
+function test_arrayContains(doh) {
+       array = ["hello", 1, 1.0, false, 'a', [], {key: 'value'}, ['nonemptyset']];
+
+       doh.assertTrue(openils.Util.arrayContains(array, "hello"));
+       doh.assertTrue(openils.Util.arrayContains(array, 1));
+       doh.assertTrue(openils.Util.arrayContains(array, 1.0));
+       doh.assertTrue(openils.Util.arrayContains(array, false));
+       doh.assertTrue(openils.Util.arrayContains(array, 'a'));
+       doh.assertTrue(openils.Util.arrayContains(array, []));
+       //doh.assertTrue(openils.Util.arrayContains(array, {key: 'value'}));
+       //doh.assertTrue(openils.Util.arrayContains(array, ['nonemptyset']));
+
+       doh.assertFalse(openils.Util.arrayContains(array, "world"));
+},
+
+function test_selectorValue(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+
+function test_getKeyCode(doh) {
+       var evt1 = {charCode:'a'};
+       var evt2 = {which:'a'};
+       var evt3 = {keyCode:'a'};
+       
+       doh.assertTrue(openils.Util.getCharCode(null) == -1);
+       doh.assertTrue(openils.Util.getCharCode(evt1) == 'a');
+       doh.assertTrue(openils.Util.getCharCode(evt2) == 'a');
+       doh.assertTrue(openils.Util.getCharCode(evt3) == 'a');
+},
+
+
+function test_registerEnterHandler(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+
+function test_readResponse(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+
+function test_addCSSClass(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+function test_removeCSSClass(doh) {
+       //TODO IMPLEMENT ME
+       //doh.assertTrue(false);
+},
+
+
+function test_objectSort(doh) {
+       var obj_list = [{
+               id: function(){return 2;},
+               name: function(){return "y";}
+       }, {
+               id: function(){return 1;},
+               name: function(){return "z";}
+       }, {
+               id: function(){return 3;},
+               name: function(){return "x";}
+       }];
+
+       var i_sorted = openils.Util.objectSort(obj_list, "id");
+       var n_sorted = openils.Util.objectSort(obj_list, "name");
+       var i_sorted_default = openils.Util.objectSort(obj_list);
+       var non_sortable = openils.Util.objectSort("Ian Malcolm with the candlestck on Isla Nubar!");
+
+       doh.assertTrue(i_sorted.length == obj_list.length);
+       doh.assertTrue(n_sorted.length == obj_list.length);
+       doh.assertTrue(i_sorted_default.length == obj_list.length);
+       doh.assertTrue(non_sortable.length == 0);
+       doh.assertTrue(non_sortable == []);
+
+       doh.assertTrue(i_sorted[1]['id']() > i_sorted[2]['id']());
+       doh.assertTrue(i_sorted[2]['id']() > i_sorted[3]['id']());
+
+       doh.assertTrue(n_sorted[1]['name']() > n_sorted[2]['name']());
+       doh.assertTrue(n_sorted[2]['name']() > n_sorted[3]['name']());
+
+       doh.assertTrue(i_sorted_default[1]['id']() > i_sorted_default[2]['id']());
+       doh.assertTrue(i_sorted_default[2]['id']() > i_sorted_default[3]['id']());
+
+},
+
+function test_isTrue(doh) {
+       //return (val && val != '0' && !(val+'').match(/^f$/i));
+       doh.assertFalse(null);
+       doh.assertFalse('0');
+       doh.assertFalse('f');
+       doh.assertTrue('false');
+       doh.assertTrue('true');
+       doh.assertTrue('t');
+       doh.assertTrue(1);
+       doh.assertTrue('T');
+       doh.assertTrue('F');
+       doh.assertFalse([]);
+},
+
+
+function test_mapList(doh) {
+       var obj_list = [{
+               id: '2',
+               name: "y"
+       }, {
+               id: '1',
+               name: "z"
+       }, {
+               id: '3',
+               name: "x"
+       }];
+       var obj_func_list = [{
+               id: function () {
+                       return '2';
+               },
+               name: "y"
+       }, {
+               id: function () {
+                       return '1';
+               },
+               name: "z"
+       }, {
+               id: function () {
+                       return '3';
+               },
+               name: "x"
+       }];
+
+
+       var id_mapping = openils.Util.mapList(obj_list, 'id', false);
+       var id_func_mapping = openils.Util.mapList(obj_func_list, 'id', true);
+       console.log(id_mapping);
+       console.log(id_func_mapping);
+       
+       doh.assertTrue(Object.keys(id_mapping).length == obj_list.length);
+       doh.assertTrue(Object.keys(id_func_mapping).length == obj_list.length);
+       
+       /**doh.assertTrue(id_mapping['1'][name] == "z");
+       doh.assertTrue(id_mapping['2'][name] == "y");
+       doh.assertTrue(id_mapping['3'][name] == "x");
+
+
+       doh.assertTrue(id_func_mapping['1'][name] == "z");
+       doh.assertTrue(id_func_mapping['2'][name] == "y");
+       doh.assertTrue(id_func_mapping['3'][name] == "x");
+
+       **/
+},
+
+       function test_trimString(doh) {
+               doh.assertTrue(openils.Util.trimString("  Hello world  ") == "Hello world");
+               doh.assertTrue(openils.Util.trimString("\t\tHello world  ") == "Hello world");
+               doh.assertTrue(openils.Util.trimString("\f\n\r\t\vHello world") == "Hello world");
+       },
+
+       function test_intervalToSeconds(doh) {
+               doh.assertTrue(openils.Util.intervalToSeconds("3 days 6 hours") == 280800);
+               doh.assertTrue(openils.Util.intervalToSeconds("1 year 2 days") == 31708800);
+       },
+
+       function test_hide(doh) {
+               //doh.assertTrue(false);
+               //TODO fixme
+               //openils.Util.hide = function(node) {
+       },
+
+
+
+       function test_show(doh) {
+               /**openils.Util.show = function(node, displayType) {
+                   if(typeof node == 'string')
+                       node = dojo.byId(node);
+                   displayType = displayType || 'block';
+                   dojo.style(node, 'display', displayType);
+                   dojo.style(node, 'visibility', 'visible');
+               };**/
+               //doh.assertTrue(false);
+       },
+
+
+       function test_toggle(doh) {
+               /**
+    openils.Util.toggle = function(node, displayType) {
+        if(typeof node == 'string')
+            node = dojo.byId(node);
+        if(dojo.style(node, 'display') == 'none')
+            openils.Util.show(node, displayType);
+        else
+            openils.Util.hide(node);
+    };**/
+               //doh.assertTrue(false);
+       },
+
+
+       function test_appendClear(doh) {
+               /**
+
+               openils.Util.appendClear = function(node, child) {
+                   if(typeof node == 'string')
+                       node = dojo.byId(node);
+                   while(node.childNodes[0])
+                       node.removeChild(node.childNodes[0]);
+                   node.appendChild(child);
+               };**/
+               //doh.assertTrue(false);
+       },
+
+       function test_playAudioUrl(doh) {
+               /**
+                * Plays a sound file via URL.  Only works with browsers
+                * that support HTML 5 <audio> element.  E.g. Firefox 3.5
+                */
+               /**openils.Util.playAudioUrl = function(urlString) {
+        if(!urlString) return;
+        var audio = document.createElement('audio');
+        audio.setAttribute('src', urlString);
+        audio.setAttribute('autoplay', 'true');
+        document.body.appendChild(audio);
+        document.body.removeChild(audio);
+    }**/
+               //doh.assertTrue(false);
+       },
+
+       function test_objectProperties(doh) {
+               var object = {
+                       dagny: "taggart",
+                       john: "galt"
+               };
+
+               var props = openils.Util.objectProperties(object);
+
+               doh.assertTrue(props.length == 2);
+       },
+
+       function test_objectValues(doh) {
+               var object = {
+                       dagny: "taggart",
+                       john: "galt"
+               };
+
+               var vals = openils.Util.objectValues(object);
+
+               doh.assertTrue(vals.length == 2);
+
+               for (var i in vals)
+                       doh.assertTrue(vals[i] == "taggart" || vals[i] == "galt");
+
+       },
+
+       function test_uniqueElements(doh) {
+               var object = {
+                       dagny: "taggart",
+                       james: "taggart"
+               };
+               var nodup = {
+                       ford: "prefect",
+                       marvin: "the paranoid"
+               };
+
+
+               doh.assertTrue(openils.Util.uniqueElements(object).length == 1);
+               doh.assertTrue(openils.Util.uniqueElements(nodup).length == 2);
+       },
+
+       function test_uniqueObjects(doh) {
+               var hc = {
+                       make: function(){return "Honda";},
+                       model: function(){return "Civic";}
+               };
+               var bc = {
+                       make: function(){return "Buick";},
+                       model: function(){return "Century";}
+               };
+               var gc = {
+                       make: function(){return "Google";},
+                       model: function(){return "Car";}
+               };
+               var hp = {
+                       make: function(){return "Honda";},
+                       model: function(){return "Pilot";}
+               };
+               var bj = {
+                       make: function(){return "Bantam";},
+                       model: function(){return "Jeep";}
+               };
+               var jj = {
+                       make: function(){return "Jeep";},
+                       model: function(){return "Jeep";}
+               };
+
+               var objectList = [hc, bc, gc, hp, bj, jj];
+
+               var unique_makes = openils.Util.uniqueObjects(objectList, "make");
+               var unique_models = openils.Util.uniqueObjects(objectList, "model");
+
+               doh.assertTrue(unique_makes.length == objectList.length - 1);
+               doh.assertTrue(unique_models.length == objectList.length - 1);
+
+       },
+
+       function test_hilightNode(doh) {
+               /**
+     * Highlight instances of each pattern in the given DOM node
+     * Inspired by the jquery plugin
+     * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
+     
+    openils.Util.hilightNode = function(node, patterns, args) {
+
+        args = args ||{};
+        var hclass = args.classname || 'oils-highlight';
+
+        function _hilightNode(node, pat) {
+
+            if(node.nodeType == 3) { 
+
+                pat = pat.toUpperCase();
+                var text = node.data.toUpperCase();
+                var pos = -1;
+
+                // find each instance of pat in the current node
+                while( (pos =  text.indexOf(pat, pos + 1)) >= 0 ) {
+
+                    var wrapper = dojo.create('span', {className : hclass});
+                    var midnode = node.splitText(pos);
+                    midnode.splitText(pat.length);
+                    wrapper.appendChild(midnode.cloneNode(true));
+                    midnode.parentNode.replaceChild(wrapper, midnode);
+                }
+
+            } else if(node.nodeType == 1 && node.childNodes[0]) {
+
+                // not a text node?  have you checked the children?
+                dojo.forEach(
+                    node.childNodes,
+                    function(child) { _hilightNode(child, pat); }
+                );
+            }
+        }
+
+        // descend the tree for each pattern, since nodes are changed during highlighting
+        dojo.forEach(patterns, function(pat) { _hilightNode(node, pat); });
+    };*/
+
+               //doh.assertTrue(false);
+       },
+
+       function test_printHTMLString(doh) {
+               var def = new doh.Deferred();
+       
+               openils.Util.printHtmlString("<h1>Hello, world!</h1>", function () {
+                       def.callback(true);
+               });
+               
+               return def;
+       },
+
+       function test_alwaysFalse(doh) {
+               //     summary:
+               //          A simple test of the alwaysFalse function
+               //     description:
+               //          A simple test of the alwaysFalse function
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_isTrue(doh) {
+               //     summary:
+               //          A simple test of the isTrue function
+               //     description:
+               //          A simple test of the isTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/editor.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/editor.js~
new file mode 100644 (file)
index 0000000..eaca6de
--- /dev/null
@@ -0,0 +1,42 @@
+dojo.provide("openils.tests.functions.CopyLocation");
+
+//Import in the code being tested.
+dojo.require("openils.CopyLocation");
+
+
+doh.register("openils.tests.functions.CopyLocation", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.CopyLocation.createStore != null);
+               doh.assertTrue(openils.CopyLocation.retrieve != null);
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/editors.js b/Open-ILS/web/js/dojo/openils/tests/functions/editors.js
new file mode 100644 (file)
index 0000000..76cb5df
--- /dev/null
@@ -0,0 +1,45 @@
+dojo.provide("openils.tests.functions.editors");
+
+//Import in the code being tested.
+dojo.require("openils.editors");
+
+
+doh.register("openils.tests.functions.editors", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.editors.NumberSpinner != null);
+               doh.assertTrue(openils.editors.FundSelectEditor != null);
+               doh.assertTrue(openils.editors.ProviderSelectEditor != null);
+               doh.assertTrue(openils.editors.OrgUnitSelectEditor != null);
+               doh.assertTrue(openils.editors.CopyLocationSelectEditor != null);
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/functions/editors.js~ b/Open-ILS/web/js/dojo/openils/tests/functions/editors.js~
new file mode 100644 (file)
index 0000000..e1c5b42
--- /dev/null
@@ -0,0 +1,42 @@
+dojo.provide("openils.tests.functions.editors");
+
+//Import in the code being tested.
+dojo.require("openils.editors");
+
+
+doh.register("openils.tests.functions.editors", [
+
+       function test_loaded(doh) {
+               //      summary:
+               //              Simplest test, fail on not loading.
+               //      description:
+               //              only checks that the functions are there, not that they work.
+               
+               doh.assertTrue(openils.CopyLocation.createStore != null);
+               doh.assertTrue(openils.CopyLocation.retrieve != null);
+       },
+
+       function test_assertFalse(doh) {
+               //     summary:
+               //          A simple test of the doh assertFalse function.
+               //     description:
+               //          A simple test of the doh assertFalse function.
+               doh.assertFalse(false);
+               doh.assertFalse(!true);
+               doh.assertFalse(null);
+               doh.assertFalse(0);
+       },
+
+       function test_assertTrue(doh) {
+               //     summary:
+               //          A simple test of the assertTrue function.
+               //     description:
+               //          A simple test of the assertTrue function with multiple permutations of 
+               //          calling it.
+               doh.assertTrue(true);
+               doh.assertTrue(!false);
+               doh.assertTrue({});
+               doh.assertTrue(!null);
+               doh.assertTrue(!0);
+       }
+]);
diff --git a/Open-ILS/web/js/dojo/openils/tests/module.js b/Open-ILS/web/js/dojo/openils/tests/module.js
new file mode 100644 (file)
index 0000000..dd3d0d6
--- /dev/null
@@ -0,0 +1,22 @@
+dojo.provide("openils.tests.module");
+//This file loads in all the test definitions.  
+
+try{
+       //Load in the functions tests.
+       dojo.require("openils.tests.functions.Util");
+       dojo.require("openils.tests.functions.CGI");
+       dojo.require("openils.tests.functions.Util");
+       dojo.require("openils.tests.functions.BibTemplate");
+       dojo.require("openils.tests.functions.editors");
+       dojo.require("openils.tests.functions.AutoSuggestStore");
+       dojo.require("openils.tests.functions.AuthorityControlSet");
+       dojo.require("openils.tests.functions.Event");
+       dojo.require("openils.tests.functions.CopyLocation");
+       dojo.require("openils.tests.functions.FlattenerStore");
+       dojo.require("openils.tests.functions.DojoPatch");
+
+     //Load in the widget tests.
+     //dojo.require("demo.doh.tests.widgets.DemoWidget");
+}catch(e){
+     doh.debug(e);
+}
diff --git a/Open-ILS/web/js/dojo/openils/tests/module.js~ b/Open-ILS/web/js/dojo/openils/tests/module.js~
new file mode 100644 (file)
index 0000000..e9976cf
--- /dev/null
@@ -0,0 +1,24 @@
+dojo.provide("openils.tests.module");");
+dojo.require("openils.tests.functions//This file loads in all the test definitions.  ");
+dojo.require("openils.tests.functions");
+dojo.require("openils.tests.functionstry{");
+dojo.require("openils.tests.functions     //Load in the functions tests.");
+dojo.require("openils.tests.functions     dojo.require("openils.tests.functions.Util");");
+dojo.require("openils.tests.functions     dojo.require("openils.");
+dojo.require("openils.tests.functions     ");
+dojo.require("openils.tests.functionsCGI");
+dojo.require("openils.tests.functionsUtil");
+dojo.require("openils.tests.functionsBibTemplate");
+dojo.require("openils.tests.functionseditors");
+dojo.require("openils.tests.functionsAutoSuggestStore");
+dojo.require("openils.tests.functionsAuthorityControlSet");
+dojo.require("openils.tests.functionsEvent");
+dojo.require("openils.tests.functionsCopyLocation");
+dojo.require("openils.tests.functionsFlattenerStore");
+dojo.require("openils.tests.functionsDojoPatch");
+dojo.require("openils.tests.functions");
+dojo.require("openils.tests.functions     //Load in the widget tests.");
+dojo.require("openils.tests.functions     //dojo.require("demo.doh.tests.widgets.DemoWidget");");
+dojo.require("openils.tests.functions}catch(e){");
+dojo.require("openils.tests.functions     doh.debug(e);");
+dojo.require("openils.tests.functions}
diff --git a/Open-ILS/web/js/dojo/openils/tests/runTests.html b/Open-ILS/web/js/dojo/openils/tests/runTests.html
new file mode 100644 (file)
index 0000000..bd25b8b
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+  <head>
+    <title>demo.doh Unit Test Runner</title>
+    <meta http-equiv="REFRESH"         
+content="0;url=/js/util/doh/runner.html?testModule=dojo.openils.tests.module">
+  </head>
+  <body>
+      Redirecting to D.O.H runner.
+  </body>
+</html>
diff --git a/Open-ILS/web/js/dojo/openils/widget/tests/module.js b/Open-ILS/web/js/dojo/openils/widget/tests/module.js
new file mode 100644 (file)
index 0000000..6a269c5
--- /dev/null
@@ -0,0 +1,11 @@
+dojo.provide("demo.doh.tests.module");
+//This file loads in all the test definitions.  
+
+try{
+     //Load in the demoFunctions module test.
+     dojo.require("demo.doh.tests.functions.demoFunctions");
+     //Load in the widget tests.
+     dojo.require("demo.doh.tests.widgets.DemoWidget");
+}catch(e){
+     doh.debug(e);
+}
diff --git a/Open-ILS/web/js/dojo/openils/widget/tests/runTests.html b/Open-ILS/web/js/dojo/openils/widget/tests/runTests.html
new file mode 100644 (file)
index 0000000..af9c238
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+  <head>
+    <title>demo.doh Unit Test Runner</title>
+    <meta http-equiv="REFRESH"         
+content="0;url=/js/util/doh/runner.html?testModule=demo.doh.tests.module">
+  </head>
+  <body>
+      Redirecting to D.O.H runner.
+  </body>
+</html>
index 4f5c3e7..0249a65 100644 (file)
-dojo.require('dijit.Dialog');
-dojo.require('dojo.cookie');
-dojo.require('fieldmapper.AutoIDL');  // make conditional.  TT variable sets JS var to enable/disable?
-dojo.require('openils.User');
-dojo.require('openils.CGI');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.XUL');
-
-var cgi = new openils.CGI();
-
-function oilsSetupUser() {
-    var authtoken = cgi.param('ses') || dojo.cookie('ses');
-    var workstation = cgi.param('ws') || dojo.cookie('ws');
-    var user;
-    var ses_user;
-
-    openils.User.user = null;
-    openils.User.authtoken = null;
-    openils.User.workstation = null;
-
-    if(openils.XUL.isXUL()) {
-               stash = openils.XUL.getStash();
-               authtoken = stash.session.key
-        ses_user = stash.list.au[0];
+require([
+       "dijit/Dialog",
+       "dojo/cookie",
+       "fieldmapper/AutoIDL",  // make conditional.  TT variable sets JS var to enable/disable?
+       "openils/User",
+       "openils/CGI",
+       "openils/Event",
+       "openils/Util",
+       "openils/XUL"
+       ],
+function(dijit_Dialog,
+       dojo_cookie,
+       fieldmapper_AutoIDL,
+       openils_User,
+       openils_CGI,
+       openils_Event,
+       openils_Util,
+       openils_XUL){
+       
+       var cgi = new openils_CGI();
+       
+       function oilsSetupUser() {
+           var authtoken = cgi.param('ses') || dojo_cookie('ses');
+           var workstation = cgi.param('ws') || dojo_cookie('ws');
+           var user;
+           var ses_user;
+       
+           openils_User.user = null;
+           openils_User.authtoken = null;
+           openils_User.workstation = null;
+       
+           if(openils_XUL.isXUL()) {
+                       stash = openils_XUL.getStash();
+                       authtoken = stash.session.key
+               ses_user = stash.list.au[0];
+               }
+       
+           if(authtoken) {
+               user = new openils_User();
+               delete user.sessionCache[authtoken];
+               user.authtoken = authtoken;
+               if(ses_user) {
+                   user.user = ses_user;
+                   user.sessionCache[authtoken] = ses_user;
+               }
+               user.user = user.getBySession();
+           }
+       
+           if(!authtoken || openils_Event.parse(user.user)) {
+       
+               authtoken = oilsLoginFromCookies();
+       
+               if(!authtoken) {
+       
+                   dojo_cookie('ses', null, {expires:-1, path:'/'}); // remove the cookie
+       
+                   dojo.addOnLoad(function(){
+                       if(openils_XUL.isXUL()) {
+                           // let XUL handle the login dialog
+                           dump('getNewSession in base.js\n');
+                           openils_XUL.getNewSession( function() { location.href = location.href } );
+                       } else {
+                           // in web-only mode, use the dojo login dialog
+                           oilsLoginDialog.show(); 
+                           var func = function(){ oilsDoLogin(); };
+                           openils_Util.registerEnterHandler(dojo.byId('oils-login-username'), func);
+                           openils_Util.registerEnterHandler(dojo.byId('oils-login-password'), func);
+                           dojo.byId('oils-login-workstation').innerHTML = workstation || '';
+                       }
+                   });
+                   return null;
+               }
+           }
+       
+           dojo_cookie('ses', authtoken, {path:'/'});
+           openils_User.authtoken = authtoken;
+           openils_User.workstation = workstation;
+           return authtoken;
        }
+       
+       // pulls username / password and optional workstation from cgi params or cookies
+       function oilsLoginFromCookies() {
+       
+           var username = cgi.param('username') || dojo_cookie('username');
+           var password = cgi.param('password') || dojo_cookie('password');
+           var workstation = cgi.param('ws') || dojo_cookie('ws');
+       
+           if(username && password) {
+       
+               var user = new openils_User();
+               var args = {
+                   username : username,
+                   passwd : password,
+                   type : 'staff'
+               };
+       
+               if(workstation) 
+                   args.workstation = workstation;
+       
+               if(user.login(args)) {
+                   // fetches the login session and sets the global vars
+                   user = new openils_User({authtoken : user.authtoken});
+                   return (user && !openils_Event.parse(user.user)) ? user.authtoken : null;
+               } 
+           }
+       
+           return null;
+       }
+       
+       function oilsDoLogin() {
+           openils_Util.hide('oils-login-failed');
+           var workstation = cgi.param('ws') || dojo_cookie('ws');
+           var user = new openils_User();
+           var args = {
+               username: dojo.byId('oils-login-username').value,
+               passwd: dojo.byId('oils-login-password').value,
+               type: 'staff', // hardcode for now
+           };
+           if(workstation) 
+               args.workstation = workstation;
+       
+           if(user.login(args)) {
+               dojo_cookie('ses', user.authtoken, {path : '/'});
+               location.href = location.href;
+           } else {
+               openils_Util.show('oils-login-failed');
+           }
+       
+           return false;
+       }
+       
+       oilsSetupUser();
+       
+       
 
-    if(authtoken) {
-        user = new openils.User();
-        delete user.sessionCache[authtoken];
-        user.authtoken = authtoken;
-        if(ses_user) {
-            user.user = ses_user;
-            user.sessionCache[authtoken] = ses_user;
-        }
-        user.user = user.getBySession();
-    }
-
-    if(!authtoken || openils.Event.parse(user.user)) {
-
-        authtoken = oilsLoginFromCookies();
-
-        if(!authtoken) {
-
-            dojo.cookie('ses', null, {expires:-1, path:'/'}); // remove the cookie
-
-            dojo.addOnLoad(function(){
-                if(openils.XUL.isXUL()) {
-                    // let XUL handle the login dialog
-                    dump('getNewSession in base.js\n');
-                    openils.XUL.getNewSession( function() { location.href = location.href } );
-                } else {
-                    // in web-only mode, use the dojo login dialog
-                    oilsLoginDialog.show(); 
-                    var func = function(){ oilsDoLogin(); };
-                    openils.Util.registerEnterHandler(dojo.byId('oils-login-username'), func);
-                    openils.Util.registerEnterHandler(dojo.byId('oils-login-password'), func);
-                    dojo.byId('oils-login-workstation').innerHTML = workstation || '';
-                }
-            });
-            return null;
-        }
-    }
-
-    dojo.cookie('ses', authtoken, {path:'/'});
-    openils.User.authtoken = authtoken;
-    openils.User.workstation = workstation;
-    return authtoken;
-}
-
-// pulls username / password and optional workstation from cgi params or cookies
-function oilsLoginFromCookies() {
-
-    var username = cgi.param('username') || dojo.cookie('username');
-    var password = cgi.param('password') || dojo.cookie('password');
-    var workstation = cgi.param('ws') || dojo.cookie('ws');
-
-    if(username && password) {
-
-        var user = new openils.User();
-        var args = {
-            username : username,
-            passwd : password,
-            type : 'staff'
-        };
-
-        if(workstation) 
-            args.workstation = workstation;
-
-        if(user.login(args)) {
-            // fetches the login session and sets the global vars
-            user = new openils.User({authtoken : user.authtoken});
-            return (user && !openils.Event.parse(user.user)) ? user.authtoken : null;
-        } 
-    }
-
-    return null;
-}
-
-function oilsDoLogin() {
-    openils.Util.hide('oils-login-failed');
-    var workstation = cgi.param('ws') || dojo.cookie('ws');
-    var user = new openils.User();
-    var args = {
-        username: dojo.byId('oils-login-username').value,
-        passwd: dojo.byId('oils-login-password').value,
-        type: 'staff', // hardcode for now
-    };
-    if(workstation) 
-        args.workstation = workstation;
-
-    if(user.login(args)) {
-        dojo.cookie('ses', user.authtoken, {path : '/'});
-        location.href = location.href;
-    } else {
-        openils.Util.show('oils-login-failed');
-    }
-
-    return false;
-}
-
-oilsSetupUser();
-
+});
\ No newline at end of file
index 6d62cc7..8dee2bc 100644 (file)
@@ -1,15 +1,21 @@
-dojo.require("dojox.encoding.base64");
+require([
+       "dojox/encoding/base64"
+       ],
+function(dojox_encoding_base64){
+       
+       function base64Encode(o) {
+           return dojox_encoding_base64.encode(
+               js2JSON(o).split("").map(function(c) { return c.charCodeAt(0); })
+           );
+       }
+       
+       function base64Decode(s) {
+           return JSON2js(
+               dojox_encoding_base64.decode(s).map(
+                   function(b) { return String.fromCharCode(b); }
+               ).join("")
+           );
+       }
+       
 
-function base64Encode(o) {
-    return dojox.encoding.base64.encode(
-        js2JSON(o).split("").map(function(c) { return c.charCodeAt(0); })
-    );
-}
-
-function base64Decode(s) {
-    return JSON2js(
-        dojox.encoding.base64.decode(s).map(
-            function(b) { return String.fromCharCode(b); }
-        ).join("")
-    );
-}
+});
\ No newline at end of file
index 55db002..fc47a7d 100644 (file)
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('dijit.layout.SplitContainer');
-dojo.require('dijit.Dialog');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.Button');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.date.locale');
-dojo.require('dojo.date.stamp');
-
-
-dojo.require('openils.User');
-dojo.require('openils.acq.Fund');
-dojo.require('openils.acq.Lineitem');
-dojo.require('openils.acq.Provider');
-dojo.require('openils.widget.FundSelector');
-dojo.require('openils.editors');
-dojo.require('openils.Event');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('fieldmapper.OrgUtils');
-
-/* put all the accessors, etc. into a local object for namespacing */
-var JUBGrid = {
-    jubGrid : null,
-    lineitems : [], // full list of lineitem objects to display 
-    getLi : function(id) { 
-        // given an ID, returns the lineitem object from the list
-        for(var i in JUBGrid.lineitems) {
-            var li = JUBGrid.lineitems[i];
-            if(li.id() == id)
-                return li;
-        }
-    },
-
-    _getMARCAttr : function(rowIndex, attr) {
-        var data = JUBGrid.jubGrid.getItem(rowIndex);
-        if (!data) return '';
-        return new openils.acq.Lineitem(
-            {lineitem:JUBGrid.getLi(data.id)}).findAttr(attr, 'lineitem_marc_attr_definition')
-    },
-    getJUBTitle : function(rowIndex) {
-        return JUBGrid._getMARCAttr(rowIndex, 'title');
-    },
-    getJUBAuthor : function(rowIndex) {
-        return JUBGrid._getMARCAttr(rowIndex, 'author');
-    },
-    getJUBIsbn : function(rowIndex) {
-        return JUBGrid._getMARCAttr(rowIndex, 'isbn');
-    },
-    getJUBActualPrice : function(rowIndex) {
-        var data = JUBGrid.jubGrid.getItem(rowIndex);
-        if (!data) return '';
-        var price = new openils.acq.Lineitem(
-            {lineitem:JUBGrid.getLi(data.id)}).getActualPrice();
-        if(price) return price.price;
-        return ''
-    },
-    getJUBEstimatedPrice : function(rowIndex) {
-        var data = JUBGrid.jubGrid.getItem(rowIndex);
-        if (!data) return '';
-           var price = new openils.acq.Lineitem(
-            {lineitem:JUBGrid.getLi(data.id)}).getEstimatedPrice();
-        if(price) return price.price;
-        return ''
-    },
-    getJUBPubdate : function(rowIndex) {
-        return JUBGrid._getMARCAttr(rowIndex, 'pubdate');
-    },
-    getProvider : function(rowIndex) {
-        data = JUBGrid.jubGrid.getItem(rowIndex);
-        if(!data || !data.provider) return;
-        return openils.acq.Provider.retrieve(data.provider).code();
-    },
-    getRecvTime : function(rowIndex) {
-        var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
-        if (!(data && data.recv_time)) return '';
-        var date = dojo.date.stamp.fromISOString(data.recv_time);
-        return dojo.date.locale.format(date, {formatLength:'medium'});
-    },
-    getCopyLocation : function(rowIndex) {
-        var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
-        if(!data || !data.location) return '';
-        return openils.CopyLocation.retrieve(data.location).name();
-    },
-    getLIDFundName : function(rowIndex) {
-        var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
-        if (!data || !data.fund) return;
-        try {
-            return openils.acq.Fund.retrieve(data.fund).name();
-        } catch (evt) {
-            return data.fund;
-        }
-    },
-    getLIDFundCode : function(rowIndex) {
-        var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
-        if (!data || !data.fund) return;
-        try {
-            return openils.acq.Fund.retrieve(data.fund).code();
-        } catch (evt) {
-            return data.fund;
-        }
-    },
-    getLIDLibName : function(rowIndex) {
-        var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
-        if (!data || !data.owning_lib) return;
-        return fieldmapper.aou.findOrgUnit(data.owning_lib).shortname();
-    },
-
-    gridDataChanged : function(newVal, rowIdx, cellIdx) {
-        // cellIdx == -1 if you are editing a column that
-        // is not represented in the data model. Khaaaaaaan!!! 
-    },
-
-    populate : function(gridWidget, model, lineitems) {
-        for (var i in lineitems) {
-            JUBGrid.lineitems[lineitems[i].id()] = lineitems[i];
-        }
-        JUBGrid.jubGrid = gridWidget;
-        //JUBGrid.jubGrid.setModel(model);
-        if(JUBGrid.showDetails) {
-            dojo.connect(gridWidget, "onRowClick", 
-                function(evt) {
-                    var jub = JubGrid.jubGrid.getItem(evt.rowIndex);
-                    var grid;
-
-                    JUBGrid.jubDetailGrid.lineitemID = jub.id;
-
-                    //if (jub.state == "approved") {
-                    if (false) { // need finer grained control here
-                        grid = JUBGrid.jubDetailGridLayoutReadOnly;
-                    } else {
-                        grid = JUBGrid.jubDetailGridLayout;
-                    }
-                    openils.acq.Lineitem.loadLIDGrid(
-                        JUBGrid.jubDetailGrid, 
-                        JUBGrid.jubGrid.getItem(evt.rowIndex).id, grid);
-                }
-            );
-        }
-        // capture changes to lineitems
-        dojo.connect(JUBGrid.jubGrid.store, "onSet", JUBGrid.onJUBSet);
-        gridWidget.update();
-    },
-
-    approveJUB: function(evt) {
-        var list = [];
-        var selected = JUBGrid.jubGrid.selection.getSelected();
-        for (var idx = 0; idx < selected.length; idx++) {
-            var rowIdx = selected[idx];
-            JUBGrid.approveSingleJUB(JUBGrid.jubGrid.getItem(rowIdx));
-        }
-    },
-
-    approveSingleJUB: function(jub) {
-        var li = new openils.acq.Lineitem({lineitem:JUBGrid.getLi(jub.id)});
-        var approveStore = function(evt) {
-            if (evt) {
-                // something bad happened
-                console.log("jubgrid.js: approveJUB: error:");
-                console.dir(evt);
-                alert("Error: "+evt.desc);
-            } else {
-                var approveACQLI = function(jub, rq) {
-                    JUBGrid.jubGrid.model.store.setValue(jub, "state", "approved");
-                    JUBGrid.jubGrid.model.refresh();
-                    JUBGrid.jubGrid.update();
-                    // Reload lineitem details, read-only
-                    //openils.acq.Lineitem.loadLIDGrid(JUBGrid.jubDetailGrid, li.id(), JUBGrid.jubDetailGridLayout);
-                        //JUBGrid.jubDetailGridLayoutReadOnly);
-                };
-                JUBGrid.jubGrid.model.store.fetch({query:{id:jub.id}, onItem: approveACQLI});
-            }
-        };
-
-        li.approve(approveStore);
-    },
-
-
-    removeSelectedJUBs: function(evt) {
-
-        function deleteList(list, idx, oncomplete) {
-            if(idx >= list.length) 
-                return oncomplete();
-            fieldmapper.standardRequest([
-                'open-ils.acq',
-                'open-ils.acq.lineitem.delete'], 
-                {   async: true,
-                    params: [openils.User.authtoken, list[idx].id()],
-                    oncomplete: function(r) {
-                        var res = r.recv().content();
-                        if(openils.Event.parse(res))
-                            alert(openils.Event.parse(res));
-                        deleteList(list, ++idx, oncomplete);
-                    }
-                }
-            );
-        }
-
-        var lineitems = JUBGrid.lineitems;
-        var deleteMe = [];
-        var keepMe = [];
-        var selected = JUBGrid.jubGrid.selection.getSelected();
-
-        for(var id in lineitems) {
-            var deleted = false;
-            for(var i = 0; i < selected.length; i++) {
-                var rowIdx = selected[i];
-                   var jubid = JUBGrid.jubGrid.getItem(rowIdx).id;
-                if(jubid == id) {
-                   if (lineitems[id].state() == 'new') {
-                       deleteMe.push(lineitems[id]);
-                       deleted = true;
-                   } else {
-                       alert("Can not delete line item "+id+
-                             ": item is "+lineitems[id].state());
-                   }
-                }
-            }
-            if(!deleted) 
-                keepMe[id] = lineitems[id];
-        }
-
-        JUBGrid.lineitems = keepMe;
-        deleteList(deleteMe, 0, function(){
-            JUBGrid.jubGrid.store = new dojo.data.ItemFileReadStore({data:jub.toStoreData(keepMe)});
-        });
-    },
-
-    deleteLID: function(evt) {
-       var list =[];
-       var selected = JUBGrid.jubDetailGrid.selection.getSelected();
-       for (var idx = 0; idx < selected.length; idx++) {
-           var rowIdx = selected[idx];
-           var lid = JUBGrid.jubDetailGrid.getItem(rowIdx);
-           var deleteFromStore = function (evt) {
-
-               if (evt) {
-                   // something bad happened
-                   alert("Error: "+evt.desc);
-               } else {
-                   var deleteItem = function(item, rq) {
-                       JUBGrid.jubDetailGrid.store.deleteItem(item);
+require([
+       "dojo/data/ItemFileReadStore",
+       "dijit/layout/SplitContainer",
+       "dijit/Dialog",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Button",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dojox/grid/DataGrid",
+       "dojo/date/locale",
+       "dojo/date/stamp",
+       "openils/User",
+       "openils/acq/Fund",
+       "openils/acq/Lineitem",
+       "openils/acq/Provider",
+       "openils/widget/FundSelector",
+       "openils/editors",
+       "openils/Event",
+       "openils/widget/OrgUnitFilteringSelect",
+       "fieldmapper/OrgUtils"
+       ],
+function(dojo_data_ItemFileReadStore,
+       dijit_layout_SplitContainer,
+       dijit_Dialog,
+       dijit_form_FilteringSelect,
+       dijit_form_Button,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dojox_grid_DataGrid,
+       dojo_date_locale,
+       dojo_date_stamp,
+       openils_User,
+       openils_acq_Fund,
+       openils_acq_Lineitem,
+       openils_acq_Provider,
+       openils_widget_FundSelector,
+       openils_editors,
+       openils_Event,
+       openils_widget_OrgUnitFilteringSelect,
+       fieldmapper_OrgUtils){
+       
+       
+       
+       /* put all the accessors, etc. into a local object for namespacing */
+       var JUBGrid = {
+           jubGrid : null,
+           lineitems : [], // full list of lineitem objects to display 
+           getLi : function(id) { 
+               // given an ID, returns the lineitem object from the list
+               for(var i in JUBGrid.lineitems) {
+                   var li = JUBGrid.lineitems[i];
+                   if(li.id() == id)
+                       return li;
+               }
+           },
+       
+           _getMARCAttr : function(rowIndex, attr) {
+               var data = JUBGrid.jubGrid.getItem(rowIndex);
+               if (!data) return '';
+               return new openils_acq_Lineitem(
+                   {lineitem:JUBGrid.getLi(data.id)}).findAttr(attr, 'lineitem_marc_attr_definition')
+           },
+           getJUBTitle : function(rowIndex) {
+               return JUBGrid._getMARCAttr(rowIndex, 'title');
+           },
+           getJUBAuthor : function(rowIndex) {
+               return JUBGrid._getMARCAttr(rowIndex, 'author');
+           },
+           getJUBIsbn : function(rowIndex) {
+               return JUBGrid._getMARCAttr(rowIndex, 'isbn');
+           },
+           getJUBActualPrice : function(rowIndex) {
+               var data = JUBGrid.jubGrid.getItem(rowIndex);
+               if (!data) return '';
+               var price = new openils_acq_Lineitem(
+                   {lineitem:JUBGrid.getLi(data.id)}).getActualPrice();
+               if(price) return price.price;
+               return ''
+           },
+           getJUBEstimatedPrice : function(rowIndex) {
+               var data = JUBGrid.jubGrid.getItem(rowIndex);
+               if (!data) return '';
+                   var price = new openils_acq_Lineitem(
+                   {lineitem:JUBGrid.getLi(data.id)}).getEstimatedPrice();
+               if(price) return price.price;
+               return ''
+           },
+           getJUBPubdate : function(rowIndex) {
+               return JUBGrid._getMARCAttr(rowIndex, 'pubdate');
+           },
+           getProvider : function(rowIndex) {
+               data = JUBGrid.jubGrid.getItem(rowIndex);
+               if(!data || !data.provider) return;
+               return openils_acq_Provider.retrieve(data.provider).code();
+           },
+           getRecvTime : function(rowIndex) {
+               var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
+               if (!(data && data.recv_time)) return '';
+               var date = dojo_date_stamp.fromISOString(data.recv_time);
+               return dojo_date_locale.format(date, {formatLength:'medium'});
+           },
+           getCopyLocation : function(rowIndex) {
+               var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
+               if(!data || !data.location) return '';
+               return openils.CopyLocation.retrieve(data.location).name();
+           },
+           getLIDFundName : function(rowIndex) {
+               var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
+               if (!data || !data.fund) return;
+               try {
+                   return openils_acq_Fund.retrieve(data.fund).name();
+               } catch (evt) {
+                   return data.fund;
+               }
+           },
+           getLIDFundCode : function(rowIndex) {
+               var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
+               if (!data || !data.fund) return;
+               try {
+                   return openils_acq_Fund.retrieve(data.fund).code();
+               } catch (evt) {
+                   return data.fund;
+               }
+           },
+           getLIDLibName : function(rowIndex) {
+               var data = JUBGrid.jubDetailGrid.getItem(rowIndex);
+               if (!data || !data.owning_lib) return;
+               return fieldmapper.aou.findOrgUnit(data.owning_lib).shortname();
+           },
+       
+           gridDataChanged : function(newVal, rowIdx, cellIdx) {
+               // cellIdx == -1 if you are editing a column that
+               // is not represented in the data model. Khaaaaaaan!!! 
+           },
+       
+           populate : function(gridWidget, model, lineitems) {
+               for (var i in lineitems) {
+                   JUBGrid.lineitems[lineitems[i].id()] = lineitems[i];
+               }
+               JUBGrid.jubGrid = gridWidget;
+               //JUBGrid.jubGrid.setModel(model);
+               if(JUBGrid.showDetails) {
+                   dojo.connect(gridWidget, "onRowClick", 
+                       function(evt) {
+                           var jub = JubGrid.jubGrid.getItem(evt.rowIndex);
+                           var grid;
+       
+                           JUBGrid.jubDetailGrid.lineitemID = jub.id;
+       
+                           //if (jub.state == "approved") {
+                           if (false) { // need finer grained control here
+                               grid = JUBGrid.jubDetailGridLayoutReadOnly;
+                           } else {
+                               grid = JUBGrid.jubDetailGridLayout;
+                           }
+                           openils_acq_Lineitem.loadLIDGrid(
+                               JUBGrid.jubDetailGrid, 
+                               JUBGrid.jubGrid.getItem(evt.rowIndex).id, grid);
+                       }
+                   );
+               }
+               // capture changes to lineitems
+               dojo.connect(JUBGrid.jubGrid.store, "onSet", JUBGrid.onJUBSet);
+               gridWidget.update();
+           },
+       
+           approveJUB: function(evt) {
+               var list = [];
+               var selected = JUBGrid.jubGrid.selection.getSelected();
+               for (var idx = 0; idx < selected.length; idx++) {
+                   var rowIdx = selected[idx];
+                   JUBGrid.approveSingleJUB(JUBGrid.jubGrid.getItem(rowIdx));
+               }
+           },
+       
+           approveSingleJUB: function(jub) {
+               var li = new openils_acq_Lineitem({lineitem:JUBGrid.getLi(jub.id)});
+               var approveStore = function(evt) {
+                   if (evt) {
+                       // something bad happened
+                       console.log("jubgrid.js: approveJUB: error:");
+                       console.dir(evt);
+                       alert("Error: "+evt.desc);
+                   } else {
+                       var approveACQLI = function(jub, rq) {
+                           JUBGrid.jubGrid.model.store.setValue(jub, "state", "approved");
+                           JUBGrid.jubGrid.model.refresh();
+                           JUBGrid.jubGrid.update();
+                           // Reload lineitem details, read-only
+                           //openils_acq_Lineitem.loadLIDGrid(JUBGrid.jubDetailGrid, li.id(), JUBGrid.jubDetailGridLayout);
+                               //JUBGrid.jubDetailGridLayoutReadOnly);
+                       };
+                       JUBGrid.jubGrid.model.store.fetch({query:{id:jub.id}, onItem: approveACQLI});
+                   }
+               };
+       
+               li.approve(approveStore);
+           },
+       
+       
+           removeSelectedJUBs: function(evt) {
+       
+               function deleteList(list, idx, oncomplete) {
+                   if(idx >= list.length) 
+                       return oncomplete();
+                   fieldmapper.standardRequest([
+                       'open-ils.acq',
+                       'open-ils.acq.lineitem.delete'], 
+                       {   async: true,
+                           params: [openils_User.authtoken, list[idx].id()],
+                           oncomplete: function(r) {
+                               var res = r.recv().content();
+                               if(openils_Event.parse(res))
+                                   alert(openils_Event.parse(res));
+                               deleteList(list, ++idx, oncomplete);
+                           }
+                       }
+                   );
+               }
+       
+               var lineitems = JUBGrid.lineitems;
+               var deleteMe = [];
+               var keepMe = [];
+               var selected = JUBGrid.jubGrid.selection.getSelected();
+       
+               for(var id in lineitems) {
+                   var deleted = false;
+                   for(var i = 0; i < selected.length; i++) {
+                       var rowIdx = selected[i];
+                           var jubid = JUBGrid.jubGrid.getItem(rowIdx).id;
+                       if(jubid == id) {
+                           if (lineitems[id].state() == 'new') {
+                               deleteMe.push(lineitems[id]);
+                               deleted = true;
+                           } else {
+                               alert("Can not delete line item "+id+
+                                     ": item is "+lineitems[id].state());
+                           }
+                       }
+                   }
+                   if(!deleted) 
+                       keepMe[id] = lineitems[id];
+               }
+       
+               JUBGrid.lineitems = keepMe;
+               deleteList(deleteMe, 0, function(){
+                   JUBGrid.jubGrid.store = new dojo_data_ItemFileReadStore({data:jub.toStoreData(keepMe)});
+               });
+           },
+       
+           deleteLID: function(evt) {
+               var list =[];
+               var selected = JUBGrid.jubDetailGrid.selection.getSelected();
+               for (var idx = 0; idx < selected.length; idx++) {
+                   var rowIdx = selected[idx];
+                   var lid = JUBGrid.jubDetailGrid.getItem(rowIdx);
+                   var deleteFromStore = function (evt) {
+       
+                       if (evt) {
+                           // something bad happened
+                           alert("Error: "+evt.desc);
+                       } else {
+                           var deleteItem = function(item, rq) {
+                               JUBGrid.jubDetailGrid.store.deleteItem(item);
+                           };
+                           var updateCount = function(item) {
+                               var newval = JUBGrid.jubGrid.store.getValue(item, "item_count");
+                               JUBGrid.jubGrid.store.setValue(item, "item_count", newval-1);
+                           };
+       
+                           JUBGrid.jubDetailGrid.store.fetch({query:{id:lid.id},
+                                                                    onItem: deleteItem});
+                           JUBGrid.jubGrid.store.fetch({query:{id:JUBGrid.jubDetailGrid.lineitemID},
+                                                              onItem: updateCount});
+                       }
                    };
-                   var updateCount = function(item) {
-                       var newval = JUBGrid.jubGrid.store.getValue(item, "item_count");
-                       JUBGrid.jubGrid.store.setValue(item, "item_count", newval-1);
-                   };
-
-                   JUBGrid.jubDetailGrid.store.fetch({query:{id:lid.id},
-                                                            onItem: deleteItem});
-                   JUBGrid.jubGrid.store.fetch({query:{id:JUBGrid.jubDetailGrid.lineitemID},
-                                                      onItem: updateCount});
+       
+                   openils_acq_Lineitem.deleteLID(lid.id, deleteFromStore);
                }
-           };
-
-           openils.acq.Lineitem.deleteLID(lid.id, deleteFromStore);
-       }
-    },
-
-    createLID: function(fields) {
-               console.log(fields);
-        fields['lineitem'] = JUBGrid.jubDetailGrid.lineitemID;
-        console.log(fields);
-        JUBGrid.jubDetailGrid.store.newItem(acqlid.toStoreData([lid]).items[0]);
-        var addToStore = function (lid) {
-            JUBGrid.jubDetailGrid.store.newItem(acqlid.toStoreData([lid]).items[0]);
-            //JUBGrid.jubDetailGrid.refresh();
-            //JUBGrid.jubGrid.update();
-            //JUBGrid.jubGrid.refresh();
-        }
-        console.log("added to store");
-        console.log(addToStore());
-        openils.acq.Lineitem.createLID(fields, addToStore);
-    },
-
-    receiveLID: function(evt) {
-           var list =[];
-           var selected = JUBGrid.jubDetailGrid.selection.getSelected();
-       for (var idx = 0; idx < selected.length; idx++) {
-           var rowIdx = selected[idx];
-           var lid = JUBGrid.jubDetailGrid.getItem(rowIdx);
-            list.push(lid.id);
-        }
-        if(lid != null) { // is at least one selected?
-            JUBGrid._receiveLIDList(list, 0, 
-                function() {
-                    delete openils.acq.Lineitem.ModelCache[lid.lineitem];
-                    openils.acq.Lineitem.loadLIDGrid(
-                        JUBGrid.jubDetailGrid, lid.lineitem, JUBGrid.jubDetailGridLayout);
-                }
-            );
-        }
-    },
-
-    // loop through the list of LIDs and mark them as received
-    _receiveLIDList: function(list, idx, callback) {
-        if(idx >= list.length)
-            return callback();
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.lineitem_detail.receive'],
-            {   asnync: true,
-                params: [openils.User.authtoken, list[idx++]],
-                oncomplete: function(r) {
-                    var res = r.recv().content();
-                    if(e = openils.Event.parse(res))
-                        return alert(e);
-                    JUBGrid._receiveLIDList(list, idx, callback);
-                }
-            }
-        );
-    },
-
-
-    // called when a lineitem is edited
-    onJUBSet: function (griditem, attr, oldVal,newVal) {
-               console.log("onJUBSet called");
-        var item;
-
-        var updateDone = function(r) {
-            var stat = r.recv().content();
-            if(e = openils.Event.parse(stat)) 
-                console.dir(e);
-        };
-
-        // after an attribute has been updated
-        var attrUpdateDone = function(r, attr) {
-            var res = r.recv().content();
-            if(e = openils.Event.parse(res))
-                return alert(e);
-
-            var oldVal = new openils.acq.Lineitem(
-                {lineitem:item}).findAttr(attr, 'lineitem_local_attr_definition');
-
-            if(oldVal) {
-                // if this attr already exists on the object, just update the value
-                for(var i = 0; i < item.attributes().length; i++) {
-                    var attrobj = item.attributes()[i];
-                    if(attrobj.attr_type() == 'lineitem_local_attr_definition' && attrobj.attr_name() == attr) {
-                        attrobj.attr_value(newVal);
-                    }
-                }
-            } else {
-                // if this is a new attribute, create a new object to match reality
-                liad = new acqlia();
-                liad.attr_type('lineitem_local_attr_definition');
-                liad.attr_value(newVal);
-                liad.attr_name(attr);
-                liad.id(res);
-                item.attributes().push(liad);
-            }
-        }
-
-        if (oldVal == newVal) {
-            return;
-        }
-
-        item = JUBGrid.lineitems[griditem.id];
-        if (attr == "provider") {
-            if(newVal == '') 
-                newVal = null;
-            item.provider(newVal);
-
-        } else if(attr == 'estimated_price' || attr == 'actual_price') {
-            fieldmapper.standardRequest(
-                ['open-ils.acq', 'open-ils.acq.lineitem_local_attr.set'],
-                {   async: true,
-                    params: [openils.User.authtoken, item.id(), attr, newVal],
-                    oncomplete: function(r) {attrUpdateDone(r, attr); }
-                }
-            );
-        } else {
-            //alert("Unexpected attr in Picklist.onSet: '"+attr+"'");
-            return;
-        }
-
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem.update"],
-            {params: [openils.User.authtoken, item],
-             oncomplete: updateDone
-            }
-        );
-    },
-};
-
-
+           },
+       
+           createLID: function(fields) {
+                       console.log(fields);
+               fields['lineitem'] = JUBGrid.jubDetailGrid.lineitemID;
+               console.log(fields);
+               JUBGrid.jubDetailGrid.store.newItem(acqlid.toStoreData([lid]).items[0]);
+               var addToStore = function (lid) {
+                   JUBGrid.jubDetailGrid.store.newItem(acqlid.toStoreData([lid]).items[0]);
+                   //JUBGrid.jubDetailGrid.refresh();
+                   //JUBGrid.jubGrid.update();
+                   //JUBGrid.jubGrid.refresh();
+               }
+               console.log("added to store");
+               console.log(addToStore());
+               openils_acq_Lineitem.createLID(fields, addToStore);
+           },
+       
+           receiveLID: function(evt) {
+                   var list =[];
+                   var selected = JUBGrid.jubDetailGrid.selection.getSelected();
+               for (var idx = 0; idx < selected.length; idx++) {
+                   var rowIdx = selected[idx];
+                   var lid = JUBGrid.jubDetailGrid.getItem(rowIdx);
+                   list.push(lid.id);
+               }
+               if(lid != null) { // is at least one selected?
+                   JUBGrid._receiveLIDList(list, 0, 
+                       function() {
+                           delete openils_acq_Lineitem.ModelCache[lid.lineitem];
+                           openils_acq_Lineitem.loadLIDGrid(
+                               JUBGrid.jubDetailGrid, lid.lineitem, JUBGrid.jubDetailGridLayout);
+                       }
+                   );
+               }
+           },
+       
+           // loop through the list of LIDs and mark them as received
+           _receiveLIDList: function(list, idx, callback) {
+               if(idx >= list.length)
+                   return callback();
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.lineitem_detail.receive'],
+                   {   asnync: true,
+                       params: [openils_User.authtoken, list[idx++]],
+                       oncomplete: function(r) {
+                           var res = r.recv().content();
+                           if(e = openils_Event.parse(res))
+                               return alert(e);
+                           JUBGrid._receiveLIDList(list, idx, callback);
+                       }
+                   }
+               );
+           },
+       
+       
+           // called when a lineitem is edited
+           onJUBSet: function (griditem, attr, oldVal,newVal) {
+                       console.log("onJUBSet called");
+               var item;
+       
+               var updateDone = function(r) {
+                   var stat = r.recv().content();
+                   if(e = openils_Event.parse(stat)) 
+                       console.dir(e);
+               };
+       
+               // after an attribute has been updated
+               var attrUpdateDone = function(r, attr) {
+                   var res = r.recv().content();
+                   if(e = openils_Event.parse(res))
+                       return alert(e);
+       
+                   var oldVal = new openils_acq_Lineitem(
+                       {lineitem:item}).findAttr(attr, 'lineitem_local_attr_definition');
+       
+                   if(oldVal) {
+                       // if this attr already exists on the object, just update the value
+                       for(var i = 0; i < item.attributes().length; i++) {
+                           var attrobj = item.attributes()[i];
+                           if(attrobj.attr_type() == 'lineitem_local_attr_definition' && attrobj.attr_name() == attr) {
+                               attrobj.attr_value(newVal);
+                           }
+                       }
+                   } else {
+                       // if this is a new attribute, create a new object to match reality
+                       liad = new acqlia();
+                       liad.attr_type('lineitem_local_attr_definition');
+                       liad.attr_value(newVal);
+                       liad.attr_name(attr);
+                       liad.id(res);
+                       item.attributes().push(liad);
+                   }
+               }
+       
+               if (oldVal == newVal) {
+                   return;
+               }
+       
+               item = JUBGrid.lineitems[griditem.id];
+               if (attr == "provider") {
+                   if(newVal == '') 
+                       newVal = null;
+                   item.provider(newVal);
+       
+               } else if(attr == 'estimated_price' || attr == 'actual_price') {
+                   fieldmapper.standardRequest(
+                       ['open-ils.acq', 'open-ils.acq.lineitem_local_attr.set'],
+                       {   async: true,
+                           params: [openils_User.authtoken, item.id(), attr, newVal],
+                           oncomplete: function(r) {attrUpdateDone(r, attr); }
+                       }
+                   );
+               } else {
+                   //alert("Unexpected attr in Picklist.onSet: '"+attr+"'");
+                   return;
+               }
+       
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem.update"],
+                   {params: [openils_User.authtoken, item],
+                    oncomplete: updateDone
+                   }
+               );
+           },
+       };
+       
+       
+       
+
+});
\ No newline at end of file
index 860f82a..86c6025 100644 (file)
-dojo.require('dojo.date.locale');
-dojo.require('dojo.date.stamp');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.Textarea');
-dojo.require('dijit.Tooltip');
-dojo.require('dijit.ProgressBar');
-dojo.require('openils.acq.Lineitem');
-dojo.require('openils.acq.PO');
-dojo.require('openils.acq.Picklist');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('openils.PermaCrud');
-dojo.require("openils.widget.PCrudAutocompleteBox");
-
-if (!localeStrings) {   /* we can do this because javascript doesn't have block scope */
-    dojo.requireLocalization('openils.acq', 'acq');
-    var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-}
-const XUL_OPAC_WRAPPER = 'chrome://open_ils_staff_client/content/cat/opac.xul';
-var li_exportable_attrs = ["issn", "isbn", "upc"];
-
-var fundLabelFormat = [
-    '<span class="fund_${0}">${1} (${2})</span>', 'id', 'code', 'year'
-];
-var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
-
-function nodeByName(name, context) {
-    return dojo.query('[name='+name+']', context)[0];
-}
-
-// for caching linked users.  e.g. lineitem_detail.receiver
-var userCache = {};
-
-var liDetailBatchFields = ['fund', 'owning_lib', 'location', 'collection_code', 'circ_modifier', 'cn_label'];
-var liDetailFields = liDetailBatchFields.concat(['barcode', 'note']);
-var fundStyles = {
-    "stop": "color: #c00; font-weight: bold;",
-    "warning": "color: #c93;"
-};
-
-function AcqLiTable() {
-
-    var self = this;
-    this.liCache = {};
-    this.plCache = {};
-    this.poCache = {};
-    this.relCache = {};
-    this.haveFundClass = {}
-    this.fundBalanceState = {};
-    this.realDfaCache = {};
-    this.virtDfaCounts = {};
-    this.virtDfaId = -1;
-    this.dfeOffset = 0;
-    this.claimEligibleLidByLi = {};
-    this.claimEligibleLid = {};
-    this.toggleState = false;
-    this.tbody = dojo.byId('acq-lit-tbody');
-    this.selectors = [];
-    this.noteAcks = {};
-    this.authtoken = openils.User.authtoken;
-    this.pcrud = new openils.PermaCrud();
-    this.rowTemplate = this.tbody.removeChild(dojo.byId('acq-lit-row'));
-    this.copyTbody = dojo.byId('acq-lit-li-details-tbody');
-    this.copyRow = this.copyTbody.removeChild(dojo.byId('acq-lit-li-details-row'));
-    this.copyBatchRow = dojo.byId('acq-lit-li-details-batch-row');
-    this.copyBatchWidgets = {};
-    this.liNotesTbody = dojo.byId('acq-lit-notes-tbody');
-    this.liNotesRow = this.liNotesTbody.removeChild(dojo.byId('acq-lit-notes-row'));
-    this.realCopiesTbody = dojo.byId('acq-lit-real-copies-tbody');
-    this.realCopiesRow = this.realCopiesTbody.removeChild(dojo.byId('acq-lit-real-copies-row'));
-    this._copy_fields_for_acqdf = ['owning_lib', 'location'];
-    this.skipInitialEligibilityCheck = false;
-    this.claimDialog = new ClaimDialogManager(
-        liClaimDialog, finalClaimDialog, this.claimEligibleLidByLi,
-        function(li) {    /* callback that fires when claims are made */
-            self.fetchClaimInfo(li.id(), /* force update */ true);
-        }
-    );
-    this.vlAgent = new VLAgent();
-
-    dojo.byId("acq-lit-li-actions-selector").onchange = function() { 
-        self.applySelectedLiAction(this.options[this.selectedIndex].value);
-        this.selectedIndex = 0;
-    };
-
-    acqLitCreatePoSubmit.onClick = function() {
-        if (!self.createPoProviderSelector.attr("value") ||
-                !self.createPoAgencySelector.attr("value")) {
-            alert(localeStrings.CREATE_PO_INVALID);
-            return false;
-        } else if (self._confirmPoPrepaySituation()) {
-            acqLitPoCreateDialog.hide();
-            self._createPO(acqLitPoCreateDialog.getValues());
-        } else {
-            return false;
-        }
-    }
-
-    acqLitSavePlButton.onClick = function() {
-        acqLitSavePlDialog.hide();
-        self._savePl(acqLitSavePlDialog.getValues());
-    }
-
-    acqLitCancelLiStateButton.onClick = function() {
-        acqLitChangeLiStateDialog.hide();
-    }
-    acqLitSaveLiStateButton.onClick = function() {
-        acqLitChangeLiStateDialog.hide();
-        self._updateLiState(acqLitChangeLiStateDialog.getValues(), acqLitChangeLiStateDialog.attr('state'));
-    }
-
-
-    dojo.byId('acq-lit-select-toggle').onclick = function(){self.toggleSelect()};
-    dojo.byId('acq-lit-info-back-button').onclick = function(){self.show('list')};
-    dojo.byId('acq-lit-copies-back-button').onclick = function(){self.show('list')};
-    dojo.byId('acq-lit-notes-back-button').onclick = function(){self.show('list')};
-    dojo.byId('acq-lit-real-copies-back-button').onclick = function(){self.show('list')};
-
-    this.reset = function(keep_selectors) {
-        while(self.tbody.childNodes[0])
-            self.tbody.removeChild(self.tbody.childNodes[0]);
-        self.noteAcks = {};
-        self.relCache = {};
-
-        if (!keep_selectors)
-            self.selectors = [];
-    };
-    
-    this.setNext = function(handler) {
-        var link = dojo.byId('acq-lit-next');
-        if(handler) {
-            dojo.style(link, 'visibility', 'visible');
-            link.onclick = handler;
-        } else {
-            dojo.style(link, 'visibility', 'hidden');
-        }
-    };
-
-    this.setPrev = function(handler) {
-        var link = dojo.byId('acq-lit-prev');
-        if(handler) {
-            dojo.style(link, 'visibility', 'visible'); 
-            link.onclick = handler; 
-        } else {
-            dojo.style(link, 'visibility', 'hidden');
-        }
-    };
-
-    this.show = function(div) {
-        openils.Util.hide('acq-lit-table-div');
-        openils.Util.hide('acq-lit-info-div');
-        openils.Util.hide('acq-lit-li-details');
-        openils.Util.hide('acq-lit-notes-div');
-        openils.Util.hide('acq-lit-real-copies-div');
-        openils.Util.hide('acq-lit-asset-creator');
-        switch(div) {
-            case 'list':
-                openils.Util.show('acq-lit-table-div');
-                break;
-            case 'info':
-                openils.Util.show('acq-lit-info-div');
-                break;
-            case 'copies':
-                openils.Util.show('acq-lit-li-details');
-                break;
-            case 'real-copies':
-                openils.Util.show('acq-lit-real-copies-div');
-                break;
-            case 'notes':
-                openils.Util.show('acq-lit-notes-div');
-                break;
-            case 'asset-creator':
-                openils.Util.show('acq-lit-asset-creator');
-                break;
-            default:
-                if(div) 
-                    openils.Util.show(div);
-        }
-    }
-
-    this.hide = function() {
-        this.show(null);
-    }
-
-    this.toggleSelect = function() {
-        if(self.toggleState) 
-            dojo.forEach(self.selectors, function(i){i.checked = false});
-        else 
-            dojo.forEach(self.selectors, function(i){i.checked = true});
-        self.toggleState = !self.toggleState;
-    };
-
-
-    this.getAll = function(callback, id_only) {
-        /* For some uses of the li table, we may not really know about "all"
-         * the lineitems that the user thinks we know about. If we're a paged
-         * picklist, for example, we only know about the lineitems we've
-         * displayed, but not necessarily all the lineitems on the picklist.
-         * So we reach out to pcrud to inform us.
-         */
-
-        var oncomplete = function(r) {
-            var id_list = openils.Util.readResponse(r);
-            if (id_only)
-                callback(id_list);
-            else
-                self.fetchLineitemsById(id_list, callback);
-        };
-
-        if (this.isPL) {
-            this.pcrud.search(
-                "jub", {"picklist": this.isPL}, {
-                    "id_list": true,    /* sic, even if id_only */
-                    "async": true,
-                    "oncomplete": oncomplete
-                }
-            );
-            return;
-        } else if (this.isPO) {
-            this.pcrud.search(
-                "jub", {"purchase_order": this.isPO}, {
-                    "id_list": true,
-                    "async": true,
-                    "oncomplete": oncomplete
-                }
-            );
-            return;
-        } else if (this.isUni && this.pager) {
-            this.pager.getAllLineitemIDs(oncomplete);
-            return;
-        }
-
-        /* If execution reaches this point, we don't need or can't perform
-         * any special tricks to find out the "real" list of "all" lineitems
-         * in this context, so we fall back to the old method.
-         */
-        callback(this.getSelected(true, null, id_only));
-    };
-
-    /** @param all If true, assume all are selected */
-    this.getSelected = function(
-        all,
-        callback /* If you want a "good" idea of "all" lineitems, you must
-        provide a callback that accepts an array parameter, rather than
-        relying on the return value of this method itself. */,
-        id_only
-    ) {
-        if (all && callback)
-            return this.getAll(callback, id_only);
-
-        var indices = {};   /* use to uniqify. needed in paging situations. */
-        dojo.forEach(this.selectors,
-            function(i) { 
-                if(i.checked || all)
-                    indices[i.parentNode.parentNode.getAttribute('li')] = true;
-            }
-        );
-
-        var result = openils.Util.objectProperties(indices);
-
-        if (!id_only)
-            result = result.map(function(liId) { return self.liCache[liId]; });
-
-        if (callback)
-            callback(result);
-        else
-            return result;
-    };
-
-    this.setRowAttr = function(td, liWrapper, field, type) {
-        var val = liWrapper.findAttr(field, type || 'lineitem_marc_attr_definition') || '';
-        td.appendChild(document.createTextNode(val));
-    };
-
-    this.setClaimPolicyControl = function(li, row) {
-        if (!self.claimPolicyPicker) {
-            self.claimPolicyPicker = true; /* prevents a race condition */
-            new openils.widget.AutoFieldWidget({
-                "parentNode": "acq-lit-li-claim-policy",
-                "fmClass": "acqclp",
-                "selfReference": true,
-                "dijitArgs": {"required": true}
-            }).build(function(w) { self.claimPolicyPicker = w; });
-        }
-
-        if (!row) row = this._findLiRow(li);
-
-        var actViewPolicy = nodeByName("action_view_claim_policy", row);
-        if (li.claim_policy())
-            actViewPolicy.innerHTML = localeStrings.CHANGE_CLAIM_POLICY;
-
-        if (!actViewPolicy.onclick) {
-            actViewPolicy.onclick = function() {
-                if (li.claim_policy())
-                    self.claimPolicyPicker.attr("value", li.claim_policy());
-                liClaimPolicyDialog.show();
-                liClaimPolicySave.onClick = function() {
-                    self.changeClaimPolicy(
-                        [li], self.claimPolicyPicker.attr("value"),
-                        function() {
-                            self.setClaimPolicyControl(li, row);
-                            self.reconsiderClaimControl(li, row);
-                            liClaimPolicyDialog.hide();
-                        }
-                    );
-                }
-            };
-        }
-    };
-
-    this.fetchClaimInfo = function(liId, force, callback, row) {
-        this._fetchLineitem(
-            liId, function(full) {
-                self.liCache[full.id()] = full;
-                self.checkClaimEligibility(full, callback, row);
-            }, force
-        );
-    }
-
-    /**
-     * Inserts a single lineitem into the growing table of lineitems
-     * @param {Object} li The lineitem object to insert
-     */
-    this.addLineitem = function(li, skip_final_placement) {
-        this.liCache[li.id()] = li;
-
-        // insert the row right away so that final order isn't
-        // dependent on how long subsequent async request take
-        // for a given line item
-        var row = self.rowTemplate.cloneNode(true);
-        if (!skip_final_placement) {
-            self.tbody.appendChild(row);
-        }
-        self.selectors.push(dojo.query('[name=selectbox]', row)[0]);
-
-        // sort the lineitem notes on edit_time
-        if(!li.lineitem_notes()) li.lineitem_notes([]);
-
-        var liWrapper = new openils.acq.Lineitem({lineitem:li});
-        row.setAttribute('li', li.id());
-        var tds = dojo.query('[attr]', row);
-        dojo.forEach(tds, function(td) {self.setRowAttr(td, liWrapper, td.getAttribute('attr'), td.getAttribute('attr_type'));});
-        dojo.query('[name=source_label]', row)[0].appendChild(document.createTextNode(li.source_label()));
-
-        var identifier =
-            liWrapper.findAttr("isbn", "lineitem_marc_attr_definition") ||
-            liWrapper.findAttr("upc", "lineitem_marc_attr_definition");
-
-        // XXX media prefix for added content
-        if (identifier) {
-            nodeByName("jacket", row).setAttribute(
-                "src", "/opac/extras/ac/jacket/small/" + identifier
-            );
-        }
-
-        nodeByName("liid", row).innerHTML += li.id();
-
-        if(li.eg_bib_id()) {
-            openils.Util.show(nodeByName('catalog', row), 'inline');
-            nodeByName("catalog_link", row).onclick = this.generateMakeRecTab(li.eg_bib_id());
-        } else {
-            openils.Util.show(nodeByName('link_to_catalog', row), 'inline');
-            nodeByName("link_to_catalog_link", row).onclick = function() { self.drawBibFinder(li) };
-        }
-
-        if (li.queued_record()) {
-            this.pcrud.retrieve('vqbr', li.queued_record(),
-                {   async : true, 
-                    oncomplete : function(r) {
-                        var qrec = openils.Util.readResponse(r);
-                        openils.Util.show(nodeByName('queue', row), 'inline');
-                        var link = nodeByName("queue_link", row);
-                        link.onclick = function() { 
-                            // open a new tab to the vandelay queue for this record
-                            openils.XUL.newTabEasy(
-                                oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + qrec.queue()
-                            );
-                        }
-                    }
-                }
-            );
-        }
-
-        nodeByName("worksheet_link", row).href =
-            oilsBasePath + "/acq/lineitem/worksheet/" + li.id();
-
-        nodeByName("show_requests_link", row).href =
-            oilsBasePath + "/acq/picklist/user_request?lineitem=" + li.id();
-
-        dojo.query('[attr=title]', row)[0].onclick = function() {self.drawInfo(li.id())};
-        dojo.query('[name=copieslink]', row)[0].onclick = function() {self.drawCopies(li.id())};
-        dojo.query('[name=noteslink]', row)[0].onclick = function() {self.drawLiNotes(li)};
-
-        if (!this.skipInitialEligibilityCheck)
-            this.fetchClaimInfo(
-                li.id(),
-                false,
-                function(full) { self.setClaimPolicyControl(full, row) },
-                row
-            );
-
-        this.updateLiNotesCount(li, row);
-
-        // show which PO this lineitem is a member of
-        if(li.purchase_order() && !this.isPO) {
-            var po = 
-                this.poCache[li.purchase_order()] =
-                this.poCache[li.purchase_order()] ||
-                fieldmapper.standardRequest(
-                    ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
-                    {params: [
-                        this.authtoken, li.purchase_order(), {
-                            "flesh_price_summary": true,
-                            "flesh_provider" : true,
-                            "flesh_lineitem_count": true
-                        }
-                    ]});
-            if(po && !this.isMeta) {
-                openils.Util.show(nodeByName('po', row), 'inline');
-                var link = nodeByName('po_link', row);
-                link.setAttribute('href', oilsBasePath + '/acq/po/view/' + li.purchase_order());
-                link.innerHTML += po.name();
-
-                openils.Util.show(nodeByName('pro', row), 'inline');
-                link = nodeByName('pro_link', row);
-                link.setAttribute('href', oilsBasePath + '/conify/global/acq/provider/' + po.provider().id())
-                link.innerHTML += po.provider().code();
-            }
-        }
-
-        // show which picklist this lineitem is a member of
-        if(li.picklist() && (this.isPO || this.isMeta || this.isUni)) {
-            var pl = 
-                this.plCache[li.picklist()] = 
-                this.plCache[li.picklist()] || 
-                fieldmapper.standardRequest(
-                    ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
-                    {params: [this.authtoken, li.picklist()]});
-            if (pl) {
-                if (pl.name() == "") {
-                    openils.Util.show(nodeByName("bib_origin", row), "inline");
-
-                } else {
-
-                    openils.Util.show(nodeByName('pl', row), 'inline');
-                    var link = nodeByName('pl_link', row);
-                    link.setAttribute('href', oilsBasePath + '/acq/picklist/view/' + li.picklist());
-                    link.innerHTML += pl.name();
-                }
-            }
-        }
-
-        var countNode = nodeByName('count', row);
-        var count = li.item_count() || 0;
-        if (typeof(this._copy_count_cb) == "function") {
-            this._copy_count_cb(li.id(), count);
-        }
-        countNode.innerHTML = count;
-        countNode.id = 'acq-lit-copy-count-label-' + li.id();
-
-        // lineitem price
-        var priceInput = dojo.query('[name=price]', row)[0];
-        priceInput.value = li.estimated_unit_price() || '';
-        priceInput.onchange = function() { self.updateLiPrice(priceInput, li) };
-
-        // show either "mark received" or "unreceive" as appropriate
-        this.updateLiState(li, row);
-
-        if (skip_final_placement) {
-            return row;
-        }
-    };
-
-    this._liCountClaims = function(li) {
-        var total = 0;
-        for (var i = 0; i < li.lineitem_details().length; i++)
-            total += li.lineitem_details()[i].claims().length;
-        return total;
-    };
-
-    this._findLiRow = function(li) {
-        return dojo.query('tr[li="' + li.id() + '"]', "acq-lit-tbody")[0];
-    };
-
-    this.reconsiderClaimControl = function(li, row) {
-        if (!row) row = this._findLiRow(li);
-        var option = nodeByName("action_manage_claims", row);
-        var eligible = this.claimEligibleLidByLi[li.id()].length;
-        var count = this._liCountClaims(li);
-
-        option.disabled = !(count || eligible);
-        option.innerHTML =
-            dojo.string.substitute(localeStrings.NUM_CLAIMS_EXISTING, [count]);
-        option.onclick = function() { self.claimDialog.show(li); };
-    };
-
-    this.clearEligibility = function(li) {
-        this.claimEligibleLidByLi[li.id()] = [];
-
-        if (li.lineitem_details()) {
-            li.lineitem_details().forEach(
-                function(lid) { delete self.claimEligibleLid[lid.id()]; }
-            );
-        }
-
-        if (this.copyCache) {
-            var to_del = [];
-            for (var k in this.copyCache) {
-                if (this.copyCache[k].lineitem() == li.id())
-                    to_del.push(k);
-            }
-            to_del.forEach(
-                function(k) { delete self.claimEligibleLid[k]; }
-            );
-        }
-    };
-
-    this.checkClaimEligibility = function(li, callback, row) {
-        /* Assume always eligible, i.e. from this interface we don't care about
-         * claim eligibility any more. this is where the user would force a
-         * claime. */
-        this.clearEligibility(li);
-        this.claimEligibleLidByLi[li.id()] = li.lineitem_details().map(
-            function(lid) { return lid.id(); }
-        );
-        li.lineitem_details().forEach(
-            function(lid) { self.claimEligibleLid[lid.id()] = true; }
-        );
-        this.reconsiderClaimControl(li, row);
-        if (callback) callback(li);
-        /*
-        this.clearEligibility(li);
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail"], {
-                "params": [openils.User.authtoken, {"lineitem": li.id()}],
-                "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        self.claimEligibleLidByLi[li.id()].push(
-                            r.lineitem_detail()
-                        );
-                        self.claimEligibleLid[r.lineitem_detail()] = true;
-                    }
-                },
-                "oncomplete": function() {
-                    self.reconsiderClaimControl(li, row);
-                    if (typeof(callback) == "function")
-                        callback();
-                }
-            }
-        );
-        */
-    };
-
-    this.updateLiNotesCount = function(li, row) {
-        if (!row) row = this._findLiRow(li);
-
-        var has_notes = (li.lineitem_notes().filter(
-                function(o) { return Boolean (o.alert_text()); }
-            ).length > 0);
-
-        /* U+2691 is the code point for a filled-in flag character */
-        nodeByName("notes_alert_flag", row).innerHTML =
-             has_notes ? "&#x2691;" : "";
-        nodeByName("noteslink", row).style.fontStyle =
-            has_notes ? "italic" : "normal";
-        nodeByName("notes_count", row).innerHTML = li.lineitem_notes().length;
-    };
-
-    /* XXX NOT related to _updateLiState(). rethink */
-    this.updateLiState = function(li, row) {
-        if (!row) row = this._findLiRow(li);
-
-        var actReceive = nodeByName("action_mark_recv", row);
-        var actUnRecv = nodeByName("action_mark_unrecv", row);
-        var actUpdateBarcodes = nodeByName("action_update_barcodes", row);
-        var actHoldingsMaint = nodeByName("action_holdings_maint", row);
-
-        var actNewInvoice = nodeByName('action_new_invoice', row);
-        var actLinkInvoice = nodeByName('action_link_invoice', row);
-        var actViewInvoice = nodeByName('action_view_invoice', row);
-
-        nodeByName('action_view_history', row).onclick = 
-            function() { location.href = oilsBasePath + '/acq/lineitem/history/' + li.id(); };
-
-        var state_cell = nodeByName("li_state", row);
-
-        if (li.state() == "cancelled") {
-            if (typeof li.cancel_reason() == "object") {
-                var holds_state = dojo.create(
-                    "span", {
-                        "style": "border-bottom: 1px dashed #000;",
-                        "innerHTML": li.state()
-                    }, state_cell, "only"
-                );
-                new dijit.Tooltip(
-                    {
-                        "label": "<em>" + li.cancel_reason().label() +
-                            "</em><br />" + li.cancel_reason().description(),
-                        "connectId": [holds_state]
-                    }, dojo.create("span", null, state_cell, "last")
-                );
-            } else {
-                state_cell.innerHTML = li.state(); // TODO i18n state labels
-            }
-        } else {
-            state_cell.innerHTML = li.state(); // TODO i18n state labels
-        }
-
-
-        /* handle row coloring for based on LI state */
-        openils.Util.removeCSSClass(row, /^oils-acq-li-state-/);
-        openils.Util.addCSSClass(row, "oils-acq-li-state-" + li.state());
-
-        /* handle links that appear/disappear based on whether LI is received */
-        if (this.isPO) {
-            var self = this;
-
-            actNewInvoice.onclick = function() {
-                location.href = oilsBasePath + '/acq/invoice/view?create=1&attach_li=' + li.id();
-                nodeByName("action_none", row).selected = true;
-            };
-            actLinkInvoice.onclick = function() {
-                if (!self.invoiceLinkDialogManager) {
-                    self.invoiceLinkDialogManager =
-                        new InvoiceLinkDialogManager("li");
-                }
-                self.invoiceLinkDialogManager.target = li;
-                acqLitLinkInvoiceDialog.show();
-                nodeByName("action_none", row).selected = true;
-            };
-            actViewInvoice.onclick = function() {
-                location.href = oilsBasePath +
-                    "/acq/search/unified?so=" +
-                    base64Encode({"jub":[{"id": li.id()}]}) +
-                    "&rt=invoice";
-                nodeByName("action_none", row).selected = true;
-            };
-
-            actNewInvoice.disabled = false;
-            actLinkInvoice.disabled = false;
-            actViewInvoice.disabled = false;
-
-            switch(li.state()) {
-                case "on-order":
-                    actReceive.disabled = false;
-                    actReceive.onclick = function() {
-                        if (self.checkLiAlerts(li.id()))
-                            self.issueReceive(li);
-                        nodeByName("action_none", row).selected = true;
-                    };
-                    return;
-
-                case "received":
-                    actUnRecv.disabled = false;
-                    actUnRecv.onclick = function() {
-                        if (confirm(localeStrings.UNRECEIVE_LI))
-                            self.issueReceive(li, /* rollback */ true);
-                        nodeByName("action_none", row).selected = true;
-                    };
-                    // TODO we should allow editing before receipt, in which case the
-                    // test should be "if 1 or more real (acp) copies exist
-                    actUpdateBarcodes.disabled = false;
-                    actUpdateBarcodes.onclick = function() {
-                        self.showRealCopyEditUI(li);
-                        nodeByName("action_none", row).selected = true;
-                    }
-                    actHoldingsMaint.disabled = false;
-                    actHoldingsMaint.onclick = self.generateMakeRecTab( li.eg_bib_id(), 'copy_browser', row );
-
-                    return;
-            }
-        }
-    };
-
-
-    this._setAlertStore = function() {
-        acqLitAlertAlertText.store = new dojo.data.ItemFileReadStore(
-            {
-                "data": acqliat.toStoreData(
-                    this.pcrud.search(
-                        "acqliat", {"id": {"!=": null}}
-                    )
-                )
-            }
-        );
-        acqLitAlertAlertText.setValue(); /* make the store "live" */
-        acqLitAlertAlertText._store_ready = true;
-    };
-
-    /**
-     * Draws and shows the lineitem notes pane
-     */
-    this.drawLiNotes = function(li) {
-        var self = this;
-
-        if (!acqLitAlertAlertText._store_ready)
-            this._setAlertStore();
-
-        li.lineitem_notes(
-            li.lineitem_notes().sort(
-                function(a, b) { 
-                    if(a.edit_time() < b.edit_time()) return 1;
-                    return -1;
-                }
-            )
-        );
-
-        while(this.liNotesTbody.childNodes[0])
-            this.liNotesTbody.removeChild(this.liNotesTbody.childNodes[0]);
-        this.show('notes');
-
-        acqLitCreateNoteSubmit.onClick = function() {
-            var value = acqLitCreateNoteText.attr('value');
-            if(!value) return;
-            var note = new fieldmapper.acqlin();
-            note.isnew(true);
-            note.vendor_public(
-                Boolean(acqLitCreateNoteVendorPublic.attr('checked'))
-            );
-            note.value(value);
-            note.lineitem(li.id());
-
-            self.updateLiNotes(li, note);
-            acqLitCreateNoteVendorPublic.attr("checked", false);
-            acqLitCreateNoteText.attr("value", "");
-        }
-
-        acqLitCreateAlertSubmit.onClick = function() {
-            if (!acqLitAlertAlertText.item) {
-                alert(localeStrings.ALERT_UNSELECTED);
-                return;
-            }
-
-            var alert_text = new fieldmapper.acqliat().fromStoreItem(
-                acqLitAlertAlertText.item
-            );
-            var value = acqLitAlertNoteValue.attr("value") || "";
-
-            var note = new fieldmapper.acqlin();
-            note.isnew(true);
-            note.lineitem(li.id());
-            note.value(value);
-            note.alert_text(alert_text);
-
-            self.updateLiNotes(li, note);
-        }
-
-        dojo.forEach(li.lineitem_notes(), function(note) { self.addLiNote(li, note) });
-    }
-
-    /**
-     * Draws a single lineitem note in the notes pane
-     */
-    this.addLiNote = function(li, note) {
-        if(note.isdeleted()) return;
-        var self = this;
-        var row = self.liNotesRow.cloneNode(true);
-        nodeByName("value", row).innerHTML = note.value();
-        var alert_node = nodeByName("alert_code", row);
-        if (note.alert_text()) {
-            alert_node.innerHTML = note.alert_text().code();
-            if (note.alert_text().description()) {
-                new dijit.Tooltip(
-                    {
-                        "connectId": [alert_node],
-                        "label": note.alert_text().description()
-                    }, dojo.create("span", null, alert_node, "after")
-                );
-            }
-        }
-
-        if (openils.Util.isTrue(note.vendor_public()))
-            nodeByName("vendor_public", row).innerHTML =
-                localeStrings.VENDOR_PUBLIC;
-
-        nodeByName("delete", row).onclick = function() {
-            note.isdeleted(true);
-            self.liNotesTbody.removeChild(row);
-            self.updateLiNotes(li);
-        };
-
-        if(note.edit_time()) {
-            nodeByName("edit_time", row).innerHTML =
-                dojo.date.locale.format(
-                    dojo.date.stamp.fromISOString(note.edit_time()), 
-                    {formatLength:'short'});
-        }
-
-        self.liNotesTbody.appendChild(row);
-    }
-
-    /**
-     * Updates any new/changed/deleted notes on the server
-     */
-    this.updateLiNotes = function(li, newNote) {
-
-        var notes;
-        if(newNote) {
-            notes = [newNote];
-        } else {
-            notes = li.lineitem_notes().filter(
-                function(note) {
-                    if(note.ischanged() || note.isnew() || note.isdeleted())
-                        return note;
-                }
-            );
-        }
-
-        if(notes.length == 0) return;
-        progressDialog.show();
-
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.lineitem_note.cud.batch'],
-            {   async : true,
-                params : [this.authtoken, notes],
-                onresponse : function(r) {
-                    var resp = openils.Util.readResponse(r);
-
-                    if(resp.complete) {
-
-                        if(!newNote) {
-                            // remove the old changed notes
-                            var list = [];
-                            dojo.forEach(li.lineitem_notes(), 
-                                function(note) {
-                                    if(!(note.ischanged() || note.isnew() || note.isdeleted()))
-                                        list.push(note);
-                                }
-                            );
-                            li.lineitem_notes(list);
-                        }
-
-                        progressDialog.hide();
-                        self.updateLiNotesCount(li);
-                        self.drawLiNotes(li);
-                        return;
-                    }
-
-                    progressDialog.update(resp);
-                    var newnote = resp.note;
-
-                    if(!newnote.isdeleted()) {
-                        newnote.isnew(false);
-                        newnote.ischanged(false);
-                        li.lineitem_notes().push(newnote);
-                    }
-                },
-            }
-        );
-    }
-
-    this.updateLiPrice = function(input, li) {
-        var self = this;
-        var price = input.value;
-        if(Number(price) == Number(li.estimated_unit_price())) return;
-
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.lineitem.price.set'],
-            {   async : false, // redundant w/ timeout
-                timeout : 10,
-                params : [this.authtoken, li.id(), price],
-                oncomplete : function(r) {
-                    openils.Util.readResponse(r);
-                    li.estimated_unit_price(price); // update local copy
-
-                    /*
-                     * If this is a PO and every visible lineitem has a price,
-                     * check again to see if this PO can be activated.  Note that 
-                     * every visible lineitem having a price does not guarantee it can
-                     * be activated, which is why we still make the call.  Having a price
-                     * set for every visiable lineitem is just the lowest barrier to entry.
-                     */
-                    if (self.isPO) {
-                        var priceNodes = dojo.query('[name=price]', dojo.byId('acq-lit-tbody'));
-                        var allSet = true;
-                        dojo.forEach(priceNodes, function(node) { if (node.value == '') allSet = false});
-                        if (allSet) checkCouldActivatePo();
-                    }
-                }
-            }
-        );
-    }
-
-    this.removeLineitem = function(liId) {
-        this.tbody.removeChild(dojo.query('[li='+liId+']', this.tbody)[0]);
-        delete this.liCache[liId];
-        //selected.push(self.liCache[i.parentNode.parentNode.getAttribute('li')]);
-    }
-
-    this.drawInfo = function(liId) {
-        if (!this._isRelatedViewer) {
-            var d = dojo.byId("acq-lit-info-related");
-            if (!this.relCache[liId]) {
-                fieldmapper.standardRequest(
-                    [
-                        "open-ils.acq",
-                        "open-ils.acq.lineitems_for_bib.by_lineitem_id.count"
-                    ], {
-                        "async": true,
-                        "params": [openils.User.authtoken, liId],
-                        "onresponse": function(r) {
-                            self.relCache[liId] = openils.Util.readResponse(r);
-                            nodeByName("related_number", d).innerHTML =
-                                self.relCache[liId];
-                            openils.Util[
-                                self.relCache[liId] >1 ? "show" : "hide"
-                            ](d);
-                        }
-                    }
-                );
-            } else {
-                nodeByName("related_number", d).innerHTML = this.relCache[liId];
-                openils.Util[this.relCache[liId] > 1 ? "show" : "hide"](d);
-            }
-        }
-
-        this.show('info');
-        openils.acq.Lineitem.fetchAttrDefs(
-            function() { 
-                self._fetchLineitem(liId, function(li){self._drawInfo(li);}); 
-            } 
-        );
-    };
-
-    /* For a given list of lineitem ids, build a list of full lineitems
-     * re-using the fetching logic that is otherwise typical to use in this
-     * module.
-     *
-     * If we've already got a lineitem in the cache, just use that.
-     *
-     * Once we've built a list of lineitems, call callback(thatlist).
-     */
-    this.fetchLineitemsById = function(id_list, callback) {
-        var total = id_list.length;
-        var result_list = [];
-
-        var inner = function(li) {
-            result_list.push(li)
-            if (--total <= 0)
-                callback(result_list);
-        };
-
-        id_list.forEach(function(id) { self._fetchLineitem(id, inner); });
-    };
-
-    this._fetchLineitem = function(liId, handler, force) {
-
-        var li = this.liCache[liId];
-        if(li && li.marc() && li.lineitem_details() && !force)
-            return handler(li);
-        
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative'],
-            {   async: true,
-
-                params: [self.authtoken, liId, {
-                    flesh_attrs: true,
-                    flesh_cancel_reason: true,
-                    flesh_li_details: true,
-                    flesh_notes: true,
-                    flesh_fund_debit: true }],
-
-                oncomplete: function(r) {
-                    var li = openils.Util.readResponse(r);
-                    self.liCache[liId] = li;
-                    handler(li)
-                }
-            }
-        );
-    };
-
-    this._drawInfo = function(li) {
-
-        acqLitEditOrderMarc.onClick = function() { self.editOrderMarc(li); }
-
-        if(li.eg_bib_id()) {
-            openils.Util.hide('acq-lit-marc-order-record-label');
-            openils.Util.hide(acqLitEditOrderMarc.domNode);
-            openils.Util.show('acq-lit-marc-real-record-label');
-        } else {
-            openils.Util.show('acq-lit-marc-order-record-label');
-            openils.Util.show(acqLitEditOrderMarc.domNode);
-            openils.Util.hide('acq-lit-marc-real-record-label');
-        }
-
-        this.drawMarcHTML(li);
-        this.infoTbody = dojo.byId('acq-lit-info-tbody');
-
-        if(!this.infoRow)
-            this.infoRow = this.infoTbody.removeChild(dojo.byId('acq-lit-info-row'));
-        while(this.infoTbody.childNodes[0])
-            this.infoTbody.removeChild(this.infoTbody.childNodes[0]);
-
-        for(var i = 0; i < li.attributes().length; i++) {
-            var attr = li.attributes()[i];
-            var row = this.infoRow.cloneNode(true);
-
-            var type = attr.attr_type().replace(/lineitem_(.*)_attr_definition/, '$1');
-            var name = openils.acq.Lineitem.attrDefs[type].filter(
-                function(a) {
-                    return (a.code() == attr.attr_name());
-                }
-            ).pop().description();
-
-            dojo.query('[name=label]', row)[0].appendChild(document.createTextNode(name));
-            dojo.query('[name=value]', row)[0].appendChild(document.createTextNode(attr.attr_value()));
-            this.infoTbody.appendChild(row);
-        }
-
-        if (!this._isRelatedViewer) {
-            nodeByName("rel_link", dojo.byId("acq-lit-info-related")).href =
-                oilsBasePath + "/acq/lineitem/related/" + li.id();
-        }
-
-    };
-
-    this.generateMakeRecTab = function(bib_id,default_view, row) {
-        return function() {
-            xulG.new_tab(
-                XUL_OPAC_WRAPPER,
-                {tab_name: localeStrings.XUL_RECORD_DETAIL_PAGE, browser:false},
-                {
-                    no_xulG : false, 
-                    show_nav_buttons : true, 
-                    show_print_button : true, 
-                    opac_url : xulG.url_prefix(xulG.urls.opac_rdetail + bib_id),
-                    default_view : default_view
-                }
-            );
-
-            if(row) nodeByName("action_none", row).selected = true;
-        }
-    };
-
-    this.drawMarcHTML = function(li) {
-        var params = [null, true, li.marc()];
-        if(li.eg_bib_id()) 
-            params = [li.eg_bib_id(), true];
-
-        fieldmapper.standardRequest(
-            ['open-ils.search', 'open-ils.search.biblio.record.html'],
-            {   async: true,
-                params: params,
-                oncomplete: function(r) {
-                    dojo.byId('acq-lit-marc-div').innerHTML = 
-                        openils.Util.readResponse(r);
-                }
-            }
-        );
-    }
-
-    this.drawCopies = function(liId, force_fetch) {
-        if (typeof force_fetch == "undefined")
-            force_fetch = false;
-
-        openils.acq.Lineitem.fetchAndRender(liId, {}, 
-            function(li, html) {
-                dojo.byId('acq-lit-copies-li-summary').innerHTML = html;
-            }
-        );
-
-        this.show('copies');
-        var self = this;
-        this.copyCache = {};
-        this.copyWidgetCache = {};
-        this.oldCopyWidgetCache = {};
-        this.virtDfaCounts = {};
-        this.realDfaCache = {};
-        this.dfeOffset = 0;
-
-        acqLitSaveCopies.onClick = function() { self.saveCopyChanges(liId) };
-        acqLitBatchUpdateCopies.onClick = function() { self.batchCopyUpdate() };
-        acqLitCopyCountInput.attr('value', '0');
-
-        while(this.copyTbody.childNodes[0])
-            this.copyTbody.removeChild(this.copyTbody.childNodes[0]);
-
-        this._drawBatchCopyWidgets();
-
-        this._drawDistribApplied(liId);
-
-        this._fetchDistribFormulas(
-            function() {
-                openils.acq.Lineitem.fetchAttrDefs(
-                    function() { 
-                        self._fetchLineitem(liId, function(li){self._drawCopies(li);}, force_fetch); 
-                    } 
-                );
-            }
-        );
-    };
-
-    this._saveDistribAppliedTemplates = function() {
-        if (!this._appliedDistribTemplate) {
-            this._appliedDistribTemplate =
-                dojo.byId("acq-lit-distrib-applied-tbody").
-                    removeChild(dojo.byId("acq-lit-distrib-applied-row"));
-            dojo.attr(this._appliedDistribTemplate, "id");
-        }
-    };
-
-    this._drawDistribApplied = function(liId) {
-        /* Build this table while hidden to prevent rendering artifacts */
-        openils.Util.hide("acq-lit-distrib-applied-tbody");
-
-        this._saveDistribAppliedTemplates();
-
-        /* Remove any rows in the table from previous populations */
-        dojo.query("tr[formula]", "acq-lit-distrib-applied-tbody").
-            forEach(dojo.destroy);
-
-        /* Unregister all dijits previously created (for some reason this isn't
-         * covered by the above destroy calls). */
-        dijit.registry.forEach(
-            function(w) { if (/^dfa-/.test(w.id)) w.destroyRecursive(); }
-        );
-
-        /* Populate the table with our liId */
-        var total = 0;
-        fieldmapper.standardRequest(
-            ["open-ils.acq",
-            "open-ils.acq.distribution_formula_application.ranged.retrieve"],
-            {
-                "async": true,
-                "params": [self.authtoken, liId],
-                "onresponse": function(r) {
-                    var dfa = openils.Util.readResponse(r);
-                    if (dfa) {
-                        total++;
-                        self.realDfaCache[dfa.id()] = dfa;
-                        self._drawDistribAppliedUnit(dfa);
-                    }
-                },
-                "oncomplete": function() {
-                    /* Reveal built table */
-                    if (total) {
-                        openils.Util.show(
-                            "acq-lit-distrib-applied-tbody", "table-row-group"
-                        );
-                    }
-                }
-            }
-        );
-    };
-
-    this._drawDistribAppliedUnit = function(dfa) {
-        var new_row = false;
-        var row = dojo.query(
-            'tr[formula="' + dfa.formula().id() + '"]',
-            "acq-lit-distrib-applied-tbody"
-        )[0];
-
-        if (!row) {
-            new_row = true;
-            row = dojo.clone(this._appliedDistribTemplate);
-            dojo.attr(row, "formula", dfa.formula().id());
-            dojo.query("th", row)[0].innerHTML = dfa.formula().name();
-        }
-
-        var td = dojo.query("td", row)[0];
-
-        dojo.create("span", {"id": "dfa-button-" + dfa.id()}, td, "last");
-        dojo.create("span", {"id": "dfa-tip-" + dfa.id()}, td, "last");
-
-        if (new_row)
-            dojo.place(row, "acq-lit-distrib-applied-tbody", "last");
-
-        new dijit.form.Button(
-            {
-                "onClick": function() {
-                    if (confirm(localeStrings.EXPLAIN_DFA_MGMT))
-                        self.deleteDfa(dfa);
-                },
-                "label": "X",
-                /* XXX I /cannot/ make the following work in as a CSS class
-                 * for some reason. So frustrating... */
-                "style": function(id) {
-                     return (id > 0 ?
-                        "font-weight: bold; color: #c00;" :
-                        "color: #666;");
-                     }(dfa.id()) + "margin: 0 6px;display: inline;"
-            }, "dfa-button-" + dfa.id()
-        );
-        new dijit.Tooltip(
-            {
-                "connectId": ["dfa-button-" + dfa.id()],
-                "label": dojo.string.substitute(
-                    localeStrings.DFA_TIP, dfa.id() > 0 ? [
-                        openils.User.formalName(dfa.creator()),
-                        dojo.date.locale.format(
-                            dojo.date.stamp.fromISOString(dfa.create_time()),
-                            {"formatLength":"short"}
-                        )
-                    ] : [localeStrings.ITS_YOU, localeStrings.JUST_NOW]
-                )
-            }, "dfa-tip-" + dfa.id()
-        );
-    }
-
-    this.deleteDfa = function(dfa) {
-        if (dfa.id() > 0) { /* real */
-            this.pcrud.eliminate(
-                dfa, {
-                    "async": true,
-                    "oncomplete": function() {
-                        self._removeDistribApplied(dfa.id());
-                        delete self.realDfaCache[dfa.id()];
-                    }
-                }
-            );
-        } else { /* virtual */
-            if (--(this.virtDfaCounts[dfa.formula().id()]) < 0)
-            this.virtDfaCounts[dfa.formula().id()] = 0;
-            /* hasn't been saved yet, so no need to do anything server side */
-            this._removeDistribApplied(dfa.id());
-        }
-
-    };
-
-    this._removeDistribApplied = function(dfaId) {
-        var re = new RegExp("^dfa-\\w+-" + String(dfaId));
-        dijit.registry.forEach(
-            function(w) { if (re.test(w.id)) w.destroyRecursive(); }
-        );
-        this._removeDistribAppliedEmptyRows();
-    };
-
-    this._removeAllDistribAppliedVirtual = function() {
-        /* Unregister dijits */
-        dijit.registry.forEach(
-            function(w) { if (/^dfa-\w+--/.test(w.id)) w.destroyRecursive(); }
-        );
-        this._removeDistribAppliedEmptyRows();
-    };
-
-    this._removeDistribAppliedEmptyRows = function() {
-        /* Remove any rows with no DFA at all */
-        dojo.query("tr[formula] td", "acq-lit-distrib-applied-tbody").forEach(
-            function(o) {
-                if (o.childNodes.length < 1) dojo.destroy(o.parentNode);
-            }
-        );
-    };
-
-    /**
-     * Insert a new row into the distribution formula selection form
-     */
-    this._addDistribFormulaRow = function() {
-        var self = this;
-
-        if (!self.distribForms) {
-            // no formulas, hide the form
-            openils.Util.hide('acq-lit-distrib-formula-table');
-            return;
-        }
-
-        if(!this.distribFormulaTemplate) 
-            this.distribFormulaTemplate = 
-                dojo.byId('acq-lit-distrib-formula-tbody').removeChild(dojo.byId('acq-lit-distrib-form-row'));
-
-        var row = this.distribFormulaTemplate.cloneNode(true);
-        dojo.place(row, "acq-lit-distrib-formula-tbody", "only");
-
-        this.dfSelector = new dijit.form.FilteringSelect(
-            {"labelAttr": "dynLabel", "labelType": "html"},
-            nodeByName("selector", row)
-        );
-        this._updateFormulaStore();
-        this.dfSelector.fetchProperties =
-            {"sort": [{"attribute": "use_count", "descending": true}]};
-
-        var apply = new dijit.form.Button(
-            {"label": localeStrings.APPLY},
-            nodeByName('set_button', row)
-        ); 
-
-        var reset = new dijit.form.Button(
-            {"label": localeStrings.RESET_FORMULAE, "disabled": true},
-            nodeByName("reset_button", row)  
-        );
-
-        dojo.connect(apply, 'onClick', 
-            function() {
-                var form_id = self.dfSelector.attr("value");
-                if(!form_id) return;
-                self._applyDistribFormula(form_id);
-                reset.attr("disabled", false);
-            }
-        );
-
-        dojo.connect(reset, 'onClick', 
-            function() {
-                self.restoreCopyFieldsBeforeDF();
-                self.virtDfaCounts = {};
-                self.virtDfaId = -1;
-                self.dfeOffset = 0;
-                self._updateFormulaStore();
-                self._removeAllDistribAppliedVirtual();
-                reset.attr("disabled", "true");
-            }
-        );
-
-    };
-
-    /**
-     * Applies a distrib formula to the current set of copies
-     */
-    this._applyDistribFormula = function(formula) {
-        if(!formula) return;
-
-        formula = this.distribForms.filter(
-            function(form) { return form.id() == formula; }
-        )[0];
-
-        var copyRows = dojo.query('tr', self.copyTbody);
-
-        if (this.dfeOffset >= copyRows.length) {
-            alert(localeStrings.OUT_OF_COPIES);
-            return;
-        }
-
-        var entries_applied = 0;
-        for(
-            var rowIndex = this.dfeOffset;
-            rowIndex < copyRows.length;
-            rowIndex++
-        ) {
-            
-            var row = copyRows[rowIndex];
-            var copy_id = row.getAttribute('copy_id');
-            var copyWidgets = this.copyWidgetCache[copy_id];
-            var entryIndex = this.dfeOffset;
-            var entry = null;
-
-            // find the correct entry for the current row
-            dojo.forEach(formula.entries(), 
-                function(e) {
-                    if(!entry) {
-                        entryIndex += e.item_count();
-                        if(entryIndex > rowIndex)
-                            entry = e;
-                    }
-                }
-            );
-
-            if(entry) {
-                
-                //console.log("rowIndex = " + rowIndex + ", entry = " + entry.id() + ", entryIndex=" + 
-                //  entryIndex + ", owning_lib = " + entry.owning_lib() + ", location = " + entry.location());
-    
-                entries_applied++;
-                this.saveCopyFieldsBeforeDF(copy_id);
-                this._copy_fields_for_acqdf.forEach(
-                    function(field) {
-                        if(entry[field]()) {
-                            copyWidgets[field].attr('value', (entry[field]()));
-                        }
-                    }
-                );
-            }
-        }
-
-        if (entries_applied) {
-            this.virtDfaCounts[formula.id()] =
-                ++(this.virtDfaCounts[formula.id()]) || 1;
-            this._updateFormulaStore();
-            this._drawDistribAppliedUnit(
-                function(df) {
-                    var dfa = new acqdfa();
-                    dfa.formula(df); dfa.id(self.virtDfaId--); return dfa;
-                }(formula)
-            );
-            this.dfeOffset += entries_applied;
-        };
-    };
-
-    /**
-     * This function updates the DF store for the dropdown so that use_counts
-     * can reflect DF applications from this session before they're saved
-     * server-side.
-     */
-    this._updateFormulaStore = function() {
-        this.dfSelector.store = new dojo.data.ItemFileReadStore(
-            {
-                "data": self._labelFormulasWithCounts(
-                    acqdf.toStoreData(self.distribForms)
-                )
-            }
-        );
-    };
-
-    this.saveCopyFieldsBeforeDF = function(copy_id) {
-        var self = this;
-        if (!this.oldCopyWidgetCache[copy_id]) {
-            var copyWidgets = this.copyWidgetCache[copy_id];
-
-            this.oldCopyWidgetCache[copy_id] = {};
-            this._copy_fields_for_acqdf.forEach(
-                function(f) {
-                    self.oldCopyWidgetCache[copy_id][f] =
-                        copyWidgets[f].attr("value");
-                }
-            );
-        }
-    };
-
-    this.restoreCopyFieldsBeforeDF = function() {
-        var self = this;
-        for (var copy_id in this.oldCopyWidgetCache) {
-            this._copy_fields_for_acqdf.forEach(
-                function(f) {
-                    self.copyWidgetCache[copy_id][f].attr(
-                        "value", self.oldCopyWidgetCache[copy_id][f]
-                    );
-                }
-            );
-        }
-    };
-
-    this._labelFormulasWithCounts = function(store_data) {
-        for (var key in store_data.items) {
-            var obj = store_data.items[key];
-            obj.use_count = Number(obj.use_count); /* needed for sorting */
-
-            if (this.virtDfaCounts[obj.id])
-                obj.use_count = obj.use_count + Number(this.virtDfaCounts[obj.id]);
-
-            obj.dynLabel = "<span class='acq-lit-distrib-form-use-count'>[" +
-                obj.use_count + "]</span>&nbsp; " + obj.name;
-        }
-        return store_data;
-    };
-
-    /**
-     * This method formerly would not refetch the DF formulas if they'd been
-     * loaded already, but now it always re-fetches, since use_count changes.
-     */
-    /** TODO: port distrib-formula selector to autofieldwidget+pcrud/dojo store */
-    this._fetchDistribFormulas = function(onload) {
-        fieldmapper.standardRequest(
-            ["open-ils.acq",
-                "open-ils.acq.distribution_formula.ranged.retrieve.atomic"],
-            {
-                "async": true,
-                "params": [openils.User.authtoken, 0, 500],
-                "oncomplete": function(r) {
-                    self.distribForms = openils.Util.readResponse(r);
-                    if(!self.distribForms || self.distribForms.length == 0) {
-                        self.distribForms = [];
-                    }
-                    self._addDistribFormulaRow();
-                    onload();
-                }
-            }
-        );
-    }
-
-    this._drawBatchCopyWidgets = function() {
-        var row = this.copyBatchRow;
-        dojo.forEach(liDetailBatchFields, 
-            function(field) {
-                if(self.copyBatchRowDrawn) {
-                    self.copyBatchWidgets[field].attr('value', null);
-                } else {
-                    var widget = new openils.widget.AutoFieldWidget({
-                        fmField : field,
-                        fmClass : 'acqlid',
-                        labelFormat : (field == 'fund') ? fundLabelFormat : null,
-                        searchFormat : (field == 'fund') ? fundSearchFormat : null,
-                        searchFilter : (field == 'fund') ? {"active": "t"} : null,
-                        parentNode : dojo.query('[name='+field+']', row)[0],
-                        orgLimitPerms : ['CREATE_PICKLIST'],
-                        dijitArgs : {
-                            "required": false,
-                            "labelType": (field == "fund") ? "html" : null
-                        },
-                        noCache: (field == "fund"),
-                        forceSync : true
-                    });
-                    widget.build(
-                        function(w, ww) {
-                            if (field == "fund" && w.store)
-                                self._ensureCSSFundClasses(w.store);
-                            self.copyBatchWidgets[field] = w;
-                        }
-                    );
-                    if (field == "fund") {
-                        dojo.connect(
-                            widget.widget, "onChange", function(val) {
-                                self._updateFundSelectorStyle(widget, val);
-                            }
-                        );
-                    }
-                }
-            }
-        );
-        this.copyBatchRowDrawn = true;
-    };
-
-    this.batchCopyUpdate = function() {
-        var self = this;
-        for(var k in this.copyWidgetCache) {
-            var cache = this.copyWidgetCache[k];
-            dojo.forEach(liDetailBatchFields, function(f) {
-                var newval = self.copyBatchWidgets[f].attr('value');
-                if(newval) cache[f].attr('value', newval);
-            });
-        }
-    };
-
-    this._drawCopies = function(li) {
-        var self = this;
-
-        // this button sets the total number of copies for a given lineitem
-        acqLitAddCopyCount.onClick = function() { 
-            var count = acqLitCopyCountInput.attr('value');
-
-            // add new rows
-            while(self.copyCount() < count)
-                self.addCopy(li); 
-            
-            // delete rows if necessary
-            var diff = self.copyCount() - count;
-            if(diff > 0) {
-                var rows = dojo.query('tr', self.copyTbody).reverse().slice(0, diff);
-                if(confirm(dojo.string.substitute(localeStrings.DELETE_LI_COPIES_CONFIRM, [diff]))) {
-                    dojo.forEach(rows, function(row) {self.deleteCopy(row); });
-                } else {
-                    acqLitCopyCountInput.attr('value', self.copyCount()+'');
-                }
-            }
-        }
-
-
-        if(li.lineitem_details().length > 0) {
-            dojo.forEach(li.lineitem_details(),
-                function(copy) {
-                    self.addCopy(li, copy);
-                }
-            );
-        } else {
-            self.addCopy(li);
-        }
-    };
-
-    this.copyCount = function() {
-        var count = 0;
-        for(var id in this.copyCache) {
-            if(!this.copyCache[id].isdeleted())
-                count++;
-        }
-        return count;
-    }
-
-    this.virtCopyId = -1;
-    this.addCopy = function(li, copy) {
-        var row = this.copyRow.cloneNode(true);
-        this.copyTbody.appendChild(row);
-        var self = this;
-
-        if(!copy) {
-            copy = new fieldmapper.acqlid();
-            copy.isnew(true);
-            copy.id(this.virtCopyId--);
-            copy.lineitem(li.id());
-        }
-
-        this.copyCache[copy.id()] = copy;
-        row.setAttribute('copy_id', copy.id());
-        self.copyWidgetCache[copy.id()] = {};
-
-        acqLitCopyCountInput.attr('value', self.copyCount()+'');
-
-        var rcvr = copy.receiver();
-        if (rcvr) {
-            if (!userCache[rcvr]) {
-                if(rcvr == openils.User.user.id()) {
-                    userCache[rcvr] = openils.User.user;
-                } else {
-                    userCache[rcvr] = fieldmapper.standardRequest(
-                        ['open-ils.actor', 'open-ils.actor.user.retrieve'],
-                        {params: [openils.User.authtoken, rcvr]}
-                    );
-                }
-            }
-            dojo.query('[name=receiver]', row)[0].innerHTML =  userCache[rcvr].usrname();
-        }
-
-        dojo.forEach(liDetailFields,
-            function(field) {
-                var searchFilter;
-                if (field == "fund") {
-                    searchFilter = (copy.fund() ?
-                        {"-or": {"active": "t", "id": copy.fund()}} :
-                        {"active" : "t"});
-                } else {
-                    searchFilter = null;
-                }
-
-                var readOnly = false;
-                
-                // TODO: Add support for changing the owning_lib after real copies have been made.  
-                // owning_lib is order data as much as its item data
-                if(copy.eg_copy_id() && ['owning_lib', 'location', 'circ_modifier', 'cn_label', 'barcode'].indexOf(field) >= 0) {
-                    readOnly = true;
-                }
-
-                // TODO: add support for changing the fund after debits have been created
-                // Note: invoicing allows the change
-                if(copy.fund_debit() && field == 'fund') {
-                    readOnly = true;
-                }
-
-
-                var widget = new openils.widget.AutoFieldWidget({
-                    fmObject : copy,
-                    fmField : field,
-                    labelFormat : (field == 'fund') ? fundLabelFormat : null,
-                    searchFormat : (field == 'fund') ? fundSearchFormat : null,
-                    dijitArgs: {"labelType": (field == 'fund') ? "html" : null},
-                    searchFilter : searchFilter,
-                    noCache: (field == "fund"),
-                    fmClass : 'acqlid',
-                    parentNode : dojo.query('[name='+field+']', row)[0],
-                    orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
-                    readOnly : readOnly,
-                    orgDefaultsToWs : true
-                });
-
-                widget.build(
-                    // make sure we capture the value from any async widgets
-                    function(w, ww) { 
-
-                        if (field == "fund" && w.store)
-                            self._ensureCSSFundClasses(w.store);
-
-                        if(!readOnly) 
-                            copy[field](ww.getFormattedValue()) 
-
-                        self.copyWidgetCache[copy.id()][field] = w;
-
-                        dojo.connect(w, 'onChange', 
-                            function(val) { 
-                                if (field == "fund")
-                                    self._updateFundSelectorStyle(widget, val);
-
-                                if (!readOnly && (copy.isnew() || val != copy[field]())) {
-                                    // prevent setting ischanged() automatically on widget load for existing copies
-                                    copy[field](widget.getFormattedValue()) 
-                                    copy.ischanged(true);
-                                }
-                            }
-                        );
-                    }
-                );
-            }
-        );
-
-        this.updateLidState(copy, row);
-    };
-
-    this._ensureCSSFundClass = function(id) {
-        if (!this.fundStyleSheet) {
-            dojo.create(
-                "style", {"type": "text/css"},
-                document.getElementsByTagName("head")[0], "last"
-            );
-            this.fundStyleSheet = document.styleSheets[
-                document.styleSheets.length - 1
-            ];
-        }
-
-        var cn = "fund_" + id;
-        if (!this.haveFundClass[cn]) {
-            fieldmapper.standardRequest(
-                ["open-ils.acq", "open-ils.acq.fund.check_balance_percentages"],
-                {
-                    "params": [openils.User.authtoken, id],
-                    "async": true,
-                    "oncomplete": function(r) {
-                        r = openils.Util.readResponse(r);
-                        self.fundBalanceState[id] = r;
-                        var style = "";
-                        if (r[0] /* stop */)
-                            style = fundStyles.stop;
-                        else if (r[1] /* warning */)
-                            style = fundStyles.warning;
-                        self.fundStyleSheet.insertRule(
-                            "." + cn + " { " + style + " }",
-                            self.fundStyleSheet.cssRules.length
-                        );
-                        self.haveFundClass[cn] = true;
-                    }
-                }
-            );
-        }
-    };
-
-    this._ensureCSSFundClasses = function(store) {
-        store.fetch({
-            "query": {"id": "*"},
-            "onItem": function(o) { self._ensureCSSFundClass(o.id[0]); }
-        });
-    };
-
-    this._updateFundSelectorStyle = function(widget, fund_id) {
-        openils.Util.removeCSSClass(widget.widget.domNode, /fund_\d+/);
-        openils.Util.addCSSClass(widget.widget.domNode, "fund_" + fund_id);
-    };
-
-    this.updateLidState = function(copy, row) {
-        if (typeof(row) == "undefined") {
-            row = dojo.query(
-                'tr[copy_id="' + copy.id() + '"]', this.copyTbody
-            )[0];
-        }
-
-        var self = this;
-        var recv_link = nodeByName("receive", row);
-        var unrecv_link = nodeByName("unreceive", row);
-        var del_link = nodeByName("delete", row);
-        var cxl_link = nodeByName("cancel", row);
-        var claim_link = nodeByName("claim", row);
-        var cxl_reason_link = nodeByName("cancel_reason", row);
-
-        if (copy.cancel_reason()) {
-            openils.Util.hide(del_link.parentNode);
-            openils.Util.hide(recv_link);
-            openils.Util.hide(unrecv_link);
-            openils.Util.hide(cxl_link);
-            openils.Util.hide(claim_link);
-
-            /* XXX the following may leak memory in a long lived table: dijits may not get destroyed... not positive. revisit. */
-            var holds_reason = dojo.create(
-                "span", {
-                    "style": "border-bottom: 1px dashed #000;",
-                    "innerHTML": "Cancelled" /* XXX [sic] and i18n */
-                }, cxl_reason_link, "only"
-            );
-            new dijit.Tooltip(
-                {
-                    "label": "<em>" + copy.cancel_reason().label() +
-                        "</em><br />" + copy.cancel_reason().description(),
-                    "connectId": [holds_reason]
-                }, dojo.create("span", null, cxl_reason_link, "last")
-            );
-            openils.Util.show(cxl_reason_link, "inline");
-        } else if (this.isPO) {
-            /* Only using this in one place so far, but may want it for better
-             * decisions on when to display certain controls. */
-            var li_state = this.liCache[copy.lineitem()].state();
-
-            openils.Util.hide(del_link.parentNode);
-            openils.Util.hide(cxl_reason_link);
-
-            /* Avoid showing (un)receive links, cancel links, for virt copies */
-            if (copy.id() > 0) {
-                if (copy.recv_time()) {
-                    openils.Util.hide(cxl_link);
-                    openils.Util.hide(recv_link);
-                    openils.Util.hide(claim_link);
-
-                    openils.Util.show(unrecv_link, "inline");
-                    unrecv_link.onclick = function() {
-                        if (confirm(localeStrings.UNRECEIVE_LID))
-                            self.issueReceive(copy, /* rollback */ true);
-                    };
-                } else {
-                    openils.Util.hide(unrecv_link);
-
-                    if (this.claimEligibleLid[copy.id()]) {
-                        openils.Util.show(claim_link, "inline");
-                        claim_link.onclick = function() {
-                            self.claimDialog.show(
-                                self.liCache[copy.lineitem()], copy.id()
-                            );
-                        };
-                    } else {
-                        openils.Util.hide(claim_link);
-                    }
-
-                    openils.Util[li_state == "on-order" ? "show" : "hide"](
-                        recv_link, "inline"
-                    );
-                    openils.Util.show(cxl_link, "inline");
-                    recv_link.onclick = function() {
-                        if (self.checkLiAlerts(copy.lineitem()))
-                            self.issueReceive(copy);
-                    };
-                    cxl_link.onclick = function() {
-                        self.cancelLid(copy.id());
-                    };
-                }
-            } else {
-                openils.Util.hide(cxl_link);
-                openils.Util.hide(unrecv_link);
-                openils.Util.hide(recv_link);
-                openils.Util.hide(claim_link);
-            }
-        } else {
-            openils.Util.hide(unrecv_link);
-            openils.Util.hide(recv_link);
-            openils.Util.hide(cxl_reason_link);
-            openils.Util.hide(claim_link);
-
-            del_link.onclick = function() { self.deleteCopy(row) };
-            openils.Util.show(del_link.parentNode);
-        }
-    }
-
-    this.cancelLid = function(lid_id) {
-        lidCancelDialog._lid_id = lid_id;
-        openils.Util.show(lidCancelDialog.domNode.parentNode);
-        lidCancelDialog.show();
-        if (!lidCancelDialog._prepared) {
-            var widget = new openils.widget.AutoFieldWidget({
-                "fmField": "cancel_reason",
-                "fmClass": "acqlid",
-                "parentNode": dojo.byId("acq-lit-lid-cancel-reason"),
-                "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
-                "forceSync": true
-            });
-            widget.build(
-                function(w, ww) {
-                    acqLidCancelButton.onClick = function() {
-                        if (w.attr("value")) {
-                            if (confirm(localeStrings.LID_CANCEL_CONFIRM)) {
-                                self._cancelLid(
-                                    lidCancelDialog._lid_id,
-                                    w.attr("value")
-                                );
-                            }
-                            lidCancelDialog.hide();
-                        }
-                    };
-                    lidCancelDialog._prepared = true;
-                }
-            );
-        }
-    };
-
-    this._cancelLid = function(lid_id, reason) {
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem_detail.cancel"], {
-                "params": [openils.User.authtoken, lid_id, reason],
-                "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        if (r.lid) {
-                            for (var id in r.lid) {
-                                /* actually this should only iterate once */
-                                self.copyCache[id].cancel_reason(
-                                    r.lid[id].cancel_reason
-                                );
-                                self.updateLidState(self.copyCache[id]);
-                            }
-                        }
-                    }
-                }
-            }
-        );
-    };
-
-    this._confirmAlert = function(li, lin) {
-        return confirm(
-            dojo.string.substitute(
-                localeStrings.CONFIRM_LI_ALERT, [
-                    (new openils.acq.Lineitem({"lineitem": li})).findAttr(
-                        "title", "lineitem_marc_attr_definition"
-                    ),
-                    lin.alert_text().code(),
-                    lin.alert_text().description() || "",
-                    lin.value()
-                ]
-            )
-        );
-    };
-
-    this.checkLiAlerts = function(li_id) {
-        var li = this.liCache[li_id];
-
-        var alert_notes = li.lineitem_notes().filter(
-            function(o) { return Boolean(o.alert_text()); }
-        );
-
-        /* this is _intentionally_ not done in a call to forEach() ... */
-        for (var i = 0; i < alert_notes.length; i++) {
-            if (this.noteAcks[alert_notes[i].id()])
-                continue;
-            else if (!this._confirmAlert(li, alert_notes[i]))
-                return false;
-            else
-                this.noteAcks[alert_notes[i].id()] = true;
-        }
-
-        return true;
-    };
-
-    this.deleteCopy = function(row) {
-        var copy = this.copyCache[row.getAttribute('copy_id')];
-        copy.isdeleted(true);
-        if(copy.isnew())
-            delete this.copyCache[copy.id()];
-        this.copyTbody.removeChild(row);
-    }
-
-    this._virtDfaCountsAsList = function() {
-        var L = [];
-        for (var key in this.virtDfaCounts) {
-            for (var i = 0; i < this.virtDfaCounts[key]; i++)
-                L.push(key);
-        }
-        return L;
-    }
-
-    this.confirmBreachedCopyFunds = function(copies) {
-        var stop = 0, warning = 0;
-        copies.forEach(
-            function(o) {
-                if (o.fund()) {
-                    var state = self.fundBalanceState[o.fund()];
-                    if (state[0] /* stop */)
-                        stop++;
-                    else if (state[1] /* warning */)
-                        warning++;
-                }
-            }
-        );
-
-        if (stop) {
-            return confirm(localeStrings.CONFIRM_FUNDS_AT_STOP);
-        } else if (warning) {
-            return confirm(localeStrings.CONFIRM_FUNDS_AT_WARNING);
-        }
-        return true;
-    };
-
-    this.saveCopyChanges = function(liId) {
-        var self = this;
-        var copies = [];
-
-
-        var total = 0;
-        for(var id in this.copyCache) {
-            var c = this.copyCache[id];
-            if(!c.isdeleted()) total++;
-            if(c.isnew() || c.ischanged() || c.isdeleted()) {
-                if(c.id() < 0) c.id(null);
-                copies.push(c);
-            }
-        }
-
-
-        dojo.byId('acq-lit-copy-count-label-' + liId).innerHTML = total;
-
-
-        if (copies.length > 0) {
-            if (!this.confirmBreachedCopyFunds(copies))
-                return;
-
-            if (typeof(this._copy_count_cb) == "function")
-                this._copy_count_cb(liId, total);
-
-            openils.Util.show("acq-lit-update-copies-progress");
-            fieldmapper.standardRequest(
-                ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
-                {   async: true,
-                    params: [openils.User.authtoken, copies],
-                    onresponse: function(r) {
-                        var res = openils.Util.readResponse(r);
-                        litUpdateCopiesProgress.update(res);
-                    },
-                    oncomplete: function() {
-                        self.drawCopies(liId, true /* force_fetch */);
-                        openils.Util.hide("acq-lit-update-copies-progress");
-                    }
-                }
-            );
-        }
-
-        var dfa_list = this._virtDfaCountsAsList();
-        if (dfa_list.length > 0) {
-            fieldmapper.standardRequest(
-                ["open-ils.acq",
-                "open-ils.acq.distribution_formula.record_application"],
-                {
-                    "async": true,
-                    "params": [openils.User.authtoken, dfa_list, liId],
-                    "onresponse": function(r) {
-                        var res = openils.Util.readResponse(r);
-                        if (res && res.length < dfa_list.length)
-                            alert(localeStrings.DFA_NOT_ALL);
-                    }
-                }
-            );
-            this.virtDfaCounts = {};
-        }
-    }
-
-    this._updateCreatePoPrepayCheckbox = function(prepay) {
-        var prepay = openils.Util.isTrue(prepay);
-        this._prepayRequiredByVendor = prepay;
-        dijit.byId("acq-lit-po-prepay").attr("checked", prepay);
-    };
-
-    this._confirmPoPrepaySituation = function() {
-        var want_prepay = dijit.byId("acq-lit-po-prepay").attr("checked");
-        if (want_prepay != this._prepayRequiredByVendor) {
-            return confirm(
-                want_prepay ?
-                    localeStrings.VENDOR_SAYS_PREPAY_NOT_NEEDED :
-                    localeStrings.VENDOR_SAYS_PREPAY_NEEDED
-            );
-        } else {
-            return true;
-        }
-    };
-
-    this.applySelectedLiAction = function(action) {
-        var self = this;
-        switch(action) {
-
-            case 'delete_selected':
-                this._deleteLiList(self.getSelected());
-                break;
-
-            case 'create_order':
-                this._loadPOSelect();
-                acqLitPoCreateDialog.show();
-                break;
-
-            case 'save_picklist':
-                acqLitSavePlDialog.show();
-                break;
-
-            case 'selector_ready':
-            case 'order_ready':
-                acqLitChangeLiStateDialog.attr('state', action.replace('_', '-'));
-                acqLitChangeLiStateDialog.show();
-                break;
-
-            case 'print_po':
-                this.printPO();
-                break;
-
-            case 'po_history':
-                location.href = oilsBasePath + '/acq/po/history/' + this.isPO;
-                break;
-
-            case 'receive_po':
-                this.receivePO();
-                break;
-
-            case 'rollback_receive_po':
-                this.rollbackPoReceive();
-                break;
-
-            case 'create_assets':
-                this.showAssetCreator();
-                break;
-
-            case 'export_attr_list':
-                this.chooseExportAttr();
-                break;
-
-            case 'batch_apply_funds':
-                this.applyBatchLiFunds();
-                break;
-
-            case 'add_brief_record':
-                if(this.isPO)
-                    location.href = oilsBasePath + '/acq/picklist/brief_record?po=' + this.isPO;
-                else
-                    location.href = oilsBasePath + '/acq/picklist/brief_record?pl=' + this.isPL;
-
-                break;
-
-            case "cancel_lineitems":
-                this.maybeCancelLineitems();
-                break;
-
-            case "change_claim_policy":
-                var li_list = this.getSelected();
-                this.claimPolicyPicker.attr("value", null);
-                liClaimPolicyDialog.show();
-                liClaimPolicySave.onClick = function() {
-                    self.changeClaimPolicy(
-                        li_list,
-                        self.claimPolicyPicker.attr("value"),
-                        function() {
-                            li_list.forEach(
-                                function(li) {
-                                    self.setClaimPolicyControl(li);
-                                    self.reconsiderClaimControl(li);
-                                }
-                            );
-                            liClaimPolicyDialog.hide();
-                        }
-                    )
-                };
-                break;
-        }
-    };
-
-    this.changeClaimPolicy = function(li_list, value, callback) {
-        li_list.forEach(
-            function(li) { li.claim_policy(value); }
-        );
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem.update"], {
-                "params": [openils.User.authtoken, li_list],
-                "async": true,
-                "oncomplete": function(r) {
-                    r = openils.Util.readResponse(r);
-                    if (callback) callback(r);
-                }
-            }
-        );
-    };
-
-    this.showAssetCreator = function(onAssetsCreated) {
-        if(!this.isPO) return;
-        var self = this;
-    
-        // first, let's see if this PO has any LI's that need to be merged/imported
-        self.pcrud.search('jub', {purchase_order : this.isPO, eg_bib_id : null}, {
-            id_list : true,
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp && resp.length) {
-                    // PO has some non-linked jubs.  
-                    
-                    self.show('asset-creator');
-                    if(!self.vlAgent.loaded)
-                        self.vlAgent.init();
-
-                    dojo.connect(assetCreatorButton, 'onClick', 
-                        function() { self.createAssets(onAssetsCreated) });
-
-                } else {
-
-                    // all jubs linked, move on to asset creation
-                    self.createAssets(onAssetsCreated, true); 
-                }
-            }
-        });
-    }
-
-    this.createAssets = function(onAssetsCreated, noVl) {
-        this.show('acq-lit-progress-numbers');
-        var self = this;
-        var vlArgs = (noVl) ? {} : {vandelay : this.vlAgent.values()};
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.assets.create'],
-            {   async: true,
-                params: [this.authtoken, this.isPO, vlArgs],
-                onresponse: function(r) {
-                    var resp = openils.Util.readResponse(r);
-                    self._updateProgressNumbers(resp, !Boolean(onAssetsCreated), onAssetsCreated);
-                }
-            }
-        );
-    }
-
-    this.maybeCancelLineitems = function() {
-        openils.Util.show("acq-lit-cancel-reason", "inline");
-        if (!acqLitCancelLineitemsButton._prepared) {
-            var widget = new openils.widget.AutoFieldWidget({
-                "fmField": "cancel_reason",
-                "fmClass": "jub",
-                "parentNode": dojo.byId("acq-lit-cancel-reason-selector"),
-                "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
-                "forceSync": true
-            });
-            widget.build(
-                function(w, ww) {
-                    acqLitCancelLineitemsButton.onClick = function() {
-                        if (w.attr("value")) {
-                            if (confirm(localeStrings.LI_CANCEL_CONFIRM)) {
-                                self._cancelLineitems(w.attr("value"));
-                            }
-                            openils.Util.hide("acq-lit-cancel-reason");
-                        }
-                    };
-                    acqLitCancelLineitemsButton._prepared = true;
-                }
-            );
-        }
-    };
-
-    this._cancelLineitems = function(reason) {
-        var id_list = this.getSelected().map(function(o) { return o.id(); });
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem.cancel.batch"], {
-                "params": [openils.User.authtoken, id_list, reason],
-                "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        if (r.li) {
-                            for (var id in r.li) {
-                                self.liCache[id].state(r.li[id].state);
-                                self.liCache[id].cancel_reason(
-                                    r.li[id].cancel_reason
-                                );
-                                self.updateLiState(self.liCache[id]);
-                            }
-                        }
-                        if (r.lid && self.copyCache) {
-                            for (var id in r.lid) {
-                                if (self.copyCache[id]) {
-                                    self.copyCache[id].cancel_reason(
-                                        r.lid[id].cancel_reason
-                                    );
-                                    self.updateLidState(self.copyCache[id]);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        );
-    };
-
-    this.chooseExportAttr = function() {
-        if (!acqLitExportAttrSelector._li_setup) {
-            var self = this;
-            acqLitExportAttrSelector.store = new dojo.data.ItemFileReadStore(
-                {
-                    "data": acqlimad.toStoreData(
-                        this.pcrud.search(
-                            "acqlimad", {"code": li_exportable_attrs}
-                        )
-                    )
-                }
-            );
-            acqLitExportAttrSelector.setValue();
-            acqLitExportAttrButton.onClick = function(){self.exportAttrList();};
-            acqLitExportAttrSelector._li_setup = true;
-        }
-        openils.Util.show("acq-lit-export-attr-holder", "inline");
-    };
-
-    this.exportAttrList = function() {
-        var attr_def = acqLitExportAttrSelector.item;
-        var li_list = this.getSelected();
-        var value_list = li_list.map(
-            function(li) {
-                return (new openils.acq.Lineitem({"lineitem": li})).findAttr(
-                    attr_def.code, "lineitem_marc_attr_definition"
-                );
-            }
-        ).filter(function(attr) { return Boolean(attr); });
-
-        if (value_list.length > 0) {
-            if (value_list.length < li_list.length) {
-                if (!confirm(
-                    dojo.string.substitute(
-                        localeStrings.EXPORT_SHORT_LIST, [attr_def.description]
-                    )
-                )) {
-                    return;
-                }
-            }
-            try {
-                openils.XUL.contentToFileSaveDialog(
-                    value_list.join("\n"),
-                    localeStrings.EXPORT_SAVE_DIALOG_TITLE
-                );
-            } catch (E) {
-                alert(E);
-            }
-        } else {
-            alert(dojo.string.substitute(
-                localeStrings.EXPORT_EMPTY_LIST, [attr_def.description]
-            ));
-        }
-
-        openils.Util.hide("acq-lit-export-attr-holder");
-    };
-
-    this.printPO = function() {
-        if(!this.isPO) return;
-        progressDialog.show(true);
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.format'],
-            {   async: true,
-                params: [this.authtoken, this.isPO, 'html'],
-                oncomplete: function(r) {
-                    progressDialog.hide();
-                    var evt = openils.Util.readResponse(r);
-                    if(evt && evt.template_output()) {
-                        openils.Util.printHtmlString(evt.template_output().data());
-                    }
-                }
-            }
-        );
-    }
-
-
-    this.receivePO = function() {
-        if (!this.isPO) return;
-
-        for (var id in this.liCache) {
-            /* assumption: liCache reflects exactly the
-             * set of LIs that belong to our PO */
-            if (this.liCache[id].state() != "received" &&
-                !this.checkLiAlerts(id)) return;
-        }
-
-        this.show('acq-lit-progress-numbers');
-        var self = this;
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.receive'],
-            {   async: true,
-                params: [this.authtoken, this.isPO],
-                onresponse : function(r) {
-                    var resp = openils.Util.readResponse(r);
-                    self._updateProgressNumbers(resp, true);
-                },
-            }
-        );
-    }
-
-    this.issueReceive = function(obj, rollback) {
-        /* (For now) there shall be no marking LI or LIDs (un)received
-         * except from the actual "view PO" interface. */
-        if (!this.isPO) return;
-
-        var part =
-            {"jub": "lineitem", "acqlid": "lineitem_detail"}[obj.classname];
-        var method =
-            "open-ils.acq." + part + ".receive" + (rollback ? ".rollback" : "");
-
-        progressDialog.show(true);
-        fieldmapper.standardRequest(
-            ["open-ils.acq", method], {
-                "async": true,
-                "params": [this.authtoken, obj.id()],
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        self.fetchClaimInfo(
-                            part == "lineitem" ? obj.id() : obj.lineitem(),
-                            /* force */ true,
-                            function() { self.handleReceive(r); }
-                        );
-                        progressDialog.hide();
-                    }
-                }
-            }
-        );
-    };
-
-    /**
-     * Handles the responses from receive and rollback ML calls.
-     */
-    this.handleReceive = function(resp) {
-        if (resp) {
-            if (resp.li) {
-                for (var li_id in resp.li) {
-                    for (var key in resp.li[li_id])
-                        self.liCache[li_id][key](resp.li[li_id][key]);
-                    self.updateLiState(self.liCache[li_id]);
-                }
-            }
-            if (resp.po) {
-                if (typeof(self.poUpdateCallback) == "function")
-                    self.poUpdateCallback(resp.po);
-            }
-            if (resp.lid) {
-                for (var lid_id in resp.lid) {
-                    for (var key in resp.lid[lid_id])
-                        self.copyCache[lid_id][key](resp.lid[lid_id][key]);
-                    self.updateLidState(self.copyCache[lid_id]);
-                }
-            }
-        }
-    };
-
-    this.rollbackPoReceive = function() {
-        if(!this.isPO) return;
-        if(!confirm(localeStrings.ROLLBACK_PO_RECEIVE_CONFIRM)) return;
-        this.show('acq-lit-progress-numbers');
-        var self = this;
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.receive.rollback'],
-            {   async: true,
-                params: [this.authtoken, this.isPO],
-                onresponse : function(r) {
-                    var resp = openils.Util.readResponse(r);
-                    self._updateProgressNumbers(resp, true);
-                },
-            }
-        );
-    }
-
-    this._updateProgressNumbers = function(resp, reloadOnComplete, onComplete) {
-        this.vlAgent.handleResponse(resp,
-            function(resp, res) {
-                if(reloadOnComplete)
-                     location.href = location.href;
-                if (onComplete)
-                    onComplete(resp, res);
-            }
-        );
-    }
-
-
-    this._createPO = function(fields) {
-        var wantall = (fields.create_from == "all");
-
-        /* If we're a picklist or purchase order already and the user wants
-         * all lineitems, we might have pages' worth of lineitems haven't all
-         * been loaded yet, so getSelected() won't find them.  The server,
-         * however, should know about all our lineitems, so let's ask the
-         * server for a complete list.
-         */
-
-        if (wantall) {
-            this.getSelected(
-                true, function(list) {
-                    self._createPOFromLineitems(fields, list);
-                }, /* id_list */ true
-            );
-        } else {
-            this._createPOFromLineitems(fields, this.getSelected(false, null, true /* id_list */));
-        }
-    };
-
-    this._createPOFromLineitems = function(fields, selected) {
-        if (selected.length == 0) return;
-        var self = this;
-
-        var po = new fieldmapper.acqpo();
-        po.provider(this.createPoProviderSelector.attr("value"));
-        po.ordering_agency(this.createPoAgencySelector.attr("value"));
-        po.prepayment_required(fields.prepayment_required[0] ? true : false);
-
-        // if we're creating assets, delay the asset creation 
-        // until after the PO is created.  This will allow us to 
-        // use showAssetCreator() directly.
-
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.purchase_order.create"],
-            {   async: true,
-                params: [
-                    openils.User.authtoken, 
-                    po, {lineitems : selected}
-                ],
-                onresponse : function(r) {
-                    var resp = openils.Util.readResponse(r);
-                    if (resp.complete) {
-                        // self.isPO is needed for showAssetCreator();
-                        self.isPO = resp.purchase_order.id(); 
-                        var redir = oilsBasePath + "/acq/po/view/" + self.isPO;
-                        if (fields.create_assets[0]) {
-                            self.showAssetCreator(
-                                function() {location.href = redir}
-                            );
-                        } else {
-                           location.href = redir;
-                        }
-                    }
-                }
-            }
-        );
-    };
-
-
-    this.batchFundWidget = null;
-
-    this.applyBatchLiFunds = function() {
-
-        var liIds = this.getSelected().map(function(li) { return li.id(); });
-        if(liIds.length == 0) return; // warn?
-
-        var self = this;
-        batchFundUpdateDialog.show();
-
-        if(!this.batchFundWidget) {
-            this.batchFundWidget = new openils.widget.AutoFieldWidget({
-                fmClass : 'acqf',
-                selfReference : true,
-                labelFormat : fundLabelFormat,
-                searchFormat : fundSearchFormat,
-                searchFilter : {"active": "t"},
-                parentNode : dojo.byId('acq-lit-batch-fund-selector'),
-                orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
-                dijitArgs : { "required": true, "labelType": "html" },
-                forceSync : true
-            });
-            this.batchFundWidget.build();
-        }
-
-        dojo.connect(batchFundUpdateCancel, 'onClick', function() { batchFundUpdateDialog.hide(); });
-        dojo.connect(batchFundUpdateSubmit, 'onClick', 
-            function() { 
-
-                // TODO: call .dry_run first to test thresholds
-                fieldmapper.standardRequest(
-                    ['open-ils.acq', 'open-ils.acq.lineitem.fund.update.batch'],
-                    {
-                        params : [
-                            openils.User.authtoken, 
-                            liIds,
-                            self.batchFundWidget.widget.attr('value')
-                        ],
-                        oncomplete : function(r) {
-                            var resp = openils.Util.readResponse(r);
-                            if(resp) {
-                                location.href = location.href;
-                            }
-                        }
-                    }
-                )
-            }
-        );
-    }
-
-    this._deleteLiList = function(list, idx) {
-        if(idx == null) idx = 0;
-        if(idx >= list.length) return;
-
-        var li = list[idx];
-        var liId = li.id();
-
-        if (this.isPO && (li.state() == "on-order" || li.state() == "received")) {
-            /* It makes little sense to delete a lineitem from a PO that has
-             * already been marked 'on-order'.  Especially if EDI is in use,
-             * such a purchase order will probably have already been shipped
-             * off to a vendor, and mucking with it at this point could leave
-             * your data in a bad state that doesn't jive with reality.
-             *
-             * I could see making this restriction even firmer.
-             *
-             * I could also see adjusting the li state comparisons, extending
-             * the comparison to the PO's state, and/or providing functions
-             * that house the logic for comparing states in a single location.
-             *
-             * Yes, this will be really annoying if you have selected a lot
-             * of lineitems to cancel that have been ordered. You'll get a
-             * confirm dialog for each one.
-             */
-
-            if (!confirm(localeStrings.DEL_LI_FROM_PO)) {
-                self._deleteLiList(list, ++idx); /* move on to next in list */
-                return;
-            }
-        }
-
-        fieldmapper.standardRequest(
-            ['open-ils.acq',
-             this.isPO ? 'open-ils.acq.purchase_order.lineitem.delete' : 'open-ils.acq.picklist.lineitem.delete'],
-            {   async: true,
-                params: [openils.User.authtoken, liId],
-                oncomplete: function(r) {
-                    self.removeLineitem(liId);
-                    self._deleteLiList(list, ++idx);
-                }
-            }
-        );
-    }
-
-    this.editOrderMarc = function(li) {
-
-        /*  To run in Firefox directly, must set signed.applets.codebase_principal_support
-            to true in about:config */
-
-        if(!openils.XUL.enableXPConnect()) return;
-
-        if(openils.XUL.isXUL()) {
-            win = window.open('/xul/' + openils.XUL.buildId() + '/server/cat/marcedit.xul');
-        } else {
-            win = window.open('/xul/server/cat/marcedit.xul'); 
-        }
-        var self = this;
-        win.xulG = {
-            record : {marc : li.marc(), "rtype": "bre"},
-            save : {
-                label: 'Save Record', // XXX I18N
-                func: function(xmlString) {
-                    li.marc(xmlString);
-                    fieldmapper.standardRequest(
-                        ['open-ils.acq', 'open-ils.acq.lineitem.update'],
-                        {   async: true,
-                            params: [openils.User.authtoken, li],
-                            oncomplete: function(r) {
-                                openils.Util.readResponse(r);
-                                win.close();
-                                self.drawInfo(li.id())
-                            }
-                        }
-                    );
-                },
-            },
-            'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
-            'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
-        };
-    }
-
-    this._savePl = function(values) {
-        this.getSelected(
-            (values.which == 'all'),
-            function(list) { self._savePlFromLineitems(values, list); }
-        );
-    };
-
-    this._savePlFromLineitems = function(values, selected) {
-        openils.Util.show("acq-lit-generic-progress");
-
-        if(values.new_name) {
-            openils.acq.Picklist.create(
-                {name: values.new_name},
-                function(id) {
-                    self._updateLiList(
-                        id, selected, 0,
-                        function() {
-                            location.href =
-                                oilsBasePath + "/acq/picklist/view/" + id;
-                        }
-                    );
-                }
-            );
-        } else if(values.existing_pl) {
-            // update lineitems to use an existing picklist
-            self._updateLiList(
-                values.existing_pl, selected, 0,
-                function(){
-                    location.href =
-                        oilsBasePath + "/acq/picklist/view/" +
-                        values.existing_pl;
-                }
-            );
-        }
-    };
-
-    this._updateLiState = function(values, state) {
-        progressDialog.show(true);
-        this.getSelected(
-            (values.which == 'all'),
-            function(list) {
-                self._updateLiStateFromLineitems(values, state, list);
-            }
-        );
-    };
-
-    this._updateLiStateFromLineitems = function(values, state, selected) {
-        if(!selected.length) return;
-        dojo.forEach(selected, function(li) {li.state(state);});
-        self._updateLiList(null, selected, 0,
-            // TODO consider inline updates for efficiency
-            function() { location.href = location.href }
-        );
-    };
-
-    this._updateLiList = function(pl, list, idx, oncomplete) {
-        if(idx >= list.length) return oncomplete();
-        var li = list[idx];
-        if(pl != null) li.picklist(pl);
-        litGenericProgress.update({maximum: list.length, progress: idx});
-        new openils.acq.Lineitem({lineitem:li}).update(
-            function(r) {
-                self._updateLiList(pl, list, ++idx, oncomplete);
-            }
-        );
-    }
-
-    this._loadPOSelect = function() {
-        if (!this.createPoProviderSelector) {
-            var widget = new openils.widget.AutoFieldWidget({
-                "fmField": "provider",
-                "fmClass": "acqpo",
-                "searchFilter": {"active": "t"},
-                "parentNode": dojo.byId("acq-lit-po-provider"),
-                "dijitArgs": {
-                    "onChange": function() {
-                        if (this.item) {
-                            self._updateCreatePoPrepayCheckbox(
-                                this.item.prepayment_required()
-                            );
-                        }
-                    }
-                }
-            });
-            widget.build(function(w) { self.createPoProviderSelector = w; });
-        }
-
-        if (!this.createPoAgencySelector) {
-            var widget = new openils.widget.AutoFieldWidget({
-                "fmField": "ordering_agency",
-                "fmClass": "acqpo",
-                "parentNode": dojo.byId("acq-lit-po-agency"),
-                "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
-            });
-            widget.build(function(w) { self.createPoAgencySelector = w; });
-        }
-    };
-
-    this.showRealCopyEditUI = function(li) {
-        copyList = [];
-        var self = this;
-        this.volCache = {};
-
-        this._fetchLineitem(li.id(), 
-            function(fullLi) {
-                li = self.liCache[li.id()] = fullLi;
-
-                self.pcrud.search(
-                    'acp', {
-                        id : li.lineitem_details().map(
-                            function(item) { return item.eg_copy_id() }
-                        )
-                    }, {
-                        async : true,
-                        oncomplete : function(r) {
-                            try {
-                                var r_list = openils.Util.readResponse( r );
-                                for (var i = 0; i < r_list.length; i++) {
-                                    var copy = r_list[i];
-                                    var volId = copy.call_number();
-                                    var volume = self.volCache[volId];
-                                    if(!volume) {
-                                        volume = self.volCache[volId] = self.pcrud.retrieve('acn', volId);
-                                    }
-                                    copy.call_number(volume);
-                                    copyList.push(copy);
-                                }
-                                if (xulG) {
-                                    xulG.volume_item_creator( { 'existing_copies' : copyList } );
-                                }
-                            } catch(E) {
-                                alert('error in oncomplete: ' + E);
-                            }
-                        }
-                    }
-                );
-            }
-        );
-    },
-
-    this.drawBibFinder = function(li) {
-
-        var query = '';
-        var liWrapper = new openils.acq.Lineitem({lineitem:li});
-
-        dojo.forEach(
-            ['isbn', 'upc', 'issn', 'title', 'author'],
-            function(field) {
-                var val = liWrapper.findAttr(field, 'lineitem_marc_attr_definition');
-                if(val) {
-                    if(field == 'title' || field == 'author') {
-                        query += field +':' + val + ' ';
-                    } else {
-                        query += 'identifier|' + field + ':' + val + ' ';
-                    }
-                }
-            }
-        );
-
-        win = window.open(
-            oilsBasePath + '/acq/lineitem/findbib?query=' + escape(query),
-            '', 'resizable,scrollbars=1');
-
-        win.window.recordFound = function(bibId) { 
-            win.close();
-
-            var attrs = li.attributes();
-            li.attributes(null);
-            li.eg_bib_id(bibId);
-
-            fieldmapper.standardRequest(
-                ["open-ils.acq", "open-ils.acq.lineitem.update"], 
-                {
-                    "params": [openils.User.authtoken, li],
-                    "async": true,
-                    "oncomplete": function(r) {
-                        if(openils.Util.readResponse(r)) {
-                            location.href = location.href;
-                        }
-                    }
-                }
-            );
-        }
-    }
-}
-
+require([
+       "dojo/date/locale",
+       "dojo/date/stamp",
+       "dijit/form/Button",
+       "dijit/form/TextBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Textarea",
+       "dijit/Tooltip",
+       "dijit/ProgressBar",
+       "openils/acq/Lineitem",
+       "openils/acq/PO",
+       "openils/acq/Picklist",
+       "openils/widget/AutoFieldWidget",
+       "dojo/data/ItemFileReadStore",
+       "openils/widget/ProgressDialog",
+       "openils/PermaCrud",
+       "openils/widget/PCrudAutocompleteBox"
+       ],
+function(dojo_date_locale,
+       dojo_date_stamp,
+       dijit_form_Button,
+       dijit_form_TextBox,
+       dijit_form_FilteringSelect,
+       dijit_form_Textarea,
+       dijit_Tooltip,
+       dijit_ProgressBar,
+       openils_acq_Lineitem,
+       openils_acq_PO,
+       openils_acq_Picklist,
+       openils_widget_AutoFieldWidget,
+       dojo_data_ItemFileReadStore,
+       openils_widget_ProgressDialog,
+       openils_PermaCrud,
+       openils_widget_PCrudAutocompleteBox){
+       
+       if (!localeStrings) {   /* we can do this because javascript doesn't have block scope */
+           dojo.requireLocalization('openils.acq', 'acq');
+           var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       }
+       const XUL_OPAC_WRAPPER = 'chrome://open_ils_staff_client/content/cat/opac.xul';
+       var li_exportable_attrs = ["issn", "isbn", "upc"];
+       
+       var fundLabelFormat = [
+           '<span class="fund_${0}">${1} (${2})</span>', 'id', 'code', 'year'
+       ];
+       var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
+       
+       function nodeByName(name, context) {
+           return dojo.query('[name='+name+']', context)[0];
+       }
+       
+       // for caching linked users.  e.g. lineitem_detail.receiver
+       var userCache = {};
+       
+       var liDetailBatchFields = ['fund', 'owning_lib', 'location', 'collection_code', 'circ_modifier', 'cn_label'];
+       var liDetailFields = liDetailBatchFields.concat(['barcode', 'note']);
+       var fundStyles = {
+           "stop": "color: #c00; font-weight: bold;",
+           "warning": "color: #c93;"
+       };
+       
+       function AcqLiTable() {
+       
+           var self = this;
+           this.liCache = {};
+           this.plCache = {};
+           this.poCache = {};
+           this.relCache = {};
+           this.haveFundClass = {}
+           this.fundBalanceState = {};
+           this.realDfaCache = {};
+           this.virtDfaCounts = {};
+           this.virtDfaId = -1;
+           this.dfeOffset = 0;
+           this.claimEligibleLidByLi = {};
+           this.claimEligibleLid = {};
+           this.toggleState = false;
+           this.tbody = dojo.byId('acq-lit-tbody');
+           this.selectors = [];
+           this.noteAcks = {};
+           this.authtoken = openils.User.authtoken;
+           this.pcrud = new openils_PermaCrud();
+           this.rowTemplate = this.tbody.removeChild(dojo.byId('acq-lit-row'));
+           this.copyTbody = dojo.byId('acq-lit-li-details-tbody');
+           this.copyRow = this.copyTbody.removeChild(dojo.byId('acq-lit-li-details-row'));
+           this.copyBatchRow = dojo.byId('acq-lit-li-details-batch-row');
+           this.copyBatchWidgets = {};
+           this.liNotesTbody = dojo.byId('acq-lit-notes-tbody');
+           this.liNotesRow = this.liNotesTbody.removeChild(dojo.byId('acq-lit-notes-row'));
+           this.realCopiesTbody = dojo.byId('acq-lit-real-copies-tbody');
+           this.realCopiesRow = this.realCopiesTbody.removeChild(dojo.byId('acq-lit-real-copies-row'));
+           this._copy_fields_for_acqdf = ['owning_lib', 'location'];
+           this.skipInitialEligibilityCheck = false;
+           this.claimDialog = new ClaimDialogManager(
+               liClaimDialog, finalClaimDialog, this.claimEligibleLidByLi,
+               function(li) {    /* callback that fires when claims are made */
+                   self.fetchClaimInfo(li.id(), /* force update */ true);
+               }
+           );
+           this.vlAgent = new VLAgent();
+       
+           dojo.byId("acq-lit-li-actions-selector").onchange = function() { 
+               self.applySelectedLiAction(this.options[this.selectedIndex].value);
+               this.selectedIndex = 0;
+           };
+       
+           acqLitCreatePoSubmit.onClick = function() {
+               if (!self.createPoProviderSelector.attr("value") ||
+                       !self.createPoAgencySelector.attr("value")) {
+                   alert(localeStrings.CREATE_PO_INVALID);
+                   return false;
+               } else if (self._confirmPoPrepaySituation()) {
+                   acqLitPoCreateDialog.hide();
+                   self._createPO(acqLitPoCreateDialog.getValues());
+               } else {
+                   return false;
+               }
+           }
+       
+           acqLitSavePlButton.onClick = function() {
+               acqLitSavePlDialog.hide();
+               self._savePl(acqLitSavePlDialog.getValues());
+           }
+       
+           acqLitCancelLiStateButton.onClick = function() {
+               acqLitChangeLiStateDialog.hide();
+           }
+           acqLitSaveLiStateButton.onClick = function() {
+               acqLitChangeLiStateDialog.hide();
+               self._updateLiState(acqLitChangeLiStateDialog.getValues(), acqLitChangeLiStateDialog.attr('state'));
+           }
+       
+       
+           dojo.byId('acq-lit-select-toggle').onclick = function(){self.toggleSelect()};
+           dojo.byId('acq-lit-info-back-button').onclick = function(){self.show('list')};
+           dojo.byId('acq-lit-copies-back-button').onclick = function(){self.show('list')};
+           dojo.byId('acq-lit-notes-back-button').onclick = function(){self.show('list')};
+           dojo.byId('acq-lit-real-copies-back-button').onclick = function(){self.show('list')};
+       
+           this.reset = function(keep_selectors) {
+               while(self.tbody.childNodes[0])
+                   self.tbody.removeChild(self.tbody.childNodes[0]);
+               self.noteAcks = {};
+               self.relCache = {};
+       
+               if (!keep_selectors)
+                   self.selectors = [];
+           };
+           
+           this.setNext = function(handler) {
+               var link = dojo.byId('acq-lit-next');
+               if(handler) {
+                   dojo.style(link, 'visibility', 'visible');
+                   link.onclick = handler;
+               } else {
+                   dojo.style(link, 'visibility', 'hidden');
+               }
+           };
+       
+           this.setPrev = function(handler) {
+               var link = dojo.byId('acq-lit-prev');
+               if(handler) {
+                   dojo.style(link, 'visibility', 'visible'); 
+                   link.onclick = handler; 
+               } else {
+                   dojo.style(link, 'visibility', 'hidden');
+               }
+           };
+       
+           this.show = function(div) {
+               openils.Util.hide('acq-lit-table-div');
+               openils.Util.hide('acq-lit-info-div');
+               openils.Util.hide('acq-lit-li-details');
+               openils.Util.hide('acq-lit-notes-div');
+               openils.Util.hide('acq-lit-real-copies-div');
+               openils.Util.hide('acq-lit-asset-creator');
+               switch(div) {
+                   case 'list':
+                       openils.Util.show('acq-lit-table-div');
+                       break;
+                   case 'info':
+                       openils.Util.show('acq-lit-info-div');
+                       break;
+                   case 'copies':
+                       openils.Util.show('acq-lit-li-details');
+                       break;
+                   case 'real-copies':
+                       openils.Util.show('acq-lit-real-copies-div');
+                       break;
+                   case 'notes':
+                       openils.Util.show('acq-lit-notes-div');
+                       break;
+                   case 'asset-creator':
+                       openils.Util.show('acq-lit-asset-creator');
+                       break;
+                   default:
+                       if(div) 
+                           openils.Util.show(div);
+               }
+           }
+       
+           this.hide = function() {
+               this.show(null);
+           }
+       
+           this.toggleSelect = function() {
+               if(self.toggleState) 
+                   dojo.forEach(self.selectors, function(i){i.checked = false});
+               else 
+                   dojo.forEach(self.selectors, function(i){i.checked = true});
+               self.toggleState = !self.toggleState;
+           };
+       
+       
+           this.getAll = function(callback, id_only) {
+               /* For some uses of the li table, we may not really know about "all"
+                * the lineitems that the user thinks we know about. If we're a paged
+                * picklist, for example, we only know about the lineitems we've
+                * displayed, but not necessarily all the lineitems on the picklist.
+                * So we reach out to pcrud to inform us.
+                */
+       
+               var oncomplete = function(r) {
+                   var id_list = openils.Util.readResponse(r);
+                   if (id_only)
+                       callback(id_list);
+                   else
+                       self.fetchLineitemsById(id_list, callback);
+               };
+       
+               if (this.isPL) {
+                   this.pcrud.search(
+                       "jub", {"picklist": this.isPL}, {
+                           "id_list": true,    /* sic, even if id_only */
+                           "async": true,
+                           "oncomplete": oncomplete
+                       }
+                   );
+                   return;
+               } else if (this.isPO) {
+                   this.pcrud.search(
+                       "jub", {"purchase_order": this.isPO}, {
+                           "id_list": true,
+                           "async": true,
+                           "oncomplete": oncomplete
+                       }
+                   );
+                   return;
+               } else if (this.isUni && this.pager) {
+                   this.pager.getAllLineitemIDs(oncomplete);
+                   return;
+               }
+       
+               /* If execution reaches this point, we don't need or can't perform
+                * any special tricks to find out the "real" list of "all" lineitems
+                * in this context, so we fall back to the old method.
+                */
+               callback(this.getSelected(true, null, id_only));
+           };
+       
+           /** @param all If true, assume all are selected */
+           this.getSelected = function(
+               all,
+               callback /* If you want a "good" idea of "all" lineitems, you must
+               provide a callback that accepts an array parameter, rather than
+               relying on the return value of this method itself. */,
+               id_only
+           ) {
+               if (all && callback)
+                   return this.getAll(callback, id_only);
+       
+               var indices = {};   /* use to uniqify. needed in paging situations. */
+               dojo.forEach(this.selectors,
+                   function(i) { 
+                       if(i.checked || all)
+                           indices[i.parentNode.parentNode.getAttribute('li')] = true;
+                   }
+               );
+       
+               var result = openils.Util.objectProperties(indices);
+       
+               if (!id_only)
+                   result = result.map(function(liId) { return self.liCache[liId]; });
+       
+               if (callback)
+                   callback(result);
+               else
+                   return result;
+           };
+       
+           this.setRowAttr = function(td, liWrapper, field, type) {
+               var val = liWrapper.findAttr(field, type || 'lineitem_marc_attr_definition') || '';
+               td.appendChild(document.createTextNode(val));
+           };
+       
+           this.setClaimPolicyControl = function(li, row) {
+               if (!self.claimPolicyPicker) {
+                   self.claimPolicyPicker = true; /* prevents a race condition */
+                   new openils_widget_AutoFieldWidget({
+                       "parentNode": "acq-lit-li-claim-policy",
+                       "fmClass": "acqclp",
+                       "selfReference": true,
+                       "dijitArgs": {"required": true}
+                   }).build(function(w) { self.claimPolicyPicker = w; });
+               }
+       
+               if (!row) row = this._findLiRow(li);
+       
+               var actViewPolicy = nodeByName("action_view_claim_policy", row);
+               if (li.claim_policy())
+                   actViewPolicy.innerHTML = localeStrings.CHANGE_CLAIM_POLICY;
+       
+               if (!actViewPolicy.onclick) {
+                   actViewPolicy.onclick = function() {
+                       if (li.claim_policy())
+                           self.claimPolicyPicker.attr("value", li.claim_policy());
+                       liClaimPolicyDialog.show();
+                       liClaimPolicySave.onClick = function() {
+                           self.changeClaimPolicy(
+                               [li], self.claimPolicyPicker.attr("value"),
+                               function() {
+                                   self.setClaimPolicyControl(li, row);
+                                   self.reconsiderClaimControl(li, row);
+                                   liClaimPolicyDialog.hide();
+                               }
+                           );
+                       }
+                   };
+               }
+           };
+       
+           this.fetchClaimInfo = function(liId, force, callback, row) {
+               this._fetchLineitem(
+                   liId, function(full) {
+                       self.liCache[full.id()] = full;
+                       self.checkClaimEligibility(full, callback, row);
+                   }, force
+               );
+           }
+       
+           /**
+            * Inserts a single lineitem into the growing table of lineitems
+            * @param {Object} li The lineitem object to insert
+            */
+           this.addLineitem = function(li, skip_final_placement) {
+               this.liCache[li.id()] = li;
+       
+               // insert the row right away so that final order isn't
+               // dependent on how long subsequent async request take
+               // for a given line item
+               var row = self.rowTemplate.cloneNode(true);
+               if (!skip_final_placement) {
+                   self.tbody.appendChild(row);
+               }
+               self.selectors.push(dojo.query('[name=selectbox]', row)[0]);
+       
+               // sort the lineitem notes on edit_time
+               if(!li.lineitem_notes()) li.lineitem_notes([]);
+       
+               var liWrapper = new openils_acq_Lineitem({lineitem:li});
+               row.setAttribute('li', li.id());
+               var tds = dojo.query('[attr]', row);
+               dojo.forEach(tds, function(td) {self.setRowAttr(td, liWrapper, td.getAttribute('attr'), td.getAttribute('attr_type'));});
+               dojo.query('[name=source_label]', row)[0].appendChild(document.createTextNode(li.source_label()));
+       
+               var identifier =
+                   liWrapper.findAttr("isbn", "lineitem_marc_attr_definition") ||
+                   liWrapper.findAttr("upc", "lineitem_marc_attr_definition");
+       
+               // XXX media prefix for added content
+               if (identifier) {
+                   nodeByName("jacket", row).setAttribute(
+                       "src", "/opac/extras/ac/jacket/small/" + identifier
+                   );
+               }
+       
+               nodeByName("liid", row).innerHTML += li.id();
+       
+               if(li.eg_bib_id()) {
+                   openils.Util.show(nodeByName('catalog', row), 'inline');
+                   nodeByName("catalog_link", row).onclick = this.generateMakeRecTab(li.eg_bib_id());
+               } else {
+                   openils.Util.show(nodeByName('link_to_catalog', row), 'inline');
+                   nodeByName("link_to_catalog_link", row).onclick = function() { self.drawBibFinder(li) };
+               }
+       
+               if (li.queued_record()) {
+                   this.pcrud.retrieve('vqbr', li.queued_record(),
+                       {   async : true, 
+                           oncomplete : function(r) {
+                               var qrec = openils.Util.readResponse(r);
+                               openils.Util.show(nodeByName('queue', row), 'inline');
+                               var link = nodeByName("queue_link", row);
+                               link.onclick = function() { 
+                                   // open a new tab to the vandelay queue for this record
+                                   openils.XUL.newTabEasy(
+                                       oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + qrec.queue()
+                                   );
+                               }
+                           }
+                       }
+                   );
+               }
+       
+               nodeByName("worksheet_link", row).href =
+                   oilsBasePath + "/acq/lineitem/worksheet/" + li.id();
+       
+               nodeByName("show_requests_link", row).href =
+                   oilsBasePath + "/acq/picklist/user_request?lineitem=" + li.id();
+       
+               dojo.query('[attr=title]', row)[0].onclick = function() {self.drawInfo(li.id())};
+               dojo.query('[name=copieslink]', row)[0].onclick = function() {self.drawCopies(li.id())};
+               dojo.query('[name=noteslink]', row)[0].onclick = function() {self.drawLiNotes(li)};
+       
+               if (!this.skipInitialEligibilityCheck)
+                   this.fetchClaimInfo(
+                       li.id(),
+                       false,
+                       function(full) { self.setClaimPolicyControl(full, row) },
+                       row
+                   );
+       
+               this.updateLiNotesCount(li, row);
+       
+               // show which PO this lineitem is a member of
+               if(li.purchase_order() && !this.isPO) {
+                   var po = 
+                       this.poCache[li.purchase_order()] =
+                       this.poCache[li.purchase_order()] ||
+                       fieldmapper.standardRequest(
+                           ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
+                           {params: [
+                               this.authtoken, li.purchase_order(), {
+                                   "flesh_price_summary": true,
+                                   "flesh_provider" : true,
+                                   "flesh_lineitem_count": true
+                               }
+                           ]});
+                   if(po && !this.isMeta) {
+                       openils.Util.show(nodeByName('po', row), 'inline');
+                       var link = nodeByName('po_link', row);
+                       link.setAttribute('href', oilsBasePath + '/acq/po/view/' + li.purchase_order());
+                       link.innerHTML += po.name();
+       
+                       openils.Util.show(nodeByName('pro', row), 'inline');
+                       link = nodeByName('pro_link', row);
+                       link.setAttribute('href', oilsBasePath + '/conify/global/acq/provider/' + po.provider().id())
+                       link.innerHTML += po.provider().code();
+                   }
+               }
+       
+               // show which picklist this lineitem is a member of
+               if(li.picklist() && (this.isPO || this.isMeta || this.isUni)) {
+                   var pl = 
+                       this.plCache[li.picklist()] = 
+                       this.plCache[li.picklist()] || 
+                       fieldmapper.standardRequest(
+                           ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
+                           {params: [this.authtoken, li.picklist()]});
+                   if (pl) {
+                       if (pl.name() == "") {
+                           openils.Util.show(nodeByName("bib_origin", row), "inline");
+       
+                       } else {
+       
+                           openils.Util.show(nodeByName('pl', row), 'inline');
+                           var link = nodeByName('pl_link', row);
+                           link.setAttribute('href', oilsBasePath + '/acq/picklist/view/' + li.picklist());
+                           link.innerHTML += pl.name();
+                       }
+                   }
+               }
+       
+               var countNode = nodeByName('count', row);
+               var count = li.item_count() || 0;
+               if (typeof(this._copy_count_cb) == "function") {
+                   this._copy_count_cb(li.id(), count);
+               }
+               countNode.innerHTML = count;
+               countNode.id = 'acq-lit-copy-count-label-' + li.id();
+       
+               // lineitem price
+               var priceInput = dojo.query('[name=price]', row)[0];
+               priceInput.value = li.estimated_unit_price() || '';
+               priceInput.onchange = function() { self.updateLiPrice(priceInput, li) };
+       
+               // show either "mark received" or "unreceive" as appropriate
+               this.updateLiState(li, row);
+       
+               if (skip_final_placement) {
+                   return row;
+               }
+           };
+       
+           this._liCountClaims = function(li) {
+               var total = 0;
+               for (var i = 0; i < li.lineitem_details().length; i++)
+                   total += li.lineitem_details()[i].claims().length;
+               return total;
+           };
+       
+           this._findLiRow = function(li) {
+               return dojo.query('tr[li="' + li.id() + '"]', "acq-lit-tbody")[0];
+           };
+       
+           this.reconsiderClaimControl = function(li, row) {
+               if (!row) row = this._findLiRow(li);
+               var option = nodeByName("action_manage_claims", row);
+               var eligible = this.claimEligibleLidByLi[li.id()].length;
+               var count = this._liCountClaims(li);
+       
+               option.disabled = !(count || eligible);
+               option.innerHTML =
+                   dojo.string.substitute(localeStrings.NUM_CLAIMS_EXISTING, [count]);
+               option.onclick = function() { self.claimDialog.show(li); };
+           };
+       
+           this.clearEligibility = function(li) {
+               this.claimEligibleLidByLi[li.id()] = [];
+       
+               if (li.lineitem_details()) {
+                   li.lineitem_details().forEach(
+                       function(lid) { delete self.claimEligibleLid[lid.id()]; }
+                   );
+               }
+       
+               if (this.copyCache) {
+                   var to_del = [];
+                   for (var k in this.copyCache) {
+                       if (this.copyCache[k].lineitem() == li.id())
+                           to_del.push(k);
+                   }
+                   to_del.forEach(
+                       function(k) { delete self.claimEligibleLid[k]; }
+                   );
+               }
+           };
+       
+           this.checkClaimEligibility = function(li, callback, row) {
+               /* Assume always eligible, i.e. from this interface we don't care about
+                * claim eligibility any more. this is where the user would force a
+                * claime. */
+               this.clearEligibility(li);
+               this.claimEligibleLidByLi[li.id()] = li.lineitem_details().map(
+                   function(lid) { return lid.id(); }
+               );
+               li.lineitem_details().forEach(
+                   function(lid) { self.claimEligibleLid[lid.id()] = true; }
+               );
+               this.reconsiderClaimControl(li, row);
+               if (callback) callback(li);
+               /*
+               this.clearEligibility(li);
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail"], {
+                       "params": [openils.User.authtoken, {"lineitem": li.id()}],
+                       "async": true,
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               self.claimEligibleLidByLi[li.id()].push(
+                                   r.lineitem_detail()
+                               );
+                               self.claimEligibleLid[r.lineitem_detail()] = true;
+                           }
+                       },
+                       "oncomplete": function() {
+                           self.reconsiderClaimControl(li, row);
+                           if (typeof(callback) == "function")
+                               callback();
+                       }
+                   }
+               );
+               */
+           };
+       
+           this.updateLiNotesCount = function(li, row) {
+               if (!row) row = this._findLiRow(li);
+       
+               var has_notes = (li.lineitem_notes().filter(
+                       function(o) { return Boolean (o.alert_text()); }
+                   ).length > 0);
+       
+               /* U+2691 is the code point for a filled-in flag character */
+               nodeByName("notes_alert_flag", row).innerHTML =
+                    has_notes ? "&#x2691;" : "";
+               nodeByName("noteslink", row).style.fontStyle =
+                   has_notes ? "italic" : "normal";
+               nodeByName("notes_count", row).innerHTML = li.lineitem_notes().length;
+           };
+       
+           /* XXX NOT related to _updateLiState(). rethink */
+           this.updateLiState = function(li, row) {
+               if (!row) row = this._findLiRow(li);
+       
+               var actReceive = nodeByName("action_mark_recv", row);
+               var actUnRecv = nodeByName("action_mark_unrecv", row);
+               var actUpdateBarcodes = nodeByName("action_update_barcodes", row);
+               var actHoldingsMaint = nodeByName("action_holdings_maint", row);
+       
+               var actNewInvoice = nodeByName('action_new_invoice', row);
+               var actLinkInvoice = nodeByName('action_link_invoice', row);
+               var actViewInvoice = nodeByName('action_view_invoice', row);
+       
+               nodeByName('action_view_history', row).onclick = 
+                   function() { location.href = oilsBasePath + '/acq/lineitem/history/' + li.id(); };
+       
+               var state_cell = nodeByName("li_state", row);
+       
+               if (li.state() == "cancelled") {
+                   if (typeof li.cancel_reason() == "object") {
+                       var holds_state = dojo.create(
+                           "span", {
+                               "style": "border-bottom: 1px dashed #000;",
+                               "innerHTML": li.state()
+                           }, state_cell, "only"
+                       );
+                       new dijit_Tooltip(
+                           {
+                               "label": "<em>" + li.cancel_reason().label() +
+                                   "</em><br />" + li.cancel_reason().description(),
+                               "connectId": [holds_state]
+                           }, dojo.create("span", null, state_cell, "last")
+                       );
+                   } else {
+                       state_cell.innerHTML = li.state(); // TODO i18n state labels
+                   }
+               } else {
+                   state_cell.innerHTML = li.state(); // TODO i18n state labels
+               }
+       
+       
+               /* handle row coloring for based on LI state */
+               openils.Util.removeCSSClass(row, /^oils-acq-li-state-/);
+               openils.Util.addCSSClass(row, "oils-acq-li-state-" + li.state());
+       
+               /* handle links that appear/disappear based on whether LI is received */
+               if (this.isPO) {
+                   var self = this;
+       
+                   actNewInvoice.onclick = function() {
+                       location.href = oilsBasePath + '/acq/invoice/view?create=1&attach_li=' + li.id();
+                       nodeByName("action_none", row).selected = true;
+                   };
+                   actLinkInvoice.onclick = function() {
+                       if (!self.invoiceLinkDialogManager) {
+                           self.invoiceLinkDialogManager =
+                               new InvoiceLinkDialogManager("li");
+                       }
+                       self.invoiceLinkDialogManager.target = li;
+                       acqLitLinkInvoiceDialog.show();
+                       nodeByName("action_none", row).selected = true;
+                   };
+                   actViewInvoice.onclick = function() {
+                       location.href = oilsBasePath +
+                           "/acq/search/unified?so=" +
+                           base64Encode({"jub":[{"id": li.id()}]}) +
+                           "&rt=invoice";
+                       nodeByName("action_none", row).selected = true;
+                   };
+       
+                   actNewInvoice.disabled = false;
+                   actLinkInvoice.disabled = false;
+                   actViewInvoice.disabled = false;
+       
+                   switch(li.state()) {
+                       case "on-order":
+                           actReceive.disabled = false;
+                           actReceive.onclick = function() {
+                               if (self.checkLiAlerts(li.id()))
+                                   self.issueReceive(li);
+                               nodeByName("action_none", row).selected = true;
+                           };
+                           return;
+       
+                       case "received":
+                           actUnRecv.disabled = false;
+                           actUnRecv.onclick = function() {
+                               if (confirm(localeStrings.UNRECEIVE_LI))
+                                   self.issueReceive(li, /* rollback */ true);
+                               nodeByName("action_none", row).selected = true;
+                           };
+                           // TODO we should allow editing before receipt, in which case the
+                           // test should be "if 1 or more real (acp) copies exist
+                           actUpdateBarcodes.disabled = false;
+                           actUpdateBarcodes.onclick = function() {
+                               self.showRealCopyEditUI(li);
+                               nodeByName("action_none", row).selected = true;
+                           }
+                           actHoldingsMaint.disabled = false;
+                           actHoldingsMaint.onclick = self.generateMakeRecTab( li.eg_bib_id(), 'copy_browser', row );
+       
+                           return;
+                   }
+               }
+           };
+       
+       
+           this._setAlertStore = function() {
+               acqLitAlertAlertText.store = new dojo_data_ItemFileReadStore(
+                   {
+                       "data": acqliat.toStoreData(
+                           this.pcrud.search(
+                               "acqliat", {"id": {"!=": null}}
+                           )
+                       )
+                   }
+               );
+               acqLitAlertAlertText.setValue(); /* make the store "live" */
+               acqLitAlertAlertText._store_ready = true;
+           };
+       
+           /**
+            * Draws and shows the lineitem notes pane
+            */
+           this.drawLiNotes = function(li) {
+               var self = this;
+       
+               if (!acqLitAlertAlertText._store_ready)
+                   this._setAlertStore();
+       
+               li.lineitem_notes(
+                   li.lineitem_notes().sort(
+                       function(a, b) { 
+                           if(a.edit_time() < b.edit_time()) return 1;
+                           return -1;
+                       }
+                   )
+               );
+       
+               while(this.liNotesTbody.childNodes[0])
+                   this.liNotesTbody.removeChild(this.liNotesTbody.childNodes[0]);
+               this.show('notes');
+       
+               acqLitCreateNoteSubmit.onClick = function() {
+                   var value = acqLitCreateNoteText.attr('value');
+                   if(!value) return;
+                   var note = new fieldmapper.acqlin();
+                   note.isnew(true);
+                   note.vendor_public(
+                       Boolean(acqLitCreateNoteVendorPublic.attr('checked'))
+                   );
+                   note.value(value);
+                   note.lineitem(li.id());
+       
+                   self.updateLiNotes(li, note);
+                   acqLitCreateNoteVendorPublic.attr("checked", false);
+                   acqLitCreateNoteText.attr("value", "");
+               }
+       
+               acqLitCreateAlertSubmit.onClick = function() {
+                   if (!acqLitAlertAlertText.item) {
+                       alert(localeStrings.ALERT_UNSELECTED);
+                       return;
+                   }
+       
+                   var alert_text = new fieldmapper.acqliat().fromStoreItem(
+                       acqLitAlertAlertText.item
+                   );
+                   var value = acqLitAlertNoteValue.attr("value") || "";
+       
+                   var note = new fieldmapper.acqlin();
+                   note.isnew(true);
+                   note.lineitem(li.id());
+                   note.value(value);
+                   note.alert_text(alert_text);
+       
+                   self.updateLiNotes(li, note);
+               }
+       
+               dojo.forEach(li.lineitem_notes(), function(note) { self.addLiNote(li, note) });
+           }
+       
+           /**
+            * Draws a single lineitem note in the notes pane
+            */
+           this.addLiNote = function(li, note) {
+               if(note.isdeleted()) return;
+               var self = this;
+               var row = self.liNotesRow.cloneNode(true);
+               nodeByName("value", row).innerHTML = note.value();
+               var alert_node = nodeByName("alert_code", row);
+               if (note.alert_text()) {
+                   alert_node.innerHTML = note.alert_text().code();
+                   if (note.alert_text().description()) {
+                       new dijit_Tooltip(
+                           {
+                               "connectId": [alert_node],
+                               "label": note.alert_text().description()
+                           }, dojo.create("span", null, alert_node, "after")
+                       );
+                   }
+               }
+       
+               if (openils.Util.isTrue(note.vendor_public()))
+                   nodeByName("vendor_public", row).innerHTML =
+                       localeStrings.VENDOR_PUBLIC;
+       
+               nodeByName("delete", row).onclick = function() {
+                   note.isdeleted(true);
+                   self.liNotesTbody.removeChild(row);
+                   self.updateLiNotes(li);
+               };
+       
+               if(note.edit_time()) {
+                   nodeByName("edit_time", row).innerHTML =
+                       dojo_date_locale.format(
+                           dojo_date_stamp.fromISOString(note.edit_time()), 
+                           {formatLength:'short'});
+               }
+       
+               self.liNotesTbody.appendChild(row);
+           }
+       
+           /**
+            * Updates any new/changed/deleted notes on the server
+            */
+           this.updateLiNotes = function(li, newNote) {
+       
+               var notes;
+               if(newNote) {
+                   notes = [newNote];
+               } else {
+                   notes = li.lineitem_notes().filter(
+                       function(note) {
+                           if(note.ischanged() || note.isnew() || note.isdeleted())
+                               return note;
+                       }
+                   );
+               }
+       
+               if(notes.length == 0) return;
+               progressDialog.show();
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.lineitem_note.cud.batch'],
+                   {   async : true,
+                       params : [this.authtoken, notes],
+                       onresponse : function(r) {
+                           var resp = openils.Util.readResponse(r);
+       
+                           if(resp.complete) {
+       
+                               if(!newNote) {
+                                   // remove the old changed notes
+                                   var list = [];
+                                   dojo.forEach(li.lineitem_notes(), 
+                                       function(note) {
+                                           if(!(note.ischanged() || note.isnew() || note.isdeleted()))
+                                               list.push(note);
+                                       }
+                                   );
+                                   li.lineitem_notes(list);
+                               }
+       
+                               progressDialog.hide();
+                               self.updateLiNotesCount(li);
+                               self.drawLiNotes(li);
+                               return;
+                           }
+       
+                           progressDialog.update(resp);
+                           var newnote = resp.note;
+       
+                           if(!newnote.isdeleted()) {
+                               newnote.isnew(false);
+                               newnote.ischanged(false);
+                               li.lineitem_notes().push(newnote);
+                           }
+                       },
+                   }
+               );
+           }
+       
+           this.updateLiPrice = function(input, li) {
+               var self = this;
+               var price = input.value;
+               if(Number(price) == Number(li.estimated_unit_price())) return;
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.lineitem.price.set'],
+                   {   async : false, // redundant w/ timeout
+                       timeout : 10,
+                       params : [this.authtoken, li.id(), price],
+                       oncomplete : function(r) {
+                           openils.Util.readResponse(r);
+                           li.estimated_unit_price(price); // update local copy
+       
+                           /*
+                            * If this is a PO and every visible lineitem has a price,
+                            * check again to see if this PO can be activated.  Note that 
+                            * every visible lineitem having a price does not guarantee it can
+                            * be activated, which is why we still make the call.  Having a price
+                            * set for every visiable lineitem is just the lowest barrier to entry.
+                            */
+                           if (self.isPO) {
+                               var priceNodes = dojo.query('[name=price]', dojo.byId('acq-lit-tbody'));
+                               var allSet = true;
+                               dojo.forEach(priceNodes, function(node) { if (node.value == '') allSet = false});
+                               if (allSet) checkCouldActivatePo();
+                           }
+                       }
+                   }
+               );
+           }
+       
+           this.removeLineitem = function(liId) {
+               this.tbody.removeChild(dojo.query('[li='+liId+']', this.tbody)[0]);
+               delete this.liCache[liId];
+               //selected.push(self.liCache[i.parentNode.parentNode.getAttribute('li')]);
+           }
+       
+           this.drawInfo = function(liId) {
+               if (!this._isRelatedViewer) {
+                   var d = dojo.byId("acq-lit-info-related");
+                   if (!this.relCache[liId]) {
+                       fieldmapper.standardRequest(
+                           [
+                               "open-ils.acq",
+                               "open-ils.acq.lineitems_for_bib.by_lineitem_id.count"
+                           ], {
+                               "async": true,
+                               "params": [openils.User.authtoken, liId],
+                               "onresponse": function(r) {
+                                   self.relCache[liId] = openils.Util.readResponse(r);
+                                   nodeByName("related_number", d).innerHTML =
+                                       self.relCache[liId];
+                                   openils.Util[
+                                       self.relCache[liId] >1 ? "show" : "hide"
+                                   ](d);
+                               }
+                           }
+                       );
+                   } else {
+                       nodeByName("related_number", d).innerHTML = this.relCache[liId];
+                       openils.Util[this.relCache[liId] > 1 ? "show" : "hide"](d);
+                   }
+               }
+       
+               this.show('info');
+               openils_acq_Lineitem.fetchAttrDefs(
+                   function() { 
+                       self._fetchLineitem(liId, function(li){self._drawInfo(li);}); 
+                   } 
+               );
+           };
+       
+           /* For a given list of lineitem ids, build a list of full lineitems
+            * re-using the fetching logic that is otherwise typical to use in this
+            * module.
+            *
+            * If we've already got a lineitem in the cache, just use that.
+            *
+            * Once we've built a list of lineitems, call callback(thatlist).
+            */
+           this.fetchLineitemsById = function(id_list, callback) {
+               var total = id_list.length;
+               var result_list = [];
+       
+               var inner = function(li) {
+                   result_list.push(li)
+                   if (--total <= 0)
+                       callback(result_list);
+               };
+       
+               id_list.forEach(function(id) { self._fetchLineitem(id, inner); });
+           };
+       
+           this._fetchLineitem = function(liId, handler, force) {
+       
+               var li = this.liCache[liId];
+               if(li && li.marc() && li.lineitem_details() && !force)
+                   return handler(li);
+               
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative'],
+                   {   async: true,
+       
+                       params: [self.authtoken, liId, {
+                           flesh_attrs: true,
+                           flesh_cancel_reason: true,
+                           flesh_li_details: true,
+                           flesh_notes: true,
+                           flesh_fund_debit: true }],
+       
+                       oncomplete: function(r) {
+                           var li = openils.Util.readResponse(r);
+                           self.liCache[liId] = li;
+                           handler(li)
+                       }
+                   }
+               );
+           };
+       
+           this._drawInfo = function(li) {
+       
+               acqLitEditOrderMarc.onClick = function() { self.editOrderMarc(li); }
+       
+               if(li.eg_bib_id()) {
+                   openils.Util.hide('acq-lit-marc-order-record-label');
+                   openils.Util.hide(acqLitEditOrderMarc.domNode);
+                   openils.Util.show('acq-lit-marc-real-record-label');
+               } else {
+                   openils.Util.show('acq-lit-marc-order-record-label');
+                   openils.Util.show(acqLitEditOrderMarc.domNode);
+                   openils.Util.hide('acq-lit-marc-real-record-label');
+               }
+       
+               this.drawMarcHTML(li);
+               this.infoTbody = dojo.byId('acq-lit-info-tbody');
+       
+               if(!this.infoRow)
+                   this.infoRow = this.infoTbody.removeChild(dojo.byId('acq-lit-info-row'));
+               while(this.infoTbody.childNodes[0])
+                   this.infoTbody.removeChild(this.infoTbody.childNodes[0]);
+       
+               for(var i = 0; i < li.attributes().length; i++) {
+                   var attr = li.attributes()[i];
+                   var row = this.infoRow.cloneNode(true);
+       
+                   var type = attr.attr_type().replace(/lineitem_(.*)_attr_definition/, '$1');
+                   var name = openils_acq_Lineitem.attrDefs[type].filter(
+                       function(a) {
+                           return (a.code() == attr.attr_name());
+                       }
+                   ).pop().description();
+       
+                   dojo.query('[name=label]', row)[0].appendChild(document.createTextNode(name));
+                   dojo.query('[name=value]', row)[0].appendChild(document.createTextNode(attr.attr_value()));
+                   this.infoTbody.appendChild(row);
+               }
+       
+               if (!this._isRelatedViewer) {
+                   nodeByName("rel_link", dojo.byId("acq-lit-info-related")).href =
+                       oilsBasePath + "/acq/lineitem/related/" + li.id();
+               }
+       
+           };
+       
+           this.generateMakeRecTab = function(bib_id,default_view, row) {
+               return function() {
+                   xulG.new_tab(
+                       XUL_OPAC_WRAPPER,
+                       {tab_name: localeStrings.XUL_RECORD_DETAIL_PAGE, browser:false},
+                       {
+                           no_xulG : false, 
+                           show_nav_buttons : true, 
+                           show_print_button : true, 
+                           opac_url : xulG.url_prefix(xulG.urls.opac_rdetail + bib_id),
+                           default_view : default_view
+                       }
+                   );
+       
+                   if(row) nodeByName("action_none", row).selected = true;
+               }
+           };
+       
+           this.drawMarcHTML = function(li) {
+               var params = [null, true, li.marc()];
+               if(li.eg_bib_id()) 
+                   params = [li.eg_bib_id(), true];
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.search', 'open-ils.search.biblio.record.html'],
+                   {   async: true,
+                       params: params,
+                       oncomplete: function(r) {
+                           dojo.byId('acq-lit-marc-div').innerHTML = 
+                               openils.Util.readResponse(r);
+                       }
+                   }
+               );
+           }
+       
+           this.drawCopies = function(liId, force_fetch) {
+               if (typeof force_fetch == "undefined")
+                   force_fetch = false;
+       
+               openils_acq_Lineitem.fetchAndRender(liId, {}, 
+                   function(li, html) {
+                       dojo.byId('acq-lit-copies-li-summary').innerHTML = html;
+                   }
+               );
+       
+               this.show('copies');
+               var self = this;
+               this.copyCache = {};
+               this.copyWidgetCache = {};
+               this.oldCopyWidgetCache = {};
+               this.virtDfaCounts = {};
+               this.realDfaCache = {};
+               this.dfeOffset = 0;
+       
+               acqLitSaveCopies.onClick = function() { self.saveCopyChanges(liId) };
+               acqLitBatchUpdateCopies.onClick = function() { self.batchCopyUpdate() };
+               acqLitCopyCountInput.attr('value', '0');
+       
+               while(this.copyTbody.childNodes[0])
+                   this.copyTbody.removeChild(this.copyTbody.childNodes[0]);
+       
+               this._drawBatchCopyWidgets();
+       
+               this._drawDistribApplied(liId);
+       
+               this._fetchDistribFormulas(
+                   function() {
+                       openils_acq_Lineitem.fetchAttrDefs(
+                           function() { 
+                               self._fetchLineitem(liId, function(li){self._drawCopies(li);}, force_fetch); 
+                           } 
+                       );
+                   }
+               );
+           };
+       
+           this._saveDistribAppliedTemplates = function() {
+               if (!this._appliedDistribTemplate) {
+                   this._appliedDistribTemplate =
+                       dojo.byId("acq-lit-distrib-applied-tbody").
+                           removeChild(dojo.byId("acq-lit-distrib-applied-row"));
+                   dojo.attr(this._appliedDistribTemplate, "id");
+               }
+           };
+       
+           this._drawDistribApplied = function(liId) {
+               /* Build this table while hidden to prevent rendering artifacts */
+               openils.Util.hide("acq-lit-distrib-applied-tbody");
+       
+               this._saveDistribAppliedTemplates();
+       
+               /* Remove any rows in the table from previous populations */
+               dojo.query("tr[formula]", "acq-lit-distrib-applied-tbody").
+                   forEach(dojo.destroy);
+       
+               /* Unregister all dijits previously created (for some reason this isn't
+                * covered by the above destroy calls). */
+               dijit.registry.forEach(
+                   function(w) { if (/^dfa-/.test(w.id)) w.destroyRecursive(); }
+               );
+       
+               /* Populate the table with our liId */
+               var total = 0;
+               fieldmapper.standardRequest(
+                   ["open-ils.acq",
+                   "open-ils.acq.distribution_formula_application.ranged.retrieve"],
+                   {
+                       "async": true,
+                       "params": [self.authtoken, liId],
+                       "onresponse": function(r) {
+                           var dfa = openils.Util.readResponse(r);
+                           if (dfa) {
+                               total++;
+                               self.realDfaCache[dfa.id()] = dfa;
+                               self._drawDistribAppliedUnit(dfa);
+                           }
+                       },
+                       "oncomplete": function() {
+                           /* Reveal built table */
+                           if (total) {
+                               openils.Util.show(
+                                   "acq-lit-distrib-applied-tbody", "table-row-group"
+                               );
+                           }
+                       }
+                   }
+               );
+           };
+       
+           this._drawDistribAppliedUnit = function(dfa) {
+               var new_row = false;
+               var row = dojo.query(
+                   'tr[formula="' + dfa.formula().id() + '"]',
+                   "acq-lit-distrib-applied-tbody"
+               )[0];
+       
+               if (!row) {
+                   new_row = true;
+                   row = dojo.clone(this._appliedDistribTemplate);
+                   dojo.attr(row, "formula", dfa.formula().id());
+                   dojo.query("th", row)[0].innerHTML = dfa.formula().name();
+               }
+       
+               var td = dojo.query("td", row)[0];
+       
+               dojo.create("span", {"id": "dfa-button-" + dfa.id()}, td, "last");
+               dojo.create("span", {"id": "dfa-tip-" + dfa.id()}, td, "last");
+       
+               if (new_row)
+                   dojo.place(row, "acq-lit-distrib-applied-tbody", "last");
+       
+               new dijit_form_Button(
+                   {
+                       "onClick": function() {
+                           if (confirm(localeStrings.EXPLAIN_DFA_MGMT))
+                               self.deleteDfa(dfa);
+                       },
+                       "label": "X",
+                       /* XXX I /cannot/ make the following work in as a CSS class
+                        * for some reason. So frustrating... */
+                       "style": function(id) {
+                            return (id > 0 ?
+                               "font-weight: bold; color: #c00;" :
+                               "color: #666;");
+                            }(dfa.id()) + "margin: 0 6px;display: inline;"
+                   }, "dfa-button-" + dfa.id()
+               );
+               new dijit_Tooltip(
+                   {
+                       "connectId": ["dfa-button-" + dfa.id()],
+                       "label": dojo.string.substitute(
+                           localeStrings.DFA_TIP, dfa.id() > 0 ? [
+                               openils.User.formalName(dfa.creator()),
+                               dojo_date_locale.format(
+                                   dojo_date_stamp.fromISOString(dfa.create_time()),
+                                   {"formatLength":"short"}
+                               )
+                           ] : [localeStrings.ITS_YOU, localeStrings.JUST_NOW]
+                       )
+                   }, "dfa-tip-" + dfa.id()
+               );
+           }
+       
+           this.deleteDfa = function(dfa) {
+               if (dfa.id() > 0) { /* real */
+                   this.pcrud.eliminate(
+                       dfa, {
+                           "async": true,
+                           "oncomplete": function() {
+                               self._removeDistribApplied(dfa.id());
+                               delete self.realDfaCache[dfa.id()];
+                           }
+                       }
+                   );
+               } else { /* virtual */
+                   if (--(this.virtDfaCounts[dfa.formula().id()]) < 0)
+                   this.virtDfaCounts[dfa.formula().id()] = 0;
+                   /* hasn't been saved yet, so no need to do anything server side */
+                   this._removeDistribApplied(dfa.id());
+               }
+       
+           };
+       
+           this._removeDistribApplied = function(dfaId) {
+               var re = new RegExp("^dfa-\\w+-" + String(dfaId));
+               dijit.registry.forEach(
+                   function(w) { if (re.test(w.id)) w.destroyRecursive(); }
+               );
+               this._removeDistribAppliedEmptyRows();
+           };
+       
+           this._removeAllDistribAppliedVirtual = function() {
+               /* Unregister dijits */
+               dijit.registry.forEach(
+                   function(w) { if (/^dfa-\w+--/.test(w.id)) w.destroyRecursive(); }
+               );
+               this._removeDistribAppliedEmptyRows();
+           };
+       
+           this._removeDistribAppliedEmptyRows = function() {
+               /* Remove any rows with no DFA at all */
+               dojo.query("tr[formula] td", "acq-lit-distrib-applied-tbody").forEach(
+                   function(o) {
+                       if (o.childNodes.length < 1) dojo.destroy(o.parentNode);
+                   }
+               );
+           };
+       
+           /**
+            * Insert a new row into the distribution formula selection form
+            */
+           this._addDistribFormulaRow = function() {
+               var self = this;
+       
+               if (!self.distribForms) {
+                   // no formulas, hide the form
+                   openils.Util.hide('acq-lit-distrib-formula-table');
+                   return;
+               }
+       
+               if(!this.distribFormulaTemplate) 
+                   this.distribFormulaTemplate = 
+                       dojo.byId('acq-lit-distrib-formula-tbody').removeChild(dojo.byId('acq-lit-distrib-form-row'));
+       
+               var row = this.distribFormulaTemplate.cloneNode(true);
+               dojo.place(row, "acq-lit-distrib-formula-tbody", "only");
+       
+               this.dfSelector = new dijit_form_FilteringSelect(
+                   {"labelAttr": "dynLabel", "labelType": "html"},
+                   nodeByName("selector", row)
+               );
+               this._updateFormulaStore();
+               this.dfSelector.fetchProperties =
+                   {"sort": [{"attribute": "use_count", "descending": true}]};
+       
+               var apply = new dijit_form_Button(
+                   {"label": localeStrings.APPLY},
+                   nodeByName('set_button', row)
+               ); 
+       
+               var reset = new dijit_form_Button(
+                   {"label": localeStrings.RESET_FORMULAE, "disabled": true},
+                   nodeByName("reset_button", row)  
+               );
+       
+               dojo.connect(apply, 'onClick', 
+                   function() {
+                       var form_id = self.dfSelector.attr("value");
+                       if(!form_id) return;
+                       self._applyDistribFormula(form_id);
+                       reset.attr("disabled", false);
+                   }
+               );
+       
+               dojo.connect(reset, 'onClick', 
+                   function() {
+                       self.restoreCopyFieldsBeforeDF();
+                       self.virtDfaCounts = {};
+                       self.virtDfaId = -1;
+                       self.dfeOffset = 0;
+                       self._updateFormulaStore();
+                       self._removeAllDistribAppliedVirtual();
+                       reset.attr("disabled", "true");
+                   }
+               );
+       
+           };
+       
+           /**
+            * Applies a distrib formula to the current set of copies
+            */
+           this._applyDistribFormula = function(formula) {
+               if(!formula) return;
+       
+               formula = this.distribForms.filter(
+                   function(form) { return form.id() == formula; }
+               )[0];
+       
+               var copyRows = dojo.query('tr', self.copyTbody);
+       
+               if (this.dfeOffset >= copyRows.length) {
+                   alert(localeStrings.OUT_OF_COPIES);
+                   return;
+               }
+       
+               var entries_applied = 0;
+               for(
+                   var rowIndex = this.dfeOffset;
+                   rowIndex < copyRows.length;
+                   rowIndex++
+               ) {
+                   
+                   var row = copyRows[rowIndex];
+                   var copy_id = row.getAttribute('copy_id');
+                   var copyWidgets = this.copyWidgetCache[copy_id];
+                   var entryIndex = this.dfeOffset;
+                   var entry = null;
+       
+                   // find the correct entry for the current row
+                   dojo.forEach(formula.entries(), 
+                       function(e) {
+                           if(!entry) {
+                               entryIndex += e.item_count();
+                               if(entryIndex > rowIndex)
+                                   entry = e;
+                           }
+                       }
+                   );
+       
+                   if(entry) {
+                       
+                       //console.log("rowIndex = " + rowIndex + ", entry = " + entry.id() + ", entryIndex=" + 
+                       //  entryIndex + ", owning_lib = " + entry.owning_lib() + ", location = " + entry.location());
+           
+                       entries_applied++;
+                       this.saveCopyFieldsBeforeDF(copy_id);
+                       this._copy_fields_for_acqdf.forEach(
+                           function(field) {
+                               if(entry[field]()) {
+                                   copyWidgets[field].attr('value', (entry[field]()));
+                               }
+                           }
+                       );
+                   }
+               }
+       
+               if (entries_applied) {
+                   this.virtDfaCounts[formula.id()] =
+                       ++(this.virtDfaCounts[formula.id()]) || 1;
+                   this._updateFormulaStore();
+                   this._drawDistribAppliedUnit(
+                       function(df) {
+                           var dfa = new acqdfa();
+                           dfa.formula(df); dfa.id(self.virtDfaId--); return dfa;
+                       }(formula)
+                   );
+                   this.dfeOffset += entries_applied;
+               };
+           };
+       
+           /**
+            * This function updates the DF store for the dropdown so that use_counts
+            * can reflect DF applications from this session before they're saved
+            * server-side.
+            */
+           this._updateFormulaStore = function() {
+               this.dfSelector.store = new dojo_data_ItemFileReadStore(
+                   {
+                       "data": self._labelFormulasWithCounts(
+                           acqdf.toStoreData(self.distribForms)
+                       )
+                   }
+               );
+           };
+       
+           this.saveCopyFieldsBeforeDF = function(copy_id) {
+               var self = this;
+               if (!this.oldCopyWidgetCache[copy_id]) {
+                   var copyWidgets = this.copyWidgetCache[copy_id];
+       
+                   this.oldCopyWidgetCache[copy_id] = {};
+                   this._copy_fields_for_acqdf.forEach(
+                       function(f) {
+                           self.oldCopyWidgetCache[copy_id][f] =
+                               copyWidgets[f].attr("value");
+                       }
+                   );
+               }
+           };
+       
+           this.restoreCopyFieldsBeforeDF = function() {
+               var self = this;
+               for (var copy_id in this.oldCopyWidgetCache) {
+                   this._copy_fields_for_acqdf.forEach(
+                       function(f) {
+                           self.copyWidgetCache[copy_id][f].attr(
+                               "value", self.oldCopyWidgetCache[copy_id][f]
+                           );
+                       }
+                   );
+               }
+           };
+       
+           this._labelFormulasWithCounts = function(store_data) {
+               for (var key in store_data.items) {
+                   var obj = store_data.items[key];
+                   obj.use_count = Number(obj.use_count); /* needed for sorting */
+       
+                   if (this.virtDfaCounts[obj.id])
+                       obj.use_count = obj.use_count + Number(this.virtDfaCounts[obj.id]);
+       
+                   obj.dynLabel = "<span class='acq-lit-distrib-form-use-count'>[" +
+                       obj.use_count + "]</span>&nbsp; " + obj.name;
+               }
+               return store_data;
+           };
+       
+           /**
+            * This method formerly would not refetch the DF formulas if they'd been
+            * loaded already, but now it always re-fetches, since use_count changes.
+            */
+           /** TODO: port distrib-formula selector to autofieldwidget+pcrud/dojo store */
+           this._fetchDistribFormulas = function(onload) {
+               fieldmapper.standardRequest(
+                   ["open-ils.acq",
+                       "open-ils.acq.distribution_formula.ranged.retrieve.atomic"],
+                   {
+                       "async": true,
+                       "params": [openils.User.authtoken, 0, 500],
+                       "oncomplete": function(r) {
+                           self.distribForms = openils.Util.readResponse(r);
+                           if(!self.distribForms || self.distribForms.length == 0) {
+                               self.distribForms = [];
+                           }
+                           self._addDistribFormulaRow();
+                           onload();
+                       }
+                   }
+               );
+           }
+       
+           this._drawBatchCopyWidgets = function() {
+               var row = this.copyBatchRow;
+               dojo.forEach(liDetailBatchFields, 
+                   function(field) {
+                       if(self.copyBatchRowDrawn) {
+                           self.copyBatchWidgets[field].attr('value', null);
+                       } else {
+                           var widget = new openils_widget_AutoFieldWidget({
+                               fmField : field,
+                               fmClass : 'acqlid',
+                               labelFormat : (field == 'fund') ? fundLabelFormat : null,
+                               searchFormat : (field == 'fund') ? fundSearchFormat : null,
+                               searchFilter : (field == 'fund') ? {"active": "t"} : null,
+                               parentNode : dojo.query('[name='+field+']', row)[0],
+                               orgLimitPerms : ['CREATE_PICKLIST'],
+                               dijitArgs : {
+                                   "required": false,
+                                   "labelType": (field == "fund") ? "html" : null
+                               },
+                               noCache: (field == "fund"),
+                               forceSync : true
+                           });
+                           widget.build(
+                               function(w, ww) {
+                                   if (field == "fund" && w.store)
+                                       self._ensureCSSFundClasses(w.store);
+                                   self.copyBatchWidgets[field] = w;
+                               }
+                           );
+                           if (field == "fund") {
+                               dojo.connect(
+                                   widget.widget, "onChange", function(val) {
+                                       self._updateFundSelectorStyle(widget, val);
+                                   }
+                               );
+                           }
+                       }
+                   }
+               );
+               this.copyBatchRowDrawn = true;
+           };
+       
+           this.batchCopyUpdate = function() {
+               var self = this;
+               for(var k in this.copyWidgetCache) {
+                   var cache = this.copyWidgetCache[k];
+                   dojo.forEach(liDetailBatchFields, function(f) {
+                       var newval = self.copyBatchWidgets[f].attr('value');
+                       if(newval) cache[f].attr('value', newval);
+                   });
+               }
+           };
+       
+           this._drawCopies = function(li) {
+               var self = this;
+       
+               // this button sets the total number of copies for a given lineitem
+               acqLitAddCopyCount.onClick = function() { 
+                   var count = acqLitCopyCountInput.attr('value');
+       
+                   // add new rows
+                   while(self.copyCount() < count)
+                       self.addCopy(li); 
+                   
+                   // delete rows if necessary
+                   var diff = self.copyCount() - count;
+                   if(diff > 0) {
+                       var rows = dojo.query('tr', self.copyTbody).reverse().slice(0, diff);
+                       if(confirm(dojo.string.substitute(localeStrings.DELETE_LI_COPIES_CONFIRM, [diff]))) {
+                           dojo.forEach(rows, function(row) {self.deleteCopy(row); });
+                       } else {
+                           acqLitCopyCountInput.attr('value', self.copyCount()+'');
+                       }
+                   }
+               }
+       
+       
+               if(li.lineitem_details().length > 0) {
+                   dojo.forEach(li.lineitem_details(),
+                       function(copy) {
+                           self.addCopy(li, copy);
+                       }
+                   );
+               } else {
+                   self.addCopy(li);
+               }
+           };
+       
+           this.copyCount = function() {
+               var count = 0;
+               for(var id in this.copyCache) {
+                   if(!this.copyCache[id].isdeleted())
+                       count++;
+               }
+               return count;
+           }
+       
+           this.virtCopyId = -1;
+           this.addCopy = function(li, copy) {
+               var row = this.copyRow.cloneNode(true);
+               this.copyTbody.appendChild(row);
+               var self = this;
+       
+               if(!copy) {
+                   copy = new fieldmapper.acqlid();
+                   copy.isnew(true);
+                   copy.id(this.virtCopyId--);
+                   copy.lineitem(li.id());
+               }
+       
+               this.copyCache[copy.id()] = copy;
+               row.setAttribute('copy_id', copy.id());
+               self.copyWidgetCache[copy.id()] = {};
+       
+               acqLitCopyCountInput.attr('value', self.copyCount()+'');
+       
+               var rcvr = copy.receiver();
+               if (rcvr) {
+                   if (!userCache[rcvr]) {
+                       if(rcvr == openils.User.user.id()) {
+                           userCache[rcvr] = openils.User.user;
+                       } else {
+                           userCache[rcvr] = fieldmapper.standardRequest(
+                               ['open-ils.actor', 'open-ils.actor.user.retrieve'],
+                               {params: [openils.User.authtoken, rcvr]}
+                           );
+                       }
+                   }
+                   dojo.query('[name=receiver]', row)[0].innerHTML =  userCache[rcvr].usrname();
+               }
+       
+               dojo.forEach(liDetailFields,
+                   function(field) {
+                       var searchFilter;
+                       if (field == "fund") {
+                           searchFilter = (copy.fund() ?
+                               {"-or": {"active": "t", "id": copy.fund()}} :
+                               {"active" : "t"});
+                       } else {
+                           searchFilter = null;
+                       }
+       
+                       var readOnly = false;
+                       
+                       // TODO: Add support for changing the owning_lib after real copies have been made.  
+                       // owning_lib is order data as much as its item data
+                       if(copy.eg_copy_id() && ['owning_lib', 'location', 'circ_modifier', 'cn_label', 'barcode'].indexOf(field) >= 0) {
+                           readOnly = true;
+                       }
+       
+                       // TODO: add support for changing the fund after debits have been created
+                       // Note: invoicing allows the change
+                       if(copy.fund_debit() && field == 'fund') {
+                           readOnly = true;
+                       }
+       
+       
+                       var widget = new openils_widget_AutoFieldWidget({
+                           fmObject : copy,
+                           fmField : field,
+                           labelFormat : (field == 'fund') ? fundLabelFormat : null,
+                           searchFormat : (field == 'fund') ? fundSearchFormat : null,
+                           dijitArgs: {"labelType": (field == 'fund') ? "html" : null},
+                           searchFilter : searchFilter,
+                           noCache: (field == "fund"),
+                           fmClass : 'acqlid',
+                           parentNode : dojo.query('[name='+field+']', row)[0],
+                           orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
+                           readOnly : readOnly,
+                           orgDefaultsToWs : true
+                       });
+       
+                       widget.build(
+                           // make sure we capture the value from any async widgets
+                           function(w, ww) { 
+       
+                               if (field == "fund" && w.store)
+                                   self._ensureCSSFundClasses(w.store);
+       
+                               if(!readOnly) 
+                                   copy[field](ww.getFormattedValue()) 
+       
+                               self.copyWidgetCache[copy.id()][field] = w;
+       
+                               dojo.connect(w, 'onChange', 
+                                   function(val) { 
+                                       if (field == "fund")
+                                           self._updateFundSelectorStyle(widget, val);
+       
+                                       if (!readOnly && (copy.isnew() || val != copy[field]())) {
+                                           // prevent setting ischanged() automatically on widget load for existing copies
+                                           copy[field](widget.getFormattedValue()) 
+                                           copy.ischanged(true);
+                                       }
+                                   }
+                               );
+                           }
+                       );
+                   }
+               );
+       
+               this.updateLidState(copy, row);
+           };
+       
+           this._ensureCSSFundClass = function(id) {
+               if (!this.fundStyleSheet) {
+                   dojo.create(
+                       "style", {"type": "text/css"},
+                       document.getElementsByTagName("head")[0], "last"
+                   );
+                   this.fundStyleSheet = document.styleSheets[
+                       document.styleSheets.length - 1
+                   ];
+               }
+       
+               var cn = "fund_" + id;
+               if (!this.haveFundClass[cn]) {
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq", "open-ils.acq.fund.check_balance_percentages"],
+                       {
+                           "params": [openils.User.authtoken, id],
+                           "async": true,
+                           "oncomplete": function(r) {
+                               r = openils.Util.readResponse(r);
+                               self.fundBalanceState[id] = r;
+                               var style = "";
+                               if (r[0] /* stop */)
+                                   style = fundStyles.stop;
+                               else if (r[1] /* warning */)
+                                   style = fundStyles.warning;
+                               self.fundStyleSheet.insertRule(
+                                   "." + cn + " { " + style + " }",
+                                   self.fundStyleSheet.cssRules.length
+                               );
+                               self.haveFundClass[cn] = true;
+                           }
+                       }
+                   );
+               }
+           };
+       
+           this._ensureCSSFundClasses = function(store) {
+               store.fetch({
+                   "query": {"id": "*"},
+                   "onItem": function(o) { self._ensureCSSFundClass(o.id[0]); }
+               });
+           };
+       
+           this._updateFundSelectorStyle = function(widget, fund_id) {
+               openils.Util.removeCSSClass(widget.widget.domNode, /fund_\d+/);
+               openils.Util.addCSSClass(widget.widget.domNode, "fund_" + fund_id);
+           };
+       
+           this.updateLidState = function(copy, row) {
+               if (typeof(row) == "undefined") {
+                   row = dojo.query(
+                       'tr[copy_id="' + copy.id() + '"]', this.copyTbody
+                   )[0];
+               }
+       
+               var self = this;
+               var recv_link = nodeByName("receive", row);
+               var unrecv_link = nodeByName("unreceive", row);
+               var del_link = nodeByName("delete", row);
+               var cxl_link = nodeByName("cancel", row);
+               var claim_link = nodeByName("claim", row);
+               var cxl_reason_link = nodeByName("cancel_reason", row);
+       
+               if (copy.cancel_reason()) {
+                   openils.Util.hide(del_link.parentNode);
+                   openils.Util.hide(recv_link);
+                   openils.Util.hide(unrecv_link);
+                   openils.Util.hide(cxl_link);
+                   openils.Util.hide(claim_link);
+       
+                   /* XXX the following may leak memory in a long lived table: dijits may not get destroyed... not positive. revisit. */
+                   var holds_reason = dojo.create(
+                       "span", {
+                           "style": "border-bottom: 1px dashed #000;",
+                           "innerHTML": "Cancelled" /* XXX [sic] and i18n */
+                       }, cxl_reason_link, "only"
+                   );
+                   new dijit_Tooltip(
+                       {
+                           "label": "<em>" + copy.cancel_reason().label() +
+                               "</em><br />" + copy.cancel_reason().description(),
+                           "connectId": [holds_reason]
+                       }, dojo.create("span", null, cxl_reason_link, "last")
+                   );
+                   openils.Util.show(cxl_reason_link, "inline");
+               } else if (this.isPO) {
+                   /* Only using this in one place so far, but may want it for better
+                    * decisions on when to display certain controls. */
+                   var li_state = this.liCache[copy.lineitem()].state();
+       
+                   openils.Util.hide(del_link.parentNode);
+                   openils.Util.hide(cxl_reason_link);
+       
+                   /* Avoid showing (un)receive links, cancel links, for virt copies */
+                   if (copy.id() > 0) {
+                       if (copy.recv_time()) {
+                           openils.Util.hide(cxl_link);
+                           openils.Util.hide(recv_link);
+                           openils.Util.hide(claim_link);
+       
+                           openils.Util.show(unrecv_link, "inline");
+                           unrecv_link.onclick = function() {
+                               if (confirm(localeStrings.UNRECEIVE_LID))
+                                   self.issueReceive(copy, /* rollback */ true);
+                           };
+                       } else {
+                           openils.Util.hide(unrecv_link);
+       
+                           if (this.claimEligibleLid[copy.id()]) {
+                               openils.Util.show(claim_link, "inline");
+                               claim_link.onclick = function() {
+                                   self.claimDialog.show(
+                                       self.liCache[copy.lineitem()], copy.id()
+                                   );
+                               };
+                           } else {
+                               openils.Util.hide(claim_link);
+                           }
+       
+                           openils.Util[li_state == "on-order" ? "show" : "hide"](
+                               recv_link, "inline"
+                           );
+                           openils.Util.show(cxl_link, "inline");
+                           recv_link.onclick = function() {
+                               if (self.checkLiAlerts(copy.lineitem()))
+                                   self.issueReceive(copy);
+                           };
+                           cxl_link.onclick = function() {
+                               self.cancelLid(copy.id());
+                           };
+                       }
+                   } else {
+                       openils.Util.hide(cxl_link);
+                       openils.Util.hide(unrecv_link);
+                       openils.Util.hide(recv_link);
+                       openils.Util.hide(claim_link);
+                   }
+               } else {
+                   openils.Util.hide(unrecv_link);
+                   openils.Util.hide(recv_link);
+                   openils.Util.hide(cxl_reason_link);
+                   openils.Util.hide(claim_link);
+       
+                   del_link.onclick = function() { self.deleteCopy(row) };
+                   openils.Util.show(del_link.parentNode);
+               }
+           }
+       
+           this.cancelLid = function(lid_id) {
+               lidCancelDialog._lid_id = lid_id;
+               openils.Util.show(lidCancelDialog.domNode.parentNode);
+               lidCancelDialog.show();
+               if (!lidCancelDialog._prepared) {
+                   var widget = new openils_widget_AutoFieldWidget({
+                       "fmField": "cancel_reason",
+                       "fmClass": "acqlid",
+                       "parentNode": dojo.byId("acq-lit-lid-cancel-reason"),
+                       "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
+                       "forceSync": true
+                   });
+                   widget.build(
+                       function(w, ww) {
+                           acqLidCancelButton.onClick = function() {
+                               if (w.attr("value")) {
+                                   if (confirm(localeStrings.LID_CANCEL_CONFIRM)) {
+                                       self._cancelLid(
+                                           lidCancelDialog._lid_id,
+                                           w.attr("value")
+                                       );
+                                   }
+                                   lidCancelDialog.hide();
+                               }
+                           };
+                           lidCancelDialog._prepared = true;
+                       }
+                   );
+               }
+           };
+       
+           this._cancelLid = function(lid_id, reason) {
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem_detail.cancel"], {
+                       "params": [openils.User.authtoken, lid_id, reason],
+                       "async": true,
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               if (r.lid) {
+                                   for (var id in r.lid) {
+                                       /* actually this should only iterate once */
+                                       self.copyCache[id].cancel_reason(
+                                           r.lid[id].cancel_reason
+                                       );
+                                       self.updateLidState(self.copyCache[id]);
+                                   }
+                               }
+                           }
+                       }
+                   }
+               );
+           };
+       
+           this._confirmAlert = function(li, lin) {
+               return confirm(
+                   dojo.string.substitute(
+                       localeStrings.CONFIRM_LI_ALERT, [
+                           (new openils_acq_Lineitem({"lineitem": li})).findAttr(
+                               "title", "lineitem_marc_attr_definition"
+                           ),
+                           lin.alert_text().code(),
+                           lin.alert_text().description() || "",
+                           lin.value()
+                       ]
+                   )
+               );
+           };
+       
+           this.checkLiAlerts = function(li_id) {
+               var li = this.liCache[li_id];
+       
+               var alert_notes = li.lineitem_notes().filter(
+                   function(o) { return Boolean(o.alert_text()); }
+               );
+       
+               /* this is _intentionally_ not done in a call to forEach() ... */
+               for (var i = 0; i < alert_notes.length; i++) {
+                   if (this.noteAcks[alert_notes[i].id()])
+                       continue;
+                   else if (!this._confirmAlert(li, alert_notes[i]))
+                       return false;
+                   else
+                       this.noteAcks[alert_notes[i].id()] = true;
+               }
+       
+               return true;
+           };
+       
+           this.deleteCopy = function(row) {
+               var copy = this.copyCache[row.getAttribute('copy_id')];
+               copy.isdeleted(true);
+               if(copy.isnew())
+                   delete this.copyCache[copy.id()];
+               this.copyTbody.removeChild(row);
+           }
+       
+           this._virtDfaCountsAsList = function() {
+               var L = [];
+               for (var key in this.virtDfaCounts) {
+                   for (var i = 0; i < this.virtDfaCounts[key]; i++)
+                       L.push(key);
+               }
+               return L;
+           }
+       
+           this.confirmBreachedCopyFunds = function(copies) {
+               var stop = 0, warning = 0;
+               copies.forEach(
+                   function(o) {
+                       if (o.fund()) {
+                           var state = self.fundBalanceState[o.fund()];
+                           if (state[0] /* stop */)
+                               stop++;
+                           else if (state[1] /* warning */)
+                               warning++;
+                       }
+                   }
+               );
+       
+               if (stop) {
+                   return confirm(localeStrings.CONFIRM_FUNDS_AT_STOP);
+               } else if (warning) {
+                   return confirm(localeStrings.CONFIRM_FUNDS_AT_WARNING);
+               }
+               return true;
+           };
+       
+           this.saveCopyChanges = function(liId) {
+               var self = this;
+               var copies = [];
+       
+       
+               var total = 0;
+               for(var id in this.copyCache) {
+                   var c = this.copyCache[id];
+                   if(!c.isdeleted()) total++;
+                   if(c.isnew() || c.ischanged() || c.isdeleted()) {
+                       if(c.id() < 0) c.id(null);
+                       copies.push(c);
+                   }
+               }
+       
+       
+               dojo.byId('acq-lit-copy-count-label-' + liId).innerHTML = total;
+       
+       
+               if (copies.length > 0) {
+                   if (!this.confirmBreachedCopyFunds(copies))
+                       return;
+       
+                   if (typeof(this._copy_count_cb) == "function")
+                       this._copy_count_cb(liId, total);
+       
+                   openils.Util.show("acq-lit-update-copies-progress");
+                   fieldmapper.standardRequest(
+                       ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
+                       {   async: true,
+                           params: [openils.User.authtoken, copies],
+                           onresponse: function(r) {
+                               var res = openils.Util.readResponse(r);
+                               litUpdateCopiesProgress.update(res);
+                           },
+                           oncomplete: function() {
+                               self.drawCopies(liId, true /* force_fetch */);
+                               openils.Util.hide("acq-lit-update-copies-progress");
+                           }
+                       }
+                   );
+               }
+       
+               var dfa_list = this._virtDfaCountsAsList();
+               if (dfa_list.length > 0) {
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq",
+                       "open-ils.acq.distribution_formula.record_application"],
+                       {
+                           "async": true,
+                           "params": [openils.User.authtoken, dfa_list, liId],
+                           "onresponse": function(r) {
+                               var res = openils.Util.readResponse(r);
+                               if (res && res.length < dfa_list.length)
+                                   alert(localeStrings.DFA_NOT_ALL);
+                           }
+                       }
+                   );
+                   this.virtDfaCounts = {};
+               }
+           }
+       
+           this._updateCreatePoPrepayCheckbox = function(prepay) {
+               var prepay = openils.Util.isTrue(prepay);
+               this._prepayRequiredByVendor = prepay;
+               dijit.byId("acq-lit-po-prepay").attr("checked", prepay);
+           };
+       
+           this._confirmPoPrepaySituation = function() {
+               var want_prepay = dijit.byId("acq-lit-po-prepay").attr("checked");
+               if (want_prepay != this._prepayRequiredByVendor) {
+                   return confirm(
+                       want_prepay ?
+                           localeStrings.VENDOR_SAYS_PREPAY_NOT_NEEDED :
+                           localeStrings.VENDOR_SAYS_PREPAY_NEEDED
+                   );
+               } else {
+                   return true;
+               }
+           };
+       
+           this.applySelectedLiAction = function(action) {
+               var self = this;
+               switch(action) {
+       
+                   case 'delete_selected':
+                       this._deleteLiList(self.getSelected());
+                       break;
+       
+                   case 'create_order':
+                       this._loadPOSelect();
+                       acqLitPoCreateDialog.show();
+                       break;
+       
+                   case 'save_picklist':
+                       acqLitSavePlDialog.show();
+                       break;
+       
+                   case 'selector_ready':
+                   case 'order_ready':
+                       acqLitChangeLiStateDialog.attr('state', action.replace('_', '-'));
+                       acqLitChangeLiStateDialog.show();
+                       break;
+       
+                   case 'print_po':
+                       this.printPO();
+                       break;
+       
+                   case 'po_history':
+                       location.href = oilsBasePath + '/acq/po/history/' + this.isPO;
+                       break;
+       
+                   case 'receive_po':
+                       this.receivePO();
+                       break;
+       
+                   case 'rollback_receive_po':
+                       this.rollbackPoReceive();
+                       break;
+       
+                   case 'create_assets':
+                       this.showAssetCreator();
+                       break;
+       
+                   case 'export_attr_list':
+                       this.chooseExportAttr();
+                       break;
+       
+                   case 'batch_apply_funds':
+                       this.applyBatchLiFunds();
+                       break;
+       
+                   case 'add_brief_record':
+                       if(this.isPO)
+                           location.href = oilsBasePath + '/acq/picklist/brief_record?po=' + this.isPO;
+                       else
+                           location.href = oilsBasePath + '/acq/picklist/brief_record?pl=' + this.isPL;
+       
+                       break;
+       
+                   case "cancel_lineitems":
+                       this.maybeCancelLineitems();
+                       break;
+       
+                   case "change_claim_policy":
+                       var li_list = this.getSelected();
+                       this.claimPolicyPicker.attr("value", null);
+                       liClaimPolicyDialog.show();
+                       liClaimPolicySave.onClick = function() {
+                           self.changeClaimPolicy(
+                               li_list,
+                               self.claimPolicyPicker.attr("value"),
+                               function() {
+                                   li_list.forEach(
+                                       function(li) {
+                                           self.setClaimPolicyControl(li);
+                                           self.reconsiderClaimControl(li);
+                                       }
+                                   );
+                                   liClaimPolicyDialog.hide();
+                               }
+                           )
+                       };
+                       break;
+               }
+           };
+       
+           this.changeClaimPolicy = function(li_list, value, callback) {
+               li_list.forEach(
+                   function(li) { li.claim_policy(value); }
+               );
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem.update"], {
+                       "params": [openils.User.authtoken, li_list],
+                       "async": true,
+                       "oncomplete": function(r) {
+                           r = openils.Util.readResponse(r);
+                           if (callback) callback(r);
+                       }
+                   }
+               );
+           };
+       
+           this.showAssetCreator = function(onAssetsCreated) {
+               if(!this.isPO) return;
+               var self = this;
+           
+               // first, let's see if this PO has any LI's that need to be merged/imported
+               self.pcrud.search('jub', {purchase_order : this.isPO, eg_bib_id : null}, {
+                   id_list : true,
+                   oncomplete : function(r) {
+                       var resp = openils.Util.readResponse(r);
+                       if (resp && resp.length) {
+                           // PO has some non-linked jubs.  
+                           
+                           self.show('asset-creator');
+                           if(!self.vlAgent.loaded)
+                               self.vlAgent.init();
+       
+                           dojo.connect(assetCreatorButton, 'onClick', 
+                               function() { self.createAssets(onAssetsCreated) });
+       
+                       } else {
+       
+                           // all jubs linked, move on to asset creation
+                           self.createAssets(onAssetsCreated, true); 
+                       }
+                   }
+               });
+           }
+       
+           this.createAssets = function(onAssetsCreated, noVl) {
+               this.show('acq-lit-progress-numbers');
+               var self = this;
+               var vlArgs = (noVl) ? {} : {vandelay : this.vlAgent.values()};
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.purchase_order.assets.create'],
+                   {   async: true,
+                       params: [this.authtoken, this.isPO, vlArgs],
+                       onresponse: function(r) {
+                           var resp = openils.Util.readResponse(r);
+                           self._updateProgressNumbers(resp, !Boolean(onAssetsCreated), onAssetsCreated);
+                       }
+                   }
+               );
+           }
+       
+           this.maybeCancelLineitems = function() {
+               openils.Util.show("acq-lit-cancel-reason", "inline");
+               if (!acqLitCancelLineitemsButton._prepared) {
+                   var widget = new openils_widget_AutoFieldWidget({
+                       "fmField": "cancel_reason",
+                       "fmClass": "jub",
+                       "parentNode": dojo.byId("acq-lit-cancel-reason-selector"),
+                       "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
+                       "forceSync": true
+                   });
+                   widget.build(
+                       function(w, ww) {
+                           acqLitCancelLineitemsButton.onClick = function() {
+                               if (w.attr("value")) {
+                                   if (confirm(localeStrings.LI_CANCEL_CONFIRM)) {
+                                       self._cancelLineitems(w.attr("value"));
+                                   }
+                                   openils.Util.hide("acq-lit-cancel-reason");
+                               }
+                           };
+                           acqLitCancelLineitemsButton._prepared = true;
+                       }
+                   );
+               }
+           };
+       
+           this._cancelLineitems = function(reason) {
+               var id_list = this.getSelected().map(function(o) { return o.id(); });
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem.cancel.batch"], {
+                       "params": [openils.User.authtoken, id_list, reason],
+                       "async": true,
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               if (r.li) {
+                                   for (var id in r.li) {
+                                       self.liCache[id].state(r.li[id].state);
+                                       self.liCache[id].cancel_reason(
+                                           r.li[id].cancel_reason
+                                       );
+                                       self.updateLiState(self.liCache[id]);
+                                   }
+                               }
+                               if (r.lid && self.copyCache) {
+                                   for (var id in r.lid) {
+                                       if (self.copyCache[id]) {
+                                           self.copyCache[id].cancel_reason(
+                                               r.lid[id].cancel_reason
+                                           );
+                                           self.updateLidState(self.copyCache[id]);
+                                       }
+                                   }
+                               }
+                           }
+                       }
+                   }
+               );
+           };
+       
+           this.chooseExportAttr = function() {
+               if (!acqLitExportAttrSelector._li_setup) {
+                   var self = this;
+                   acqLitExportAttrSelector.store = new dojo_data_ItemFileReadStore(
+                       {
+                           "data": acqlimad.toStoreData(
+                               this.pcrud.search(
+                                   "acqlimad", {"code": li_exportable_attrs}
+                               )
+                           )
+                       }
+                   );
+                   acqLitExportAttrSelector.setValue();
+                   acqLitExportAttrButton.onClick = function(){self.exportAttrList();};
+                   acqLitExportAttrSelector._li_setup = true;
+               }
+               openils.Util.show("acq-lit-export-attr-holder", "inline");
+           };
+       
+           this.exportAttrList = function() {
+               var attr_def = acqLitExportAttrSelector.item;
+               var li_list = this.getSelected();
+               var value_list = li_list.map(
+                   function(li) {
+                       return (new openils_acq_Lineitem({"lineitem": li})).findAttr(
+                           attr_def.code, "lineitem_marc_attr_definition"
+                       );
+                   }
+               ).filter(function(attr) { return Boolean(attr); });
+       
+               if (value_list.length > 0) {
+                   if (value_list.length < li_list.length) {
+                       if (!confirm(
+                           dojo.string.substitute(
+                               localeStrings.EXPORT_SHORT_LIST, [attr_def.description]
+                           )
+                       )) {
+                           return;
+                       }
+                   }
+                   try {
+                       openils.XUL.contentToFileSaveDialog(
+                           value_list.join("\n"),
+                           localeStrings.EXPORT_SAVE_DIALOG_TITLE
+                       );
+                   } catch (E) {
+                       alert(E);
+                   }
+               } else {
+                   alert(dojo.string.substitute(
+                       localeStrings.EXPORT_EMPTY_LIST, [attr_def.description]
+                   ));
+               }
+       
+               openils.Util.hide("acq-lit-export-attr-holder");
+           };
+       
+           this.printPO = function() {
+               if(!this.isPO) return;
+               progressDialog.show(true);
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.purchase_order.format'],
+                   {   async: true,
+                       params: [this.authtoken, this.isPO, 'html'],
+                       oncomplete: function(r) {
+                           progressDialog.hide();
+                           var evt = openils.Util.readResponse(r);
+                           if(evt && evt.template_output()) {
+                               openils.Util.printHtmlString(evt.template_output().data());
+                           }
+                       }
+                   }
+               );
+           }
+       
+       
+           this.receivePO = function() {
+               if (!this.isPO) return;
+       
+               for (var id in this.liCache) {
+                   /* assumption: liCache reflects exactly the
+                    * set of LIs that belong to our PO */
+                   if (this.liCache[id].state() != "received" &&
+                       !this.checkLiAlerts(id)) return;
+               }
+       
+               this.show('acq-lit-progress-numbers');
+               var self = this;
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.purchase_order.receive'],
+                   {   async: true,
+                       params: [this.authtoken, this.isPO],
+                       onresponse : function(r) {
+                           var resp = openils.Util.readResponse(r);
+                           self._updateProgressNumbers(resp, true);
+                       },
+                   }
+               );
+           }
+       
+           this.issueReceive = function(obj, rollback) {
+               /* (For now) there shall be no marking LI or LIDs (un)received
+                * except from the actual "view PO" interface. */
+               if (!this.isPO) return;
+       
+               var part =
+                   {"jub": "lineitem", "acqlid": "lineitem_detail"}[obj.classname];
+               var method =
+                   "open-ils.acq." + part + ".receive" + (rollback ? ".rollback" : "");
+       
+               progressDialog.show(true);
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", method], {
+                       "async": true,
+                       "params": [this.authtoken, obj.id()],
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               self.fetchClaimInfo(
+                                   part == "lineitem" ? obj.id() : obj.lineitem(),
+                                   /* force */ true,
+                                   function() { self.handleReceive(r); }
+                               );
+                               progressDialog.hide();
+                           }
+                       }
+                   }
+               );
+           };
+       
+           /**
+            * Handles the responses from receive and rollback ML calls.
+            */
+           this.handleReceive = function(resp) {
+               if (resp) {
+                   if (resp.li) {
+                       for (var li_id in resp.li) {
+                           for (var key in resp.li[li_id])
+                               self.liCache[li_id][key](resp.li[li_id][key]);
+                           self.updateLiState(self.liCache[li_id]);
+                       }
+                   }
+                   if (resp.po) {
+                       if (typeof(self.poUpdateCallback) == "function")
+                           self.poUpdateCallback(resp.po);
+                   }
+                   if (resp.lid) {
+                       for (var lid_id in resp.lid) {
+                           for (var key in resp.lid[lid_id])
+                               self.copyCache[lid_id][key](resp.lid[lid_id][key]);
+                           self.updateLidState(self.copyCache[lid_id]);
+                       }
+                   }
+               }
+           };
+       
+           this.rollbackPoReceive = function() {
+               if(!this.isPO) return;
+               if(!confirm(localeStrings.ROLLBACK_PO_RECEIVE_CONFIRM)) return;
+               this.show('acq-lit-progress-numbers');
+               var self = this;
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.purchase_order.receive.rollback'],
+                   {   async: true,
+                       params: [this.authtoken, this.isPO],
+                       onresponse : function(r) {
+                           var resp = openils.Util.readResponse(r);
+                           self._updateProgressNumbers(resp, true);
+                       },
+                   }
+               );
+           }
+       
+           this._updateProgressNumbers = function(resp, reloadOnComplete, onComplete) {
+               this.vlAgent.handleResponse(resp,
+                   function(resp, res) {
+                       if(reloadOnComplete)
+                            location.href = location.href;
+                       if (onComplete)
+                           onComplete(resp, res);
+                   }
+               );
+           }
+       
+       
+           this._createPO = function(fields) {
+               var wantall = (fields.create_from == "all");
+       
+               /* If we're a picklist or purchase order already and the user wants
+                * all lineitems, we might have pages' worth of lineitems haven't all
+                * been loaded yet, so getSelected() won't find them.  The server,
+                * however, should know about all our lineitems, so let's ask the
+                * server for a complete list.
+                */
+       
+               if (wantall) {
+                   this.getSelected(
+                       true, function(list) {
+                           self._createPOFromLineitems(fields, list);
+                       }, /* id_list */ true
+                   );
+               } else {
+                   this._createPOFromLineitems(fields, this.getSelected(false, null, true /* id_list */));
+               }
+           };
+       
+           this._createPOFromLineitems = function(fields, selected) {
+               if (selected.length == 0) return;
+               var self = this;
+       
+               var po = new fieldmapper.acqpo();
+               po.provider(this.createPoProviderSelector.attr("value"));
+               po.ordering_agency(this.createPoAgencySelector.attr("value"));
+               po.prepayment_required(fields.prepayment_required[0] ? true : false);
+       
+               // if we're creating assets, delay the asset creation 
+               // until after the PO is created.  This will allow us to 
+               // use showAssetCreator() directly.
+       
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.purchase_order.create"],
+                   {   async: true,
+                       params: [
+                           openils.User.authtoken, 
+                           po, {lineitems : selected}
+                       ],
+                       onresponse : function(r) {
+                           var resp = openils.Util.readResponse(r);
+                           if (resp.complete) {
+                               // self.isPO is needed for showAssetCreator();
+                               self.isPO = resp.purchase_order.id(); 
+                               var redir = oilsBasePath + "/acq/po/view/" + self.isPO;
+                               if (fields.create_assets[0]) {
+                                   self.showAssetCreator(
+                                       function() {location.href = redir}
+                                   );
+                               } else {
+                                  location.href = redir;
+                               }
+                           }
+                       }
+                   }
+               );
+           };
+       
+       
+           this.batchFundWidget = null;
+       
+           this.applyBatchLiFunds = function() {
+       
+               var liIds = this.getSelected().map(function(li) { return li.id(); });
+               if(liIds.length == 0) return; // warn?
+       
+               var self = this;
+               batchFundUpdateDialog.show();
+       
+               if(!this.batchFundWidget) {
+                   this.batchFundWidget = new openils_widget_AutoFieldWidget({
+                       fmClass : 'acqf',
+                       selfReference : true,
+                       labelFormat : fundLabelFormat,
+                       searchFormat : fundSearchFormat,
+                       searchFilter : {"active": "t"},
+                       parentNode : dojo.byId('acq-lit-batch-fund-selector'),
+                       orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
+                       dijitArgs : { "required": true, "labelType": "html" },
+                       forceSync : true
+                   });
+                   this.batchFundWidget.build();
+               }
+       
+               dojo.connect(batchFundUpdateCancel, 'onClick', function() { batchFundUpdateDialog.hide(); });
+               dojo.connect(batchFundUpdateSubmit, 'onClick', 
+                   function() { 
+       
+                       // TODO: call .dry_run first to test thresholds
+                       fieldmapper.standardRequest(
+                           ['open-ils.acq', 'open-ils.acq.lineitem.fund.update.batch'],
+                           {
+                               params : [
+                                   openils.User.authtoken, 
+                                   liIds,
+                                   self.batchFundWidget.widget.attr('value')
+                               ],
+                               oncomplete : function(r) {
+                                   var resp = openils.Util.readResponse(r);
+                                   if(resp) {
+                                       location.href = location.href;
+                                   }
+                               }
+                           }
+                       )
+                   }
+               );
+           }
+       
+           this._deleteLiList = function(list, idx) {
+               if(idx == null) idx = 0;
+               if(idx >= list.length) return;
+       
+               var li = list[idx];
+               var liId = li.id();
+       
+               if (this.isPO && (li.state() == "on-order" || li.state() == "received")) {
+                   /* It makes little sense to delete a lineitem from a PO that has
+                    * already been marked 'on-order'.  Especially if EDI is in use,
+                    * such a purchase order will probably have already been shipped
+                    * off to a vendor, and mucking with it at this point could leave
+                    * your data in a bad state that doesn't jive with reality.
+                    *
+                    * I could see making this restriction even firmer.
+                    *
+                    * I could also see adjusting the li state comparisons, extending
+                    * the comparison to the PO's state, and/or providing functions
+                    * that house the logic for comparing states in a single location.
+                    *
+                    * Yes, this will be really annoying if you have selected a lot
+                    * of lineitems to cancel that have been ordered. You'll get a
+                    * confirm dialog for each one.
+                    */
+       
+                   if (!confirm(localeStrings.DEL_LI_FROM_PO)) {
+                       self._deleteLiList(list, ++idx); /* move on to next in list */
+                       return;
+                   }
+               }
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.acq',
+                    this.isPO ? 'open-ils.acq.purchase_order.lineitem.delete' : 'open-ils.acq.picklist.lineitem.delete'],
+                   {   async: true,
+                       params: [openils.User.authtoken, liId],
+                       oncomplete: function(r) {
+                           self.removeLineitem(liId);
+                           self._deleteLiList(list, ++idx);
+                       }
+                   }
+               );
+           }
+       
+           this.editOrderMarc = function(li) {
+       
+               /*  To run in Firefox directly, must set signed.applets.codebase_principal_support
+                   to true in about:config */
+       
+               if(!openils.XUL.enableXPConnect()) return;
+       
+               if(openils.XUL.isXUL()) {
+                   win = window.open('/xul/' + openils.XUL.buildId() + '/server/cat/marcedit.xul');
+               } else {
+                   win = window.open('/xul/server/cat/marcedit.xul'); 
+               }
+               var self = this;
+               win.xulG = {
+                   record : {marc : li.marc(), "rtype": "bre"},
+                   save : {
+                       label: 'Save Record', // XXX I18N
+                       func: function(xmlString) {
+                           li.marc(xmlString);
+                           fieldmapper.standardRequest(
+                               ['open-ils.acq', 'open-ils.acq.lineitem.update'],
+                               {   async: true,
+                                   params: [openils.User.authtoken, li],
+                                   oncomplete: function(r) {
+                                       openils.Util.readResponse(r);
+                                       win.close();
+                                       self.drawInfo(li.id())
+                                   }
+                               }
+                           );
+                       },
+                   },
+                   'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
+                   'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
+               };
+           }
+       
+           this._savePl = function(values) {
+               this.getSelected(
+                   (values.which == 'all'),
+                   function(list) { self._savePlFromLineitems(values, list); }
+               );
+           };
+       
+           this._savePlFromLineitems = function(values, selected) {
+               openils.Util.show("acq-lit-generic-progress");
+       
+               if(values.new_name) {
+                   openils_acq_Picklist.create(
+                       {name: values.new_name},
+                       function(id) {
+                           self._updateLiList(
+                               id, selected, 0,
+                               function() {
+                                   location.href =
+                                       oilsBasePath + "/acq/picklist/view/" + id;
+                               }
+                           );
+                       }
+                   );
+               } else if(values.existing_pl) {
+                   // update lineitems to use an existing picklist
+                   self._updateLiList(
+                       values.existing_pl, selected, 0,
+                       function(){
+                           location.href =
+                               oilsBasePath + "/acq/picklist/view/" +
+                               values.existing_pl;
+                       }
+                   );
+               }
+           };
+       
+           this._updateLiState = function(values, state) {
+               progressDialog.show(true);
+               this.getSelected(
+                   (values.which == 'all'),
+                   function(list) {
+                       self._updateLiStateFromLineitems(values, state, list);
+                   }
+               );
+           };
+       
+           this._updateLiStateFromLineitems = function(values, state, selected) {
+               if(!selected.length) return;
+               dojo.forEach(selected, function(li) {li.state(state);});
+               self._updateLiList(null, selected, 0,
+                   // TODO consider inline updates for efficiency
+                   function() { location.href = location.href }
+               );
+           };
+       
+           this._updateLiList = function(pl, list, idx, oncomplete) {
+               if(idx >= list.length) return oncomplete();
+               var li = list[idx];
+               if(pl != null) li.picklist(pl);
+               litGenericProgress.update({maximum: list.length, progress: idx});
+               new openils_acq_Lineitem({lineitem:li}).update(
+                   function(r) {
+                       self._updateLiList(pl, list, ++idx, oncomplete);
+                   }
+               );
+           }
+       
+           this._loadPOSelect = function() {
+               if (!this.createPoProviderSelector) {
+                   var widget = new openils_widget_AutoFieldWidget({
+                       "fmField": "provider",
+                       "fmClass": "acqpo",
+                       "searchFilter": {"active": "t"},
+                       "parentNode": dojo.byId("acq-lit-po-provider"),
+                       "dijitArgs": {
+                           "onChange": function() {
+                               if (this.item) {
+                                   self._updateCreatePoPrepayCheckbox(
+                                       this.item.prepayment_required()
+                                   );
+                               }
+                           }
+                       }
+                   });
+                   widget.build(function(w) { self.createPoProviderSelector = w; });
+               }
+       
+               if (!this.createPoAgencySelector) {
+                   var widget = new openils_widget_AutoFieldWidget({
+                       "fmField": "ordering_agency",
+                       "fmClass": "acqpo",
+                       "parentNode": dojo.byId("acq-lit-po-agency"),
+                       "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
+                   });
+                   widget.build(function(w) { self.createPoAgencySelector = w; });
+               }
+           };
+       
+           this.showRealCopyEditUI = function(li) {
+               copyList = [];
+               var self = this;
+               this.volCache = {};
+       
+               this._fetchLineitem(li.id(), 
+                   function(fullLi) {
+                       li = self.liCache[li.id()] = fullLi;
+       
+                       self.pcrud.search(
+                           'acp', {
+                               id : li.lineitem_details().map(
+                                   function(item) { return item.eg_copy_id() }
+                               )
+                           }, {
+                               async : true,
+                               oncomplete : function(r) {
+                                   try {
+                                       var r_list = openils.Util.readResponse( r );
+                                       for (var i = 0; i < r_list.length; i++) {
+                                           var copy = r_list[i];
+                                           var volId = copy.call_number();
+                                           var volume = self.volCache[volId];
+                                           if(!volume) {
+                                               volume = self.volCache[volId] = self.pcrud.retrieve('acn', volId);
+                                           }
+                                           copy.call_number(volume);
+                                           copyList.push(copy);
+                                       }
+                                       if (xulG) {
+                                           xulG.volume_item_creator( { 'existing_copies' : copyList } );
+                                       }
+                                   } catch(E) {
+                                       alert('error in oncomplete: ' + E);
+                                   }
+                               }
+                           }
+                       );
+                   }
+               );
+           },
+       
+           this.drawBibFinder = function(li) {
+       
+               var query = '';
+               var liWrapper = new openils_acq_Lineitem({lineitem:li});
+       
+               dojo.forEach(
+                   ['isbn', 'upc', 'issn', 'title', 'author'],
+                   function(field) {
+                       var val = liWrapper.findAttr(field, 'lineitem_marc_attr_definition');
+                       if(val) {
+                           if(field == 'title' || field == 'author') {
+                               query += field +':' + val + ' ';
+                           } else {
+                               query += 'identifier|' + field + ':' + val + ' ';
+                           }
+                       }
+                   }
+               );
+       
+               win = window.open(
+                   oilsBasePath + '/acq/lineitem/findbib?query=' + escape(query),
+                   '', 'resizable,scrollbars=1');
+       
+               win.window.recordFound = function(bibId) { 
+                   win.close();
+       
+                   var attrs = li.attributes();
+                   li.attributes(null);
+                   li.eg_bib_id(bibId);
+       
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq", "open-ils.acq.lineitem.update"], 
+                       {
+                           "params": [openils.User.authtoken, li],
+                           "async": true,
+                           "oncomplete": function(r) {
+                               if(openils.Util.readResponse(r)) {
+                                   location.href = location.href;
+                               }
+                           }
+                       }
+                   );
+               }
+           }
+       }
+       
+       
+
+});
\ No newline at end of file
index 6a49421..eded8f3 100644 (file)
-dojo.require("openils.PermaCrud");
-dojo.require("dojo.data.ItemFileReadStore");
+require([
+       "openils/PermaCrud",
+       "dojo/data/ItemFileReadStore"
+       ],
+function(openils_PermaCrud,
+       dojo_data_ItemFileReadStore){
+       
+       if (typeof(localeStrings) == "undefined") {
+           dojo.requireLocalization("openils.acq", "acq");
+           var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
+       }
+       
+       function TagManager(displayNode) {
+           var self = this;
+           this.tagCache = {};
+           this.displayNode = displayNode;
+       
+           this.pcrud = new openils_PermaCrud();
+       
+       // selected (by checkbox) id's from an autogrid of objects:
+       //        return grid.getSelectedItems().map(function(o) { return o.id[0]; });
+       
+           this.displayFund = function(fund) {
+               if (!fund) {
+                   this.displayNode.innerHTML = localeStrings.FUND_NOT_YET_LOADED;
+                   return;
+               }
+       
+               dojo.empty(this.displayNode);
+               fund.tags().forEach(
+                   function(o) {
+                       dojo.place(self.renderTagMapping(o), self.displayNode, "last");
+                   }
+               );
+           };
+       
+           this.renderTagMapping = function(mapping) {
+               var span = dojo.create(
+                   "span", {
+                       "id": "oils-acq-fund-tag-mapping-" + mapping.id(),
+                       "className": "oils-acq-fund-tag",
+                       "innerHTML": mapping.tag().name()
+                   }
+               );
+               dojo.create(
+                   "a", {
+                       "href": "javascript:void(0);",
+                       "innerHTML": "X",
+                       "onclick": function() { self.deleteMapping(mapping); },
+                   },
+                   span, "last"
+               );
+               return span;
+           };
+       
+           this.deleteMapping = function(mapping) {
+               if (confirm(localeStrings.CONFIRM_DELETE_MAPPING)) {
+                   this.pcrud.eliminate(
+                       mapping, {
+                           "oncomplete": function(r) {
+                               dojo.destroy(
+                                   "oils-acq-fund-tag-mapping-" + mapping.id()
+                               );
+                               fund.tags(
+                                   fund.tags().filter(
+                                       function(o) { return o.id() != mapping.id(); }
+                                   )
+                               );
+                           },
+                           "onerror": function() {
+                               /* XXX does onerror not actually work? */
+                               alert(localeStrings.COULD_NOT_DELETE_MAPPING);
+                           }
+                       }
+                   );
+               }
+           };
+       
+           this.addMapping = function(fund, tag) {
+               var mapping = new acqftm();
+               mapping.fund(fund.id());
+               mapping.tag(tag.id());
+       
+               this.pcrud.create(
+                   mapping, {
+                       "onerror": function(r) {
+                           /* XXX does onerror not actually work? */
+                           alert(localeStrings.COULD_NOT_CREATE_MAPPING);
+                       },
+                       "oncomplete": function(r, list) {
+                           mapping = list[0]; /* get the new mapping's ID this way */
+                           mapping.tag(tag); /* re-"flesh" */
+                           fund.tags().push(mapping); /* save local reference */
+                           dojo.place(
+                               self.renderTagMapping(mapping),
+                               self.displayNode, "last"
+                           );
+                       }
+                   }
+               );
+           };
+       
+           this.prepareTagSelector = function(selector) {
+               this.pcrud.search(
+                   "acqft", {
+                       "owner": fieldmapper.aou.orgNodeTrail(
+                           fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()),
+                           true /* asId */
+                       )
+                   }, {
+                       "async": true,
+                       "oncomplete": function(r) {
+                           if ((r = openils.Util.readResponse(r))) {
+                               selector.store = new dojo_data_ItemFileReadStore(
+                                   {"data": acqft.toStoreData(r)}
+                               );
+                               selector.startup();
+                           }
+                       }
+                   }
+               );
+           };
+       }
+       
 
-if (typeof(localeStrings) == "undefined") {
-    dojo.requireLocalization("openils.acq", "acq");
-    var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
-}
-
-function TagManager(displayNode) {
-    var self = this;
-    this.tagCache = {};
-    this.displayNode = displayNode;
-
-    this.pcrud = new openils.PermaCrud();
-
-// selected (by checkbox) id's from an autogrid of objects:
-//        return grid.getSelectedItems().map(function(o) { return o.id[0]; });
-
-    this.displayFund = function(fund) {
-        if (!fund) {
-            this.displayNode.innerHTML = localeStrings.FUND_NOT_YET_LOADED;
-            return;
-        }
-
-        dojo.empty(this.displayNode);
-        fund.tags().forEach(
-            function(o) {
-                dojo.place(self.renderTagMapping(o), self.displayNode, "last");
-            }
-        );
-    };
-
-    this.renderTagMapping = function(mapping) {
-        var span = dojo.create(
-            "span", {
-                "id": "oils-acq-fund-tag-mapping-" + mapping.id(),
-                "className": "oils-acq-fund-tag",
-                "innerHTML": mapping.tag().name()
-            }
-        );
-        dojo.create(
-            "a", {
-                "href": "javascript:void(0);",
-                "innerHTML": "X",
-                "onclick": function() { self.deleteMapping(mapping); },
-            },
-            span, "last"
-        );
-        return span;
-    };
-
-    this.deleteMapping = function(mapping) {
-        if (confirm(localeStrings.CONFIRM_DELETE_MAPPING)) {
-            this.pcrud.eliminate(
-                mapping, {
-                    "oncomplete": function(r) {
-                        dojo.destroy(
-                            "oils-acq-fund-tag-mapping-" + mapping.id()
-                        );
-                        fund.tags(
-                            fund.tags().filter(
-                                function(o) { return o.id() != mapping.id(); }
-                            )
-                        );
-                    },
-                    "onerror": function() {
-                        /* XXX does onerror not actually work? */
-                        alert(localeStrings.COULD_NOT_DELETE_MAPPING);
-                    }
-                }
-            );
-        }
-    };
-
-    this.addMapping = function(fund, tag) {
-        var mapping = new acqftm();
-        mapping.fund(fund.id());
-        mapping.tag(tag.id());
-
-        this.pcrud.create(
-            mapping, {
-                "onerror": function(r) {
-                    /* XXX does onerror not actually work? */
-                    alert(localeStrings.COULD_NOT_CREATE_MAPPING);
-                },
-                "oncomplete": function(r, list) {
-                    mapping = list[0]; /* get the new mapping's ID this way */
-                    mapping.tag(tag); /* re-"flesh" */
-                    fund.tags().push(mapping); /* save local reference */
-                    dojo.place(
-                        self.renderTagMapping(mapping),
-                        self.displayNode, "last"
-                    );
-                }
-            }
-        );
-    };
-
-    this.prepareTagSelector = function(selector) {
-        this.pcrud.search(
-            "acqft", {
-                "owner": fieldmapper.aou.orgNodeTrail(
-                    fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()),
-                    true /* asId */
-                )
-            }, {
-                "async": true,
-                "oncomplete": function(r) {
-                    if ((r = openils.Util.readResponse(r))) {
-                        selector.store = new dojo.data.ItemFileReadStore(
-                            {"data": acqft.toStoreData(r)}
-                        );
-                        selector.startup();
-                    }
-                }
-            }
-        );
-    };
-}
+});
\ No newline at end of file
index dbfc70f..04c74e5 100644 (file)
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.PermaCrud');
-
-function VLAgent(args) {
-    args = args || {};
-    for (var key in args) { 
-        this[key] = args[key]; 
-    }
-
-    this.widgets = [  
-        {key : 'import_no_match'},
-        {key : 'auto_overlay_exact'},
-        {key : 'auto_overlay_1match'},
-        {key : 'auto_overlay_best_match'},
-        {key : 'match_quality_ratio'},
-        {key : 'queue_name'},
-        {key : 'match_set', cls : 'vms'},
-        {key : 'bib_source', cls : 'cbs'},
-        {key : 'merge_profile', cls : 'vmp'},
-        {key : 'fall_through_merge_profile', cls : 'vmp'},
-        {key : 'existing_queue', cls : 'vbq'}
-    ];
-    
-    this.loaded = false;
-
-    this.init = function() {
-        var self = this;
-
-        dojo.forEach(this.widgets,
-            function(widg) {
-                if (widg.cls) { // selectors
-
-                    new openils.widget.AutoFieldWidget({
-                        fmClass : widg.cls,
-                        selfReference : true,
-                        orgLimitPerms : [self.limitPerm || 'CREATE_PURCHASE_ORDER'],
-                        parentNode : dojo.byId('acq_vl:' + widg.key),
-                        searchFilter : (widg.cls == 'vbq') ? {queue_type : 'acq'} : null,
-                        useWriteStore :  (widg.cls == 'vbq')
-                    }).build(function(dijit) { 
-                        widg.dijit = dijit; 
-                        self.attachOnChange(widg);
-                    }); 
-
-                } else { // bools
-                    widg.dijit = dijit.byId('acq_vl:' + widg.key);
-                    self.attachOnChange(widg);
-                }
-            }
-        );
-        
-        // loaded != all widgets are done rendering,
-        // only that init() has been called.
-        this.loaded = true;
-    }
-
-    this.attachOnChange = function(widg) {
-        var self = this;
-        var qInputChange;
-
-        var qSelChange = function(val) {
-            // user selected a queue from the selector;  clear the text input 
-            // and set the item import profile already defined for the queue
-
-            var qInput = self.getDijit('queue_name');
-            var matchSetSelector = self.getDijit('match_set');
-            var qSelector = self.getDijit('existing_queue');
-
-            if(val) {
-                qSelector.store.fetch({
-                    query : {id : val+''},
-                    onComplete : function(items) {
-                        matchSetSelector.attr('value', items[0].match_set[0] || '');
-                        matchSetSelector.attr('disabled', true);
-                    }
-                });
-            } else {
-                matchSetSelector.attr('value', '');
-                matchSetSelector.attr('disabled', false);
-            }
-
-            // detach and reattach to avoid onchange firing while when we clear
-            dojo.disconnect(qInput._onchange);
-            qInput.attr('value', '');
-            qInput._onchange = dojo.connect(qInput, 'onChange', qInputChange);
-        }
-
-        qInputChange = function(val) {
-
-            var qSelector = self.getDijit('existing_queue');
-            var matchSetSelector = self.getDijit('match_set');
-            var foundMatch = false;
-
-            if (val) {
-
-                // if the user entered the name of an existing queue, update the 
-                // queue selector to match the value (and clear the text input 
-                // via qselector onchange)
-                qSelector.store.fetch({
-                    query:{name:val},
-                    onComplete:function(items) {
-                        if(items.length == 0) return;
-                        var item = items[0];
-                        qSelector.attr('value', item.id);
-                        foundMatch = true;
-                    }
-                });
-            }
-
-            if (!foundMatch) {
-                self.getDijit('match_set').attr('disabled', false);
-                dojo.disconnect(qSelector._onchange);
-                qSelector.attr('value', '');
-                qSelector._onchange = dojo.connect(qSelector, 'onChange', qSelChange);
-            }
-        }
-
-        if (widg.key == 'existing_queue') {
-            var qSelector = self.getDijit('existing_queue');
-            qSelector._onchange = dojo.connect(qSelector, 'onChange', qSelChange);
-        } else if(widg.key == 'queue_name') {
-            var qInput = self.getDijit('queue_name');
-            qInput._onchange = dojo.connect(qInput, 'onChange', qInputChange);
-        }
-    }
-
-    this.getDijit = function(key) {
-        return this.widgets.filter(function(w) {return (w.key == key)})[0].dijit;
-    }
-
-    this.values = function() {
-        var values = {};
-        dojo.forEach(this.widgets,
-            function(widg) {
-                values[widg.key] = widg.dijit.attr('value');
-            }
-        );
-        return values;
-    }
-
-    this.handleResponse = function(resp, oncomplete) {
-        if(!resp) return;
-        var res = {}
-
-        console.log('vandelay import returned : ' + js2JSON(resp));
-
-        // update the display counts
-        dojo.byId('acq_vl:li-processed').innerHTML = resp.li;
-        dojo.byId('acq_vl:vqbr-processed').innerHTML = resp.vqbr;
-        dojo.byId('acq_vl:bibs-processed').innerHTML = resp.bibs;
-        dojo.byId('acq_vl:lid-processed').innerHTML = resp.lid;
-        dojo.byId('acq_vl:debits-processed').innerHTML = resp.debits_accrued;
-        dojo.byId('acq_vl:copies-processed').innerHTML = resp.copies;
-
-        if (resp.complete) {
-
-            if(resp.picklist) {
-                res.picklist_url = oilsBasePath + '/acq/picklist/view/' + resp.picklist.id();
-            } 
-
-            if(resp.purchase_order) {
-                res.po_url = oilsBasePath + '/acq/po/view/' + resp.purchase_order.id();
-            }
-
-            if (resp.queue) {
-                var newQid = resp.queue.id();
-                res.queue_url = oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + newQid;
-
-                var qInput = this.getDijit('queue_name');
-
-                if (newQName = qInput.attr('value')) {
-                    // user created a new queue.  Fetch the new queue object,
-                    // replace the ReadStore with a WriteStore and insert.
-                    qInput.attr('value', '');
-                    var qSelector = this.getDijit('existing_queue');
-                    var newQ = new openils.PermaCrud().retrieve('vbq', newQid);
-                    qSelector.store.newItem(newQ.toStoreItem());
-                    qSelector.attr('value', newQid);
-                }
-            }
-
-            if (oncomplete) 
-                oncomplete(resp, res);
-
-            return res;
-        }
-
-        return false; // not yet complete
-    }
-}
+require([
+       "openils/widget/AutoFieldWidget",
+       "openils/PermaCrud"
+       ],
+function(openils_widget_AutoFieldWidget,
+       openils_PermaCrud){
+       
+       function VLAgent(args) {
+           args = args || {};
+           for (var key in args) { 
+               this[key] = args[key]; 
+           }
+       
+           this.widgets = [  
+               {key : 'import_no_match'},
+               {key : 'auto_overlay_exact'},
+               {key : 'auto_overlay_1match'},
+               {key : 'auto_overlay_best_match'},
+               {key : 'match_quality_ratio'},
+               {key : 'queue_name'},
+               {key : 'match_set', cls : 'vms'},
+               {key : 'bib_source', cls : 'cbs'},
+               {key : 'merge_profile', cls : 'vmp'},
+               {key : 'fall_through_merge_profile', cls : 'vmp'},
+               {key : 'existing_queue', cls : 'vbq'}
+           ];
+           
+           this.loaded = false;
+       
+           this.init = function() {
+               var self = this;
+       
+               dojo.forEach(this.widgets,
+                   function(widg) {
+                       if (widg.cls) { // selectors
+       
+                           new openils_widget_AutoFieldWidget({
+                               fmClass : widg.cls,
+                               selfReference : true,
+                               orgLimitPerms : [self.limitPerm || 'CREATE_PURCHASE_ORDER'],
+                               parentNode : dojo.byId('acq_vl:' + widg.key),
+                               searchFilter : (widg.cls == 'vbq') ? {queue_type : 'acq'} : null,
+                               useWriteStore :  (widg.cls == 'vbq')
+                           }).build(function(dijit) { 
+                               widg.dijit = dijit; 
+                               self.attachOnChange(widg);
+                           }); 
+       
+                       } else { // bools
+                           widg.dijit = dijit.byId('acq_vl:' + widg.key);
+                           self.attachOnChange(widg);
+                       }
+                   }
+               );
+               
+               // loaded != all widgets are done rendering,
+               // only that init() has been called.
+               this.loaded = true;
+           }
+       
+           this.attachOnChange = function(widg) {
+               var self = this;
+               var qInputChange;
+       
+               var qSelChange = function(val) {
+                   // user selected a queue from the selector;  clear the text input 
+                   // and set the item import profile already defined for the queue
+       
+                   var qInput = self.getDijit('queue_name');
+                   var matchSetSelector = self.getDijit('match_set');
+                   var qSelector = self.getDijit('existing_queue');
+       
+                   if(val) {
+                       qSelector.store.fetch({
+                           query : {id : val+''},
+                           onComplete : function(items) {
+                               matchSetSelector.attr('value', items[0].match_set[0] || '');
+                               matchSetSelector.attr('disabled', true);
+                           }
+                       });
+                   } else {
+                       matchSetSelector.attr('value', '');
+                       matchSetSelector.attr('disabled', false);
+                   }
+       
+                   // detach and reattach to avoid onchange firing while when we clear
+                   dojo.disconnect(qInput._onchange);
+                   qInput.attr('value', '');
+                   qInput._onchange = dojo.connect(qInput, 'onChange', qInputChange);
+               }
+       
+               qInputChange = function(val) {
+       
+                   var qSelector = self.getDijit('existing_queue');
+                   var matchSetSelector = self.getDijit('match_set');
+                   var foundMatch = false;
+       
+                   if (val) {
+       
+                       // if the user entered the name of an existing queue, update the 
+                       // queue selector to match the value (and clear the text input 
+                       // via qselector onchange)
+                       qSelector.store.fetch({
+                           query:{name:val},
+                           onComplete:function(items) {
+                               if(items.length == 0) return;
+                               var item = items[0];
+                               qSelector.attr('value', item.id);
+                               foundMatch = true;
+                           }
+                       });
+                   }
+       
+                   if (!foundMatch) {
+                       self.getDijit('match_set').attr('disabled', false);
+                       dojo.disconnect(qSelector._onchange);
+                       qSelector.attr('value', '');
+                       qSelector._onchange = dojo.connect(qSelector, 'onChange', qSelChange);
+                   }
+               }
+       
+               if (widg.key == 'existing_queue') {
+                   var qSelector = self.getDijit('existing_queue');
+                   qSelector._onchange = dojo.connect(qSelector, 'onChange', qSelChange);
+               } else if(widg.key == 'queue_name') {
+                   var qInput = self.getDijit('queue_name');
+                   qInput._onchange = dojo.connect(qInput, 'onChange', qInputChange);
+               }
+           }
+       
+           this.getDijit = function(key) {
+               return this.widgets.filter(function(w) {return (w.key == key)})[0].dijit;
+           }
+       
+           this.values = function() {
+               var values = {};
+               dojo.forEach(this.widgets,
+                   function(widg) {
+                       values[widg.key] = widg.dijit.attr('value');
+                   }
+               );
+               return values;
+           }
+       
+           this.handleResponse = function(resp, oncomplete) {
+               if(!resp) return;
+               var res = {}
+       
+               console.log('vandelay import returned : ' + js2JSON(resp));
+       
+               // update the display counts
+               dojo.byId('acq_vl:li-processed').innerHTML = resp.li;
+               dojo.byId('acq_vl:vqbr-processed').innerHTML = resp.vqbr;
+               dojo.byId('acq_vl:bibs-processed').innerHTML = resp.bibs;
+               dojo.byId('acq_vl:lid-processed').innerHTML = resp.lid;
+               dojo.byId('acq_vl:debits-processed').innerHTML = resp.debits_accrued;
+               dojo.byId('acq_vl:copies-processed').innerHTML = resp.copies;
+       
+               if (resp.complete) {
+       
+                   if(resp.picklist) {
+                       res.picklist_url = oilsBasePath + '/acq/picklist/view/' + resp.picklist.id();
+                   } 
+       
+                   if(resp.purchase_order) {
+                       res.po_url = oilsBasePath + '/acq/po/view/' + resp.purchase_order.id();
+                   }
+       
+                   if (resp.queue) {
+                       var newQid = resp.queue.id();
+                       res.queue_url = oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + newQid;
+       
+                       var qInput = this.getDijit('queue_name');
+       
+                       if (newQName = qInput.attr('value')) {
+                           // user created a new queue.  Fetch the new queue object,
+                           // replace the ReadStore with a WriteStore and insert.
+                           qInput.attr('value', '');
+                           var qSelector = this.getDijit('existing_queue');
+                           var newQ = new openils_PermaCrud().retrieve('vbq', newQid);
+                           qSelector.store.newItem(newQ.toStoreItem());
+                           qSelector.attr('value', newQid);
+                       }
+                   }
+       
+                   if (oncomplete) 
+                       oncomplete(resp, res);
+       
+                   return res;
+               }
+       
+               return false; // not yet complete
+           }
+       }
+       
+
+});
\ No newline at end of file
index 5762458..e7039a3 100644 (file)
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.TextBox");
-dojo.require("openils.acq.Lineitem");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
-dojo.require("openils.widget.ProgressDialog");
-dojo.require("openils.widget.AutoFieldWidget");
-
-var eligibleLiTable;
-
-function nodeByName(n, c) { return dojo.query("[name='" + n + "']", c)[0]; }
-
-function EligibleLiTable(filter) {
-    var self = this;
-
-    this.filter = filter;
-    this.liCache = {};
-    this.numClaimableLids = {};
-
-    this.claimNote = dijit.byId("acq-eligible-claim-note");
-    this.table = dojo.byId("acq-eligible-li-table");
-    this.tBody = dojo.query("tbody", this.table)[0];
-    this.tHead = dojo.query("thead", this.table)[0];
-    [this.rowTemplate, this.emptyTemplate] =
-        dojo.query("tr", this.tBody).map(
-            function(o) { return self.tBody.removeChild(o); }
-        );
-
-    nodeByName("selector_all", this.tHead).onclick = function() {
-        var value = this.checked;
-        dojo.query("[name='selector']", self.tBody).forEach(
-            function(o) { o.checked = value; }
-        );
-    };
-
-    new openils.widget.AutoFieldWidget({
-        "fmClass": "acqclt",
-        "selfReference": true,
-        "dijitArgs": {"required": true},
-        "parentNode": dojo.byId("acq-eligible-claim-type")
-    }).build(function(w) { self.claimType = w; });
-
-    new openils.User().buildPermOrgSelector(
-        "VIEW_PURCHASE_ORDER", orderingAgency, null,
-        function() {
-            orderingAgency.attr("value", self.filter.ordering_agency);
-            dojo.connect(
-                orderingAgency, "onChange",
-                function() {
-                    self.filter.ordering_agency = this.attr("value");
-                    self.load();
-                }
-            );
-            self.load();
-        }
-    );
-
-    dojo.byId("acq-eligible-claim-submit").onclick = function() {
-        finalClaimDialog.hide();
-        self.claim(self.getSelected());
-    };
-
-    dojo.query("button[name='claim_submit']").forEach(
-        function(button) {
-            button.onclick = function() {
-                if (self.getSelected().length)
-                    finalClaimDialog.show();
-                else
-                    alert(localeStrings.NO_LI_TO_CLAIM);
-            };
-        }
-    );
-
-    this.showEmpty = function() {
-        dojo.place(dojo.clone(this.emptyTemplate), this.tBody, "only");
-        openils.Util.hide("acq-eligible-claim-controls");
-    };
-
-    this.load = function() {
-        progressDialog.show(true);
-
-        var count = 0;
-        this.reset();
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail.atomic"], {
-                "params": [openils.User.authtoken, this.filter],
-                "async": true,
-                "oncomplete": function(r) {
-                    progressDialog.hide();
-                    var rset = openils.Util.readResponse(r);
-                    if (rset.length < 1) self.showEmpty();
-                    else {
-                        var byLi = {};
-                        rset.forEach(
-                            function(r) {
-                                byLi[r.lineitem()] =
-                                    (byLi[r.lineitem()] || 0) + 1;
-                            }
-                        );
-                        for (var key in byLi)
-                            self.addIfMissing(key, byLi[key]);
-                    }
-                }
-            }
-        );
-    };
-
-    this.reset = function() {
-        this.liCache = {};
-        this.numClaimableLids = {};
-        dojo.empty(this.tBody);
-    };
-
-    this._updateLidLink = function(liId) {
-        this.numClaimableLids[liId] = (this.numClaimableLids[liId] || 0) + 1;
-        if (this.numClaimableLids[liId] == 2) {
-            nodeByName("lid_link", "eligible-li-" + liId).onclick =
-                function() {
-                    location.href = oilsBasePath + "/acq/po/view/" +
-                        self.liCache[liId].purchase_order().id() + "/" +
-                        liId;
-                };
-            openils.Util.show(
-                nodeByName("lid_link_holder", "eligible-li-" + liId)
-            );
-        }
-    };
-
-    /* Despite being called with an argument that's a lineitem ID, this method
-     * is actually called once per lineitem _detail_. */
-    this.addIfMissing = function(liId, number_of_appearances) {
-        var row = dojo.clone(this.rowTemplate);
-
-        var checkbox = nodeByName("selector", row);
-        var desc = nodeByName("description", row);
-
-        openils.acq.Lineitem.fetchAndRender(
-            liId, null, function(li, contents) {
-                self.liCache[liId] = li;
-
-                desc.innerHTML = contents;
-                dojo.attr(row, "id", "eligible-li-" + liId);
-                dojo.attr(checkbox, "value", liId);
-                dojo.place(row, self.tBody, "last");
-
-                for (var i = 0; i < number_of_appearances; i++)
-                    self._updateLidLink(liId);
-            }
-        );
-    };
-
-    /* Despite being called with an argument that's a lineitem ID, this method
-     * is actually called once per lineitem _detail_. */
-    this.removeIfPresent = function(liId) {
-        if (this.liCache[liId]) {
-            delete this.liCache[liId];
-            delete this.numClaimableLids[liId];
-            this.tBody.removeChild(dojo.byId("eligible-li-" + liId));
-        }
-    };
-
-    this.getSelected = function() {
-        return dojo.query("[name='selector']", this.tBody).
-            filter(function(o) { return o.checked; }).
-            map(function(o) { return o.value; });
-    };
-
-    this.resetVoucher = function() { this.voucherWin = null; };
-
-    this.addToVoucher = function(contents) {
-        if (!this.voucherWin)
-            this.voucherWin = openClaimVoucherWindow();
-        dojo.byId("main", this.voucherWin.document).innerHTML +=
-            (contents + "<hr />");
-    };
-
-    this.finishVoucher = function() {
-        var print_btn = dojo.byId("print", this.voucherWin.document);
-        print_btn.disabled = false;
-        print_btn.innerHTML = localeStrings.PRINT;
-    };
-
-    this.claim = function(lineitems) {
-        progressDialog.show(true);
-        self.resetVoucher();
-
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.claim.lineitem"], {
-                "params": [
-                    openils.User.authtoken, lineitems, null,
-                    this.claimType.attr("value"), this.claimNote.attr("value")
-                ],
-                "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r))
-                        self.addToVoucher(r.template_output().data());
-                    else
-                        progressDialog.hide();
-                },
-                "oncomplete": function() {
-                    lineitems.forEach(
-                        function(liId) { self.removeIfPresent(liId); }
-                    );
-                    if (!nodeByName("selector", self.tBody)) // emptiness test
-                        self.showEmpty();
-
-                    self.finishVoucher();
-                    progressDialog.hide();
-                }
-            }
-        );
-    };
-}
-
-function init() {
-    var finished_filter = {};
-    if (filter && filter.indexOf(":") != -1) {
-        filter.split(",").forEach(
-            function(chunk) {
-                var kvlist = chunk.split(":");
-                finished_filter[kvlist[0]] = kvlist[1];
-            }
-        );
-    }
-    filter = finished_filter;
-
-    if (!filter.ordering_agency)
-        filter.ordering_agency = openils.User.user.ws_ou();
-
-    eligibleLiTable = new EligibleLiTable(filter);
-}
-
-openils.Util.addOnLoad(init);
+require([
+       "dijit/form/Button",
+       "dijit/form/TextBox",
+       "openils/acq/Lineitem",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/ProgressDialog",
+       "openils/widget/AutoFieldWidget"
+       ],
+function(dijit_form_Button,
+       dijit_form_TextBox,
+       openils_acq_Lineitem,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_ProgressDialog,
+       openils_widget_AutoFieldWidget){
+       
+       var eligibleLiTable;
+       
+       function nodeByName(n, c) { return dojo.query("[name='" + n + "']", c)[0]; }
+       
+       function EligibleLiTable(filter) {
+           var self = this;
+       
+           this.filter = filter;
+           this.liCache = {};
+           this.numClaimableLids = {};
+       
+           this.claimNote = dijit.byId("acq-eligible-claim-note");
+           this.table = dojo.byId("acq-eligible-li-table");
+           this.tBody = dojo.query("tbody", this.table)[0];
+           this.tHead = dojo.query("thead", this.table)[0];
+           [this.rowTemplate, this.emptyTemplate] =
+               dojo.query("tr", this.tBody).map(
+                   function(o) { return self.tBody.removeChild(o); }
+               );
+       
+           nodeByName("selector_all", this.tHead).onclick = function() {
+               var value = this.checked;
+               dojo.query("[name='selector']", self.tBody).forEach(
+                   function(o) { o.checked = value; }
+               );
+           };
+       
+           new openils_widget_AutoFieldWidget({
+               "fmClass": "acqclt",
+               "selfReference": true,
+               "dijitArgs": {"required": true},
+               "parentNode": dojo.byId("acq-eligible-claim-type")
+           }).build(function(w) { self.claimType = w; });
+       
+           new openils.User().buildPermOrgSelector(
+               "VIEW_PURCHASE_ORDER", orderingAgency, null,
+               function() {
+                   orderingAgency.attr("value", self.filter.ordering_agency);
+                   dojo.connect(
+                       orderingAgency, "onChange",
+                       function() {
+                           self.filter.ordering_agency = this.attr("value");
+                           self.load();
+                       }
+                   );
+                   self.load();
+               }
+           );
+       
+           dojo.byId("acq-eligible-claim-submit").onclick = function() {
+               finalClaimDialog.hide();
+               self.claim(self.getSelected());
+           };
+       
+           dojo.query("button[name='claim_submit']").forEach(
+               function(button) {
+                   button.onclick = function() {
+                       if (self.getSelected().length)
+                           finalClaimDialog.show();
+                       else
+                           alert(localeStrings.NO_LI_TO_CLAIM);
+                   };
+               }
+           );
+       
+           this.showEmpty = function() {
+               dojo.place(dojo.clone(this.emptyTemplate), this.tBody, "only");
+               openils.Util.hide("acq-eligible-claim-controls");
+           };
+       
+           this.load = function() {
+               progressDialog.show(true);
+       
+               var count = 0;
+               this.reset();
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail.atomic"], {
+                       "params": [openils.User.authtoken, this.filter],
+                       "async": true,
+                       "oncomplete": function(r) {
+                           progressDialog.hide();
+                           var rset = openils.Util.readResponse(r);
+                           if (rset.length < 1) self.showEmpty();
+                           else {
+                               var byLi = {};
+                               rset.forEach(
+                                   function(r) {
+                                       byLi[r.lineitem()] =
+                                           (byLi[r.lineitem()] || 0) + 1;
+                                   }
+                               );
+                               for (var key in byLi)
+                                   self.addIfMissing(key, byLi[key]);
+                           }
+                       }
+                   }
+               );
+           };
+       
+           this.reset = function() {
+               this.liCache = {};
+               this.numClaimableLids = {};
+               dojo.empty(this.tBody);
+           };
+       
+           this._updateLidLink = function(liId) {
+               this.numClaimableLids[liId] = (this.numClaimableLids[liId] || 0) + 1;
+               if (this.numClaimableLids[liId] == 2) {
+                   nodeByName("lid_link", "eligible-li-" + liId).onclick =
+                       function() {
+                           location.href = oilsBasePath + "/acq/po/view/" +
+                               self.liCache[liId].purchase_order().id() + "/" +
+                               liId;
+                       };
+                   openils.Util.show(
+                       nodeByName("lid_link_holder", "eligible-li-" + liId)
+                   );
+               }
+           };
+       
+           /* Despite being called with an argument that's a lineitem ID, this method
+            * is actually called once per lineitem _detail_. */
+           this.addIfMissing = function(liId, number_of_appearances) {
+               var row = dojo.clone(this.rowTemplate);
+       
+               var checkbox = nodeByName("selector", row);
+               var desc = nodeByName("description", row);
+       
+               openils_acq_Lineitem.fetchAndRender(
+                   liId, null, function(li, contents) {
+                       self.liCache[liId] = li;
+       
+                       desc.innerHTML = contents;
+                       dojo.attr(row, "id", "eligible-li-" + liId);
+                       dojo.attr(checkbox, "value", liId);
+                       dojo.place(row, self.tBody, "last");
+       
+                       for (var i = 0; i < number_of_appearances; i++)
+                           self._updateLidLink(liId);
+                   }
+               );
+           };
+       
+           /* Despite being called with an argument that's a lineitem ID, this method
+            * is actually called once per lineitem _detail_. */
+           this.removeIfPresent = function(liId) {
+               if (this.liCache[liId]) {
+                   delete this.liCache[liId];
+                   delete this.numClaimableLids[liId];
+                   this.tBody.removeChild(dojo.byId("eligible-li-" + liId));
+               }
+           };
+       
+           this.getSelected = function() {
+               return dojo.query("[name='selector']", this.tBody).
+                   filter(function(o) { return o.checked; }).
+                   map(function(o) { return o.value; });
+           };
+       
+           this.resetVoucher = function() { this.voucherWin = null; };
+       
+           this.addToVoucher = function(contents) {
+               if (!this.voucherWin)
+                   this.voucherWin = openClaimVoucherWindow();
+               dojo.byId("main", this.voucherWin.document).innerHTML +=
+                   (contents + "<hr />");
+           };
+       
+           this.finishVoucher = function() {
+               var print_btn = dojo.byId("print", this.voucherWin.document);
+               print_btn.disabled = false;
+               print_btn.innerHTML = localeStrings.PRINT;
+           };
+       
+           this.claim = function(lineitems) {
+               progressDialog.show(true);
+               self.resetVoucher();
+       
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.claim.lineitem"], {
+                       "params": [
+                           openils.User.authtoken, lineitems, null,
+                           this.claimType.attr("value"), this.claimNote.attr("value")
+                       ],
+                       "async": true,
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r))
+                               self.addToVoucher(r.template_output().data());
+                           else
+                               progressDialog.hide();
+                       },
+                       "oncomplete": function() {
+                           lineitems.forEach(
+                               function(liId) { self.removeIfPresent(liId); }
+                           );
+                           if (!nodeByName("selector", self.tBody)) // emptiness test
+                               self.showEmpty();
+       
+                           self.finishVoucher();
+                           progressDialog.hide();
+                       }
+                   }
+               );
+           };
+       }
+       
+       function init() {
+           var finished_filter = {};
+           if (filter && filter.indexOf(":") != -1) {
+               filter.split(",").forEach(
+                   function(chunk) {
+                       var kvlist = chunk.split(":");
+                       finished_filter[kvlist[0]] = kvlist[1];
+                   }
+               );
+           }
+           filter = finished_filter;
+       
+           if (!filter.ordering_agency)
+               filter.ordering_agency = openils.User.user.ws_ou();
+       
+           eligibleLiTable = new EligibleLiTable(filter);
+       }
+       
+       openils.Util.addOnLoad(init);
+       
+
+});
\ No newline at end of file
index 4169bb5..839f8df 100644 (file)
@@ -1,48 +1,64 @@
-dojo.require("dijit.Dialog");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require('openils.acq.FundingSource');
-dojo.require('openils.acq.CurrencyType');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('dijit.form.Button');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.widget.AutoGrid');
-
-function getOrgInfo(rowIndex, item) {
-    if(!item) return ''; 
-    var owner = this.grid.store.getValue(item, 'owner'); 
-    return fieldmapper.aou.findOrgUnit(owner).shortname();
-
-}
-
-function getBalanceInfo(rowIndex, item) {
-    if(!item) return '';
-    var id = this.grid.store.getValue( item, 'id');   
-    var fs = openils.acq.FundingSource.cache[id];
-    if(fs && fs.summary())
-        return fs.summary().balance;
-    return 0;
-}
-
-function loadFSGrid() {
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.funding_source.org.retrieve'], {
-            async: true,
-            params: [openils.User.authtoken, null, {flesh_summary:1}],
-            onresponse: function(r) { /* request object*/
-                if(fs = openils.Util.readResponse(r)) {
-                    openils.acq.FundingSource.cache[fs.id()] = fs;
-                    fsGrid.store.newItem(acqfs.toStoreItem(fs));
-                }
-            },
-            oncomplete: function() {
-                fsGrid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(loadFSGrid);
+require([
+       "dijit/Dialog",
+       "dijit/form/FilteringSelect",
+       "openils/acq/FundingSource",
+       "openils/acq/CurrencyType",
+       "openils/widget/OrgUnitFilteringSelect",
+       "dijit/form/Button",
+       "dojo/data/ItemFileWriteStore",
+       "dojox/grid/DataGrid",
+       "openils/Event",
+       "openils/Util",
+       "openils/widget/AutoGrid"
+       ],
+function(dijit_Dialog,
+       dijit_form_FilteringSelect,
+       openils_acq_FundingSource,
+       openils_acq_CurrencyType,
+       openils_widget_OrgUnitFilteringSelect,
+       dijit_form_Button,
+       dojo_data_ItemFileWriteStore,
+       dojox_grid_DataGrid,
+       openils_Event,
+       openils_Util,
+       openils_widget_AutoGrid){
+       
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return ''; 
+           var owner = this.grid.store.getValue(item, 'owner'); 
+           return fieldmapper.aou.findOrgUnit(owner).shortname();
+       
+       }
+       
+       function getBalanceInfo(rowIndex, item) {
+           if(!item) return '';
+           var id = this.grid.store.getValue( item, 'id');   
+           var fs = openils_acq_FundingSource.cache[id];
+           if(fs && fs.summary())
+               return fs.summary().balance;
+           return 0;
+       }
+       
+       function loadFSGrid() {
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.funding_source.org.retrieve'], {
+                   async: true,
+                   params: [openils.User.authtoken, null, {flesh_summary:1}],
+                   onresponse: function(r) { /* request object*/
+                       if(fs = openils_Util.readResponse(r)) {
+                           openils_acq_FundingSource.cache[fs.id()] = fs;
+                           fsGrid.store.newItem(acqfs.toStoreItem(fs));
+                       }
+                   },
+                   oncomplete: function() {
+                       fsGrid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(loadFSGrid);
+       
+       
 
+});
\ No newline at end of file
index 5b212c6..028c295 100644 (file)
-dojo.require("dijit.Dialog");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require('dijit.form.Button');
-dojo.require('dijit.TooltipDialog');
-dojo.require('dijit.form.DropDownButton');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.acq.CurrencyType');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.User');
-dojo.require('openils.CGI');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('fieldmapper.OrgUtils');
-dojo.requireLocalization('openils.acq', 'acq');
-var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-
-var contextOrg;
-var rolloverResponses;
-var rolloverMode = false;
-var fundFleshFields = [
-    'spent_balance', 
-    'combined_balance', 
-    'spent_total', 
-    'encumbrance_total', 
-    'debit_total', 
-    'allocation_total'
-];
-
-var adminPermOrgs = [];
-var cachedFunds = [];
-
-function initPage() {
-    contextOrg = openils.User.user.ws_ou();
-
-    var connect = function() {
-        dojo.connect(contextOrgSelector, 'onChange',
-            function() {
-                contextOrg = this.attr('value');
-                dojo.byId('oils-acq-rollover-ctxt-org').innerHTML = 
-                    fieldmapper.aou.findOrgUnit(contextOrg).shortname();
-                rolloverMode = false;
-                gridDataLoader();
-            }
-        );
-    };
-
-    dojo.connect(refreshButton, 'onClick', 
-        function() { rolloverMode = false; gridDataLoader(); });
-
-    new openils.User().buildPermOrgSelector(
-        ['ADMIN_ACQ_FUND', 'VIEW_FUND'], 
-        contextOrgSelector, contextOrg, connect);
-
-    dojo.byId('oils-acq-rollover-ctxt-org').innerHTML = 
-        fieldmapper.aou.findOrgUnit(contextOrg).shortname();
-
-    loadYearSelector();
-    lfGrid.onItemReceived = function(item) {cachedFunds.push(item)};
-
-    new openils.User().getPermOrgList(
-        'ADMIN_ACQ_FUND',
-        function(list) {
-            adminPermOrgs = list;
-            loadFundGrid(
-                new openils.CGI().param('year') 
-                    || new Date().getFullYear().toString());
-        },
-        true, true
-    );
-}
-
-function gridDataLoader() {
-    lfGrid.resetStore();
-    if(rolloverMode) {
-        var offset = lfGrid.displayOffset;
-        for(var i = offset; i < (offset + lfGrid.displayLimit - 1); i++) {
-            var fund = rolloverResponses[i];
-            if(!fund) break;
-            lfGrid.store.newItem(fieldmapper.acqf.toStoreItem(fund));
-        }
-    } else {
-        loadFundGrid();
-    }
-}
-
-function getBalanceInfo(rowIdx, item) {
-    if (!item) return '';
-    var fundId = this.grid.store.getValue(item, 'id');
-    var fund = cachedFunds.filter(function(f) { return f.id() == fundId })[0];
-    var cb = fund.combined_balance();
-    return cb ? cb.amount() : '0';
-}
-
-function loadFundGrid(year) {
-    openils.Util.hide('acq-fund-list-rollover-summary');
-    year = year || fundFilterYearSelect.attr('value');
-    cachedFunds = [];
-
-    lfGrid.loadAll(
-        {
-            flesh : 1,  
-            flesh_fields : {acqf : fundFleshFields},
-            
-            // by default, sort funds I can edit to the front
-            order_by : [
-                {   'class' : 'acqf',
-                    field : 'org',
-                    compare : {'in' : adminPermOrgs},
-                    direction : 'desc'
-                },
-                {   'class' : 'acqf',
-                    field : 'name'
-                }
-            ]
-        }, {   
-            year : year, 
-            org : fieldmapper.aou.descendantNodeList(contextOrg, true) 
-        } 
-    );
-}
-
-function loadYearSelector() {
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.fund.org.years.retrieve'],
-        {   async : true,
-            params : [openils.User.authtoken, {}, {limit_perm : 'VIEW_FUND'}],
-            oncomplete : function(r) {
-
-                var yearList = openils.Util.readResponse(r);
-                if(!yearList) return;
-                yearList = yearList.map(function(year){return {year:year+''};}); // dojo wants strings
-
-                var yearStore = {identifier:'year', name:'year', items:yearList};
-                yearStore.items = yearStore.items.sort().reverse();
-                fundFilterYearSelect.store = new dojo.data.ItemFileWriteStore({data:yearStore});
-
-                // default to this year
-                fundFilterYearSelect.setValue(new Date().getFullYear().toString());
-
-                dojo.connect(
-                    fundFilterYearSelect, 
-                    'onChange', 
-                    function() { 
-                        rolloverMode = false;
-                        gridDataLoader();
-                    }
-                );
-            }
-        }
-    );
-}
-
-function performRollover(args) {
-
-    rolloverMode = true;
-    progressDialog.show(true, "Processing...");
-    rolloverResponses = [];
-
-    var method = 'open-ils.acq.fiscal_rollover';
-
-    if(args.rollover[0] == 'on') {
-        method += '.combined';
-    } else {
-        method += '.propagate';
-    }
-        
-    var dryRun = args.dry_run[0] == 'on';
-    if(dryRun) method += '.dry_run';
-
-    var count = 0;
-    var amount_rolled = 0;
-    var year = fundFilterYearSelect.attr('value'); // TODO alternate selector?
-    
-    fieldmapper.standardRequest(
-        ['open-ils.acq', method],
-        {
-            async : true,
-
-            params : [
-                openils.User.authtoken, 
-                year,
-                contextOrg,
-                (args.child_orgs[0] == 'on')
-            ],
-
-            onresponse : function(r) {
-                var resp = openils.Util.readResponse(r);
-                rolloverResponses.push(resp.fund);
-                count += 1;
-                amount_rolled += Number(resp.rollover_amount);
-            }, 
-
-            oncomplete : function() {
-                
-                var nextYear = Number(year) + 1;
-                rolloverResponses = rolloverResponses.sort(
-                    function(a, b) {
-                        if(a.code() > b.code())
-                            return 1;
-                        return -1;
-                    }
-                )
-
-                dojo.byId('acq-fund-list-rollover-summary-header').innerHTML = 
-                    dojo.string.substitute(
-                        localeStrings.FUND_LIST_ROLLOVER_SUMMARY,
-                        [nextYear]
-                    );
-
-                dojo.byId('acq-fund-list-rollover-summary-funds').innerHTML = 
-                    dojo.string.substitute(
-                        localeStrings.FUND_LIST_ROLLOVER_SUMMARY_FUNDS,
-                        [nextYear, count]
-                    );
-
-                dojo.byId('acq-fund-list-rollover-summary-rollover-amount').innerHTML = 
-                    dojo.string.substitute(
-                        localeStrings.FUND_LIST_ROLLOVER_SUMMARY_ROLLOVER_AMOUNT,
-                        [nextYear, amount_rolled]
-                    );
-
-                if(!dryRun) {
-                    openils.Util.hide('acq-fund-list-rollover-summary-dry-run');
-                    
-                    // add the new year to the year selector if it's not already there
-                    fundFilterYearSelect.store.fetch({
-                        query : {year : nextYear}, 
-                        onComplete:
-                            function(list) {
-                                if(list && list.length > 0) return;
-                                fundFilterYearSelect.store.newItem({year : nextYear});
-                            }
-                    });
-                }
-
-                openils.Util.show('acq-fund-list-rollover-summary');
-                progressDialog.hide();
-                gridDataLoader();
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(initPage);
+require([
+       "dijit/Dialog",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Button",
+       "dijit/TooltipDialog",
+       "dijit/form/DropDownButton",
+       "dijit/form/CheckBox",
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileWriteStore",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/acq/CurrencyType",
+       "openils/Event",
+       "openils/Util",
+       "openils/User",
+       "openils/CGI",
+       "openils/PermaCrud",
+       "openils/widget/AutoGrid",
+       "openils/widget/ProgressDialog",
+       "fieldmapper/OrgUtils"
+       ],
+function(dijit_Dialog,
+       dijit_form_FilteringSelect,
+       dijit_form_Button,
+       dijit_TooltipDialog,
+       dijit_form_DropDownButton,
+       dijit_form_CheckBox,
+       dojox_grid_DataGrid,
+       dojo_data_ItemFileWriteStore,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_acq_CurrencyType,
+       openils_Event,
+       openils_Util,
+       openils_User,
+       openils_CGI,
+       openils_PermaCrud,
+       openils_widget_AutoGrid,
+       openils_widget_ProgressDialog,
+       fieldmapper_OrgUtils){
+       dojo.requireLocalization('openils.acq', 'acq');
+       var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       
+       var contextOrg;
+       var rolloverResponses;
+       var rolloverMode = false;
+       var fundFleshFields = [
+           'spent_balance', 
+           'combined_balance', 
+           'spent_total', 
+           'encumbrance_total', 
+           'debit_total', 
+           'allocation_total'
+       ];
+       
+       var adminPermOrgs = [];
+       var cachedFunds = [];
+       
+       function initPage() {
+           contextOrg = openils_User.user.ws_ou();
+       
+           var connect = function() {
+               dojo.connect(contextOrgSelector, 'onChange',
+                   function() {
+                       contextOrg = this.attr('value');
+                       dojo.byId('oils-acq-rollover-ctxt-org').innerHTML = 
+                           fieldmapper.aou.findOrgUnit(contextOrg).shortname();
+                       rolloverMode = false;
+                       gridDataLoader();
+                   }
+               );
+           };
+       
+           dojo.connect(refreshButton, 'onClick', 
+               function() { rolloverMode = false; gridDataLoader(); });
+       
+           new openils_User().buildPermOrgSelector(
+               ['ADMIN_ACQ_FUND', 'VIEW_FUND'], 
+               contextOrgSelector, contextOrg, connect);
+       
+           dojo.byId('oils-acq-rollover-ctxt-org').innerHTML = 
+               fieldmapper.aou.findOrgUnit(contextOrg).shortname();
+       
+           loadYearSelector();
+           lfGrid.onItemReceived = function(item) {cachedFunds.push(item)};
+       
+           new openils_User().getPermOrgList(
+               'ADMIN_ACQ_FUND',
+               function(list) {
+                   adminPermOrgs = list;
+                   loadFundGrid(
+                       new openils_CGI().param('year') 
+                           || new Date().getFullYear().toString());
+               },
+               true, true
+           );
+       }
+       
+       function gridDataLoader() {
+           lfGrid.resetStore();
+           if(rolloverMode) {
+               var offset = lfGrid.displayOffset;
+               for(var i = offset; i < (offset + lfGrid.displayLimit - 1); i++) {
+                   var fund = rolloverResponses[i];
+                   if(!fund) break;
+                   lfGrid.store.newItem(fieldmapper.acqf.toStoreItem(fund));
+               }
+           } else {
+               loadFundGrid();
+           }
+       }
+       
+       function getBalanceInfo(rowIdx, item) {
+           if (!item) return '';
+           var fundId = this.grid.store.getValue(item, 'id');
+           var fund = cachedFunds.filter(function(f) { return f.id() == fundId })[0];
+           var cb = fund.combined_balance();
+           return cb ? cb.amount() : '0';
+       }
+       
+       function loadFundGrid(year) {
+           openils_Util.hide('acq-fund-list-rollover-summary');
+           year = year || fundFilterYearSelect.attr('value');
+           cachedFunds = [];
+       
+           lfGrid.loadAll(
+               {
+                   flesh : 1,  
+                   flesh_fields : {acqf : fundFleshFields},
+                   
+                   // by default, sort funds I can edit to the front
+                   order_by : [
+                       {   'class' : 'acqf',
+                           field : 'org',
+                           compare : {'in' : adminPermOrgs},
+                           direction : 'desc'
+                       },
+                       {   'class' : 'acqf',
+                           field : 'name'
+                       }
+                   ]
+               }, {   
+                   year : year, 
+                   org : fieldmapper.aou.descendantNodeList(contextOrg, true) 
+               } 
+           );
+       }
+       
+       function loadYearSelector() {
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.fund.org.years.retrieve'],
+               {   async : true,
+                   params : [openils_User.authtoken, {}, {limit_perm : 'VIEW_FUND'}],
+                   oncomplete : function(r) {
+       
+                       var yearList = openils_Util.readResponse(r);
+                       if(!yearList) return;
+                       yearList = yearList.map(function(year){return {year:year+''};}); // dojo wants strings
+       
+                       var yearStore = {identifier:'year', name:'year', items:yearList};
+                       yearStore.items = yearStore.items.sort().reverse();
+                       fundFilterYearSelect.store = new dojo_data_ItemFileWriteStore({data:yearStore});
+       
+                       // default to this year
+                       fundFilterYearSelect.setValue(new Date().getFullYear().toString());
+       
+                       dojo.connect(
+                           fundFilterYearSelect, 
+                           'onChange', 
+                           function() { 
+                               rolloverMode = false;
+                               gridDataLoader();
+                           }
+                       );
+                   }
+               }
+           );
+       }
+       
+       function performRollover(args) {
+       
+           rolloverMode = true;
+           progressDialog.show(true, "Processing...");
+           rolloverResponses = [];
+       
+           var method = 'open-ils.acq.fiscal_rollover';
+       
+           if(args.rollover[0] == 'on') {
+               method += '.combined';
+           } else {
+               method += '.propagate';
+           }
+               
+           var dryRun = args.dry_run[0] == 'on';
+           if(dryRun) method += '.dry_run';
+       
+           var count = 0;
+           var amount_rolled = 0;
+           var year = fundFilterYearSelect.attr('value'); // TODO alternate selector?
+           
+           fieldmapper.standardRequest(
+               ['open-ils.acq', method],
+               {
+                   async : true,
+       
+                   params : [
+                       openils_User.authtoken, 
+                       year,
+                       contextOrg,
+                       (args.child_orgs[0] == 'on')
+                   ],
+       
+                   onresponse : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       rolloverResponses.push(resp.fund);
+                       count += 1;
+                       amount_rolled += Number(resp.rollover_amount);
+                   }, 
+       
+                   oncomplete : function() {
+                       
+                       var nextYear = Number(year) + 1;
+                       rolloverResponses = rolloverResponses.sort(
+                           function(a, b) {
+                               if(a.code() > b.code())
+                                   return 1;
+                               return -1;
+                           }
+                       )
+       
+                       dojo.byId('acq-fund-list-rollover-summary-header').innerHTML = 
+                           dojo.string.substitute(
+                               localeStrings.FUND_LIST_ROLLOVER_SUMMARY,
+                               [nextYear]
+                           );
+       
+                       dojo.byId('acq-fund-list-rollover-summary-funds').innerHTML = 
+                           dojo.string.substitute(
+                               localeStrings.FUND_LIST_ROLLOVER_SUMMARY_FUNDS,
+                               [nextYear, count]
+                           );
+       
+                       dojo.byId('acq-fund-list-rollover-summary-rollover-amount').innerHTML = 
+                           dojo.string.substitute(
+                               localeStrings.FUND_LIST_ROLLOVER_SUMMARY_ROLLOVER_AMOUNT,
+                               [nextYear, amount_rolled]
+                           );
+       
+                       if(!dryRun) {
+                           openils_Util.hide('acq-fund-list-rollover-summary-dry-run');
+                           
+                           // add the new year to the year selector if it's not already there
+                           fundFilterYearSelect.store.fetch({
+                               query : {year : nextYear}, 
+                               onComplete:
+                                   function(list) {
+                                       if(list && list.length > 0) return;
+                                       fundFilterYearSelect.store.newItem({year : nextYear});
+                                   }
+                           });
+                       }
+       
+                       openils_Util.show('acq-fund-list-rollover-summary');
+                       progressDialog.hide();
+                       gridDataLoader();
+                   }
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(initPage);
+       
+
+});
\ No newline at end of file
index 6cc0d7c..ba869f4 100644 (file)
-dojo.require("dijit.Dialog");
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.layout.TabContainer');
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require("dijit.form.CheckBox");
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require("fieldmapper.OrgUtils");
-dojo.require('openils.acq.Fund');
-dojo.require('openils.acq.FundingSource');
-dojo.require('openils.Event');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require("openils.widget.AutoFieldWidget");
-dojo.require("openils.widget.AutoGrid");
-
-var fund = null;
-var tagManager;
-var xferManager;
-
-function getSummaryInfo(rowIndex, item) {
-    if(!item) return'';
-    return new String(fund.summary()[this.field]);
-}
-
-function createAllocation(fields) {
-    fields.fund = fundID;
-    if(isNaN(fields.amount)) fields.amount = null;
-    openils.acq.Fund.createAllocation(fields, 
-        function(r){location.href = location.href;});
-}
-function getOrgInfo(rowIndex, item) {
-    if(!item) return ''; 
-    var owner = this.grid.store.getValue(item, 'org'); 
-    return fieldmapper.aou.findOrgUnit(owner).shortname();
-
-}
-
-
-function getFundingSource(rowIndex, item) {
-    if(item) {
-        var fsId = this.grid.store.getValue(item, 'funding_source');
-        return openils.acq.FundingSource.retrieve(fsId);
-    }
-}
-
-function formatFundingSource(fs) {
-    if(fs) {
-        return '<a href="' + oilsBasePath + '/acq/funding_source/view/'+fs.id()+'">'+fs.code()+'</a>';
-    }
-}
-
-function getXferDest(rowIndex, item) {
-    if(!item) return '';
-    var xfer_destination = this.grid.store.getValue(item, 'xfer_destination');
-    if(!(item && xfer_destination)) return '';
-    return xfer_destination;
-}
-
-function loadFundGrid() {
-    var store = new dojo.data.ItemFileReadStore({data:acqf.toStoreData([fund])});
-    fundGrid.setStore(store);
-    fundGrid.render();
-}
-
-function loadAllocationGrid() {
-    if(fundAllocationGrid.isLoaded) return;
-    /* XXX If we want to show allocating user with a username instead of just
-     * ID#, the following pcrud search will have to be replaced with an API
-     * call. */
-    fundAllocationGrid.loadAll({order_by : {acqfa :  'create_time DESC'}}, {fund : fundID});
-    fundAllocationGrid.isLoaded = true;
-}
-
-function loadDebitGrid() {
-    if(fundDebitGrid.isLoaded) return;
-    fundDebitGrid.loadAll({order_by : {acqfdeb :  'create_time DESC'}}, {fund : fundID});
-    fundDebitGrid.isLoaded = true;
-}
-
-function fetchFund() {
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.fund.retrieve'],
-        {   async: true,
-            params: [
-                openils.User.authtoken, fundID, 
-                {flesh_summary:1, flesh_tags:1} 
-            ],
-            oncomplete: function(r) {
-                fund = r.recv().content();
-                loadFundGrid(fund);
-            }
-        }
-    );
-}
-
-function TransferManager() {
-    var self = this;
-
-    this._init = function() {
-        new openils.widget.AutoFieldWidget({
-            "fmField": "fund",
-            /* We're not really using LIDs here, we just need some class
-             * that has a fund field to take advantage of AutoFieldWidget's
-             * magic. */
-            "fmClass": "acqlid",
-            "labelFormat": ["${0} (${1})", "code", "year"],
-            "searchFormat": ["${0} (${1})", "code", "year"],
-            "searchFilter": {"active": "t"}, /* consider making it possible
-                                                to select inactive? */
-            "parentNode": dojo.byId("oils-acq-fund-xfer-d-selector"),
-            "orgLimitPerms": ["ADMIN_ACQ_FUND"], /* XXX is there a more
-                                                    appropriate permission
-                                                    for this? */
-            "dijitArgs": {
-                "onChange": function() {
-                    openils.Util[
-                        this.item.currency_type == fund.currency_type() ?
-                            "hide" : "show"
-                    ]("oils-acq-fund-xfer-dest-amount", "table-row");
-                }
-            },
-            "forceSync": true
-        }).build(function(w, ww) { self.fundSelector = w; });
-
-        dijit.byId("oils-acq-fund-xfer-same-o-d").onChange = function() {
-            dijit.byId("oils-acq-fund-xfer-d-amount").attr(
-                "disabled", this.attr("checked")
-            );
-        }
-    };
-
-    this._init();
-
-    this.clearFundSelector = function() {
-        if (this.fundSelector.attr("value"))
-            this.fundSelector.attr("value", "");
-    };
-
-    this.setFundName = function(fund) {
-        dojo.byId("oils-acq-fund-xfer-name-fund").innerHTML =
-            fund.code() + " (" + fund.year() + ") / " + fund.name();
-    };
-
-    this.submit = function() {
-        var values = xferDialog.getValues();
-        var dfund = this.fundSelector.item;
-        var dfund_id = typeof(dfund.id) == "object" ? dfund.id[0] : dfund.id;
-
-        if (dfund_id == fund.id()) {
-            alert(localeStrings.FUND_XFER_SAME_SOURCE_AND_DEST);
-            return false;
-        }
-        if (confirm(localeStrings.FUND_XFER_CONFIRM)) {
-            fieldmapper.standardRequest(
-                ["open-ils.acq", "open-ils.acq.funds.transfer_money"], {
-                    "params": [
-                        openils.User.authtoken,
-                        fund.id(),
-                        values.o_amount,
-                        dfund_id,
-                        (dfund.currency_type != fund.currency_type() &&
-                            values.same_o_d.length) ? null : values.d_amount,
-                        values.note
-                    ],
-                    "async": true,
-                    "oncomplete": function(r) {
-                        if (openils.Util.readResponse(r) == 1) {
-                            location.href = location.href;
-                        }
-                    }
-                }
-            );
-        }
-    };
-}
-
-function load() {
-    tagManager = new TagManager(dojo.byId("oils-acq-tag-manager-display"));
-    tagManager.prepareTagSelector(tagSelector);
-
-    xferManager = new TransferManager();
-
-    fetchFund();
-}
-
-openils.Util.addOnLoad(load);
+require([
+       "dijit/Dialog",
+       "dijit/form/FilteringSelect",
+       "dijit/layout/TabContainer",
+       "dijit/layout/ContentPane",
+       "dojox/grid/DataGrid",
+       "dijit/form/CurrencyTextBox",
+       "dijit/form/CheckBox",
+       "dojo/data/ItemFileReadStore",
+       "fieldmapper/OrgUtils",
+       "openils/acq/Fund",
+       "openils/acq/FundingSource",
+       "openils/Event",
+       "openils/User",
+       "openils/Util",
+       "openils/widget/AutoFieldWidget",
+       "openils/widget/AutoGrid"
+       ],
+function(dijit_Dialog,
+       dijit_form_FilteringSelect,
+       dijit_layout_TabContainer,
+       dijit_layout_ContentPane,
+       dojox_grid_DataGrid,
+       dijit_form_CurrencyTextBox,
+       dijit_form_CheckBox,
+       dojo_data_ItemFileReadStore,
+       fieldmapper_OrgUtils,
+       openils_acq_Fund,
+       openils_acq_FundingSource,
+       openils_Event,
+       openils_User,
+       openils_Util,
+       openils_widget_AutoFieldWidget,
+       openils_widget_AutoGrid){
+       
+       var fund = null;
+       var tagManager;
+       var xferManager;
+       
+       function getSummaryInfo(rowIndex, item) {
+           if(!item) return'';
+           return new String(fund.summary()[this.field]);
+       }
+       
+       function createAllocation(fields) {
+           fields.fund = fundID;
+           if(isNaN(fields.amount)) fields.amount = null;
+           openils_acq_Fund.createAllocation(fields, 
+               function(r){location.href = location.href;});
+       }
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return ''; 
+           var owner = this.grid.store.getValue(item, 'org'); 
+           return fieldmapper.aou.findOrgUnit(owner).shortname();
+       
+       }
+       
+       
+       function getFundingSource(rowIndex, item) {
+           if(item) {
+               var fsId = this.grid.store.getValue(item, 'funding_source');
+               return openils_acq_FundingSource.retrieve(fsId);
+           }
+       }
+       
+       function formatFundingSource(fs) {
+           if(fs) {
+               return '<a href="' + oilsBasePath + '/acq/funding_source/view/'+fs.id()+'">'+fs.code()+'</a>';
+           }
+       }
+       
+       function getXferDest(rowIndex, item) {
+           if(!item) return '';
+           var xfer_destination = this.grid.store.getValue(item, 'xfer_destination');
+           if(!(item && xfer_destination)) return '';
+           return xfer_destination;
+       }
+       
+       function loadFundGrid() {
+           var store = new dojo_data_ItemFileReadStore({data:acqf.toStoreData([fund])});
+           fundGrid.setStore(store);
+           fundGrid.render();
+       }
+       
+       function loadAllocationGrid() {
+           if(fundAllocationGrid.isLoaded) return;
+           /* XXX If we want to show allocating user with a username instead of just
+            * ID#, the following pcrud search will have to be replaced with an API
+            * call. */
+           fundAllocationGrid.loadAll({order_by : {acqfa :  'create_time DESC'}}, {fund : fundID});
+           fundAllocationGrid.isLoaded = true;
+       }
+       
+       function loadDebitGrid() {
+           if(fundDebitGrid.isLoaded) return;
+           fundDebitGrid.loadAll({order_by : {acqfdeb :  'create_time DESC'}}, {fund : fundID});
+           fundDebitGrid.isLoaded = true;
+       }
+       
+       function fetchFund() {
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.fund.retrieve'],
+               {   async: true,
+                   params: [
+                       openils_User.authtoken, fundID, 
+                       {flesh_summary:1, flesh_tags:1} 
+                   ],
+                   oncomplete: function(r) {
+                       fund = r.recv().content();
+                       loadFundGrid(fund);
+                   }
+               }
+           );
+       }
+       
+       function TransferManager() {
+           var self = this;
+       
+           this._init = function() {
+               new openils_widget_AutoFieldWidget({
+                   "fmField": "fund",
+                   /* We're not really using LIDs here, we just need some class
+                    * that has a fund field to take advantage of AutoFieldWidget's
+                    * magic. */
+                   "fmClass": "acqlid",
+                   "labelFormat": ["${0} (${1})", "code", "year"],
+                   "searchFormat": ["${0} (${1})", "code", "year"],
+                   "searchFilter": {"active": "t"}, /* consider making it possible
+                                                       to select inactive? */
+                   "parentNode": dojo.byId("oils-acq-fund-xfer-d-selector"),
+                   "orgLimitPerms": ["ADMIN_ACQ_FUND"], /* XXX is there a more
+                                                           appropriate permission
+                                                           for this? */
+                   "dijitArgs": {
+                       "onChange": function() {
+                           openils_Util[
+                               this.item.currency_type == fund.currency_type() ?
+                                   "hide" : "show"
+                           ]("oils-acq-fund-xfer-dest-amount", "table-row");
+                       }
+                   },
+                   "forceSync": true
+               }).build(function(w, ww) { self.fundSelector = w; });
+       
+               dijit.byId("oils-acq-fund-xfer-same-o-d").onChange = function() {
+                   dijit.byId("oils-acq-fund-xfer-d-amount").attr(
+                       "disabled", this.attr("checked")
+                   );
+               }
+           };
+       
+           this._init();
+       
+           this.clearFundSelector = function() {
+               if (this.fundSelector.attr("value"))
+                   this.fundSelector.attr("value", "");
+           };
+       
+           this.setFundName = function(fund) {
+               dojo.byId("oils-acq-fund-xfer-name-fund").innerHTML =
+                   fund.code() + " (" + fund.year() + ") / " + fund.name();
+           };
+       
+           this.submit = function() {
+               var values = xferDialog.getValues();
+               var dfund = this.fundSelector.item;
+               var dfund_id = typeof(dfund.id) == "object" ? dfund.id[0] : dfund.id;
+       
+               if (dfund_id == fund.id()) {
+                   alert(localeStrings.FUND_XFER_SAME_SOURCE_AND_DEST);
+                   return false;
+               }
+               if (confirm(localeStrings.FUND_XFER_CONFIRM)) {
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq", "open-ils.acq.funds.transfer_money"], {
+                           "params": [
+                               openils_User.authtoken,
+                               fund.id(),
+                               values.o_amount,
+                               dfund_id,
+                               (dfund.currency_type != fund.currency_type() &&
+                                   values.same_o_d.length) ? null : values.d_amount,
+                               values.note
+                           ],
+                           "async": true,
+                           "oncomplete": function(r) {
+                               if (openils_Util.readResponse(r) == 1) {
+                                   location.href = location.href;
+                               }
+                           }
+                       }
+                   );
+               }
+           };
+       }
+       
+       function load() {
+           tagManager = new TagManager(dojo.byId("oils-acq-tag-manager-display"));
+           tagManager.prepareTagSelector(tagSelector);
+       
+           xferManager = new TransferManager();
+       
+           fetchFund();
+       }
+       
+       openils_Util.addOnLoad(load);
+       
+
+});
\ No newline at end of file
index 5a68df1..ae1c824 100644 (file)
-dojo.require("dijit.Dialog");
-dojo.require('dijit.layout.TabContainer');
-dojo.require('dijit.layout.ContentPane');
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("dijit.form.Textarea");
-dojo.require("dijit.form.CurrencyTextBox");
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require("fieldmapper.OrgUtils");
-dojo.require('openils.acq.FundingSource');
-dojo.require('openils.acq.Fund');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.widget.AutoGrid');
-    
-var ses = new OpenSRF.ClientSession('open-ils.acq');
-var fundingSource = null;
-
-function resetPage(also_load_grid) {
-    fundingSource = null;
-    fsCreditGrid.isLoaded = false;
-    fsAllocationGrid.isLoaded = false;
-    loadFS(also_load_grid);
-}
-
-function getFund(rowIndex, item) {
-    return '';
-    //return '<a href="[% ctx.base_path %]/acq/fund/view/'+fund.id()+'">'+fund.code()+'</a>';
-}
-
-
-/** creates a new funding_source_credit from the dialog ----- */
-function applyFSCredit(fields) {
-    fields.funding_source = fundingSourceID;
-    openils.acq.FundingSource.createCredit(
-        fields, function() { resetPage(loadCreditGrid); }
-    );
-}
-
-function applyFSAllocation(fields) {
-    fields.funding_source = fundingSourceID;
-    if(isNaN(fields.amount)) fields.amount = null;
-    openils.acq.Fund.createAllocation(
-        fields, function() { resetPage(loadAllocationGrid); }
-    );
-}
-
-/** fetch the fleshed funding source ----- */
-function loadFS(also_load_grid) {
-    var req = ses.request(
-        'open-ils.acq.funding_source.retrieve', 
-        openils.User.authtoken, fundingSourceID, 
-        {flesh_summary:1, flesh_credits:1,flesh_allocations:1}
-    );
-
-    req.oncomplete = function(r) {
-        var msg = req.recv();
-        fundingSource = msg.content();
-        var evt = openils.Event.parse(fundingSource);
-        if(evt) {
-            alert(evt);
-            return;
-        }
-        loadFSGrid();
-        if (typeof(also_load_grid) == "function")
-            also_load_grid(true /* reset_first */);
-    }
-    req.send();
-}
-
-/** Some grid rendering accessor functions ----- */
-function getOrgInfo(rowIndex, item) {
-    if(!item) return ''; 
-    var owner = this.grid.store.getValue(item, 'owner'); 
-    return fieldmapper.aou.findOrgUnit(owner).shortname();
-
-}
-
-function getSummaryInfo(rowIndex) {
-    return new String(fundingSource.summary()[this.field]);
-}
-
-function getFund(rowIndex, item) {
-    if(item) {
-        var fId = this.grid.store.getValue(item, 'fund');
-        return openils.acq.Fund.retrieve(fId);
-    }
-}
-
-function formatFund(fund) {
-    if(fund) {
-        return '<a href="' + oilsBasePath + '/acq/fund/view/'+fund.id()+'">'+fund.code()+'</a>';
-    }
-}
-
-/** builds the summary grid ----- */
-function loadFSGrid() {
-    if(!fundingSource) return;
-    var store = new dojo.data.ItemFileReadStore({data:acqfs.toStoreData([fundingSource])});
-    fundingSourceGrid.setStore(store);
-    fundingSourceGrid.render();
-}
-
-
-/** builds the credits grid ----- */
-function loadCreditGrid(reset_first) {
-    if (fsCreditGrid.isLoaded) return;
-    if (reset_first) fsCreditGrid.resetStore();
-    fsCreditGrid.loadAll(
-        {"order_by": {"acqfscred": "effective_date DESC"}},
-        {"funding_source": fundingSource.id()}
-    );
-    fsCreditGrid.isLoaded = true;
-}
-
-function loadAllocationGrid(reset_first) {
-    if (fsAllocationGrid.isLoaded) return;
-    if (reset_first) fsCreditGrid.resetStore();
-    fsAllocationGrid.loadAll(
-        {"order_by": {"acqfa": "create_time DESC"}},
-        {"funding_source": fundingSource.id()}
-    );
-    fsAllocationGrid.isLoaded = true;
-}
-
-openils.Util.addOnLoad(loadFS);
+require([
+       "dijit/Dialog",
+       "dijit/layout/TabContainer",
+       "dijit/layout/ContentPane",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Textarea",
+       "dijit/form/CurrencyTextBox",
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileReadStore",
+       "fieldmapper/OrgUtils",
+       "openils/acq/FundingSource",
+       "openils/acq/Fund",
+       "openils/Event",
+       "openils/Util",
+       "openils/widget/AutoGrid"
+       ],
+function(dijit_Dialog,
+       dijit_layout_TabContainer,
+       dijit_layout_ContentPane,
+       dijit_form_FilteringSelect,
+       dijit_form_Textarea,
+       dijit_form_CurrencyTextBox,
+       dojox_grid_DataGrid,
+       dojo_data_ItemFileReadStore,
+       fieldmapper_OrgUtils,
+       openils_acq_FundingSource,
+       openils_acq_Fund,
+       openils_Event,
+       openils_Util,
+       openils_widget_AutoGrid){
+           
+       var ses = new OpenSRF.ClientSession('open-ils.acq');
+       var fundingSource = null;
+       
+       function resetPage(also_load_grid) {
+           fundingSource = null;
+           fsCreditGrid.isLoaded = false;
+           fsAllocationGrid.isLoaded = false;
+           loadFS(also_load_grid);
+       }
+       
+       function getFund(rowIndex, item) {
+           return '';
+           //return '<a href="[% ctx.base_path %]/acq/fund/view/'+fund.id()+'">'+fund.code()+'</a>';
+       }
+       
+       
+       /** creates a new funding_source_credit from the dialog ----- */
+       function applyFSCredit(fields) {
+           fields.funding_source = fundingSourceID;
+           openils_acq_FundingSource.createCredit(
+               fields, function() { resetPage(loadCreditGrid); }
+           );
+       }
+       
+       function applyFSAllocation(fields) {
+           fields.funding_source = fundingSourceID;
+           if(isNaN(fields.amount)) fields.amount = null;
+           openils_acq_Fund.createAllocation(
+               fields, function() { resetPage(loadAllocationGrid); }
+           );
+       }
+       
+       /** fetch the fleshed funding source ----- */
+       function loadFS(also_load_grid) {
+           var req = ses.request(
+               'open-ils.acq.funding_source.retrieve', 
+               openils.User.authtoken, fundingSourceID, 
+               {flesh_summary:1, flesh_credits:1,flesh_allocations:1}
+           );
+       
+           req.oncomplete = function(r) {
+               var msg = req.recv();
+               fundingSource = msg.content();
+               var evt = openils_Event.parse(fundingSource);
+               if(evt) {
+                   alert(evt);
+                   return;
+               }
+               loadFSGrid();
+               if (typeof(also_load_grid) == "function")
+                   also_load_grid(true /* reset_first */);
+           }
+           req.send();
+       }
+       
+       /** Some grid rendering accessor functions ----- */
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return ''; 
+           var owner = this.grid.store.getValue(item, 'owner'); 
+           return fieldmapper.aou.findOrgUnit(owner).shortname();
+       
+       }
+       
+       function getSummaryInfo(rowIndex) {
+           return new String(fundingSource.summary()[this.field]);
+       }
+       
+       function getFund(rowIndex, item) {
+           if(item) {
+               var fId = this.grid.store.getValue(item, 'fund');
+               return openils_acq_Fund.retrieve(fId);
+           }
+       }
+       
+       function formatFund(fund) {
+           if(fund) {
+               return '<a href="' + oilsBasePath + '/acq/fund/view/'+fund.id()+'">'+fund.code()+'</a>';
+           }
+       }
+       
+       /** builds the summary grid ----- */
+       function loadFSGrid() {
+           if(!fundingSource) return;
+           var store = new dojo_data_ItemFileReadStore({data:acqfs.toStoreData([fundingSource])});
+           fundingSourceGrid.setStore(store);
+           fundingSourceGrid.render();
+       }
+       
+       
+       /** builds the credits grid ----- */
+       function loadCreditGrid(reset_first) {
+           if (fsCreditGrid.isLoaded) return;
+           if (reset_first) fsCreditGrid.resetStore();
+           fsCreditGrid.loadAll(
+               {"order_by": {"acqfscred": "effective_date DESC"}},
+               {"funding_source": fundingSource.id()}
+           );
+           fsCreditGrid.isLoaded = true;
+       }
+       
+       function loadAllocationGrid(reset_first) {
+           if (fsAllocationGrid.isLoaded) return;
+           if (reset_first) fsCreditGrid.resetStore();
+           fsAllocationGrid.loadAll(
+               {"order_by": {"acqfa": "create_time DESC"}},
+               {"funding_source": fundingSource.id()}
+           );
+           fsAllocationGrid.isLoaded = true;
+       }
+       
+       openils_Util.addOnLoad(loadFS);
+       
+
+});
\ No newline at end of file
index 58bee4c..a1be448 100644 (file)
-dojo.require("dijit.Dialog");
-dojo.require('dijit.layout.TabContainer');
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require("fieldmapper.OrgUtils");
-dojo.require('openils.acq.Provider');
-dojo.require('openils.Event');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-
-var provider = null;
-var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/;
-
-function getOrgInfo(rowIndex, item) {
-    if(!item) return ''; 
-    var owner = this.grid.store.getValue(item, 'owner'); 
-    return fieldmapper.aou.findOrgUnit(owner).shortname();
-
-}
-
-function getTag(rowIdx, item) {
-    if(!item) return '';
-    var xpath = this.grid.store.getValue(item, 'xpath');
-    return xpath.replace(marcRegex, '$1');
-}
-
-function getSubfield(rowIdx, item) {
-    if(!item) return '';
-    var xpath = this.grid.store.getValue(item, 'xpath');
-    return xpath.replace(marcRegex, '$2');
-}
-
-function loadProviderGrid() {
-    var store = new dojo.data.ItemFileReadStore({data:acqpro.toStoreData([provider])});
-   
-    providerGrid.setStore(store);
-    providerGrid.render();
-}
-
-function loadPADGrid() {
-    openils.acq.Provider.retrieveLineitemProviderAttrDefs(providerId, 
-        function(attrs) {
-            var store = new dojo.data.ItemFileReadStore({data:acqlipad.toStoreData(attrs)});
-            var model = new dojox.grid.data.DojoData(
-                null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
-            padGrid.setModel(model);
-            padGrid.update();
-        }
-    );
-}
-
-
-function fetchProvider() {
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.provider.retrieve'],
-        {   async: true,
-            params: [ openils.User.authtoken, providerId ],
-            oncomplete: function(r) {
-                provider = r.recv().content();
-                loadProviderGrid(provider);
-            }
-        }
-    );
-}
-
-function createOrderRecordField(fields) {
-    fields.provider = providerId;
-    if(!fields.xpath) 
-        fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]';
-    delete fields.tag;
-    delete fields.subfield;
-    openils.acq.Provider.createLineitemProviderAttrDef(fields, 
-        function(id) {
-            loadPADGrid();
-        }
-    );
-}
-
-function setORDesc() {
-    var code = dijit.byId('oils-acq-provider-or-code');
-    var desc = dijit.byId('oils-acq-provider-or-desc');
-    desc.setValue(code.getDisplayedValue());
-}
-
-function deleteORDataFields() {
-    var list = []
-    var selected = padGrid.selection.getSelected();
-    for(var idx = 0; idx < selected.length; idx++) 
-        list.push(padGrid.model.getRow(selected[idx]).id);
-    openils.acq.Provider.lineitemProviderAttrDefDeleteList(
-        list, function(){loadPADGrid();});
-}
-
-
-openils.Util.addOnLoad(fetchProvider);
-
-
+require([
+       "dijit/Dialog",
+       "dijit/layout/TabContainer",
+       "dijit/layout/ContentPane",
+       "dijit/form/FilteringSelect",
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileReadStore",
+       "fieldmapper/OrgUtils",
+       "openils/acq/Provider",
+       "openils/Event",
+       "openils/User",
+       "openils/Util"
+       ],
+function(dijit_Dialog,
+       dijit_layout_TabContainer,
+       dijit_layout_ContentPane,
+       dijit_form_FilteringSelect,
+       dojox_grid_DataGrid,
+       dojo_data_ItemFileReadStore,
+       fieldmapper_OrgUtils,
+       openils_acq_Provider,
+       openils_Event,
+       openils_User,
+       openils_Util){
+       
+       var provider = null;
+       var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/;
+       
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return ''; 
+           var owner = this.grid.store.getValue(item, 'owner'); 
+           return fieldmapper.aou.findOrgUnit(owner).shortname();
+       
+       }
+       
+       function getTag(rowIdx, item) {
+           if(!item) return '';
+           var xpath = this.grid.store.getValue(item, 'xpath');
+           return xpath.replace(marcRegex, '$1');
+       }
+       
+       function getSubfield(rowIdx, item) {
+           if(!item) return '';
+           var xpath = this.grid.store.getValue(item, 'xpath');
+           return xpath.replace(marcRegex, '$2');
+       }
+       
+       function loadProviderGrid() {
+           var store = new dojo_data_ItemFileReadStore({data:acqpro.toStoreData([provider])});
+          
+           providerGrid.setStore(store);
+           providerGrid.render();
+       }
+       
+       function loadPADGrid() {
+           openils_acq_Provider.retrieveLineitemProviderAttrDefs(providerId, 
+               function(attrs) {
+                   var store = new dojo_data_ItemFileReadStore({data:acqlipad.toStoreData(attrs)});
+                   var model = new dojox.grid.data.DojoData(
+                       null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+                   padGrid.setModel(model);
+                   padGrid.update();
+               }
+           );
+       }
+       
+       
+       function fetchProvider() {
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.provider.retrieve'],
+               {   async: true,
+                   params: [ openils_User.authtoken, providerId ],
+                   oncomplete: function(r) {
+                       provider = r.recv().content();
+                       loadProviderGrid(provider);
+                   }
+               }
+           );
+       }
+       
+       function createOrderRecordField(fields) {
+           fields.provider = providerId;
+           if(!fields.xpath) 
+               fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]';
+           delete fields.tag;
+           delete fields.subfield;
+           openils_acq_Provider.createLineitemProviderAttrDef(fields, 
+               function(id) {
+                   loadPADGrid();
+               }
+           );
+       }
+       
+       function setORDesc() {
+           var code = dijit.byId('oils-acq-provider-or-code');
+           var desc = dijit.byId('oils-acq-provider-or-desc');
+           desc.setValue(code.getDisplayedValue());
+       }
+       
+       function deleteORDataFields() {
+           var list = []
+           var selected = padGrid.selection.getSelected();
+           for(var idx = 0; idx < selected.length; idx++) 
+               list.push(padGrid.model.getRow(selected[idx]).id);
+           openils_acq_Provider.lineitemProviderAttrDefDeleteList(
+               list, function(){loadPADGrid();});
+       }
+       
+       
+       openils_Util.addOnLoad(fetchProvider);
+       
+       
+       
+
+});
\ No newline at end of file
index 03fade4..158ca80 100644 (file)
@@ -1,55 +1,63 @@
-dojo.require('dojo.date.stamp');
-dojo.require('openils.User');
-dojo.require('openils.widget.EditPane');
-
-function drawInvoicePane(parentNode, inv, args) {
-    args = args || {};
-
-    var override = {};
-    if(!inv) {
-        override = {
-            recv_date : {widgetValue : dojo.date.stamp.toISOString(new Date())},
-            receiver : {widgetValue : openils.User.user.ws_ou()},
-            recv_method : {widgetValue : 'PPR'}
-        };
-    }
-
-    dojo.mixin(override, {
-        provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
-        shipper  : { dijitArgs : { store_options : { base_filter : { active :"t" } } } }
-    });
-
-    for(var field in args) {
-        override[field] = {widgetValue : args[field]};
-    }
-
-    var pane = new openils.widget.EditPane({
-        fmObject : inv,
-        paneStackCount : 2,
-        fmClass : 'acqinv',
-        mode : (inv) ? 'edit' : 'create',
-        hideActionButtons : true,
-        overrideWidgetArgs : override,
-        readOnly : (inv) && openils.Util.isTrue(inv.complete()),
-        requiredFields : [
-            'inv_ident', 
-            'recv_date', 
-            'provider', 
-            'shipper'
-        ],
-        fieldOrder : [
-            'inv_ident', 
-            'recv_date', 
-            'recv_method', 
-            'inv_type', 
-            'provider', 
-            'shipper'
-        ],
-        suppressFields : ['id', 'complete']
-    });
-
-    pane.startup();
-    parentNode.appendChild(pane.domNode);
-    return pane;
-}
+require([
+       "dojo/date/stamp",
+       "openils/User",
+       "openils/widget/EditPane"
+       ],
+function(dojo_date_stamp,
+       openils_User,
+       openils_widget_EditPane){
+       
+       function drawInvoicePane(parentNode, inv, args) {
+           args = args || {};
+       
+           var override = {};
+           if(!inv) {
+               override = {
+                   recv_date : {widgetValue : dojo_date_stamp.toISOString(new Date())},
+                   receiver : {widgetValue : openils_User.user.ws_ou()},
+                   recv_method : {widgetValue : 'PPR'}
+               };
+           }
+       
+           dojo.mixin(override, {
+               provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
+               shipper  : { dijitArgs : { store_options : { base_filter : { active :"t" } } } }
+           });
+       
+           for(var field in args) {
+               override[field] = {widgetValue : args[field]};
+           }
+       
+           var pane = new openils_widget_EditPane({
+               fmObject : inv,
+               paneStackCount : 2,
+               fmClass : 'acqinv',
+               mode : (inv) ? 'edit' : 'create',
+               hideActionButtons : true,
+               overrideWidgetArgs : override,
+               readOnly : (inv) && openils.Util.isTrue(inv.complete()),
+               requiredFields : [
+                   'inv_ident', 
+                   'recv_date', 
+                   'provider', 
+                   'shipper'
+               ],
+               fieldOrder : [
+                   'inv_ident', 
+                   'recv_date', 
+                   'recv_method', 
+                   'inv_type', 
+                   'provider', 
+                   'shipper'
+               ],
+               suppressFields : ['id', 'complete']
+           });
+       
+           pane.startup();
+           parentNode.appendChild(pane.domNode);
+           return pane;
+       }
+       
+       
 
+});
\ No newline at end of file
index d414676..fc7a9eb 100644 (file)
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.NumberSpinner");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.acq.Lineitem");
-dojo.require("openils.widget.AutoFieldWidget");
-dojo.require("openils.widget.ProgressDialog");
-dojo.requireLocalization("openils.acq", "acq");
-
-var copy_table;
-var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
-
-function ReceivableCopyTable() {
-    var self = this;
-
-    this._init = function() {
-        this.columns = ["owning_lib", "location", "collection_code",
-            "circ_modifier", "fund", "cn_label", "barcode"];
-
-        this.tbody = dojo.byId("rows-here");
-        this.pcrud = new openils.PermaCrud();
-
-        this.mode = "number";   /* can be "number" or "list" */
-        this.some_receiving_done = false;
-
-        this._init_select_all();
-    };
-
-    this._init_select_all = function() {
-        dojo.byId("select_all").onchange = function() {
-            var checked = this.checked;
-            dojo.query("input[type='checkbox']", self.tbody).forEach(
-                function(cb) { cb.checked = checked; }
-            );
-        };
-    };
-
-    this._set_invoice_header = function() {
-        dojo.byId("inv-header").innerHTML = dojo.string.substitute(
-            localeStrings.INVOICE_NUMBER, [this.invoice.inv_ident()]
-        );
-    };
-
-    this._configure_for_mode = function() {
-        if (this.mode == "list") {
-            openils.Util.show("list-mode-headings", "table-header-group");
-            openils.Util.hide("set-list-mode");
-            openils.Util.show("set-number-mode");
-            dojo.byId("set-number-mode-link").onclick = function() {
-                self.reset("number");
-                self.load();
-            };
-        } else { /* number */
-            openils.Util.hide("list-mode-headings");
-            openils.Util.show("set-list-mode");
-            openils.Util.hide("set-number-mode");
-            dojo.byId("set-list-mode-link").onclick = function() {
-                self.reset("list");
-                self.load();
-            };
-        }
-    };
-
-    this._get_receivable_details = function(li) {
-        return li.lineitem_details().filter(
-            function(lid) { return (!lid.recv_time() && !lid.cancel_reason()); }
-        );
-    };
-
-    this._create_receiver = function(lid, tr, precheck) {
-        var args = {
-            "type": "checkbox",
-            "name": "receive",
-            "value": lid.id()
-        };
-
-        if (precheck) args.checked = "checked";
-
-        dojo.create("input", args, dojo.create("td", null, tr));
-    };
-
-    this._get_selected_list_mode = function() {
-        return dojo.query("input[type=checkbox]", this.tbody).filter(
-            function(cb) { return cb.checked; }
-        ).map(
-            function(cb) { return cb.value; }
-        );
-    };
-
-    this._get_selected_number_mode = function() {
-        var list = [];
-        for (var li_id in this.spinners) {
-            var spinner = this.spinners[li_id];
-            var li = spinner._li;
-
-            var number = spinner.attr("value");
-            list = list.concat(
-                this._get_receivable_details(li).slice(0, number)
-            );
-        }
-        return list.map(function(lid) { return lid.id(); });
-    };
-
-    /* The first time this interface is loaded, use the phys_item_count field
-     * (the "# paid" column on an invoice) to determing how man items to
-     * preselect.  Otherwise use 0.
-     */
-    this._number_to_preselect = function(ie, li) {
-        return (this.some_receiving_done) ? 0 :
-            Number(ie.phys_item_count() || 0);
-
-//        var n = Number(ie.phys_item_count() || 0) -
-//            li.lineitem_details().filter(
-//                function(lid) {
-//                    return lid.recv_time() || lid.cancel_reason()
-//                }
-//            ).length;
-//
-//        return n > 0 ? n : 0;
-    };
-
-    this._render_copy_count_info = function() {
-        dojo.byId("inv-copy-count-info").innerHTML =
-            dojo.string.substitute(
-                localeStrings.INVOICE_COPY_COUNT_INFO,
-                [this.copy_number_received, this.copy_number_total]
-            );
-    };
-
-    this._increment_copy_count_info = function(li) {
-        var all_uncanceled = li.lineitem_details().filter(
-            function(lid) { return !lid.cancel_reason(); }
-        );
-        this.copy_number_total += all_uncanceled.length;
-        this.copy_number_received += all_uncanceled.filter(
-            function(lid) { return Boolean(lid.recv_time()); }
-        ).length;
-    };
-
-    this._add_lineitem_number_mode = function(details, li, preselect_count) {
-        var tr = dojo.create("tr", null, this.tbody);
-        var td = dojo.create("td", {
-            "colspan": 1 + this.columns.length,
-            "className": "spinner-cell"
-        }, tr);
-
-        var span_id = "number-mode-li-" + li.id();
-
-        td.innerHTML = localeStrings.COPIES_TO_RECEIVE;
-        dojo.create("span", {"id": span_id}, td);
-
-        var max = details.length;
-        var value = (preselect_count <= max ? preselect_count : max);
-
-        this.spinners[li.id()] = new dijit.form.NumberSpinner({
-            "constraints": {"min": 0, "max": max},
-            "value": value
-        }, span_id);
-        this.spinners[li.id()]._li = li;
-    };
-
-    this._add_lineitem_list_mode = function(details, li, preselect_count) {
-        details.forEach(
-            function(lid) {
-                dump("preselect_count "+ preselect_count+"\n");
-                self.add_lineitem_detail(
-                    lid, li, Boolean(preselect_count-- > 0)
-                );
-            }
-        );
-    };
-
-    this.add_lineitem_detail = function(lid, li, precheck) {
-        var tr = dojo.create(
-            "tr", {"className": "copy-row"}, this.tbody
-        );
-
-        /* Make receive checkbox cell. */
-        this._create_receiver(lid, tr, precheck);
-
-        /* Make cells for all the other columns.  Using a read-only
-         * AutoFieldWidget to show the value of each field on a lineitem
-         * detail is much easier than worrying about fleshing enough
-         * information to do the same ourselves. */
-        this.columns.forEach(
-            function(column) {
-                var td = dojo.create("td", null, tr);
-                new openils.widget.AutoFieldWidget({
-                    "parentNode": dojo.create("div", null, td),
-                    "fmField": column,
-                    "fmObject": lid,
-                    "readOnly": true,
-                    "dijitArgs": {"labelType": (column=='fund') ? "html" : null}
-                }).build();
-            }
-        );
-    };
-
-    /* /maybe/ add a lineitem to the table, if it has any lineitem details
-     * that are still receivable, and preselect lineitem details up to the
-     * number specified in ie.phys_item_count() */
-    this.add_lineitem = function(ie, li, displayHTML) {
-        /* This call only affects the blurb about received vs. total copies
-         * on the invoice near the top of the display. */
-        this._increment_copy_count_info(li);
-
-        var receivable_details = this._get_receivable_details(li);
-        if (!receivable_details.length) return;
-
-        /* show lineitem overall description */
-        /* add rows for copies (lineitem details) */
-        dojo.create(
-            "td", {
-                "colspan": 1 + this.columns.length,
-                "innerHTML": displayHTML
-            }, dojo.create("tr", null, this.tbody)
-        );
-
-        /* build look-up table */
-        receivable_details.forEach(
-            function(lid) { self.li_by_lid[lid.id()] = li; }
-        );
-
-        /* Render something for receiving the lineitem details, depending
-         * on mode. */
-        this["_add_lineitem_" + this.mode + "_mode"](
-            receivable_details, li, this._number_to_preselect(ie, li)
-        );
-    };
-
-    this.reset = function(mode) {
-        if (mode)
-            this.mode = mode;
-
-        this.user_has_acked = [];
-        this.li_by_lid = {};
-        this.copy_number_received = 0;
-        this.copy_number_total = 0;
-
-        if (this.spinners) {
-            for (var key in this.spinners)
-                this.spinners[key].destroy();
-        }
-
-        this.spinners = {};
-
-        this._configure_for_mode();
-
-        dojo.empty(this.tbody);
-    };
-
-    /* It's important to remember that an invoice doesn't actually have
-     * lineitems, but rather is made up of invoice entries and invoice items.
-     * Invoice entries usually link to lineitems, though (invoice items
-     * usually link to po_items).
-     */
-    this.load = function(inv_id) {
-        if (inv_id)
-            this.inv_id = inv_id;
-
-        this.reset();
-        progress_dialog.show(true);
-
-        if (!this.invoice) {
-            this.invoice = this.pcrud.retrieve("acqinv", this.inv_id);
-            this._set_invoice_header();
-        }
-
-        this.pcrud.search("acqie", {"invoice": this.inv_id}).forEach(
-            function(entry) {
-                if (entry.lineitem()) {
-                    openils.acq.Lineitem.fetchAndRender(
-                        entry.lineitem(),
-                        {"flesh_li_details": true, "flesh_notes": true},
-                        function(li, str) { self.add_lineitem(entry, li, str); }
-                    );
-                }
-            }
-        );
-
-        this._render_copy_count_info();
-
-        if (openils.Util.objectProperties(this.li_by_lid).length) {
-            openils.Util.show("non-empty");
-            openils.Util.hide("empty");
-        } else {
-            openils.Util.hide("non-empty");
-            openils.Util.show("empty");
-        }
-        progress_dialog.hide();
-    };
-
-    /* returns an array of lineitem_detail IDs */
-    this.get_selected = function() {
-        return this["_get_selected_" + this.mode + "_mode"]();
-    };
-
-    this.receive_lineitem_detail = function(id_list, index) {
-        if (index >= id_list.length) {
-            progress_dialog.hide();
-            this.load();
-
-            return;
-        }
-
-        var lid_id = id_list[index];
-        var li = this.li_by_lid[lid_id];
-
-        if (!this.check_lineitem_alerts(li)) {
-            self.receive_lineitem_detail(id_list, ++index);
-            return;
-        }
-
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem_detail.receive"], {
-                "async": false,
-                "params": [openils.User.authtoken, lid_id],
-                "oncomplete": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        self.some_receiving_done = true;
-                        /* receive the next lid in our list */
-                        self.receive_lineitem_detail(id_list, ++index);
-                    }
-                }
-            }
-        );
-    };
-
-    this.receive_selected = function() {
-        var lid_ids = this.get_selected();
-
-        progress_dialog.show(true);
-
-        this.receive_lineitem_detail(lid_ids, 0);
-    };
-
-    /* 1st of 2 functions all but copied from li_table.js. Refactor this and
-     * that to share code from a 3rd place.
-     */
-    this.check_lineitem_alerts = function(lineitem) {
-        var alert_notes = lineitem.lineitem_notes().filter(
-            function(o) { return Boolean(o.alert_text()); }
-        );
-
-        for (var i = 0; i < alert_notes.length; i++) {
-            if (this.user_has_acked[alert_notes[i].id()])
-                continue;
-            else if (!this.confirm_alert(li, alert_notes[i]))
-                return false;
-            else
-                this.user_has_acked[alert_notes[i].id()] = true;
-        }
-
-        return true;
-    };
-
-    /* 2nd of 2 functions all but copied from li_table.js. Refactor this and
-     * that to share code from a 3rd place.
-     */
-    this.confirm_alert = function(lineitem, note) {
-        return confirm(
-            dojo.string.substitute(
-                localeStrings.CONFIRM_LI_ALERT, [
-                    (new openils.acq.Lineitem({"lineitem": lineitem})).findAttr(
-                        "title", "lineitem_marc_attr_definition"
-                    ),
-                    note.alert_text().code(),
-                    note.alert_text().description() || "",
-                    note.value()
-                ]
-            )
-        );
-    };
-
-    this.back_to_invoice = function() {
-        location.href = oilsBasePath + "/acq/invoice/view/" + this.inv_id;
-    };
-
-    this._init.apply(this, arguments);
-}
-
-function my_init() {
-    copy_table = new ReceivableCopyTable();
-    copy_table.load(inv_id);
-}
-
-openils.Util.addOnLoad(my_init);
+require([
+       "dijit/form/Button",
+       "dijit/form/NumberSpinner",
+       "openils/PermaCrud",
+       "openils/acq/Lineitem",
+       "openils/widget/AutoFieldWidget",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_form_Button,
+       dijit_form_NumberSpinner,
+       openils_PermaCrud,
+       openils_acq_Lineitem,
+       openils_widget_AutoFieldWidget,
+       openils_widget_ProgressDialog){
+       dojo.requireLocalization("openils.acq", "acq");
+       
+       var copy_table;
+       var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
+       
+       function ReceivableCopyTable() {
+           var self = this;
+       
+           this._init = function() {
+               this.columns = ["owning_lib", "location", "collection_code",
+                   "circ_modifier", "fund", "cn_label", "barcode"];
+       
+               this.tbody = dojo.byId("rows-here");
+               this.pcrud = new openils_PermaCrud();
+       
+               this.mode = "number";   /* can be "number" or "list" */
+               this.some_receiving_done = false;
+       
+               this._init_select_all();
+           };
+       
+           this._init_select_all = function() {
+               dojo.byId("select_all").onchange = function() {
+                   var checked = this.checked;
+                   dojo.query("input[type='checkbox']", self.tbody).forEach(
+                       function(cb) { cb.checked = checked; }
+                   );
+               };
+           };
+       
+           this._set_invoice_header = function() {
+               dojo.byId("inv-header").innerHTML = dojo.string.substitute(
+                   localeStrings.INVOICE_NUMBER, [this.invoice.inv_ident()]
+               );
+           };
+       
+           this._configure_for_mode = function() {
+               if (this.mode == "list") {
+                   openils.Util.show("list-mode-headings", "table-header-group");
+                   openils.Util.hide("set-list-mode");
+                   openils.Util.show("set-number-mode");
+                   dojo.byId("set-number-mode-link").onclick = function() {
+                       self.reset("number");
+                       self.load();
+                   };
+               } else { /* number */
+                   openils.Util.hide("list-mode-headings");
+                   openils.Util.show("set-list-mode");
+                   openils.Util.hide("set-number-mode");
+                   dojo.byId("set-list-mode-link").onclick = function() {
+                       self.reset("list");
+                       self.load();
+                   };
+               }
+           };
+       
+           this._get_receivable_details = function(li) {
+               return li.lineitem_details().filter(
+                   function(lid) { return (!lid.recv_time() && !lid.cancel_reason()); }
+               );
+           };
+       
+           this._create_receiver = function(lid, tr, precheck) {
+               var args = {
+                   "type": "checkbox",
+                   "name": "receive",
+                   "value": lid.id()
+               };
+       
+               if (precheck) args.checked = "checked";
+       
+               dojo.create("input", args, dojo.create("td", null, tr));
+           };
+       
+           this._get_selected_list_mode = function() {
+               return dojo.query("input[type=checkbox]", this.tbody).filter(
+                   function(cb) { return cb.checked; }
+               ).map(
+                   function(cb) { return cb.value; }
+               );
+           };
+       
+           this._get_selected_number_mode = function() {
+               var list = [];
+               for (var li_id in this.spinners) {
+                   var spinner = this.spinners[li_id];
+                   var li = spinner._li;
+       
+                   var number = spinner.attr("value");
+                   list = list.concat(
+                       this._get_receivable_details(li).slice(0, number)
+                   );
+               }
+               return list.map(function(lid) { return lid.id(); });
+           };
+       
+           /* The first time this interface is loaded, use the phys_item_count field
+            * (the "# paid" column on an invoice) to determing how man items to
+            * preselect.  Otherwise use 0.
+            */
+           this._number_to_preselect = function(ie, li) {
+               return (this.some_receiving_done) ? 0 :
+                   Number(ie.phys_item_count() || 0);
+       
+       //        var n = Number(ie.phys_item_count() || 0) -
+       //            li.lineitem_details().filter(
+       //                function(lid) {
+       //                    return lid.recv_time() || lid.cancel_reason()
+       //                }
+       //            ).length;
+       //
+       //        return n > 0 ? n : 0;
+           };
+       
+           this._render_copy_count_info = function() {
+               dojo.byId("inv-copy-count-info").innerHTML =
+                   dojo.string.substitute(
+                       localeStrings.INVOICE_COPY_COUNT_INFO,
+                       [this.copy_number_received, this.copy_number_total]
+                   );
+           };
+       
+           this._increment_copy_count_info = function(li) {
+               var all_uncanceled = li.lineitem_details().filter(
+                   function(lid) { return !lid.cancel_reason(); }
+               );
+               this.copy_number_total += all_uncanceled.length;
+               this.copy_number_received += all_uncanceled.filter(
+                   function(lid) { return Boolean(lid.recv_time()); }
+               ).length;
+           };
+       
+           this._add_lineitem_number_mode = function(details, li, preselect_count) {
+               var tr = dojo.create("tr", null, this.tbody);
+               var td = dojo.create("td", {
+                   "colspan": 1 + this.columns.length,
+                   "className": "spinner-cell"
+               }, tr);
+       
+               var span_id = "number-mode-li-" + li.id();
+       
+               td.innerHTML = localeStrings.COPIES_TO_RECEIVE;
+               dojo.create("span", {"id": span_id}, td);
+       
+               var max = details.length;
+               var value = (preselect_count <= max ? preselect_count : max);
+       
+               this.spinners[li.id()] = new dijit_form_NumberSpinner({
+                   "constraints": {"min": 0, "max": max},
+                   "value": value
+               }, span_id);
+               this.spinners[li.id()]._li = li;
+           };
+       
+           this._add_lineitem_list_mode = function(details, li, preselect_count) {
+               details.forEach(
+                   function(lid) {
+                       dump("preselect_count "+ preselect_count+"\n");
+                       self.add_lineitem_detail(
+                           lid, li, Boolean(preselect_count-- > 0)
+                       );
+                   }
+               );
+           };
+       
+           this.add_lineitem_detail = function(lid, li, precheck) {
+               var tr = dojo.create(
+                   "tr", {"className": "copy-row"}, this.tbody
+               );
+       
+               /* Make receive checkbox cell. */
+               this._create_receiver(lid, tr, precheck);
+       
+               /* Make cells for all the other columns.  Using a read-only
+                * AutoFieldWidget to show the value of each field on a lineitem
+                * detail is much easier than worrying about fleshing enough
+                * information to do the same ourselves. */
+               this.columns.forEach(
+                   function(column) {
+                       var td = dojo.create("td", null, tr);
+                       new openils_widget_AutoFieldWidget({
+                           "parentNode": dojo.create("div", null, td),
+                           "fmField": column,
+                           "fmObject": lid,
+                           "readOnly": true,
+                           "dijitArgs": {"labelType": (column=='fund') ? "html" : null}
+                       }).build();
+                   }
+               );
+           };
+       
+           /* /maybe/ add a lineitem to the table, if it has any lineitem details
+            * that are still receivable, and preselect lineitem details up to the
+            * number specified in ie.phys_item_count() */
+           this.add_lineitem = function(ie, li, displayHTML) {
+               /* This call only affects the blurb about received vs. total copies
+                * on the invoice near the top of the display. */
+               this._increment_copy_count_info(li);
+       
+               var receivable_details = this._get_receivable_details(li);
+               if (!receivable_details.length) return;
+       
+               /* show lineitem overall description */
+               /* add rows for copies (lineitem details) */
+               dojo.create(
+                   "td", {
+                       "colspan": 1 + this.columns.length,
+                       "innerHTML": displayHTML
+                   }, dojo.create("tr", null, this.tbody)
+               );
+       
+               /* build look-up table */
+               receivable_details.forEach(
+                   function(lid) { self.li_by_lid[lid.id()] = li; }
+               );
+       
+               /* Render something for receiving the lineitem details, depending
+                * on mode. */
+               this["_add_lineitem_" + this.mode + "_mode"](
+                   receivable_details, li, this._number_to_preselect(ie, li)
+               );
+           };
+       
+           this.reset = function(mode) {
+               if (mode)
+                   this.mode = mode;
+       
+               this.user_has_acked = [];
+               this.li_by_lid = {};
+               this.copy_number_received = 0;
+               this.copy_number_total = 0;
+       
+               if (this.spinners) {
+                   for (var key in this.spinners)
+                       this.spinners[key].destroy();
+               }
+       
+               this.spinners = {};
+       
+               this._configure_for_mode();
+       
+               dojo.empty(this.tbody);
+           };
+       
+           /* It's important to remember that an invoice doesn't actually have
+            * lineitems, but rather is made up of invoice entries and invoice items.
+            * Invoice entries usually link to lineitems, though (invoice items
+            * usually link to po_items).
+            */
+           this.load = function(inv_id) {
+               if (inv_id)
+                   this.inv_id = inv_id;
+       
+               this.reset();
+               progress_dialog.show(true);
+       
+               if (!this.invoice) {
+                   this.invoice = this.pcrud.retrieve("acqinv", this.inv_id);
+                   this._set_invoice_header();
+               }
+       
+               this.pcrud.search("acqie", {"invoice": this.inv_id}).forEach(
+                   function(entry) {
+                       if (entry.lineitem()) {
+                           openils_acq_Lineitem.fetchAndRender(
+                               entry.lineitem(),
+                               {"flesh_li_details": true, "flesh_notes": true},
+                               function(li, str) { self.add_lineitem(entry, li, str); }
+                           );
+                       }
+                   }
+               );
+       
+               this._render_copy_count_info();
+       
+               if (openils.Util.objectProperties(this.li_by_lid).length) {
+                   openils.Util.show("non-empty");
+                   openils.Util.hide("empty");
+               } else {
+                   openils.Util.hide("non-empty");
+                   openils.Util.show("empty");
+               }
+               progress_dialog.hide();
+           };
+       
+           /* returns an array of lineitem_detail IDs */
+           this.get_selected = function() {
+               return this["_get_selected_" + this.mode + "_mode"]();
+           };
+       
+           this.receive_lineitem_detail = function(id_list, index) {
+               if (index >= id_list.length) {
+                   progress_dialog.hide();
+                   this.load();
+       
+                   return;
+               }
+       
+               var lid_id = id_list[index];
+               var li = this.li_by_lid[lid_id];
+       
+               if (!this.check_lineitem_alerts(li)) {
+                   self.receive_lineitem_detail(id_list, ++index);
+                   return;
+               }
+       
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem_detail.receive"], {
+                       "async": false,
+                       "params": [openils.User.authtoken, lid_id],
+                       "oncomplete": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               self.some_receiving_done = true;
+                               /* receive the next lid in our list */
+                               self.receive_lineitem_detail(id_list, ++index);
+                           }
+                       }
+                   }
+               );
+           };
+       
+           this.receive_selected = function() {
+               var lid_ids = this.get_selected();
+       
+               progress_dialog.show(true);
+       
+               this.receive_lineitem_detail(lid_ids, 0);
+           };
+       
+           /* 1st of 2 functions all but copied from li_table.js. Refactor this and
+            * that to share code from a 3rd place.
+            */
+           this.check_lineitem_alerts = function(lineitem) {
+               var alert_notes = lineitem.lineitem_notes().filter(
+                   function(o) { return Boolean(o.alert_text()); }
+               );
+       
+               for (var i = 0; i < alert_notes.length; i++) {
+                   if (this.user_has_acked[alert_notes[i].id()])
+                       continue;
+                   else if (!this.confirm_alert(li, alert_notes[i]))
+                       return false;
+                   else
+                       this.user_has_acked[alert_notes[i].id()] = true;
+               }
+       
+               return true;
+           };
+       
+           /* 2nd of 2 functions all but copied from li_table.js. Refactor this and
+            * that to share code from a 3rd place.
+            */
+           this.confirm_alert = function(lineitem, note) {
+               return confirm(
+                   dojo.string.substitute(
+                       localeStrings.CONFIRM_LI_ALERT, [
+                           (new openils_acq_Lineitem({"lineitem": lineitem})).findAttr(
+                               "title", "lineitem_marc_attr_definition"
+                           ),
+                           note.alert_text().code(),
+                           note.alert_text().description() || "",
+                           note.value()
+                       ]
+                   )
+               );
+           };
+       
+           this.back_to_invoice = function() {
+               location.href = oilsBasePath + "/acq/invoice/view/" + this.inv_id;
+           };
+       
+           this._init.apply(this, arguments);
+       }
+       
+       function my_init() {
+           copy_table = new ReceivableCopyTable();
+           copy_table.load(inv_id);
+       }
+       
+       openils.Util.addOnLoad(my_init);
+       
+
+});
\ No newline at end of file
index a326299..a58b967 100644 (file)
-dojo.require('dojo.date.locale');
-dojo.require('dojo.date.stamp');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.form.NumberTextBox');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require('openils.CGI');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.EditPane');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('openils.acq.Lineitem');
-
-dojo.requireLocalization('openils.acq', 'acq');
-var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-
-var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
-var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
-
-var cgi = new openils.CGI();
-var pcrud = new openils.PermaCrud();
-var attachLi;
-var attachPo;
-var invoice;
-var itemTbody;
-var itemTemplate;
-var entryTemplate;
-var totalInvoicedBox;
-var totalPaidBox;
-var balanceOwedBox;
-var invoicePane;
-var itemTypes;
-var virtualId = -1;
-var extraCopies = {};
-var extraCopiesFund;
-var widgetRegistry = {acqie : {}, acqii : {}};
-
-function nodeByName(name, context) {
-    return dojo.query('[name='+name+']', context)[0];
-}
-
-function init() {
-
-    attachLi = cgi.param('attach_li');
-    attachPo = cgi.param('attach_po');
-
-    itemTypes = pcrud.retrieveAll('aiit');
-
-    if(cgi.param('create')) {
-        renderInvoice();
-
-    } else {
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.invoice.retrieve.authoritative'],
-            {
-                params : [openils.User.authtoken, invoiceId],
-                oncomplete : function(r) {
-                    invoice = openils.Util.readResponse(r);     
-                    renderInvoice();
-                }
-            }
-        );
-    }
-
-    extraCopiesFund = new openils.widget.AutoFieldWidget({
-        fmField : 'fund',
-        fmClass : 'acqlid',
-        searchFilter : {active : 't'},
-        labelFormat : fundLabelFormat,
-        searchFormat : fundSearchFormat,
-        dijitArgs : {required : true},
-        parentNode : dojo.byId('acq-invoice-extra-copies-fund')
-    });
-    extraCopiesFund.build();
-}
-
-function renderInvoice() {
-
-    // in create mode, let the LI or PO render the invoice with seed data
-    if( !(cgi.param('create') && (attachPo || attachLi)) ) {
-        invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
-    }
-
-    dojo.byId('acq-invoice-new-item').onclick = function() {
-        var item = new fieldmapper.acqii();
-        item.id(virtualId--);
-        item.isnew(true);
-        addInvoiceItem(item);
-    }
-
-    updateTotalCost();
-
-    if(invoice && openils.Util.isTrue(invoice.complete())) {
-
-        dojo.forEach( // hide widgets that should not be visible for a completed invoice
-            dojo.query('.hide-complete'), 
-            function(node) { openils.Util.hide(node); }
-        );
-
-        new openils.User().getPermOrgList(
-            'ACQ_INVOICE_REOPEN', 
-            function (orgs) {
-                if(orgs.indexOf(invoice.receiver()) >= 0)
-                    openils.Util.show('acq-invoice-reopen-button-wrapper', 'inline');
-            }, 
-            true, 
-            true
-        );
-    }
-
-    if(invoice) {
-        dojo.forEach(
-            invoice.items(),
-            function(item) {
-                addInvoiceItem(item);
-            }
-        );
-
-        dojo.forEach(
-            invoice.entries(),
-            function(entry) {
-                addInvoiceEntry(entry);
-            }
-        );
-    }
-
-    if(attachLi) doAttachLi();
-    if(attachPo) doAttachPo();
-}
-
-function doAttachLi() {
-
-    //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()}; 
-    if(cgi.param('create')) {
-
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative'],
-            {
-                params : [openils.User.authtoken, attachLi, {clear_marc:1}],
-                oncomplete : function(r) {
-                    var li = openils.Util.readResponse(r);
-                    invoicePane = drawInvoicePane(
-                        dojo.byId('acq-view-invoice-div'), null, 
-                        {provider : li.provider(), shipper : li.provider()}
-                    );
-                }
-            }
-        );
-    }
-
-    var entry = new fieldmapper.acqie();
-    entry.id(virtualId--);
-    entry.isnew(true);
-    entry.lineitem(attachLi);
-    addInvoiceEntry(entry);
-}
-
-function doAttachPo() {
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
-        {   async: true,
-            params: [
-                openils.User.authtoken, attachPo, 
-                {flesh_lineitem_ids : true, flesh_po_items : true}
-            ],
-            oncomplete: function(r) {
-                var po = openils.Util.readResponse(r);
-
-                if(cgi.param('create')) {
-                    // render the invoice using some seed data from the PO
-                    var invoiceArgs = {provider : po.provider(), shipper : po.provider()}; 
-                    invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
-                }
-
-                dojo.forEach(po.lineitems(), 
-                    function(lineitem) {
-                        var entry = new fieldmapper.acqie();
-                        entry.id(virtualId--);
-                        entry.isnew(true);
-                        entry.lineitem(lineitem);
-                        entry.purchase_order(po);
-                        addInvoiceEntry(entry);
-                    }
-                );
-
-                dojo.forEach(po.po_items(),
-                    function(poItem) {
-                        var item = new fieldmapper.acqii();
-                        item.id(virtualId--);
-                        item.isnew(true);
-                        item.fund(poItem.fund());
-                        item.title(poItem.title());
-                        item.author(poItem.author());
-                        item.note(poItem.note());
-                        item.inv_item_type(poItem.inv_item_type());
-                        item.purchase_order(po);
-                        item.po_item(poItem);
-                        addInvoiceItem(item);
-                    }
-                );
-            }
-        }
-    );
-}
-
-function updateTotalCost() {
-
-    var totalCost = 0;    
-    for(var id in widgetRegistry.acqii) 
-        if(!widgetRegistry.acqii[id]._object.isdeleted())
-            totalCost += Number(widgetRegistry.acqii[id].cost_billed.getFormattedValue());
-    for(var id in widgetRegistry.acqie) 
-        if(!widgetRegistry.acqie[id]._object.isdeleted())
-            totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
-    totalInvoicedBox.attr('value', totalCost);
-
-    totalPaid = 0;    
-    for(var id in widgetRegistry.acqii) 
-        if(!widgetRegistry.acqii[id]._object.isdeleted())
-            totalPaid += Number(widgetRegistry.acqii[id].amount_paid.getFormattedValue());
-    for(var id in widgetRegistry.acqie) 
-        if(!widgetRegistry.acqie[id]._object.isdeleted())
-            totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
-    totalPaidBox.attr('value', totalPaid);
-
-    var buttonsDisabled = false;
-
-    if(totalPaid > totalCost || totalPaid < 0) {
-        openils.Util.addCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
-        invoiceSaveButton.attr('disabled', true);
-        invoiceProrateButton.attr('disabled', true);
-        buttonsDisabled = true;
-    } else {
-        openils.Util.removeCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
-        invoiceSaveButton.attr('disabled', false);
-        invoiceProrateButton.attr('disabled', false);
-    }
-
-    if(totalCost < 0) {
-        openils.Util.addCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
-        invoiceSaveButton.attr('disabled', true);
-        invoiceProrateButton.attr('disabled', true);
-    } else {
-        openils.Util.removeCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
-        if(!buttonsDisabled) {
-            invoiceSaveButton.attr('disabled', false);
-            invoiceProrateButton.attr('disabled', false);
-        }
-    }
-
-    if(totalPaid == totalCost) { // XXX: too rigid?
-        invoiceCloseButton.attr('disabled', false);
-    } else {
-        invoiceCloseButton.attr('disabled', true);
-    }
-
-    balanceOwedBox.attr('value', (totalCost - totalPaid));
-}
-
-
-function registerWidget(obj, field, widget, callback) {
-    var blob = widgetRegistry[obj.classname];
-    if(!blob[obj.id()]) 
-        blob[obj.id()] = {_object : obj};
-    blob[obj.id()][field] = widget;
-    widget.build(
-        function(w, ww) {
-            dojo.connect(w, 'onChange', 
-                function(newVal) { 
-                    obj.ischanged(true); 
-                    updateTotalCost();
-                }
-            );
-            if(callback) callback(w, ww);
-        }
-    );
-    return widget;
-}
-
-function addInvoiceItem(item) {
-    itemTbody = dojo.byId('acq-invoice-item-tbody');
-    if(itemTemplate == null) {
-        itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
-    }
-
-    var row = itemTemplate.cloneNode(true);
-    var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
-
-    dojo.forEach(
-        ['title', 'author', 'cost_billed', 'amount_paid'], 
-        function(field) {
-            
-            var args;
-            if(field == 'title' || field == 'author') {
-                //args = {style : 'width:10em'};
-            } else if(field == 'cost_billed' || field == 'amount_paid') {
-                args = {required : true, style : 'width: 8em'};
-            }
-            registerWidget(
-                item,
-                field,
-                new openils.widget.AutoFieldWidget({
-                    fmClass : 'acqii',
-                    fmObject : item,
-                    fmField : field,
-                    readOnly : invoice && openils.Util.isTrue(invoice.complete()),
-                    dijitArgs : args,
-                    parentNode : nodeByName(field, row)
-                })
-            )
-        }
-    );
-
-
-    /* ----------- fund -------------- */
-    var fundArgs = {
-        fmClass : 'acqii',
-        fmObject : item,
-        fmField : 'fund',
-        labelFormat : fundLabelFormat,
-        searchFormat : fundSearchFormat,
-        readOnly : invoice && openils.Util.isTrue(invoice.complete()),
-        dijitArgs : {required : true},
-        parentNode : nodeByName('fund', row)
-    }
-
-    if(item.fund_debit()) {
-        fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
-    } else {
-        fundArgs.searchFilter = {active : 't'}
-        if(itemType && openils.Util.isTrue(itemType.prorate()))
-            fundArgs.dijitArgs = {disabled : true};
-    }
-
-    var fundWidget = new openils.widget.AutoFieldWidget(fundArgs);
-    registerWidget(item, 'fund', fundWidget);
-
-    /* ---------- inv_item_type ------------- */
-
-    if(item.po_item()) {
-
-        // read-only item view for items that were the result of a po-item
-        var po = item.purchase_order();
-        var po_item = item.po_item();
-        var node = nodeByName('inv_item_type', row);
-        var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
-        orderDate = (!po.order_date()) ? '' : 
-                dojo.date.locale.format(dojo.date.stamp.fromISOString(po.order_date()), {selector:'date'});
-
-        node.innerHTML = dojo.string.substitute(
-            localeStrings.INVOICE_ITEM_PO_DETAILS, 
-            [ 
-                itemType.name(),
-                oilsBasePath, 
-                po.id(), 
-                po.name(), 
-                orderDate,
-                po_item.estimated_cost() 
-            ]
-        );
-
-    } else {
-
-        registerWidget(
-            item,
-            'inv_item_type',
-            new openils.widget.AutoFieldWidget({
-                fmObject : item,
-                fmField : 'inv_item_type',
-                parentNode : nodeByName('inv_item_type', row),
-                readOnly : invoice && openils.Util.isTrue(invoice.complete()),
-                dijitArgs : {required : true}
-            }),
-            function(w, ww) {
-                // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
-                // since this charge will be prorated against (potentially) multiple funds
-                dojo.connect(w, 'onChange', 
-                    function() {
-                        if(!item.fund_debit()) {
-                            var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
-                            if(!itemType) return;
-                            if(openils.Util.isTrue(itemType.prorate())) {
-                                fundWidget.widget.attr('disabled', true);
-                                fundWidget.widget.attr('value', '');
-                            } else {
-                                fundWidget.widget.attr('disabled', false);
-                            }
-                        }
-                    }
-                );
-            }
-        );
-    }
-
-    nodeByName('delete', row).onclick = function() {
-        var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
-        var msg = dojo.string.substitute(
-            localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
-                cost || 0,
-                widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue() || ''
-            ]
-        );
-        if(!confirm(msg)) return;
-        itemTbody.removeChild(row);
-        item.isdeleted(true);
-        if(item.isnew())
-            delete widgetRegistry.acqii[item.id()];
-        updateTotalCost();
-    }
-
-    itemTbody.appendChild(row);
-    updateTotalCost();
-}
-
-function updateReceiveLink(li) {
-    if (!invoiceId)
-        return; /* can't do this with unsaved invoices */
-
-    var link = dojo.byId("acq-view-invoice-receive-link");
-    if (link.onclick) return; /* only need to do this once */
-
-    /* don't do this if there's nothing receivable on the lineitem */
-    if (li.order_summary().recv_count() + li.order_summary().cancel_count() >=
-        li.order_summary().item_count())
-        return;
-
-    openils.Util.show("acq-view-invoice-receive");
-    link.onclick = function() { location.href =  oilsBasePath + '/acq/invoice/receive/' + invoiceId; };
-}
-
-function addInvoiceEntry(entry) {
-
-    openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
-    openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
-    openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
-
-    entryTbody = dojo.byId('acq-invoice-entry-tbody');
-    if(entryTemplate == null) {
-        entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
-    }
-
-    if(dojo.query('[lineitem=' + entry.lineitem() +']', entryTbody)[0])
-        // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
-        return;
-
-    var row = entryTemplate.cloneNode(true);
-    row.setAttribute('lineitem', entry.lineitem());
-
-    openils.acq.Lineitem.fetchAndRender(
-        entry.lineitem(), {}, 
-        function(li, html) { 
-            entry.lineitem(li);
-            entry.purchase_order(li.purchase_order());
-            nodeByName('title_details', row).innerHTML = html;
-
-            updateReceiveLink(li);
-
-            dojo.forEach(
-                ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
-                function(field) {
-                    var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
-                    if(!field.match(/count/)) dijitArgs.style = 'width:9em';
-                    if(entry.isnew() && field == 'phys_item_count') {
-                        // by default, attempt to pay for all non-canceled and as-of-yet-un-invoiced items
-                        var count = Number(li.order_summary().item_count() || 0) - 
-                                    Number(li.order_summary().cancel_count() || 0) -
-                                    Number(li.order_summary().invoice_count() || 0);
-                        if(count < 0) count = 0;
-                        dijitArgs.value = count;
-                    }
-                    registerWidget(
-                        entry, 
-                        field,
-                        new openils.widget.AutoFieldWidget({
-                            fmObject : entry,
-                            fmClass : 'acqie',
-                            fmField : field,
-                            dijitArgs : dijitArgs,
-                            readOnly : invoice && openils.Util.isTrue(invoice.complete()),
-                            parentNode : nodeByName(field, row)
-                        }),
-                        function(w) {    
-                            if(field == 'phys_item_count') {
-                                dojo.connect(w, 'onChange', 
-                                    function() {
-                                        // staff entered a higher number in the receive field than was originally ordered
-                                        // taking into account already invoiced items
-                                        var extra = Number(this.attr('value')) - 
-                                            (Number(entry.lineitem().item_count()) - Number(entry.lineitem().order_summary().invoice_count()));
-                                        if(extra > 0) {
-                                            storeExtraCopies(entry, extra);
-                                        }
-                                    }
-                                )
-                            }
-                        }
-                    );
-                }
-            );
-        }
-    );
-
-    nodeByName('detach', row).onclick = function() {
-        var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
-        var idents = [];
-        dojo.forEach(['isbn', 'upc', 'issn'], 
-            function(ident) { 
-                var val = liMarcAttr(entry.lineitem(), ident);
-                if(val) idents.push(val); 
-            }
-        );
-
-        var msg = dojo.string.substitute(
-            localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
-                cost || 0,
-                liMarcAttr(entry.lineitem(), 'title'),
-                liMarcAttr(entry.lineitem(), 'author'),
-                idents.join(',')
-            ]
-        );
-        if(!confirm(msg)) return;
-        entryTbody.removeChild(row);
-        entry.isdeleted(true);
-        if(entry.isnew())
-            delete widgetRegistry.acqie[entry.id()];
-        updateTotalCost();
-    }
-
-    entryTbody.appendChild(row);
-    updateTotalCost();
-}
-
-function liMarcAttr(lineitem, name) {
-    var attr = lineitem.attributes().filter(
-        function(attr) { 
-            if(
-                attr.attr_type() == 'lineitem_marc_attr_definition' && 
-                attr.attr_name() == name) 
-                    return attr 
-        } 
-    )[0];
-    return (attr) ? attr.attr_value() : '';
-}
-
-function saveChanges(doProrate, doClose, doReopen) {
-    createExtraCopies(
-        function() {
-            saveChangesPartTwo(doProrate, doClose, doReopen);
-        }
-    );
-}
-
-function saveChangesPartTwo(doProrate, doClose, doReopen) {
-    
-    progressDialog.show(true);
-
-    if(doReopen) {
-        invoice.complete('f');
-
-    } else {
-
-
-        var updateItems = [];
-        for(var id in widgetRegistry.acqii) {
-            var reg = widgetRegistry.acqii[id];
-            var item = reg._object;
-            if(item.ischanged() || item.isnew() || item.isdeleted()) {
-                updateItems.push(item);
-                if(item.isnew()) item.id(null);
-                for(var field in reg) {
-                    if(field != '_object')
-                        item[field]( reg[field].getFormattedValue() );
-                }
-                
-                // unflesh
-                if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
-                    item.purchase_order( item.purchase_order().id() );
-            }
-        }
-
-        var updateEntries = [];
-        for(var id in widgetRegistry.acqie) {
-            var reg = widgetRegistry.acqie[id];
-            var entry = reg._object;
-            if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
-                entry.lineitem(entry.lineitem().id());
-                entry.purchase_order(entry.purchase_order().id());
-                updateEntries.push(entry);
-                if(entry.isnew()) entry.id(null);
-
-                for(var field in reg) {
-                    if(field != '_object')
-                        entry[field]( reg[field].getFormattedValue() );
-                }
-                
-                // unflesh
-                dojo.forEach(['purchase_order', 'lineitem'],
-                    function(field) {
-                        if(entry[field]() != null && typeof entry[field]() == 'object')
-                            entry[field]( entry[field]().id() );
-                    }
-                );
-            }
-        }
-
-        if(!invoice) {
-            invoice = new fieldmapper.acqinv();
-            invoice.isnew(true);
-        } else {
-            invoice.ischanged(true); // for now, just always update
-        }
-
-        dojo.forEach(invoicePane.fieldList, 
-            function(field) {
-                invoice[field.name]( field.widget.getFormattedValue() );
-            }
-        );
-
-        if(doClose) 
-            invoice.complete('t');
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.invoice.update'],
-        {
-            params : [openils.User.authtoken, invoice, updateEntries, updateItems],
-            oncomplete : function(r) {
-                progressDialog.hide();
-                var invoice = openils.Util.readResponse(r);
-                if(invoice) {
-                    if(doProrate)
-                        return prorateInvoice(invoice);
-                    location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
-                }
-            }
-        }
-    );
-}
-
-function prorateInvoice(invoice) {
-    if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
-    progressDialog.show(true);
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
-        {
-            params : [openils.User.authtoken, invoice.id()],
-            oncomplete : function(r) {
-                progressDialog.hide();
-                var invoice = openils.Util.readResponse(r);
-                if(invoice) {
-                    location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
-                }
-            }
-        }
-    );
-}
-
-function storeExtraCopies(entry, numExtra) {
-
-    dojo.byId('acq-invoice-extra-copies-message').innerHTML = 
-        dojo.string.substitute(
-            localeStrings.INVOICE_EXTRA_COPIES, [numExtra]);
-
-    var addCopyHandler;
-    addCopyHandler = dojo.connect(
-        extraCopiesGo, 
-        'onClick',
-        function() {
-            extraCopies[entry.lineitem().id()] = {
-                numExtra : numExtra, 
-                fund : extraCopiesFund.widget.attr('value')
-            }
-            extraItemsDialog.hide();
-            dojo.disconnect(addCopyHandler);
-        }
-    );
-
-    dojo.connect(
-        extraCopiesCancel, 
-        'onClick',
-        function() { 
-            widgetRegistry.acqie[entry.id()].phys_item_count.widget.attr('value', '');
-            extraItemsDialog.hide() 
-        }
-    );
-
-    extraItemsDialog.show();
-}
-
-function createExtraCopies(oncomplete) {
-
-    var lids = [];
-    for(var liId in extraCopies) {
-        var data = extraCopies[liId];
-        for(var i = 0; i < data.numExtra; i++) {
-            var lid = new fieldmapper.acqlid();
-            lid.isnew(true);
-            lid.lineitem(liId);
-            lid.fund(data.fund);
-            lid.recv_time('now');
-            lids.push(lid);
-        }
-    }
-
-    if(lids.length == 0) 
-        return oncomplete();
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
-        {
-            params : [openils.User.authtoken, lids, true],
-            oncomplete : function(r) {
-                if(openils.Util.readResponse(r))
-                    oncomplete();
-            }
-        }
-    );
-
-}
-
-
-openils.Util.addOnLoad(init);
-
-
+require([
+       "dojo/date/locale",
+       "dojo/date/stamp",
+       "dijit/form/CheckBox",
+       "dijit/form/CurrencyTextBox",
+       "dijit/form/NumberTextBox",
+       "openils/User",
+       "openils/Util",
+       "openils/CGI",
+       "openils/PermaCrud",
+       "openils/widget/EditPane",
+       "openils/widget/AutoFieldWidget",
+       "openils/widget/ProgressDialog",
+       "openils/acq/Lineitem"
+       ],
+function(dojo_date_locale,
+       dojo_date_stamp,
+       dijit_form_CheckBox,
+       dijit_form_CurrencyTextBox,
+       dijit_form_NumberTextBox,
+       openils_User,
+       openils_Util,
+       openils_CGI,
+       openils_PermaCrud,
+       openils_widget_EditPane,
+       openils_widget_AutoFieldWidget,
+       openils_widget_ProgressDialog,
+       openils_acq_Lineitem){
+       
+       dojo.requireLocalization('openils.acq', 'acq');
+       var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       
+       var fundLabelFormat = ['${0} (${1})', 'code', 'year'];
+       var fundSearchFormat = ['${0} (${1})', 'code', 'year'];
+       
+       var cgi = new openils_CGI();
+       var pcrud = new openils_PermaCrud();
+       var attachLi;
+       var attachPo;
+       var invoice;
+       var itemTbody;
+       var itemTemplate;
+       var entryTemplate;
+       var totalInvoicedBox;
+       var totalPaidBox;
+       var balanceOwedBox;
+       var invoicePane;
+       var itemTypes;
+       var virtualId = -1;
+       var extraCopies = {};
+       var extraCopiesFund;
+       var widgetRegistry = {acqie : {}, acqii : {}};
+       
+       function nodeByName(name, context) {
+           return dojo.query('[name='+name+']', context)[0];
+       }
+       
+       function init() {
+       
+           attachLi = cgi.param('attach_li');
+           attachPo = cgi.param('attach_po');
+       
+           itemTypes = pcrud.retrieveAll('aiit');
+       
+           if(cgi.param('create')) {
+               renderInvoice();
+       
+           } else {
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.invoice.retrieve.authoritative'],
+                   {
+                       params : [openils_User.authtoken, invoiceId],
+                       oncomplete : function(r) {
+                           invoice = openils_Util.readResponse(r);     
+                           renderInvoice();
+                       }
+                   }
+               );
+           }
+       
+           extraCopiesFund = new openils_widget_AutoFieldWidget({
+               fmField : 'fund',
+               fmClass : 'acqlid',
+               searchFilter : {active : 't'},
+               labelFormat : fundLabelFormat,
+               searchFormat : fundSearchFormat,
+               dijitArgs : {required : true},
+               parentNode : dojo.byId('acq-invoice-extra-copies-fund')
+           });
+           extraCopiesFund.build();
+       }
+       
+       function renderInvoice() {
+       
+           // in create mode, let the LI or PO render the invoice with seed data
+           if( !(cgi.param('create') && (attachPo || attachLi)) ) {
+               invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), invoice);
+           }
+       
+           dojo.byId('acq-invoice-new-item').onclick = function() {
+               var item = new fieldmapper.acqii();
+               item.id(virtualId--);
+               item.isnew(true);
+               addInvoiceItem(item);
+           }
+       
+           updateTotalCost();
+       
+           if(invoice && openils_Util.isTrue(invoice.complete())) {
+       
+               dojo.forEach( // hide widgets that should not be visible for a completed invoice
+                   dojo.query('.hide-complete'), 
+                   function(node) { openils_Util.hide(node); }
+               );
+       
+               new openils_User().getPermOrgList(
+                   'ACQ_INVOICE_REOPEN', 
+                   function (orgs) {
+                       if(orgs.indexOf(invoice.receiver()) >= 0)
+                           openils_Util.show('acq-invoice-reopen-button-wrapper', 'inline');
+                   }, 
+                   true, 
+                   true
+               );
+           }
+       
+           if(invoice) {
+               dojo.forEach(
+                   invoice.items(),
+                   function(item) {
+                       addInvoiceItem(item);
+                   }
+               );
+       
+               dojo.forEach(
+                   invoice.entries(),
+                   function(entry) {
+                       addInvoiceEntry(entry);
+                   }
+               );
+           }
+       
+           if(attachLi) doAttachLi();
+           if(attachPo) doAttachPo();
+       }
+       
+       function doAttachLi() {
+       
+           //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()}; 
+           if(cgi.param('create')) {
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative'],
+                   {
+                       params : [openils_User.authtoken, attachLi, {clear_marc:1}],
+                       oncomplete : function(r) {
+                           var li = openils_Util.readResponse(r);
+                           invoicePane = drawInvoicePane(
+                               dojo.byId('acq-view-invoice-div'), null, 
+                               {provider : li.provider(), shipper : li.provider()}
+                           );
+                       }
+                   }
+               );
+           }
+       
+           var entry = new fieldmapper.acqie();
+           entry.id(virtualId--);
+           entry.isnew(true);
+           entry.lineitem(attachLi);
+           addInvoiceEntry(entry);
+       }
+       
+       function doAttachPo() {
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
+               {   async: true,
+                   params: [
+                       openils_User.authtoken, attachPo, 
+                       {flesh_lineitem_ids : true, flesh_po_items : true}
+                   ],
+                   oncomplete: function(r) {
+                       var po = openils_Util.readResponse(r);
+       
+                       if(cgi.param('create')) {
+                           // render the invoice using some seed data from the PO
+                           var invoiceArgs = {provider : po.provider(), shipper : po.provider()}; 
+                           invoicePane = drawInvoicePane(dojo.byId('acq-view-invoice-div'), null, invoiceArgs);
+                       }
+       
+                       dojo.forEach(po.lineitems(), 
+                           function(lineitem) {
+                               var entry = new fieldmapper.acqie();
+                               entry.id(virtualId--);
+                               entry.isnew(true);
+                               entry.lineitem(lineitem);
+                               entry.purchase_order(po);
+                               addInvoiceEntry(entry);
+                           }
+                       );
+       
+                       dojo.forEach(po.po_items(),
+                           function(poItem) {
+                               var item = new fieldmapper.acqii();
+                               item.id(virtualId--);
+                               item.isnew(true);
+                               item.fund(poItem.fund());
+                               item.title(poItem.title());
+                               item.author(poItem.author());
+                               item.note(poItem.note());
+                               item.inv_item_type(poItem.inv_item_type());
+                               item.purchase_order(po);
+                               item.po_item(poItem);
+                               addInvoiceItem(item);
+                           }
+                       );
+                   }
+               }
+           );
+       }
+       
+       function updateTotalCost() {
+       
+           var totalCost = 0;    
+           for(var id in widgetRegistry.acqii) 
+               if(!widgetRegistry.acqii[id]._object.isdeleted())
+                   totalCost += Number(widgetRegistry.acqii[id].cost_billed.getFormattedValue());
+           for(var id in widgetRegistry.acqie) 
+               if(!widgetRegistry.acqie[id]._object.isdeleted())
+                   totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
+           totalInvoicedBox.attr('value', totalCost);
+       
+           totalPaid = 0;    
+           for(var id in widgetRegistry.acqii) 
+               if(!widgetRegistry.acqii[id]._object.isdeleted())
+                   totalPaid += Number(widgetRegistry.acqii[id].amount_paid.getFormattedValue());
+           for(var id in widgetRegistry.acqie) 
+               if(!widgetRegistry.acqie[id]._object.isdeleted())
+                   totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
+           totalPaidBox.attr('value', totalPaid);
+       
+           var buttonsDisabled = false;
+       
+           if(totalPaid > totalCost || totalPaid < 0) {
+               openils_Util.addCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+               invoiceSaveButton.attr('disabled', true);
+               invoiceProrateButton.attr('disabled', true);
+               buttonsDisabled = true;
+           } else {
+               openils_Util.removeCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+               invoiceSaveButton.attr('disabled', false);
+               invoiceProrateButton.attr('disabled', false);
+           }
+       
+           if(totalCost < 0) {
+               openils_Util.addCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+               invoiceSaveButton.attr('disabled', true);
+               invoiceProrateButton.attr('disabled', true);
+           } else {
+               openils_Util.removeCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+               if(!buttonsDisabled) {
+                   invoiceSaveButton.attr('disabled', false);
+                   invoiceProrateButton.attr('disabled', false);
+               }
+           }
+       
+           if(totalPaid == totalCost) { // XXX: too rigid?
+               invoiceCloseButton.attr('disabled', false);
+           } else {
+               invoiceCloseButton.attr('disabled', true);
+           }
+       
+           balanceOwedBox.attr('value', (totalCost - totalPaid));
+       }
+       
+       
+       function registerWidget(obj, field, widget, callback) {
+           var blob = widgetRegistry[obj.classname];
+           if(!blob[obj.id()]) 
+               blob[obj.id()] = {_object : obj};
+           blob[obj.id()][field] = widget;
+           widget.build(
+               function(w, ww) {
+                   dojo.connect(w, 'onChange', 
+                       function(newVal) { 
+                           obj.ischanged(true); 
+                           updateTotalCost();
+                       }
+                   );
+                   if(callback) callback(w, ww);
+               }
+           );
+           return widget;
+       }
+       
+       function addInvoiceItem(item) {
+           itemTbody = dojo.byId('acq-invoice-item-tbody');
+           if(itemTemplate == null) {
+               itemTemplate = itemTbody.removeChild(dojo.byId('acq-invoice-item-template'));
+           }
+       
+           var row = itemTemplate.cloneNode(true);
+           var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
+       
+           dojo.forEach(
+               ['title', 'author', 'cost_billed', 'amount_paid'], 
+               function(field) {
+                   
+                   var args;
+                   if(field == 'title' || field == 'author') {
+                       //args = {style : 'width:10em'};
+                   } else if(field == 'cost_billed' || field == 'amount_paid') {
+                       args = {required : true, style : 'width: 8em'};
+                   }
+                   registerWidget(
+                       item,
+                       field,
+                       new openils_widget_AutoFieldWidget({
+                           fmClass : 'acqii',
+                           fmObject : item,
+                           fmField : field,
+                           readOnly : invoice && openils_Util.isTrue(invoice.complete()),
+                           dijitArgs : args,
+                           parentNode : nodeByName(field, row)
+                       })
+                   )
+               }
+           );
+       
+       
+           /* ----------- fund -------------- */
+           var fundArgs = {
+               fmClass : 'acqii',
+               fmObject : item,
+               fmField : 'fund',
+               labelFormat : fundLabelFormat,
+               searchFormat : fundSearchFormat,
+               readOnly : invoice && openils_Util.isTrue(invoice.complete()),
+               dijitArgs : {required : true},
+               parentNode : nodeByName('fund', row)
+           }
+       
+           if(item.fund_debit()) {
+               fundArgs.searchFilter = {'-or' : [{active : 't'}, {id : item.fund()}]};
+           } else {
+               fundArgs.searchFilter = {active : 't'}
+               if(itemType && openils_Util.isTrue(itemType.prorate()))
+                   fundArgs.dijitArgs = {disabled : true};
+           }
+       
+           var fundWidget = new openils_widget_AutoFieldWidget(fundArgs);
+           registerWidget(item, 'fund', fundWidget);
+       
+           /* ---------- inv_item_type ------------- */
+       
+           if(item.po_item()) {
+       
+               // read-only item view for items that were the result of a po-item
+               var po = item.purchase_order();
+               var po_item = item.po_item();
+               var node = nodeByName('inv_item_type', row);
+               var itemType = itemTypes.filter(function(t) { return (t.code() == item.inv_item_type()) })[0];
+               orderDate = (!po.order_date()) ? '' : 
+                       dojo_date_locale.format(dojo_date_stamp.fromISOString(po.order_date()), {selector:'date'});
+       
+               node.innerHTML = dojo.string.substitute(
+                   localeStrings.INVOICE_ITEM_PO_DETAILS, 
+                   [ 
+                       itemType.name(),
+                       oilsBasePath, 
+                       po.id(), 
+                       po.name(), 
+                       orderDate,
+                       po_item.estimated_cost() 
+                   ]
+               );
+       
+           } else {
+       
+               registerWidget(
+                   item,
+                   'inv_item_type',
+                   new openils_widget_AutoFieldWidget({
+                       fmObject : item,
+                       fmField : 'inv_item_type',
+                       parentNode : nodeByName('inv_item_type', row),
+                       readOnly : invoice && openils_Util.isTrue(invoice.complete()),
+                       dijitArgs : {required : true}
+                   }),
+                   function(w, ww) {
+                       // When the inv_item_type is set to prorate=true, don't allow the user the edit the fund
+                       // since this charge will be prorated against (potentially) multiple funds
+                       dojo.connect(w, 'onChange', 
+                           function() {
+                               if(!item.fund_debit()) {
+                                   var itemType = itemTypes.filter(function(t) { return (t.code() == w.attr('value')) })[0];
+                                   if(!itemType) return;
+                                   if(openils_Util.isTrue(itemType.prorate())) {
+                                       fundWidget.widget.attr('disabled', true);
+                                       fundWidget.widget.attr('value', '');
+                                   } else {
+                                       fundWidget.widget.attr('disabled', false);
+                                   }
+                               }
+                           }
+                       );
+                   }
+               );
+           }
+       
+           nodeByName('delete', row).onclick = function() {
+               var cost = widgetRegistry.acqii[item.id()].cost_billed.getFormattedValue();
+               var msg = dojo.string.substitute(
+                   localeStrings.INVOICE_CONFIRM_ITEM_DELETE, [
+                       cost || 0,
+                       widgetRegistry.acqii[item.id()].inv_item_type.getFormattedValue() || ''
+                   ]
+               );
+               if(!confirm(msg)) return;
+               itemTbody.removeChild(row);
+               item.isdeleted(true);
+               if(item.isnew())
+                   delete widgetRegistry.acqii[item.id()];
+               updateTotalCost();
+           }
+       
+           itemTbody.appendChild(row);
+           updateTotalCost();
+       }
+       
+       function updateReceiveLink(li) {
+           if (!invoiceId)
+               return; /* can't do this with unsaved invoices */
+       
+           var link = dojo.byId("acq-view-invoice-receive-link");
+           if (link.onclick) return; /* only need to do this once */
+       
+           /* don't do this if there's nothing receivable on the lineitem */
+           if (li.order_summary().recv_count() + li.order_summary().cancel_count() >=
+               li.order_summary().item_count())
+               return;
+       
+           openils_Util.show("acq-view-invoice-receive");
+           link.onclick = function() { location.href =  oilsBasePath + '/acq/invoice/receive/' + invoiceId; };
+       }
+       
+       function addInvoiceEntry(entry) {
+       
+           openils_Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
+           openils_Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
+           openils_Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
+       
+           entryTbody = dojo.byId('acq-invoice-entry-tbody');
+           if(entryTemplate == null) {
+               entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
+           }
+       
+           if(dojo.query('[lineitem=' + entry.lineitem() +']', entryTbody)[0])
+               // Is it ever valid to have multiple entries for 1 lineitem in a single invoice?
+               return;
+       
+           var row = entryTemplate.cloneNode(true);
+           row.setAttribute('lineitem', entry.lineitem());
+       
+           openils_acq_Lineitem.fetchAndRender(
+               entry.lineitem(), {}, 
+               function(li, html) { 
+                   entry.lineitem(li);
+                   entry.purchase_order(li.purchase_order());
+                   nodeByName('title_details', row).innerHTML = html;
+       
+                   updateReceiveLink(li);
+       
+                   dojo.forEach(
+                       ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
+                       function(field) {
+                           var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
+                           if(!field.match(/count/)) dijitArgs.style = 'width:9em';
+                           if(entry.isnew() && field == 'phys_item_count') {
+                               // by default, attempt to pay for all non-canceled and as-of-yet-un-invoiced items
+                               var count = Number(li.order_summary().item_count() || 0) - 
+                                           Number(li.order_summary().cancel_count() || 0) -
+                                           Number(li.order_summary().invoice_count() || 0);
+                               if(count < 0) count = 0;
+                               dijitArgs.value = count;
+                           }
+                           registerWidget(
+                               entry, 
+                               field,
+                               new openils_widget_AutoFieldWidget({
+                                   fmObject : entry,
+                                   fmClass : 'acqie',
+                                   fmField : field,
+                                   dijitArgs : dijitArgs,
+                                   readOnly : invoice && openils_Util.isTrue(invoice.complete()),
+                                   parentNode : nodeByName(field, row)
+                               }),
+                               function(w) {    
+                                   if(field == 'phys_item_count') {
+                                       dojo.connect(w, 'onChange', 
+                                           function() {
+                                               // staff entered a higher number in the receive field than was originally ordered
+                                               // taking into account already invoiced items
+                                               var extra = Number(this.attr('value')) - 
+                                                   (Number(entry.lineitem().item_count()) - Number(entry.lineitem().order_summary().invoice_count()));
+                                               if(extra > 0) {
+                                                   storeExtraCopies(entry, extra);
+                                               }
+                                           }
+                                       )
+                                   }
+                               }
+                           );
+                       }
+                   );
+               }
+           );
+       
+           nodeByName('detach', row).onclick = function() {
+               var cost = widgetRegistry.acqie[entry.id()].cost_billed.getFormattedValue();
+               var idents = [];
+               dojo.forEach(['isbn', 'upc', 'issn'], 
+                   function(ident) { 
+                       var val = liMarcAttr(entry.lineitem(), ident);
+                       if(val) idents.push(val); 
+                   }
+               );
+       
+               var msg = dojo.string.substitute(
+                   localeStrings.INVOICE_CONFIRM_ENTRY_DETACH, [
+                       cost || 0,
+                       liMarcAttr(entry.lineitem(), 'title'),
+                       liMarcAttr(entry.lineitem(), 'author'),
+                       idents.join(',')
+                   ]
+               );
+               if(!confirm(msg)) return;
+               entryTbody.removeChild(row);
+               entry.isdeleted(true);
+               if(entry.isnew())
+                   delete widgetRegistry.acqie[entry.id()];
+               updateTotalCost();
+           }
+       
+           entryTbody.appendChild(row);
+           updateTotalCost();
+       }
+       
+       function liMarcAttr(lineitem, name) {
+           var attr = lineitem.attributes().filter(
+               function(attr) { 
+                   if(
+                       attr.attr_type() == 'lineitem_marc_attr_definition' && 
+                       attr.attr_name() == name) 
+                           return attr 
+               } 
+           )[0];
+           return (attr) ? attr.attr_value() : '';
+       }
+       
+       function saveChanges(doProrate, doClose, doReopen) {
+           createExtraCopies(
+               function() {
+                   saveChangesPartTwo(doProrate, doClose, doReopen);
+               }
+           );
+       }
+       
+       function saveChangesPartTwo(doProrate, doClose, doReopen) {
+           
+           progressDialog.show(true);
+       
+           if(doReopen) {
+               invoice.complete('f');
+       
+           } else {
+       
+       
+               var updateItems = [];
+               for(var id in widgetRegistry.acqii) {
+                   var reg = widgetRegistry.acqii[id];
+                   var item = reg._object;
+                   if(item.ischanged() || item.isnew() || item.isdeleted()) {
+                       updateItems.push(item);
+                       if(item.isnew()) item.id(null);
+                       for(var field in reg) {
+                           if(field != '_object')
+                               item[field]( reg[field].getFormattedValue() );
+                       }
+                       
+                       // unflesh
+                       if(item.purchase_order() != null && typeof item.purchase_order() == 'object')
+                           item.purchase_order( item.purchase_order().id() );
+                   }
+               }
+       
+               var updateEntries = [];
+               for(var id in widgetRegistry.acqie) {
+                   var reg = widgetRegistry.acqie[id];
+                   var entry = reg._object;
+                   if(entry.ischanged() || entry.isnew() || entry.isdeleted()) {
+                       entry.lineitem(entry.lineitem().id());
+                       entry.purchase_order(entry.purchase_order().id());
+                       updateEntries.push(entry);
+                       if(entry.isnew()) entry.id(null);
+       
+                       for(var field in reg) {
+                           if(field != '_object')
+                               entry[field]( reg[field].getFormattedValue() );
+                       }
+                       
+                       // unflesh
+                       dojo.forEach(['purchase_order', 'lineitem'],
+                           function(field) {
+                               if(entry[field]() != null && typeof entry[field]() == 'object')
+                                   entry[field]( entry[field]().id() );
+                           }
+                       );
+                   }
+               }
+       
+               if(!invoice) {
+                   invoice = new fieldmapper.acqinv();
+                   invoice.isnew(true);
+               } else {
+                   invoice.ischanged(true); // for now, just always update
+               }
+       
+               dojo.forEach(invoicePane.fieldList, 
+                   function(field) {
+                       invoice[field.name]( field.widget.getFormattedValue() );
+                   }
+               );
+       
+               if(doClose) 
+                   invoice.complete('t');
+           }
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.invoice.update'],
+               {
+                   params : [openils_User.authtoken, invoice, updateEntries, updateItems],
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       var invoice = openils_Util.readResponse(r);
+                       if(invoice) {
+                           if(doProrate)
+                               return prorateInvoice(invoice);
+                           location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+                       }
+                   }
+               }
+           );
+       }
+       
+       function prorateInvoice(invoice) {
+           if(!confirm(localeStrings.INVOICE_CONFIRM_PRORATE)) return;
+           progressDialog.show(true);
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.invoice.apply_prorate'],
+               {
+                   params : [openils_User.authtoken, invoice.id()],
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       var invoice = openils_Util.readResponse(r);
+                       if(invoice) {
+                           location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+                       }
+                   }
+               }
+           );
+       }
+       
+       function storeExtraCopies(entry, numExtra) {
+       
+           dojo.byId('acq-invoice-extra-copies-message').innerHTML = 
+               dojo.string.substitute(
+                   localeStrings.INVOICE_EXTRA_COPIES, [numExtra]);
+       
+           var addCopyHandler;
+           addCopyHandler = dojo.connect(
+               extraCopiesGo, 
+               'onClick',
+               function() {
+                   extraCopies[entry.lineitem().id()] = {
+                       numExtra : numExtra, 
+                       fund : extraCopiesFund.widget.attr('value')
+                   }
+                   extraItemsDialog.hide();
+                   dojo.disconnect(addCopyHandler);
+               }
+           );
+       
+           dojo.connect(
+               extraCopiesCancel, 
+               'onClick',
+               function() { 
+                   widgetRegistry.acqie[entry.id()].phys_item_count.widget.attr('value', '');
+                   extraItemsDialog.hide() 
+               }
+           );
+       
+           extraItemsDialog.show();
+       }
+       
+       function createExtraCopies(oncomplete) {
+       
+           var lids = [];
+           for(var liId in extraCopies) {
+               var data = extraCopies[liId];
+               for(var i = 0; i < data.numExtra; i++) {
+                   var lid = new fieldmapper.acqlid();
+                   lid.isnew(true);
+                   lid.lineitem(liId);
+                   lid.fund(data.fund);
+                   lid.recv_time('now');
+                   lids.push(lid);
+               }
+           }
+       
+           if(lids.length == 0) 
+               return oncomplete();
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'],
+               {
+                   params : [openils_User.authtoken, lids, true],
+                   oncomplete : function(r) {
+                       if(openils_Util.readResponse(r))
+                           oncomplete();
+                   }
+               }
+           );
+       
+       }
+       
+       
+       openils_Util.addOnLoad(init);
+       
+       
+       
+
+});
\ No newline at end of file
index 5dc23ac..5f60b4a 100644 (file)
@@ -1,83 +1,94 @@
-dojo.require('openils.Util');
-dojo.require('openils.BibTemplate');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.CGI');
-dojo.require('dijit.form.Button');
-dojo.require('openils.widget.ProgressDialog');
-
-var limit = 15;
-var offset = 0;
-var template;
-var container;
-
-function drawSearch() {
-    container = dojo.byId('acq-findbib-container');
-    template = container.removeChild(dojo.byId('acq-findbib-template'));
-    var cgi = new openils.CGI();
-    searchQuery.attr('value', cgi.param('query') || '');
-    searchQuery.domNode.select();
-    openils.Util.registerEnterHandler(searchQuery.domNode, doSearch);
-}
-
-function doSearch() {
-    while(container.childNodes[0])
-        container.removeChild(container.childNodes[0])
-    progressDialog.show(true);
-    var query = searchQuery.attr('value');
-    fieldmapper.standardRequest(
-        ['open-ils.search', 'open-ils.search.biblio.multiclass.query.staff'],
-        {
-            async : true,
-            params : [{limit : limit}, query, 1],
-            oncomplete : drawResult
-        }
-    );
-}
-
-function drawResult(r) {
-    progressDialog.hide();
-    var result = openils.Util.readResponse(r);
-    dojo.forEach(
-        result.ids,
-        function(id) {
-            id = id[0];
-            var div = template.cloneNode(true);
-            container.appendChild(div);
-
-            var viewMarc = dojo.query('[name=view-marc]', div)[0];
-            viewMarc.onclick = function() { showMARC(id); };
-            var selectRec = dojo.query('[name=select-rec]', div)[0];
-            selectRec.onclick = function() { selectRecord(id); };
-
-            new openils.BibTemplate({
-                record : id,
-                org_unit : fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname(),
-                root : div
-            }).render();
-        }
-    );
-}
-
-function showMARC(bibId) {
-    openils.Util.show(dojo.byId('marc-div'));
-    fieldmapper.standardRequest(
-        ['open-ils.search', 'open-ils.search.biblio.record.html'],
-        {   
-            async: true,
-            params: [bibId, true],
-            oncomplete: function(r) {
-                dojo.byId('marc-html-div').innerHTML = openils.Util.readResponse(r);
-            }
-        }
-    );
-}
-
-function selectRecord(bibId) {
-    if(window.recordFound) {
-        window.recordFound(bibId);
-    }
-}
-
-
-openils.Util.addOnLoad(drawSearch);
+require([
+       "openils/Util",
+       "openils/BibTemplate",
+       "fieldmapper/OrgUtils",
+       "openils/CGI",
+       "dijit/form/Button",
+       "openils/widget/ProgressDialog"
+       ],
+function(openils_Util,
+       openils_BibTemplate,
+       fieldmapper_OrgUtils,
+       openils_CGI,
+       dijit_form_Button,
+       openils_widget_ProgressDialog){
+       
+       var limit = 15;
+       var offset = 0;
+       var template;
+       var container;
+       
+       function drawSearch() {
+           container = dojo.byId('acq-findbib-container');
+           template = container.removeChild(dojo.byId('acq-findbib-template'));
+           var cgi = new openils_CGI();
+           searchQuery.attr('value', cgi.param('query') || '');
+           searchQuery.domNode.select();
+           openils_Util.registerEnterHandler(searchQuery.domNode, doSearch);
+       }
+       
+       function doSearch() {
+           while(container.childNodes[0])
+               container.removeChild(container.childNodes[0])
+           progressDialog.show(true);
+           var query = searchQuery.attr('value');
+           fieldmapper.standardRequest(
+               ['open-ils.search', 'open-ils.search.biblio.multiclass.query.staff'],
+               {
+                   async : true,
+                   params : [{limit : limit}, query, 1],
+                   oncomplete : drawResult
+               }
+           );
+       }
+       
+       function drawResult(r) {
+           progressDialog.hide();
+           var result = openils_Util.readResponse(r);
+           dojo.forEach(
+               result.ids,
+               function(id) {
+                   id = id[0];
+                   var div = template.cloneNode(true);
+                   container.appendChild(div);
+       
+                   var viewMarc = dojo.query('[name=view-marc]', div)[0];
+                   viewMarc.onclick = function() { showMARC(id); };
+                   var selectRec = dojo.query('[name=select-rec]', div)[0];
+                   selectRec.onclick = function() { selectRecord(id); };
+       
+                   new openils_BibTemplate({
+                       record : id,
+                       org_unit : fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname(),
+                       root : div
+                   }).render();
+               }
+           );
+       }
+       
+       function showMARC(bibId) {
+           openils_Util.show(dojo.byId('marc-div'));
+           fieldmapper.standardRequest(
+               ['open-ils.search', 'open-ils.search.biblio.record.html'],
+               {   
+                   async: true,
+                   params: [bibId, true],
+                   oncomplete: function(r) {
+                       dojo.byId('marc-html-div').innerHTML = openils_Util.readResponse(r);
+                   }
+               }
+           );
+       }
+       
+       function selectRecord(bibId) {
+           if(window.recordFound) {
+               window.recordFound(bibId);
+           }
+       }
+       
+       
+       openils_Util.addOnLoad(drawSearch);
+       
+       
 
+});
\ No newline at end of file
index 0f3b91b..50300c5 100644 (file)
-dojo.require("openils.acq.Lineitem");
-dojo.require("openils.Util");
-dojo.require("openils.XUL");
-dojo.require("openils.CGI");
-dojo.require("openils.PermaCrud");
-dojo.require('openils.BibTemplate');
-dojo.require('fieldmapper.OrgUtils');
-
-dojo.requireLocalization('openils.acq', 'acq');
-var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-
-var liTable;
-var identTarget;
-var bibRecord;
-var paramPL;
-var paramPO;
-
-function fetchLi() {
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.lineitem.retrieve.authoritative"], {
-            "async": true,
-            "params": [openils.User.authtoken, targetId, {
-                "flesh_attrs": true,
-                "flesh_li_details": true,
-                "flesh_fund_debit": true,
-                "flesh_cancel_reason": true
-            }],
-            "oncomplete": function(r) {
-                var li = openils.Util.readResponse(r);
-                fetchBib(li.eg_bib_id());
-            }
-        }
-    );
-}
-
-
-function fetchRelated() {
-    var method = 'open-ils.acq.lineitems_for_bib.by_lineitem_id';
-    if(identTarget == 'bib')
-        var method = 'open-ils.acq.lineitems_for_bib.by_bib_id';
-
-    var total = 0;
-    fieldmapper.standardRequest(
-        ["open-ils.acq", method], {
-            "async": true,
-            "params": [openils.User.authtoken, targetId, {
-                "flesh_attrs": true,
-                "flesh_notes": true,
-                "flesh_cancel_reason": true
-            }],
-            "onresponse": function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp) {
-                    total++;
-                    liTable.show("list");
-                    liTable.addLineitem(resp);
-                }
-            }
-        }
-    );
-}
-
-function fetchBib(bibId) {
-    bibId = bibId || targetId;
-    new openils.BibTemplate({ 
-        record : bibId, 
-        org_unit : fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname()
-    }).render();
-
-    new openils.PermaCrud().retrieve('bre', bibId, {
-        oncomplete : function(r) {
-            bibRecord = openils.Util.readResponse(r);
-            // render bib details
-            // perhaps we just pull these from the beating heart of bibtemplate
-        }
-    }) 
-}
-
-function createLi(oncomplete) {
-    return function() {
-        progressDialog.show();
-        liTable.reset();
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.biblio.create_by_id"], {
-                "params": [
-                    openils.User.authtoken, [bibRecord.id()], {
-                        "flesh_attrs": true,
-                        "flesh_cancel_reason": true,
-                        "flesh_notes": true
-                    }
-                ],
-                "async": false,
-                "onresponse": function(r) {
-                    var li = openils.Util.readResponse(r);
-                    if (typeof(li) == "object") {
-                        liTable.show("list");
-                        liTable.addLineitem(li);
-                        dojo.query(
-                            "input[name='selectbox']", liTable._findLiRow(li)
-                        )[0].checked = true;
-                    }
-                },
-                "oncomplete": function() {
-                    progressDialog.hide();
-                    oncomplete();
-                }
-            }
-        );
-    };
-}
-
-function prepareButtons() {
-    addToPlButton.onClick = createLi(
-        function() { /* oncomplete */
-            acqLitSavePlDialog.show();
-        }
-    );
-    addToPoButton.onClick = createLi(
-        function() { /* oncomplete */
-            addToPoDialog.show();
-        }
-    );
-    createPoButton.onClick = createLi(
-        function() { /* oncomplete */
-            liTable._loadPOSelect();
-            acqLitPoCreateDialog.show();
-        }
-    );
-}
-
-function load() {
-    var cgi = new openils.CGI();
-
-    identTarget = cgi.param('target');
-    paramPL = cgi.param('pl');
-//    paramPO = cgi.param('po');
-
-    if (identTarget == 'bib') {
-        fetchBib();
-    } else {
-        fetchLi(); 
-    }
-
-    liTable = new AcqLiTable();
-    liTable.reset();
-    liTable._isRelatedViewer = true;
-
-    prepareButtons();
-    fetchRelated();
-    dojo.connect(addToPoSave, 'onClick', addToPo)
-    openils.Util.registerEnterHandler(addToPoInput.domNode, addToPo);
-}
-
-var _addToPoHappened = false;
-function addToPo(args) {
-    var poId = addToPoInput.attr('value');
-    if (!poId) return false;
-    if (_addToPoHappened) return false;
-
-    var liId =  liTable.getSelected()[0].id();
-    console.log("adding li " + liId + " to PO " + poId);
-
-    // hmm, addToPo is invoked twice for some reason...
-    _addToPoHappened = true;
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.purchase_order.add_lineitem'],
-        {   async : true,
-            params : [openils.User.authtoken, poId, liId],
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp.success) {
-                    location.href = oilsBasePath + '/acq/po/view/' + poId;
-                } else {
-                    _addToPoHappened = false;
-                    if (resp.error == 'bad-po-state') {
-                        alert(localeStrings.ADD_LI_TO_PO_BAD_PO_STATE);
-                    } else if (resp.error == 'bad-li-state') {
-                        alert(localeStrings.ADD_LI_TO_PO_BAD_LI_STATE);
-                    }
-                }
-            }
-        }
-    );
-
-    addToPoDialog.hide();
-    return false; // prevent form submission
-}
-
-openils.Util.addOnLoad(load);
+require([
+       "openils/acq/Lineitem",
+       "openils/Util",
+       "openils/XUL",
+       "openils/CGI",
+       "openils/PermaCrud",
+       "openils/BibTemplate",
+       "fieldmapper/OrgUtils"
+       ],
+function(openils_acq_Lineitem,
+       openils_Util,
+       openils_XUL,
+       openils_CGI,
+       openils_PermaCrud,
+       openils_BibTemplate,
+       fieldmapper_OrgUtils){
+       
+       dojo.requireLocalization('openils.acq', 'acq');
+       var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       
+       var liTable;
+       var identTarget;
+       var bibRecord;
+       var paramPL;
+       var paramPO;
+       
+       function fetchLi() {
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.lineitem.retrieve.authoritative"], {
+                   "async": true,
+                   "params": [openils.User.authtoken, targetId, {
+                       "flesh_attrs": true,
+                       "flesh_li_details": true,
+                       "flesh_fund_debit": true,
+                       "flesh_cancel_reason": true
+                   }],
+                   "oncomplete": function(r) {
+                       var li = openils_Util.readResponse(r);
+                       fetchBib(li.eg_bib_id());
+                   }
+               }
+           );
+       }
+       
+       
+       function fetchRelated() {
+           var method = 'open-ils.acq.lineitems_for_bib.by_lineitem_id';
+           if(identTarget == 'bib')
+               var method = 'open-ils.acq.lineitems_for_bib.by_bib_id';
+       
+           var total = 0;
+           fieldmapper.standardRequest(
+               ["open-ils.acq", method], {
+                   "async": true,
+                   "params": [openils.User.authtoken, targetId, {
+                       "flesh_attrs": true,
+                       "flesh_notes": true,
+                       "flesh_cancel_reason": true
+                   }],
+                   "onresponse": function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       if (resp) {
+                           total++;
+                           liTable.show("list");
+                           liTable.addLineitem(resp);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function fetchBib(bibId) {
+           bibId = bibId || targetId;
+           new openils_BibTemplate({ 
+               record : bibId, 
+               org_unit : fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname()
+           }).render();
+       
+           new openils_PermaCrud().retrieve('bre', bibId, {
+               oncomplete : function(r) {
+                   bibRecord = openils_Util.readResponse(r);
+                   // render bib details
+                   // perhaps we just pull these from the beating heart of bibtemplate
+               }
+           }) 
+       }
+       
+       function createLi(oncomplete) {
+           return function() {
+               progressDialog.show();
+               liTable.reset();
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.biblio.create_by_id"], {
+                       "params": [
+                           openils.User.authtoken, [bibRecord.id()], {
+                               "flesh_attrs": true,
+                               "flesh_cancel_reason": true,
+                               "flesh_notes": true
+                           }
+                       ],
+                       "async": false,
+                       "onresponse": function(r) {
+                           var li = openils_Util.readResponse(r);
+                           if (typeof(li) == "object") {
+                               liTable.show("list");
+                               liTable.addLineitem(li);
+                               dojo.query(
+                                   "input[name='selectbox']", liTable._findLiRow(li)
+                               )[0].checked = true;
+                           }
+                       },
+                       "oncomplete": function() {
+                           progressDialog.hide();
+                           oncomplete();
+                       }
+                   }
+               );
+           };
+       }
+       
+       function prepareButtons() {
+           addToPlButton.onClick = createLi(
+               function() { /* oncomplete */
+                   acqLitSavePlDialog.show();
+               }
+           );
+           addToPoButton.onClick = createLi(
+               function() { /* oncomplete */
+                   addToPoDialog.show();
+               }
+           );
+           createPoButton.onClick = createLi(
+               function() { /* oncomplete */
+                   liTable._loadPOSelect();
+                   acqLitPoCreateDialog.show();
+               }
+           );
+       }
+       
+       function load() {
+           var cgi = new openils_CGI();
+       
+           identTarget = cgi.param('target');
+           paramPL = cgi.param('pl');
+       //    paramPO = cgi.param('po');
+       
+           if (identTarget == 'bib') {
+               fetchBib();
+           } else {
+               fetchLi(); 
+           }
+       
+           liTable = new AcqLiTable();
+           liTable.reset();
+           liTable._isRelatedViewer = true;
+       
+           prepareButtons();
+           fetchRelated();
+           dojo.connect(addToPoSave, 'onClick', addToPo)
+           openils_Util.registerEnterHandler(addToPoInput.domNode, addToPo);
+       }
+       
+       var _addToPoHappened = false;
+       function addToPo(args) {
+           var poId = addToPoInput.attr('value');
+           if (!poId) return false;
+           if (_addToPoHappened) return false;
+       
+           var liId =  liTable.getSelected()[0].id();
+           console.log("adding li " + liId + " to PO " + poId);
+       
+           // hmm, addToPo is invoked twice for some reason...
+           _addToPoHappened = true;
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.purchase_order.add_lineitem'],
+               {   async : true,
+                   params : [openils.User.authtoken, poId, liId],
+                   oncomplete : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       if (resp.success) {
+                           location.href = oilsBasePath + '/acq/po/view/' + poId;
+                       } else {
+                           _addToPoHappened = false;
+                           if (resp.error == 'bad-po-state') {
+                               alert(localeStrings.ADD_LI_TO_PO_BAD_PO_STATE);
+                           } else if (resp.error == 'bad-li-state') {
+                               alert(localeStrings.ADD_LI_TO_PO_BAD_LI_STATE);
+                           }
+                       }
+                   }
+               }
+           );
+       
+           addToPoDialog.hide();
+           return false; // prevent form submission
+       }
+       
+       openils_Util.addOnLoad(load);
+       
+
+});
\ No newline at end of file
index 5c7405e..13e8509 100644 (file)
-dojo.require("dijit.form.Form");
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.RadioButton");
-dojo.require("dijit.form.TextBox");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("dojo.data.ItemFileReadStore");
-dojo.require("openils.User");
-dojo.require("openils.Util");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.XUL");
-dojo.require("openils.widget.AutoFieldWidget");
+require([
+       "dijit/form/Form",
+       "dijit/form/Button",
+       "dijit/form/RadioButton",
+       "dijit/form/TextBox",
+       "dijit/form/FilteringSelect",
+       "dojo/data/ItemFileReadStore",
+       "openils/User",
+       "openils/Util",
+       "openils/PermaCrud",
+       "openils/XUL",
+       "openils/widget/AutoFieldWidget"
+       ],
+function(dijit_form_Form,
+       dijit_form_Button,
+       dijit_form_RadioButton,
+       dijit_form_TextBox,
+       dijit_form_FilteringSelect,
+       dojo_data_ItemFileReadStore,
+       openils_User,
+       openils_Util,
+       openils_PermaCrud,
+       openils_XUL,
+       openils_widget_AutoFieldWidget){
+       
+       var combinedAttrValueArray = [];
+       var scalarAttrSearchManager;
+       var liTable;
+       
+       function prepareStateStore(pcrud) {
+           stateSelector.store = new dojo_data_ItemFileReadStore({
+               "data": {
+                   "label": "description",
+                   "identifier": "code",
+                   "items": [
+                       /* XXX i18n; Also, this list shouldn't be hardcoded here. */
+                       {"code": "new", "description": "New"},
+                       {"code": "on-order", "description": "On Order"},
+                       {"code": "pending-order", "description": "Pending Order"}
+                   ]
+               }
+           });
+       }
+       
+       function prepareScalarSearchStore(pcrud) {
+       }
+       
+       function prepareArraySearchStore(pcrud) {
+           attrArrayDefSelector.store = new dojo_data_ItemFileReadStore({
+               "data": acqliad.toStoreData(
+                   pcrud.search("acqliad", {"code": li_exportable_attrs})
+               )
+           });
+       }
+       
+       function prepareAgencySelector() {
+           new openils_widget_AutoFieldWidget({
+               "fmClass": "acqpo",
+               "fmField": "ordering_agency",
+               "parentNode": dojo.byId("agency_selector"),
+               "orgLimitPerms": ["VIEW_PURCHASE_ORDER"],
+               "dijitArgs": {"name": "agency", "required": false}
+           }).build();
+       }
+       
+       function toggleAttrSearchType(which, checked) {
+           /* This would be cooler with a slick dispatch table instead of branchy
+            * logic, but whatever... */
+           if (checked) {
+               if (which == "scalar") {
+                   if (scalarAttrSearchManager.index < 1)
+                       scalarAttrSearchManager.add();
+                   openils_Util.show("oils-acq-li-search-attr-scalar", "inline-block");
+                   openils_Util.hide("oils-acq-li-search-attr-array");
+               } else if (which == "array") {
+                   openils_Util.hide("oils-acq-li-search-attr-scalar");
+                   openils_Util.show("oils-acq-li-search-attr-array", "inline");
+               } else {
+                   openils_Util.hide("oils-acq-li-search-attr-scalar");
+                   openils_Util.hide("oils-acq-li-search-attr-array");
+               }
+           }
+       }
+       
+       var buildAttrSearchClause = {
+           "array": function(v) {
+               if (!v.array_def) {
+                   throw new Error(localeStrings.SELECT_AN_LI_ATTRIBUTE);
+               }
+               return {
+                   "attr_value_pairs":
+                       [[Number(v.array_def), combinedAttrValueArray]] /* [[sic]] */
+               };
+           },
+           "scalar": function(v) {
+               var r = scalarAttrSearchManager.buildSearchClause();
+               if (r.attr_value_pairs.length < 1) {
+                   throw new Error(localeStrings.SELECT_AN_LI_ATTRIBUTE);
+               } else {
+                   return r;
+               }
+           },
+           "none": function(v) {
+               return {};
+           }
+       };
+       
+       function naivelyParse(data) {
+           return data.split(/[\n, ]/).filter(function(o) {return o.length > 0; });
+       }
+       
+       function clearTerms() {
+           combinedAttrValueArray = [];
+           dojo.byId("records-up").innerHTML = 0;
+       }
+       
+       function loadTermsFromFile() {
+           var rawdata;
+       
+           try {
+               /* FIXME 128k is completely arbitrary; needs researched for
+                * a sane limit and should also be made configurable. */
+               rawdata = openils_XUL.contentFromFileOpenDialog(
+                   localeStrings.LI_ATTR_SEARCH_CHOOSE_FILE, 1024 * 128
+               );
+           } catch (E) {
+               alert(E);
+           }
+       
+           if (rawdata) {
+               try {
+                   combinedAttrValueArray =
+                       combinedAttrValueArray.concat(naivelyParse(rawdata));
+                   dojo.byId("records-up").innerHTML = combinedAttrValueArray.length;
+               } catch (E) {
+                   alert(E);
+               }
+           }
+       }
+       
+       function buildSearchClause(values) {
+           var o = {};
+           if (values.state) o.li_states = [values.state];
+           if (values.agency) o.po_agencies = [Number(values.agency)];
+           return o;
+       }
+       
+       function doSearch(values) {
+           var results_this_time = 0;
+           liTable.reset();
+           try {
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.lineitem.search.by_attributes"], {
+                       "params": [
+                           openils_User.authtoken,
+                           dojo.mixin(
+                               buildAttrSearchClause[values.attr_search_type](values),
+                               buildSearchClause(values)
+                           ),
+                           {
+                               "clear_marc": true, "flesh_attrs": true,
+                               "flesh_notes": true
+                           }
+                       ],
+                       "async": true,
+                       "onresponse": function(r) {
+                           var li = openils_Util.readResponse(r);
+                           if (li) {
+                               results_this_time++;
+                               liTable.addLineitem(li);
+                               liTable.show("list");
+                           }
+                       },
+                       "oncomplete": function() {
+                           if (results_this_time < 1) {
+                               alert(localeStrings.NO_RESULTS);
+                           }
+                       }
+                   }
+               );
+           } catch (E) {
+               alert(E); // XXX
+           }
+       }
+       
+       function myScalarAttrSearchManager(template_id, pcrud) {
+           this.template = dojo.byId(template_id);
+           this.store = new dojo_data_ItemFileReadStore({
+               "data": acqliad.toStoreData(
+                   pcrud.search("acqliad", {"id": {"!=": null}})
+               )
+           });
+           this.rows = {};
+           this.index = 0;
+       };
+       myScalarAttrSearchManager.prototype.remove = function(n) {
+           dojo.destroy("scalar_attr_holder_" + n);
+           delete this.rows[n];
+       };
+       myScalarAttrSearchManager.prototype.add = function() {
+           var self = this;
+           var n = this.index;
+           var clone = dojo.clone(this.template);
+           var def = dojo.query('input[name="def"]', clone)[0];
+           var value = dojo.query('input[name="value"]', clone)[0];
+           var a = dojo.query('a', clone)[0];
+       
+           clone.id = "scalar_attr_holder_" + n;
+           a.onclick = function() { self.remove(n); };
+       
+           this.rows[n] = [
+               new dijit_form_FilteringSelect({
+                   "id": "scalar_def_" + n,
+                   "name": "scalar_def_" + n,
+                   "store": this.store,
+                   "labelAttr": "description",
+                   "searchAttr": "description"
+               }, def),
+               new dijit_form_TextBox({
+                   "id": "scalar_value_" + n,
+                   "name": "scalar_value_" + n
+               }, value)
+           ];
+       
+           this.index++;
+       
+           dojo.place(clone, "oils-acq-li-search-scalar-adder", "before");
+           openils_Util.show(clone);
+       };
+       myScalarAttrSearchManager.prototype.buildSearchClause = function() {
+           var list = [];
+           for (var k in this.rows) {
+               var def = this.rows[k][0].attr("value");
+               var val = this.rows[k][1].attr("value");
+               if (def != "" && val != "")
+                   list.push([Number(def), val]);
+           }
+           return {"attr_value_pairs": list};
+       };
+       myScalarAttrSearchManager.prototype.simplifiedPairs = function() {
+           var result = {};
+           for (var k in this.rows) {
+               result[this.rows[k][0].attr("value")] = this.rows[k][1].attr("value");
+           }
+           return result;
+       };
+       myScalarAttrSearchManager.prototype.newBrief = function() {
+           location.href = oilsBasePath + "/acq/picklist/brief_record?prepop=" +
+               encodeURIComponent(js2JSON(this.simplifiedPairs()));
+       };
+       
+       
+       function load() {
+           var pcrud = new openils_PermaCrud();
+       
+           prepareStateStore(pcrud);
+           prepareArraySearchStore(pcrud);
+       
+           prepareAgencySelector();
+       
+           liTable = new AcqLiTable();
+           scalarAttrSearchManager = new myScalarAttrSearchManager(
+               "oils-acq-li-search-scalar-template", pcrud
+           );
+       
+           openils_Util.show("oils-acq-li-search-form-holder");
+       }
+       
+       openils_Util.addOnLoad(load);
+       
 
-var combinedAttrValueArray = [];
-var scalarAttrSearchManager;
-var liTable;
-
-function prepareStateStore(pcrud) {
-    stateSelector.store = new dojo.data.ItemFileReadStore({
-        "data": {
-            "label": "description",
-            "identifier": "code",
-            "items": [
-                /* XXX i18n; Also, this list shouldn't be hardcoded here. */
-                {"code": "new", "description": "New"},
-                {"code": "on-order", "description": "On Order"},
-                {"code": "pending-order", "description": "Pending Order"}
-            ]
-        }
-    });
-}
-
-function prepareScalarSearchStore(pcrud) {
-}
-
-function prepareArraySearchStore(pcrud) {
-    attrArrayDefSelector.store = new dojo.data.ItemFileReadStore({
-        "data": acqliad.toStoreData(
-            pcrud.search("acqliad", {"code": li_exportable_attrs})
-        )
-    });
-}
-
-function prepareAgencySelector() {
-    new openils.widget.AutoFieldWidget({
-        "fmClass": "acqpo",
-        "fmField": "ordering_agency",
-        "parentNode": dojo.byId("agency_selector"),
-        "orgLimitPerms": ["VIEW_PURCHASE_ORDER"],
-        "dijitArgs": {"name": "agency", "required": false}
-    }).build();
-}
-
-function toggleAttrSearchType(which, checked) {
-    /* This would be cooler with a slick dispatch table instead of branchy
-     * logic, but whatever... */
-    if (checked) {
-        if (which == "scalar") {
-            if (scalarAttrSearchManager.index < 1)
-                scalarAttrSearchManager.add();
-            openils.Util.show("oils-acq-li-search-attr-scalar", "inline-block");
-            openils.Util.hide("oils-acq-li-search-attr-array");
-        } else if (which == "array") {
-            openils.Util.hide("oils-acq-li-search-attr-scalar");
-            openils.Util.show("oils-acq-li-search-attr-array", "inline");
-        } else {
-            openils.Util.hide("oils-acq-li-search-attr-scalar");
-            openils.Util.hide("oils-acq-li-search-attr-array");
-        }
-    }
-}
-
-var buildAttrSearchClause = {
-    "array": function(v) {
-        if (!v.array_def) {
-            throw new Error(localeStrings.SELECT_AN_LI_ATTRIBUTE);
-        }
-        return {
-            "attr_value_pairs":
-                [[Number(v.array_def), combinedAttrValueArray]] /* [[sic]] */
-        };
-    },
-    "scalar": function(v) {
-        var r = scalarAttrSearchManager.buildSearchClause();
-        if (r.attr_value_pairs.length < 1) {
-            throw new Error(localeStrings.SELECT_AN_LI_ATTRIBUTE);
-        } else {
-            return r;
-        }
-    },
-    "none": function(v) {
-        return {};
-    }
-};
-
-function naivelyParse(data) {
-    return data.split(/[\n, ]/).filter(function(o) {return o.length > 0; });
-}
-
-function clearTerms() {
-    combinedAttrValueArray = [];
-    dojo.byId("records-up").innerHTML = 0;
-}
-
-function loadTermsFromFile() {
-    var rawdata;
-
-    try {
-        /* FIXME 128k is completely arbitrary; needs researched for
-         * a sane limit and should also be made configurable. */
-        rawdata = openils.XUL.contentFromFileOpenDialog(
-            localeStrings.LI_ATTR_SEARCH_CHOOSE_FILE, 1024 * 128
-        );
-    } catch (E) {
-        alert(E);
-    }
-
-    if (rawdata) {
-        try {
-            combinedAttrValueArray =
-                combinedAttrValueArray.concat(naivelyParse(rawdata));
-            dojo.byId("records-up").innerHTML = combinedAttrValueArray.length;
-        } catch (E) {
-            alert(E);
-        }
-    }
-}
-
-function buildSearchClause(values) {
-    var o = {};
-    if (values.state) o.li_states = [values.state];
-    if (values.agency) o.po_agencies = [Number(values.agency)];
-    return o;
-}
-
-function doSearch(values) {
-    var results_this_time = 0;
-    liTable.reset();
-    try {
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.lineitem.search.by_attributes"], {
-                "params": [
-                    openils.User.authtoken,
-                    dojo.mixin(
-                        buildAttrSearchClause[values.attr_search_type](values),
-                        buildSearchClause(values)
-                    ),
-                    {
-                        "clear_marc": true, "flesh_attrs": true,
-                        "flesh_notes": true
-                    }
-                ],
-                "async": true,
-                "onresponse": function(r) {
-                    var li = openils.Util.readResponse(r);
-                    if (li) {
-                        results_this_time++;
-                        liTable.addLineitem(li);
-                        liTable.show("list");
-                    }
-                },
-                "oncomplete": function() {
-                    if (results_this_time < 1) {
-                        alert(localeStrings.NO_RESULTS);
-                    }
-                }
-            }
-        );
-    } catch (E) {
-        alert(E); // XXX
-    }
-}
-
-function myScalarAttrSearchManager(template_id, pcrud) {
-    this.template = dojo.byId(template_id);
-    this.store = new dojo.data.ItemFileReadStore({
-        "data": acqliad.toStoreData(
-            pcrud.search("acqliad", {"id": {"!=": null}})
-        )
-    });
-    this.rows = {};
-    this.index = 0;
-};
-myScalarAttrSearchManager.prototype.remove = function(n) {
-    dojo.destroy("scalar_attr_holder_" + n);
-    delete this.rows[n];
-};
-myScalarAttrSearchManager.prototype.add = function() {
-    var self = this;
-    var n = this.index;
-    var clone = dojo.clone(this.template);
-    var def = dojo.query('input[name="def"]', clone)[0];
-    var value = dojo.query('input[name="value"]', clone)[0];
-    var a = dojo.query('a', clone)[0];
-
-    clone.id = "scalar_attr_holder_" + n;
-    a.onclick = function() { self.remove(n); };
-
-    this.rows[n] = [
-        new dijit.form.FilteringSelect({
-            "id": "scalar_def_" + n,
-            "name": "scalar_def_" + n,
-            "store": this.store,
-            "labelAttr": "description",
-            "searchAttr": "description"
-        }, def),
-        new dijit.form.TextBox({
-            "id": "scalar_value_" + n,
-            "name": "scalar_value_" + n
-        }, value)
-    ];
-
-    this.index++;
-
-    dojo.place(clone, "oils-acq-li-search-scalar-adder", "before");
-    openils.Util.show(clone);
-};
-myScalarAttrSearchManager.prototype.buildSearchClause = function() {
-    var list = [];
-    for (var k in this.rows) {
-        var def = this.rows[k][0].attr("value");
-        var val = this.rows[k][1].attr("value");
-        if (def != "" && val != "")
-            list.push([Number(def), val]);
-    }
-    return {"attr_value_pairs": list};
-};
-myScalarAttrSearchManager.prototype.simplifiedPairs = function() {
-    var result = {};
-    for (var k in this.rows) {
-        result[this.rows[k][0].attr("value")] = this.rows[k][1].attr("value");
-    }
-    return result;
-};
-myScalarAttrSearchManager.prototype.newBrief = function() {
-    location.href = oilsBasePath + "/acq/picklist/brief_record?prepop=" +
-        encodeURIComponent(js2JSON(this.simplifiedPairs()));
-};
-
-
-function load() {
-    var pcrud = new openils.PermaCrud();
-
-    prepareStateStore(pcrud);
-    prepareArraySearchStore(pcrud);
-
-    prepareAgencySelector();
-
-    liTable = new AcqLiTable();
-    scalarAttrSearchManager = new myScalarAttrSearchManager(
-        "oils-acq-li-search-scalar-template", pcrud
-    );
-
-    openils.Util.show("oils-acq-li-search-form-holder");
-}
-
-openils.Util.addOnLoad(load);
+});
\ No newline at end of file
index f80086e..74da78c 100644 (file)
@@ -1,27 +1,34 @@
-dojo.require('dijit.form.Button');
-dojo.require('openils.widget.ProgressDialog');
-dojo.requireLocalization("openils.acq", "acq");
-var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
+require([
+       "dijit/form/Button",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_form_Button,
+       openils_widget_ProgressDialog){
+       dojo.requireLocalization("openils.acq", "acq");
+       var localeStrings = dojo.i18n.getLocalization("openils.acq", "acq");
+       
+       function load() {
+           progressDialog.show(true);
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.lineitem.format"], {
+                   "params": [openils.User.authtoken, liId, "html"],
+                   "async": true,
+                   "oncomplete": function(r) {
+                       r = openils.Util.readResponse(r);
+                       progressDialog.hide();
+                       var d = dojo.byId("acq-worksheet-contents");
+                       if (r.template_output())
+                           d.innerHTML = r.template_output().data();
+                       else if (r.error_output())
+                           d.innerHTML = r.error_output().data();
+                       else
+                           d.innerHTML = localeStrings.LI_FORMAT_ERROR;
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(load);
+       
 
-function load() {
-    progressDialog.show(true);
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.lineitem.format"], {
-            "params": [openils.User.authtoken, liId, "html"],
-            "async": true,
-            "oncomplete": function(r) {
-                r = openils.Util.readResponse(r);
-                progressDialog.hide();
-                var d = dojo.byId("acq-worksheet-contents");
-                if (r.template_output())
-                    d.innerHTML = r.template_output().data();
-                else if (r.error_output())
-                    d.innerHTML = r.error_output().data();
-                else
-                    d.innerHTML = localeStrings.LI_FORMAT_ERROR;
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(load);
+});
\ No newline at end of file
index 4345728..126a7c5 100644 (file)
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.ProgressBar');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.form.CheckedMultiSelect');
-dojo.require('fieldmapper.Fieldmapper');
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.NumberSpinner');
-dojo.require('openils.Event');
-dojo.require('openils.acq.Picklist');
-dojo.require('openils.acq.Lineitem');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-
-var searchFields = [];
-var resultPicklist;
-var resultLIs;
-var selectedLIs;
-var recvCount = 0;
-var sourceCount = 0; // how many sources are we searching
-var user = new openils.User();
-var searchLimit = 10;
-var liCache = {};
-var liTable;
-
-function drawForm() {
-    liTable = new AcqLiTable();
-    liTable.skipInitialEligibilityCheck = true;
-
-    fieldmapper.standardRequest(
-        ['open-ils.search', 'open-ils.search.z3950.retrieve_services'], 
-        {   async: true,
-            params: [user.authtoken],
-            oncomplete: _drawForm
-        }
-    );
-}
-
-function _drawForm(r) {
-
-    var sources = openils.Util.readResponse(r);
-    if(!sources) return;
-
-    for(var name in sources) {
-        source = sources[name];
-        if(name == 'native-evergreen-catalog') continue;
-        bibSourceSelect.addOption({value:name, label:source.label});
-        for(var attr in source.attrs) 
-            if(!attr.match(/^#/)) // xml comment nodes
-                searchFields.push(source.attrs[attr]);
-    }
-
-    searchFields = searchFields.sort(
-        function(a,b) {
-            if(a.label < b.label) 
-                return -1;
-            if(a.label > b.label) 
-                return 1;
-            return 0;
-        }
-    );
-
-    var tbody = dojo.byId('oils-acq-search-fields-tbody');
-    var tmpl = tbody.removeChild(dojo.byId('oils-acq-search-fields-template'));
-
-    for(var f in searchFields) {
-        var field = searchFields[f];
-        if(dijit.byId('text_input_'+field.name)) continue;
-        var row = tmpl.cloneNode(true);
-        tbody.insertBefore(row, dojo.byId('oils-acq-seach-fields-count-row'));
-        var labelCell = dojo.query('[name=label]', row)[0];
-        var inputCell = dojo.query('[name=input]', row)[0];
-        labelCell.appendChild(document.createTextNode(field.label));
-        input = new dijit.form.TextBox({name:field.name, label:field.label, id:'text_input_'+field.name});
-        inputCell.appendChild(input.domNode);
-    }
-}
-
-function clearSearchForm() {
-    for(var f in searchFields) {
-        var field = searchFields[f];
-        dijit.byId('text_input_'+field.name).setValue('');
-    }
-}
-
-var resultRow;
-function doSearch(values) {
-    liTable.reset();
-    showDiv('oils-acq-pl-loading');
-
-    search = {
-        service : [],
-        username : [],
-        password : [],
-        search : {},
-        limit : values.limit,
-        offset : searchOffset
-    };
-    searchLimit = values.limit;
-    delete values.limit;
-
-    var selected = bibSourceSelect.getValue();
-    for(var i = 0; i < selected.length; i++) {
-        search.service.push(selected[i]);
-        search.username.push('');
-        search.password.push('');
-        sourceCount++;
-    }
-
-    for(var v in values) {
-        if(values[v]) {
-            var input = dijit.byId('text_input_'+v);
-            search.search[v] = values[v];
-        }
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.picklist.search.z3950'],
-        {   async: true,
-            params: [user.authtoken, search, null, {respond_li:1, flesh_attrs:1, clear_marc:1}],
-            onresponse: handleResult
-        }
-    );
-}
-
-
-function setRowAttr(td, liWrapper, field) {
-    var val = liWrapper.findAttr(field, 'lineitem_marc_attr_definition') || '';
-    td.appendChild(document.createTextNode(val));
-}
-
-function handleResult(r) {
-    var result = openils.Util.readResponse(r);
-    liTable.show('list');
-    dojo.style(dojo.byId('oils-acq-pl-search-results'), 'display', 'block');
-    var tbody = dojo.byId('plist-tbody');
-    if(result.lineitem)
-        liTable.addLineitem(result.lineitem);
-    if(result.complete) // hide the loading image
-        dojo.style('oils-acq-pl-loading','display', 'none');
-}
-
-function showDiv(div) {
-    var divs = [
-        'oils-acq-search-block', 
-        'oils-acq-pl-loading' ];
-    dojo.forEach(divs, function(d) {dojo.style(d,'display', 'none')});
-    liTable.hide();
-    dojo.style(div, 'display', 'block');
-}
-
-
-openils.Util.addOnLoad(drawForm);
-
-
+require([
+       "dijit/form/FilteringSelect",
+       "dijit/ProgressBar",
+       "dijit/Dialog",
+       "dojox/form/CheckedMultiSelect",
+       "fieldmapper/Fieldmapper",
+       "dijit/form/Form",
+       "dijit/form/TextBox",
+       "dijit/form/NumberSpinner",
+       "openils/Event",
+       "openils/acq/Picklist",
+       "openils/acq/Lineitem",
+       "openils/User",
+       "openils/Util"
+       ],
+function(dijit_form_FilteringSelect,
+       dijit_ProgressBar,
+       dijit_Dialog,
+       dojox_form_CheckedMultiSelect,
+       fieldmapper_Fieldmapper,
+       dijit_form_Form,
+       dijit_form_TextBox,
+       dijit_form_NumberSpinner,
+       openils_Event,
+       openils_acq_Picklist,
+       openils_acq_Lineitem,
+       openils_User,
+       openils_Util){
+       
+       var searchFields = [];
+       var resultPicklist;
+       var resultLIs;
+       var selectedLIs;
+       var recvCount = 0;
+       var sourceCount = 0; // how many sources are we searching
+       var user = new openils_User();
+       var searchLimit = 10;
+       var liCache = {};
+       var liTable;
+       
+       function drawForm() {
+           liTable = new AcqLiTable();
+           liTable.skipInitialEligibilityCheck = true;
+       
+           fieldmapper.standardRequest(
+               ['open-ils.search', 'open-ils.search.z3950.retrieve_services'], 
+               {   async: true,
+                   params: [user.authtoken],
+                   oncomplete: _drawForm
+               }
+           );
+       }
+       
+       function _drawForm(r) {
+       
+           var sources = openils_Util.readResponse(r);
+           if(!sources) return;
+       
+           for(var name in sources) {
+               source = sources[name];
+               if(name == 'native-evergreen-catalog') continue;
+               bibSourceSelect.addOption({value:name, label:source.label});
+               for(var attr in source.attrs) 
+                   if(!attr.match(/^#/)) // xml comment nodes
+                       searchFields.push(source.attrs[attr]);
+           }
+       
+           searchFields = searchFields.sort(
+               function(a,b) {
+                   if(a.label < b.label) 
+                       return -1;
+                   if(a.label > b.label) 
+                       return 1;
+                   return 0;
+               }
+           );
+       
+           var tbody = dojo.byId('oils-acq-search-fields-tbody');
+           var tmpl = tbody.removeChild(dojo.byId('oils-acq-search-fields-template'));
+       
+           for(var f in searchFields) {
+               var field = searchFields[f];
+               if(dijit.byId('text_input_'+field.name)) continue;
+               var row = tmpl.cloneNode(true);
+               tbody.insertBefore(row, dojo.byId('oils-acq-seach-fields-count-row'));
+               var labelCell = dojo.query('[name=label]', row)[0];
+               var inputCell = dojo.query('[name=input]', row)[0];
+               labelCell.appendChild(document.createTextNode(field.label));
+               input = new dijit_form_TextBox({name:field.name, label:field.label, id:'text_input_'+field.name});
+               inputCell.appendChild(input.domNode);
+           }
+       }
+       
+       function clearSearchForm() {
+           for(var f in searchFields) {
+               var field = searchFields[f];
+               dijit.byId('text_input_'+field.name).setValue('');
+           }
+       }
+       
+       var resultRow;
+       function doSearch(values) {
+           liTable.reset();
+           showDiv('oils-acq-pl-loading');
+       
+           search = {
+               service : [],
+               username : [],
+               password : [],
+               search : {},
+               limit : values.limit,
+               offset : searchOffset
+           };
+           searchLimit = values.limit;
+           delete values.limit;
+       
+           var selected = bibSourceSelect.getValue();
+           for(var i = 0; i < selected.length; i++) {
+               search.service.push(selected[i]);
+               search.username.push('');
+               search.password.push('');
+               sourceCount++;
+           }
+       
+           for(var v in values) {
+               if(values[v]) {
+                   var input = dijit.byId('text_input_'+v);
+                   search.search[v] = values[v];
+               }
+           }
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.picklist.search.z3950'],
+               {   async: true,
+                   params: [user.authtoken, search, null, {respond_li:1, flesh_attrs:1, clear_marc:1}],
+                   onresponse: handleResult
+               }
+           );
+       }
+       
+       
+       function setRowAttr(td, liWrapper, field) {
+           var val = liWrapper.findAttr(field, 'lineitem_marc_attr_definition') || '';
+           td.appendChild(document.createTextNode(val));
+       }
+       
+       function handleResult(r) {
+           var result = openils_Util.readResponse(r);
+           liTable.show('list');
+           dojo.style(dojo.byId('oils-acq-pl-search-results'), 'display', 'block');
+           var tbody = dojo.byId('plist-tbody');
+           if(result.lineitem)
+               liTable.addLineitem(result.lineitem);
+           if(result.complete) // hide the loading image
+               dojo.style('oils-acq-pl-loading','display', 'none');
+       }
+       
+       function showDiv(div) {
+           var divs = [
+               'oils-acq-search-block', 
+               'oils-acq-pl-loading' ];
+           dojo.forEach(divs, function(d) {dojo.style(d,'display', 'none')});
+           liTable.hide();
+           dojo.style(div, 'display', 'block');
+       }
+       
+       
+       openils_Util.addOnLoad(drawForm);
+       
+       
+       
+
+});
\ No newline at end of file
index f59b93b..155c0e2 100644 (file)
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.DateTextBox');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.ComboBox');
-dojo.require('openils.User');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.MarcXPathParser');
-dojo.require('openils.acq.Picklist');
-dojo.require('openils.CGI');
-
-var attrDefs = {};
-var paramPL = null;
-var paramPO = null;
-var paramUR = null; // User Request ID
-
-function drawBriefRecordForm(fields) {
-
-    var tbody = dojo.byId('acq-brief-record-tbody');
-    var rowTmpl = dojo.byId('acq-brief-record-row');
-    var cgi = new openils.CGI();
-    paramPL = cgi.param('pl');
-    paramPO = cgi.param('po');
-    paramUR = cgi.param('ur');
-    prepop = JSON2js(cgi.param('prepop'));
-
-
-    if(paramPL) {
-        openils.Util.hide('acq-brief-record-po-row');
-
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
-            {   async: true,
-                params: [openils.User.authtoken, paramPL], 
-                oncomplete : function(r) {
-                    var pl = openils.Util.readResponse(r);
-                    plSelector.store = 
-                        new dojo.data.ItemFileReadStore({data:acqpl.toStoreData([pl])});
-                    plSelector.attr('value', pl.name());
-                    plSelector.attr('disabled', true);
-                }
-            }
-        );
-
-    } else {
-
-        if(paramPO) {
-            openils.Util.hide('acq-brief-record-pl-row');
-            poNumber.attr('value', paramPO);
-
-        } else {
-            openils.Util.hide('acq-brief-record-po-row');
-            fieldmapper.standardRequest(
-                ['open-ils.acq', 'open-ils.acq.picklist.user.retrieve.atomic'],
-                {   async: true,
-                    params: [openils.User.authtoken], 
-                    oncomplete : function(r) {
-                        var list = openils.Util.readResponse(r);
-                        plSelector.store = 
-                            new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(list)});
-                    }
-                }
-            );
-        }
-    }
-
-
-    /*
-    marcEditButton.onClick = function(fields) {
-        saveBriefRecord(fields, true);
-    }
-    */
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem_attr_definition.retrieve.all'],
-        {   async : true,
-            params : [openils.User.authtoken],
-
-            oncomplete : function(r) {
-                var attrs = openils.Util.readResponse(r);
-                if(attrs && attrs.marc) {
-
-                    attrs = attrs.marc.sort(
-                        function(a, b) {
-                            if(a.description < b.description)
-                                return 1;
-                            return -1;
-                        }
-                    );
-
-                    var xpathParser = new openils.MarcXPathParser();
-                    dojo.forEach(attrs,
-                        function(def) {
-                            attrDefs[def.code()] = xpathParser.parse(def.xpath());
-                            var row = rowTmpl.cloneNode(true);
-                            dojo.query('[name=name]', row)[0].innerHTML = def.description();
-                            var textbox = new dijit.form.TextBox(
-                                {"name": def.code()},
-                                dojo.query('[name=widget]', row)[0]
-                            );
-                            if (prepop && prepop[def.id()])
-                                textbox.attr("value", prepop[def.id()]);
-                            tbody.appendChild(row);
-                        }
-                    );
-                }
-            }
-        }
-    );
-}
-
-function saveBriefRecord(fields, editMarc) {
-
-    if(paramPL) {
-        fields.picklist = paramPL;
-        delete fields.po;
-        compileBriefRecord(fields, editMarc);
-        return false;
-    }
-
-    if(paramPO) {
-        fields.po = paramPO;
-        delete fields.picklist;
-        compileBriefRecord(fields, editMarc);
-        return false;
-    }
-
-    // first, deal with the selection list
-    var picklist = plSelector.attr('value');
-
-    if(!picklist) {
-        compileBriefRecord(fields, editMarc);
-        return false;
-    }
-
-    // ComboBox value is the display string.  find the actual picklist
-    // and create a new one if necessary
-    plSelector.store.fetch({
-        query : {name:picklist}, 
-
-        onComplete : function(items) {
-            if(items.length == 0) {
-                
-                // create a new picklist for these items
-                openils.acq.Picklist.create(
-                    {name:picklist, org_unit: openils.User.user.ws_ou()},
-                    function(plId) { 
-                        fields.picklist = plId;
-                        compileBriefRecord(fields, editMarc);
-                    }
-                );
-
-            } else {
-                var id = plSelector.store.getValue(items[0], 'id');
-                fields.picklist = id;
-                compileBriefRecord(fields, editMarc);
-            }
-        }
-    });
-
-    return false;
-}
-
-function compileBriefRecord(fields, editMarc) {
-
-    var baseString = '<record xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
-        'xmlns="http://www.loc.gov/MARC21/slim" ' +
-        'xmlns:marc="http://www.loc.gov/MARC21/slim" ' +
-        'xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd">' +
-        '<leader>00000nam a22000007a 4500</leader></record>';
-
-    var doc = new DOMParser().parseFromString(baseString, 'text/xml');
-
-    for(var f in fields) {
-
-        var def = attrDefs[f];
-        if(!def) continue;
-        var value = fields[f];
-        if(!value) continue;
-
-        var dfNode = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'marc:datafield');
-        var sfNode = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'marc:subfield');
-        
-        // creates tags and fields in the document.  If there are more than one
-        // option for the tag or code, use the first in the list
-        dfNode.setAttribute('tag', ''+def.tags[0]);
-        dfNode.setAttribute('ind1', ' ');
-        dfNode.setAttribute('ind2', ' ');
-        sfNode.setAttribute('code', ''+def.subfields[0]);
-        tNode = doc.createTextNode(value);
-
-        sfNode.appendChild(tNode);
-        dfNode.appendChild(sfNode);
-        doc.documentElement.appendChild(dfNode);
-    }
-    
-
-    var xmlString = new XMLSerializer().serializeToString(doc);
-
-    var li = new fieldmapper.jub();
-    li.marc(xmlString);
-    li.picklist(fields.picklist);
-    if(fields.po) li.purchase_order(fields.po);
-    li.selector(openils.User.user.id());
-    li.creator(openils.User.user.id());
-    li.editor(openils.User.user.id());
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem.create'],
-        {   async : true,
-            params : [openils.User.authtoken, li],
-            oncomplete : function(r) {
-                var id = openils.Util.readResponse(r);
-                if(!id) return;
-                if(editMarc) {
-                    // XXX load marc editor
-                } else if (paramUR) {
-                    // update User Request with Lineitem and reload request interface
-                    var pcrud = new openils.PermaCrud({ authtoken : openils.User.authtoken });
-                    var aur_obj = pcrud.retrieve('aur',paramUR);
-                    aur_obj.lineitem( id );
-                    pcrud.update( aur_obj, {
-                        'oncomplete' : function(r, cudResults) {
-                            // Goes back to the list view
-                            location.href = oilsBasePath + '/acq/picklist/user_request';
-                        }
-                    });
-                } else {
-                    if(fields.picklist) 
-                        location.href = oilsBasePath + '/acq/picklist/view/' + fields.picklist;
-                    else
-                        location.href = oilsBasePath + '/acq/po/view/' + fields.po;
-                }
-            }
-        }
-    );
-
-    return false;
-}
-
-openils.Util.addOnLoad(drawBriefRecordForm);
+require([
+       "dojo/data/ItemFileReadStore",
+       "dijit/form/Form",
+       "dijit/form/TextBox",
+       "dijit/form/DateTextBox",
+       "dijit/form/Button",
+       "dijit/form/ComboBox",
+       "openils/User",
+       "openils/widget/AutoFieldWidget",
+       "openils/MarcXPathParser",
+       "openils/acq/Picklist",
+       "openils/CGI"
+       ],
+function(dojo_data_ItemFileReadStore,
+       dijit_form_Form,
+       dijit_form_TextBox,
+       dijit_form_DateTextBox,
+       dijit_form_Button,
+       dijit_form_ComboBox,
+       openils_User,
+       openils_widget_AutoFieldWidget,
+       openils_MarcXPathParser,
+       openils_acq_Picklist,
+       openils_CGI){
+       
+       var attrDefs = {};
+       var paramPL = null;
+       var paramPO = null;
+       var paramUR = null; // User Request ID
+       
+       function drawBriefRecordForm(fields) {
+       
+           var tbody = dojo.byId('acq-brief-record-tbody');
+           var rowTmpl = dojo.byId('acq-brief-record-row');
+           var cgi = new openils_CGI();
+           paramPL = cgi.param('pl');
+           paramPO = cgi.param('po');
+           paramUR = cgi.param('ur');
+           prepop = JSON2js(cgi.param('prepop'));
+       
+       
+           if(paramPL) {
+               openils.Util.hide('acq-brief-record-po-row');
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
+                   {   async: true,
+                       params: [openils_User.authtoken, paramPL], 
+                       oncomplete : function(r) {
+                           var pl = openils.Util.readResponse(r);
+                           plSelector.store = 
+                               new dojo_data_ItemFileReadStore({data:acqpl.toStoreData([pl])});
+                           plSelector.attr('value', pl.name());
+                           plSelector.attr('disabled', true);
+                       }
+                   }
+               );
+       
+           } else {
+       
+               if(paramPO) {
+                   openils.Util.hide('acq-brief-record-pl-row');
+                   poNumber.attr('value', paramPO);
+       
+               } else {
+                   openils.Util.hide('acq-brief-record-po-row');
+                   fieldmapper.standardRequest(
+                       ['open-ils.acq', 'open-ils.acq.picklist.user.retrieve.atomic'],
+                       {   async: true,
+                           params: [openils_User.authtoken], 
+                           oncomplete : function(r) {
+                               var list = openils.Util.readResponse(r);
+                               plSelector.store = 
+                                   new dojo_data_ItemFileReadStore({data:acqpl.toStoreData(list)});
+                           }
+                       }
+                   );
+               }
+           }
+       
+       
+           /*
+           marcEditButton.onClick = function(fields) {
+               saveBriefRecord(fields, true);
+           }
+           */
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem_attr_definition.retrieve.all'],
+               {   async : true,
+                   params : [openils_User.authtoken],
+       
+                   oncomplete : function(r) {
+                       var attrs = openils.Util.readResponse(r);
+                       if(attrs && attrs.marc) {
+       
+                           attrs = attrs.marc.sort(
+                               function(a, b) {
+                                   if(a.description < b.description)
+                                       return 1;
+                                   return -1;
+                               }
+                           );
+       
+                           var xpathParser = new openils_MarcXPathParser();
+                           dojo.forEach(attrs,
+                               function(def) {
+                                   attrDefs[def.code()] = xpathParser.parse(def.xpath());
+                                   var row = rowTmpl.cloneNode(true);
+                                   dojo.query('[name=name]', row)[0].innerHTML = def.description();
+                                   var textbox = new dijit_form_TextBox(
+                                       {"name": def.code()},
+                                       dojo.query('[name=widget]', row)[0]
+                                   );
+                                   if (prepop && prepop[def.id()])
+                                       textbox.attr("value", prepop[def.id()]);
+                                   tbody.appendChild(row);
+                               }
+                           );
+                       }
+                   }
+               }
+           );
+       }
+       
+       function saveBriefRecord(fields, editMarc) {
+       
+           if(paramPL) {
+               fields.picklist = paramPL;
+               delete fields.po;
+               compileBriefRecord(fields, editMarc);
+               return false;
+           }
+       
+           if(paramPO) {
+               fields.po = paramPO;
+               delete fields.picklist;
+               compileBriefRecord(fields, editMarc);
+               return false;
+           }
+       
+           // first, deal with the selection list
+           var picklist = plSelector.attr('value');
+       
+           if(!picklist) {
+               compileBriefRecord(fields, editMarc);
+               return false;
+           }
+       
+           // ComboBox value is the display string.  find the actual picklist
+           // and create a new one if necessary
+           plSelector.store.fetch({
+               query : {name:picklist}, 
+       
+               onComplete : function(items) {
+                   if(items.length == 0) {
+                       
+                       // create a new picklist for these items
+                       openils_acq_Picklist.create(
+                           {name:picklist, org_unit: openils_User.user.ws_ou()},
+                           function(plId) { 
+                               fields.picklist = plId;
+                               compileBriefRecord(fields, editMarc);
+                           }
+                       );
+       
+                   } else {
+                       var id = plSelector.store.getValue(items[0], 'id');
+                       fields.picklist = id;
+                       compileBriefRecord(fields, editMarc);
+                   }
+               }
+           });
+       
+           return false;
+       }
+       
+       function compileBriefRecord(fields, editMarc) {
+       
+           var baseString = '<record xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
+               'xmlns="http://www.loc.gov/MARC21/slim" ' +
+               'xmlns:marc="http://www.loc.gov/MARC21/slim" ' +
+               'xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd">' +
+               '<leader>00000nam a22000007a 4500</leader></record>';
+       
+           var doc = new DOMParser().parseFromString(baseString, 'text/xml');
+       
+           for(var f in fields) {
+       
+               var def = attrDefs[f];
+               if(!def) continue;
+               var value = fields[f];
+               if(!value) continue;
+       
+               var dfNode = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'marc:datafield');
+               var sfNode = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'marc:subfield');
+               
+               // creates tags and fields in the document.  If there are more than one
+               // option for the tag or code, use the first in the list
+               dfNode.setAttribute('tag', ''+def.tags[0]);
+               dfNode.setAttribute('ind1', ' ');
+               dfNode.setAttribute('ind2', ' ');
+               sfNode.setAttribute('code', ''+def.subfields[0]);
+               tNode = doc.createTextNode(value);
+       
+               sfNode.appendChild(tNode);
+               dfNode.appendChild(sfNode);
+               doc.documentElement.appendChild(dfNode);
+           }
+           
+       
+           var xmlString = new XMLSerializer().serializeToString(doc);
+       
+           var li = new fieldmapper.jub();
+           li.marc(xmlString);
+           li.picklist(fields.picklist);
+           if(fields.po) li.purchase_order(fields.po);
+           li.selector(openils_User.user.id());
+           li.creator(openils_User.user.id());
+           li.editor(openils_User.user.id());
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem.create'],
+               {   async : true,
+                   params : [openils_User.authtoken, li],
+                   oncomplete : function(r) {
+                       var id = openils.Util.readResponse(r);
+                       if(!id) return;
+                       if(editMarc) {
+                           // XXX load marc editor
+                       } else if (paramUR) {
+                           // update User Request with Lineitem and reload request interface
+                           var pcrud = new openils.PermaCrud({ authtoken : openils_User.authtoken });
+                           var aur_obj = pcrud.retrieve('aur',paramUR);
+                           aur_obj.lineitem( id );
+                           pcrud.update( aur_obj, {
+                               'oncomplete' : function(r, cudResults) {
+                                   // Goes back to the list view
+                                   location.href = oilsBasePath + '/acq/picklist/user_request';
+                               }
+                           });
+                       } else {
+                           if(fields.picklist) 
+                               location.href = oilsBasePath + '/acq/picklist/view/' + fields.picklist;
+                           else
+                               location.href = oilsBasePath + '/acq/po/view/' + fields.po;
+                       }
+                   }
+               }
+           );
+       
+           return false;
+       }
+       
+       openils.Util.addOnLoad(drawBriefRecordForm);
+       
+
+});
\ No newline at end of file
index 4853d16..af66629 100644 (file)
@@ -1,76 +1,83 @@
-dojo.require("dijit.form.Button");
-dojo.require("openils.widget.XULTermLoader");
+require([
+       "dijit/form/Button",
+       "openils/widget/XULTermLoader"
+       ],
+function(dijit_form_Button,
+       openils_widget_XULTermLoader){
+       
+       var termLoader = null;
+       var liTable = null;
+       var pager = null;
+       var usingPl = null;
+       
+       function fetchRecords() {
+           var data = openils.Util.uniqueElements(termLoader.attr("value"));
+           var result_count = 0;
+           // Don't show a total for now... This total is the total number of
+           // search terms, but a user would take it to mean the total number of
+           // results, which we don't have a straightfoward way of getting without
+           // doing the search more that once.
+       
+           // pager.total = data.length;
+       
+           progressDialog.show(true);
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.biblio.create_by_id"], {
+                   "params": [
+                       openils.User.authtoken,
+                       data.slice(
+                           pager.displayOffset,
+                           pager.displayOffset + pager.displayLimit
+                       ), {
+                           "flesh_attrs": true,
+                           "flesh_cancel_reason": true,
+                           "flesh_notes": true,
+                           "reuse_picklist": usingPl
+                       }
+                   ],
+                   "onresponse": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           if (typeof(r) != "object") {
+                               usingPl = r;
+                           } else if (r.classname && r.classname == "jub") {
+                               result_count++;
+                               liTable.addLineitem(r);
+                           }
+                           /* The ML method is buggy and sometimes responds with
+                            * more objects that we don't want, hence the specific
+                            * conditionals above that don't necesarily consume all
+                            * responses. */
+                       }
+                   }
+               }
+           );
+           pager.batch_length = result_count;
+           progressDialog.hide();
+       }
+       
+       function beginSearch() {
+           var data = termLoader.attr("value");
+           if (!data || !data.length) {
+               alert(localeStrings.LOAD_TERMS_FIRST);
+               return;
+           }
+       
+           pager.go(0);
+           openils.Util.hide("acq-frombib-upload-box");
+           openils.Util.show("acq-frombib-reload-box");
+       }
+       
+       function init() {
+           new openils_widget_XULTermLoader(
+               {"parentNode": "acq-frombib-upload", "parseCSV": true}
+           ).build(function(w) { termLoader = w; });
+           liTable = new AcqLiTable();
+           pager = new LiTablePager(fetchRecords, liTable);
+       
+           openils.Util.show("acq-frombib-begin-holder");
+       }
+       
+       openils.Util.addOnLoad(init);
+       
 
-var termLoader = null;
-var liTable = null;
-var pager = null;
-var usingPl = null;
-
-function fetchRecords() {
-    var data = openils.Util.uniqueElements(termLoader.attr("value"));
-    var result_count = 0;
-    // Don't show a total for now... This total is the total number of
-    // search terms, but a user would take it to mean the total number of
-    // results, which we don't have a straightfoward way of getting without
-    // doing the search more that once.
-
-    // pager.total = data.length;
-
-    progressDialog.show(true);
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.biblio.create_by_id"], {
-            "params": [
-                openils.User.authtoken,
-                data.slice(
-                    pager.displayOffset,
-                    pager.displayOffset + pager.displayLimit
-                ), {
-                    "flesh_attrs": true,
-                    "flesh_cancel_reason": true,
-                    "flesh_notes": true,
-                    "reuse_picklist": usingPl
-                }
-            ],
-            "onresponse": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    if (typeof(r) != "object") {
-                        usingPl = r;
-                    } else if (r.classname && r.classname == "jub") {
-                        result_count++;
-                        liTable.addLineitem(r);
-                    }
-                    /* The ML method is buggy and sometimes responds with
-                     * more objects that we don't want, hence the specific
-                     * conditionals above that don't necesarily consume all
-                     * responses. */
-                }
-            }
-        }
-    );
-    pager.batch_length = result_count;
-    progressDialog.hide();
-}
-
-function beginSearch() {
-    var data = termLoader.attr("value");
-    if (!data || !data.length) {
-        alert(localeStrings.LOAD_TERMS_FIRST);
-        return;
-    }
-
-    pager.go(0);
-    openils.Util.hide("acq-frombib-upload-box");
-    openils.Util.show("acq-frombib-reload-box");
-}
-
-function init() {
-    new openils.widget.XULTermLoader(
-        {"parentNode": "acq-frombib-upload", "parseCSV": true}
-    ).build(function(w) { termLoader = w; });
-    liTable = new AcqLiTable();
-    pager = new LiTablePager(fetchRecords, liTable);
-
-    openils.Util.show("acq-frombib-begin-holder");
-}
-
-openils.Util.addOnLoad(init);
+});
\ No newline at end of file
index 1a6653a..6c9541f 100644 (file)
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.ProgressBar');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.ComboBox');
-dojo.require('dijit.form.Button');
-dojo.require("dojo.io.iframe");
-dojo.require('openils.User');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.acq.Picklist');
-dojo.require('openils.XUL');
-dojo.require('openils.PermaCrud');
-
-var VANDELAY_URL = '/vandelay-upload';
-var providerWidget;
-var orderAgencyWidget;
-var vlAgent;
-var usingNewPl = false;
-
-function init() {
-    dojo.byId('acq-pl-upload-ses').value = openils.User.authtoken;
-
-    new openils.widget.AutoFieldWidget({
-        fmClass : 'acqpo',
-        fmField : 'provider',
-        orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
-        parentNode : dojo.byId('acq-pl-upload-provider'),
-    }).build(
-        function(w) { providerWidget = w }
-    );
-
-    new openils.widget.AutoFieldWidget({
-        fmClass : 'acqpo',
-        fmField : 'ordering_agency',
-        orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
-        parentNode : dojo.byId('acq-pl-upload-agency'),
-    }).build(
-        function(w) { orderAgencyWidget = w }
-    );
-
-    vlAgent = new VLAgent();
-    vlAgent.init();
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.picklist.user.retrieve.atomic'],
-        {   async: true,
-            params: [openils.User.authtoken], 
-            oncomplete : function(r) {
-                var list = openils.Util.readResponse(r);
-                acqPlUploadPlSelector.store = 
-                    new dojo.data.ItemFileWriteStore({data:acqpl.toStoreData(list)});
-            }
-        }
-    );
-}
-
-function acqUploadRecords() {
-    openils.Util.show('acq-pl-upload-progress');
-    var picklist = acqPlUploadPlSelector.attr('value');
-    if(picklist) {
-        // ComboBox value is the display string.  find the actual picklist
-        // and create a new one if necessary
-        acqPlUploadPlSelector.store.fetch({
-            query : {name:picklist}, 
-            onComplete : function(items) {
-                if(items.length == 0) {
-                    // create a new picklist for these items
-                    usingNewPl = true;
-                    openils.acq.Picklist.create(
-                        {name:picklist, org_unit: orderAgencyWidget.attr('value')},
-                        function(plId) { acqSendUploadForm({picklist:plId}) }
-                    );
-                } else {
-                    usingNewPl = false;
-                    acqSendUploadForm({picklist:items[0].id[0]});
-                }
-            }
-        });
-    } else {
-        acqSendUploadForm({picklist:null});
-    }
-}
-
-function acqSendUploadForm(args) {
-    dojo.io.iframe.send({
-        url: VANDELAY_URL,
-        method: "post",
-        handleAs: "html",
-        form: dojo.byId('acq-pl-upload-form'),
-        handle: function(data, ioArgs){
-            acqHandlePostUpload(data.documentElement.textContent, args.picklist);
-        }
-    });
-}
-
-
-function acqHandlePostUpload(key, plId) {
-
-    var args = {
-        picklist : plId,
-        provider : providerWidget.attr('value'),
-        ordering_agency : orderAgencyWidget.attr('value'),
-        create_po : acqPlUploadCreatePo.attr('value'),
-        activate_po : acqPlUploadActivatePo.attr('value'),
-        vandelay : vlAgent.values()
-    };
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.process_upload_records'],
-        {   async: true,
-            params: [openils.User.authtoken, key, args],
-            onresponse : function(r) {
-
-                vlAgent.handleResponse(
-                    openils.Util.readResponse(r),
-                    function(resp, res) {
-
-                        openils.Util.hide('acq-pl-upload-complete-pl');
-                        openils.Util.hide('acq-pl-upload-complete-po');
-                        openils.Util.hide('acq-pl-upload-complete-q');
-                        openils.Util.hide('acq-pl-upload-progress-bar');
-                        openils.Util.show('acq-pl-upload-complete');
-
-                        function activateLink(link, url, name) {
-                            link = dojo.byId(link);
-                            openils.Util.show(link);
-                            if (name) link.innerHTML = name;
-                            if (typeof xulG == 'undefined') { // browser
-                                link.setAttribute('href', url); 
-                            } else {
-                                link.setAttribute('href', 'javascript:;'); // for linky-ness
-                                link.onclick = function() { openils.XUL.newTabEasy(url, null, null, true) };
-                            }
-                        }
-                            
-                        if(res.picklist_url) {
-                            activateLink('acq-pl-upload-complete-pl', res.picklist_url);
-
-                            // if the user entered a new picklist, refetch the set to pick
-                            // up the ID and redraw the list with the new one selected
-                            if (usingNewPl) {
-                                var newPl = new openils.PermaCrud().retrieve('acqpl', resp.picklist.id());
-                                acqPlUploadPlSelector.store.newItem(newPl.toStoreItem());
-                                acqPlUploadPlSelector.attr('value', newPl.name());
-                            }
-                        } 
-
-                        if(res.po_url) {
-                            activateLink('acq-pl-upload-complete-po', res.po_url);
-                        }
-
-                        if (res.queue_url) {
-                            activateLink('acq-pl-upload-complete-q', res.queue_url);
-                        }
-                    }
-                );
-            },
-        }
-    );
-}
-
-
-openils.Util.addOnLoad(init);
-
+require([
+       "dojo/data/ItemFileReadStore",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/ProgressBar",
+       "dijit/form/CheckBox",
+       "dijit/form/TextBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/ComboBox",
+       "dijit/form/Button",
+       "dojo/io/iframe",
+       "openils/User",
+       "openils/widget/AutoFieldWidget",
+       "openils/acq/Picklist",
+       "openils/XUL",
+       "openils/PermaCrud"
+       ],
+function(dojo_data_ItemFileReadStore,
+       dojo_data_ItemFileWriteStore,
+       dijit_ProgressBar,
+       dijit_form_CheckBox,
+       dijit_form_TextBox,
+       dijit_form_FilteringSelect,
+       dijit_form_ComboBox,
+       dijit_form_Button,
+       dojo_io_iframe,
+       openils_User,
+       openils_widget_AutoFieldWidget,
+       openils_acq_Picklist,
+       openils_XUL,
+       openils_PermaCrud){
+       
+       var VANDELAY_URL = '/vandelay-upload';
+       var providerWidget;
+       var orderAgencyWidget;
+       var vlAgent;
+       var usingNewPl = false;
+       
+       function init() {
+           dojo.byId('acq-pl-upload-ses').value = openils_User.authtoken;
+       
+           new openils_widget_AutoFieldWidget({
+               fmClass : 'acqpo',
+               fmField : 'provider',
+               orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
+               parentNode : dojo.byId('acq-pl-upload-provider'),
+           }).build(
+               function(w) { providerWidget = w }
+           );
+       
+           new openils_widget_AutoFieldWidget({
+               fmClass : 'acqpo',
+               fmField : 'ordering_agency',
+               orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'],
+               parentNode : dojo.byId('acq-pl-upload-agency'),
+           }).build(
+               function(w) { orderAgencyWidget = w }
+           );
+       
+           vlAgent = new VLAgent();
+           vlAgent.init();
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.picklist.user.retrieve.atomic'],
+               {   async: true,
+                   params: [openils_User.authtoken], 
+                   oncomplete : function(r) {
+                       var list = openils.Util.readResponse(r);
+                       acqPlUploadPlSelector.store = 
+                           new dojo_data_ItemFileWriteStore({data:acqpl.toStoreData(list)});
+                   }
+               }
+           );
+       }
+       
+       function acqUploadRecords() {
+           openils.Util.show('acq-pl-upload-progress');
+           var picklist = acqPlUploadPlSelector.attr('value');
+           if(picklist) {
+               // ComboBox value is the display string.  find the actual picklist
+               // and create a new one if necessary
+               acqPlUploadPlSelector.store.fetch({
+                   query : {name:picklist}, 
+                   onComplete : function(items) {
+                       if(items.length == 0) {
+                           // create a new picklist for these items
+                           usingNewPl = true;
+                           openils_acq_Picklist.create(
+                               {name:picklist, org_unit: orderAgencyWidget.attr('value')},
+                               function(plId) { acqSendUploadForm({picklist:plId}) }
+                           );
+                       } else {
+                           usingNewPl = false;
+                           acqSendUploadForm({picklist:items[0].id[0]});
+                       }
+                   }
+               });
+           } else {
+               acqSendUploadForm({picklist:null});
+           }
+       }
+       
+       function acqSendUploadForm(args) {
+           dojo_io_iframe.send({
+               url: VANDELAY_URL,
+               method: "post",
+               handleAs: "html",
+               form: dojo.byId('acq-pl-upload-form'),
+               handle: function(data, ioArgs){
+                   acqHandlePostUpload(data.documentElement.textContent, args.picklist);
+               }
+           });
+       }
+       
+       
+       function acqHandlePostUpload(key, plId) {
+       
+           var args = {
+               picklist : plId,
+               provider : providerWidget.attr('value'),
+               ordering_agency : orderAgencyWidget.attr('value'),
+               create_po : acqPlUploadCreatePo.attr('value'),
+               activate_po : acqPlUploadActivatePo.attr('value'),
+               vandelay : vlAgent.values()
+           };
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.process_upload_records'],
+               {   async: true,
+                   params: [openils_User.authtoken, key, args],
+                   onresponse : function(r) {
+       
+                       vlAgent.handleResponse(
+                           openils.Util.readResponse(r),
+                           function(resp, res) {
+       
+                               openils.Util.hide('acq-pl-upload-complete-pl');
+                               openils.Util.hide('acq-pl-upload-complete-po');
+                               openils.Util.hide('acq-pl-upload-complete-q');
+                               openils.Util.hide('acq-pl-upload-progress-bar');
+                               openils.Util.show('acq-pl-upload-complete');
+       
+                               function activateLink(link, url, name) {
+                                   link = dojo.byId(link);
+                                   openils.Util.show(link);
+                                   if (name) link.innerHTML = name;
+                                   if (typeof xulG == 'undefined') { // browser
+                                       link.setAttribute('href', url); 
+                                   } else {
+                                       link.setAttribute('href', 'javascript:;'); // for linky-ness
+                                       link.onclick = function() { openils_XUL.newTabEasy(url, null, null, true) };
+                                   }
+                               }
+                                   
+                               if(res.picklist_url) {
+                                   activateLink('acq-pl-upload-complete-pl', res.picklist_url);
+       
+                                   // if the user entered a new picklist, refetch the set to pick
+                                   // up the ID and redraw the list with the new one selected
+                                   if (usingNewPl) {
+                                       var newPl = new openils_PermaCrud().retrieve('acqpl', resp.picklist.id());
+                                       acqPlUploadPlSelector.store.newItem(newPl.toStoreItem());
+                                       acqPlUploadPlSelector.attr('value', newPl.name());
+                                   }
+                               } 
+       
+                               if(res.po_url) {
+                                   activateLink('acq-pl-upload-complete-po', res.po_url);
+                               }
+       
+                               if (res.queue_url) {
+                                   activateLink('acq-pl-upload-complete-q', res.queue_url);
+                               }
+                           }
+                       );
+                   },
+               }
+           );
+       }
+       
+       
+       openils.Util.addOnLoad(init);
+       
+       
+
+});
\ No newline at end of file
index 8e7f7c1..5c172b1 100644 (file)
-dojo.require('openils.CGI');
-dojo.require('openils.Util');
-dojo.require('openils.User');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.widget.EditPane');
-dojo.require("dijit.layout.StackContainer");
-dojo.require('openils.PermaCrud');
-dojo.requireLocalization("openils.acq", "acq");
-dojo.require('openils.acq.Lineitem');
-
-var contextOrg;
-var contextUsr;
-var contextUsrObj;
-var contextLI;
-var contextEg_bib;
-var aur_obj;
-var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-var cgi = new openils.CGI();
-
-function setup() {
-
-    changeBib(cgi.param('eg_bib'));
-    changeLI(cgi.param('lineitem'));
-
-    if (cgi.param('usr')) {
-        var usr_obj = fieldmapper.standardRequest(
-            [
-                'open-ils.actor',
-                'open-ils.actor.user.fleshed.retrieve.authoritative'
-            ],
-            {
-                params: [openils.User.authtoken, cgi.param('usr')]
-            }
-        );
-        if (typeof usr_obj.textcode == 'undefined') {
-            contextUsrObj = usr_obj;
-            changeUser(usr_obj.id(),usr_obj.card().barcode());
-        } else {
-            alert(usr_obj.textcode + ' : ' + usr_obj.desc);
-        }
-    }
-
-    if(reqId) {
-        drawRequest();
-    } else {
-        drawList();
-    }
-}
-
-function drawRequest() {
-    var pcrud = new openils.PermaCrud({ authtoken : openils.User.authtoken });
-    aur_obj = pcrud.retrieve('aur',reqId);
-
-    // hide the grid and the context selector
-    dijit.byId('stackContainer').forward();
-
-    // purge any previous lineitem display
-    // FIXME: I thought it would be cool to have this, but I can't get it 
-    // to look right with our dojo div/contentPanes.  So just testing for
-    // a DOM hook for now.
-    if (dojo.byId('lineitem')) {
-        //openils.Util.hide( 'lineitem_container' );
-        dojo.byId('lineitem').innerHTML = '';
-    }
-
-    // toggle the View Picklist/Add to Picklist button label
-    if (aur_obj.lineitem()) {
-        openils.Util.hide( 'add_to_picklist' );
-        openils.Util.show( 'view_picklist' );
-    } else {
-        openils.Util.hide( 'view_picklist' );
-        openils.Util.show( 'add_to_picklist' );
-    }
-
-    // draw a detail page for a particular request
-    var div = document.getElementById('detail_content_pane');
-    while (div.lastChild) { div.removeChild( div.lastChild ); }
-    var pane = new openils.widget.EditPane({ 
-        fmObject : aur_obj,
-        readOnly : true
-    });
-    pane.domNode = div;
-    pane.hideActionButtons = true;
-    pane.startup();
-
-    // lineitem summary
-    if (dojo.byId('lineitem') && aur_obj.lineitem()) {
-        //openils.Util.show( 'lineitem_container' );
-        openils.acq.Lineitem.fetchAndRender(aur_obj.lineitem(), {},
-            function(li, html) {
-                dojo.byId('lineitem').innerHTML = html;
-            }
-        );
-    }
-
-    // including ability to add request to a picklist
-    // and to "reject" it (aka apply a cancel reason)
-
-    dojo.byId("acq-ur-cancel-reason").innerHTML = '';
-    var widget = new openils.widget.AutoFieldWidget({
-        "fmField": "cancel_reason",
-        "fmClass": "aur",
-        "parentNode": dojo.byId("acq-ur-cancel-reason"),
-        "orgLimitPerms": ["CREATE_PURCHASE_REQUEST"],
-        "forceSync": true
-    });
-
-    widget.build(
-        function(w, ww) {
-            acqUrCancelReasonSubmit.onClick = function() {
-                if (w.attr("value")) {
-                    if (confirm( localeStrings.UR_CANCEL_CONFIRM )) {
-                        fieldmapper.standardRequest(
-                            [ 'open-ils.acq', 'open-ils.acq.user_request.cancel.batch' ],
-                            {   async: true,
-                                params: [openils.User.authtoken, [reqId], w.attr("value")],
-                                oncomplete: function(r) {
-                                    location.href = location.href; // kludge to reload the interface
-                                }
-                            }
-                        );
-                    }
-                }
-            };
-        }
-    );
-}
-
-function fooPicklist() {
-    if (aur_obj.lineitem()) {
-        viewPicklist();
-    } else {
-        addToPicklist();
-    }
-}
-
-function viewPicklist() {
-    var lineitem = fieldmapper.standardRequest(
-        [ 'open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative' ],
-        {
-            params: [openils.User.authtoken, aur_obj.lineitem()]
-        }
-    );
-    location.href = oilsBasePath + "/acq/picklist/view/" + lineitem.picklist();
-}
-
-function addToPicklist() {
-    // reqId, from detail view
-    location.href = oilsBasePath + "/acq/picklist/brief_record?ur=" + reqId + "&prepop=" + encodeURIComponent(js2JSON({
-        "1": aur_obj.title() || aur_obj.article_title() || aur_obj.volume(),
-        "2": aur_obj.author(),
-        "5": aur_obj.isxn(),
-        "9": aur_obj.publisher(),
-        "10": aur_obj.pubdate()
-    }));
-}
-
-function setNoHold() {
-    // reqId, from detail view
-    fieldmapper.standardRequest(
-        [ 'open-ils.acq', 'open-ils.acq.user_request.set_no_hold.batch' ],
-        {   async: true,
-            params: [openils.User.authtoken, [reqId]],
-            oncomplete: function(r) {
-                location.href = location.href; // kludge to reload the interface
-            }
-        }
-    );
-}
-
-// format the title data as id:title
-function getTitle(idx, item) {
-    if(item) {
-        return this.grid.store.getValue(item, 'id') + ':' + 
-        this.grid.store.getValue(item, 'title');
-    }
-    return '';
-}
-
-// turn id:title into a url
-function formatTitle(value) {
-    if(value) {
-        var parts = value.split(/:/);
-        return '<a href="' + oilsBasePath + 
-            '/acq/picklist/user_request/' + parts[0] + '">' + parts[1] + '</a>';
-    }
-}
-
-function drawList() {
-    buildGrid();
-
-    var connect = function() {
-        dojo.connect(contextOrgSelector, 'onChange',
-            function() {
-                contextOrg = this.attr('value');
-                rGrid.resetStore();
-                buildGrid();
-            }
-        );
-    };
-
-    new openils.User().buildPermOrgSelector(
-        'CREATE_PICKLIST', contextOrgSelector, null, connect);
-}
-
-function buildGrid() {
-
-    if(contextOrg == null)
-        contextOrg = openils.User.user.ws_ou();
-
-    var query = {
-        cancel_reason : null,
-        '+au' : {
-            home_ou : fieldmapper.aou.descendantNodeList(contextOrg).map(
-            function(item) { return item.id(); })
-        }
-    };
-
-    if (contextUsr) {
-        delete query['+au']['home_ou'];
-        query['+au']['id'] = contextUsr;
-    }
-
-    if (contextEg_bib) {
-        query['eg_bib'] = contextEg_bib;
-    }
-
-    if (contextLI) {
-        query['lineitem'] = contextLI;
-    }
-
-    rGrid.resetStore();
-    rGrid.loadAll(
-        {   order_by : {aur : 'request_date'},
-            join : 'au' 
-        },
-        query
-    );
-}
-
-function changeBib(value) {
-    contextEg_bib = value;
-    rGrid.overrideEditWidgets.eg_bib = new dijit.form.TextBox({"disabled": true});
-    rGrid.overrideEditWidgets.eg_bib.shove = { create : contextEg_bib };
-}
-
-function changeLI(value,display_value) {
-    contextLI = value;
-    contextLITextbox.setValue( contextLI );
-    contextLITextbox.setDisplayedValue( display_value || contextLI );
-    rGrid.overrideEditWidgets.lineitem = new dijit.form.TextBox({"disabled": true});
-    rGrid.overrideEditWidgets.lineitem.shove = { create : contextLI };
-}
-
-function changeLIPrompt() {
-    var lineitem = window.prompt(localeStrings.UR_FILTER_LINEITEM);
-    if(lineitem != '' && (lineitem == null || Number(lineitem) == NaN)) {
-        return;
-    }
-    changeLI(lineitem);
-    buildGrid();
-}
-
-function changeUser(value,display_value) {
-    contextUsr = value;
-    contextUsrTextbox.setValue( contextUsr );
-    contextUsrTextbox.setDisplayedValue( display_value || contextUsr );
-    rGrid.overrideEditWidgets.usr = new dijit.form.TextBox({"disabled": true});
-    rGrid.overrideEditWidgets.usr.shove = { create : contextUsr };
-}
-
-function changeUserPrompt() {
-    var barcode = window.prompt(localeStrings.UR_FILTER_USER);
-    if(barcode == null) {
-        return;
-    }
-    if(typeof xulG != 'undefined' && xulG.get_barcode) {
-        // We have a "complete the barcode" function, call it (actor = users only)
-        var new_barcode = xulG.get_barcode(window, 'actor', barcode);
-        // If we got a result (boolean false is "no result") check it
-        if(new_barcode) {
-            // user_false string means they picked "None of the above"
-            // Abort before any other events can fire
-            if(new_barcode == "user_false") return;
-            // No error means we have a (hopefully valid) completed barcode to use.
-            // Otherwise, fall through to other methods of checking
-            if(typeof new_barcode.ilsevent == 'undefined')
-                barcode = new_barcode.barcode;
-        }
-    }
-    if (barcode == '') {
-        contextUsrObj = null;
-        changeUser('','');
-    } else {
-        var usr_obj = fieldmapper.standardRequest(
-            [
-                'open-ils.actor',
-                'open-ils.actor.user.fleshed.retrieve_by_barcode.authoritative'
-            ],
-            {
-                params: [openils.User.authtoken, barcode]
-            }
-        );
-        if (typeof usr_obj.textcode != 'undefined') {
-            alert(usr_obj.textcode + ' : ' + usr_obj.desc);
-            return;
-        } else {
-            contextUsrObj = usr_obj;
-            changeUser(usr_obj.id(),usr_obj.card().barcode());
-        }
-    }
-    buildGrid();
-}
-
-function createRequest() {
-    if (!contextUsr) {
-        changeUserPrompt();
-    }
-    if (contextUsr) {
-        rGrid.overrideEditWidgets.pickup_lib = new dijit.form.TextBox({"disabled": true});
-        rGrid.overrideEditWidgets.pickup_lib.shove = { create : contextUsrObj.home_ou() };
-        rGrid.showCreateDialog();
-    }
-}
-
-openils.Util.addOnLoad(setup);
-
-
+require([
+       "openils/CGI",
+       "openils/Util",
+       "openils/User",
+       "openils/widget/AutoGrid",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/EditPane",
+       "dijit/layout/StackContainer",
+       "openils/PermaCrud",
+       "openils/acq/Lineitem"
+       ],
+function(openils_CGI,
+       openils_Util,
+       openils_User,
+       openils_widget_AutoGrid,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_EditPane,
+       dijit_layout_StackContainer,
+       openils_PermaCrud,
+       openils_acq_Lineitem){
+       dojo.requireLocalization("openils.acq", "acq");
+       
+       var contextOrg;
+       var contextUsr;
+       var contextUsrObj;
+       var contextLI;
+       var contextEg_bib;
+       var aur_obj;
+       var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       var cgi = new openils_CGI();
+       
+       function setup() {
+       
+           changeBib(cgi.param('eg_bib'));
+           changeLI(cgi.param('lineitem'));
+       
+           if (cgi.param('usr')) {
+               var usr_obj = fieldmapper.standardRequest(
+                   [
+                       'open-ils.actor',
+                       'open-ils.actor.user.fleshed.retrieve.authoritative'
+                   ],
+                   {
+                       params: [openils_User.authtoken, cgi.param('usr')]
+                   }
+               );
+               if (typeof usr_obj.textcode == 'undefined') {
+                   contextUsrObj = usr_obj;
+                   changeUser(usr_obj.id(),usr_obj.card().barcode());
+               } else {
+                   alert(usr_obj.textcode + ' : ' + usr_obj.desc);
+               }
+           }
+       
+           if(reqId) {
+               drawRequest();
+           } else {
+               drawList();
+           }
+       }
+       
+       function drawRequest() {
+           var pcrud = new openils_PermaCrud({ authtoken : openils_User.authtoken });
+           aur_obj = pcrud.retrieve('aur',reqId);
+       
+           // hide the grid and the context selector
+           dijit.byId('stackContainer').forward();
+       
+           // purge any previous lineitem display
+           // FIXME: I thought it would be cool to have this, but I can't get it 
+           // to look right with our dojo div/contentPanes.  So just testing for
+           // a DOM hook for now.
+           if (dojo.byId('lineitem')) {
+               //openils_Util.hide( 'lineitem_container' );
+               dojo.byId('lineitem').innerHTML = '';
+           }
+       
+           // toggle the View Picklist/Add to Picklist button label
+           if (aur_obj.lineitem()) {
+               openils_Util.hide( 'add_to_picklist' );
+               openils_Util.show( 'view_picklist' );
+           } else {
+               openils_Util.hide( 'view_picklist' );
+               openils_Util.show( 'add_to_picklist' );
+           }
+       
+           // draw a detail page for a particular request
+           var div = document.getElementById('detail_content_pane');
+           while (div.lastChild) { div.removeChild( div.lastChild ); }
+           var pane = new openils_widget_EditPane({ 
+               fmObject : aur_obj,
+               readOnly : true
+           });
+           pane.domNode = div;
+           pane.hideActionButtons = true;
+           pane.startup();
+       
+           // lineitem summary
+           if (dojo.byId('lineitem') && aur_obj.lineitem()) {
+               //openils_Util.show( 'lineitem_container' );
+               openils_acq_Lineitem.fetchAndRender(aur_obj.lineitem(), {},
+                   function(li, html) {
+                       dojo.byId('lineitem').innerHTML = html;
+                   }
+               );
+           }
+       
+           // including ability to add request to a picklist
+           // and to "reject" it (aka apply a cancel reason)
+       
+           dojo.byId("acq-ur-cancel-reason").innerHTML = '';
+           var widget = new openils.widget.AutoFieldWidget({
+               "fmField": "cancel_reason",
+               "fmClass": "aur",
+               "parentNode": dojo.byId("acq-ur-cancel-reason"),
+               "orgLimitPerms": ["CREATE_PURCHASE_REQUEST"],
+               "forceSync": true
+           });
+       
+           widget.build(
+               function(w, ww) {
+                   acqUrCancelReasonSubmit.onClick = function() {
+                       if (w.attr("value")) {
+                           if (confirm( localeStrings.UR_CANCEL_CONFIRM )) {
+                               fieldmapper.standardRequest(
+                                   [ 'open-ils.acq', 'open-ils.acq.user_request.cancel.batch' ],
+                                   {   async: true,
+                                       params: [openils_User.authtoken, [reqId], w.attr("value")],
+                                       oncomplete: function(r) {
+                                           location.href = location.href; // kludge to reload the interface
+                                       }
+                                   }
+                               );
+                           }
+                       }
+                   };
+               }
+           );
+       }
+       
+       function fooPicklist() {
+           if (aur_obj.lineitem()) {
+               viewPicklist();
+           } else {
+               addToPicklist();
+           }
+       }
+       
+       function viewPicklist() {
+           var lineitem = fieldmapper.standardRequest(
+               [ 'open-ils.acq', 'open-ils.acq.lineitem.retrieve.authoritative' ],
+               {
+                   params: [openils_User.authtoken, aur_obj.lineitem()]
+               }
+           );
+           location.href = oilsBasePath + "/acq/picklist/view/" + lineitem.picklist();
+       }
+       
+       function addToPicklist() {
+           // reqId, from detail view
+           location.href = oilsBasePath + "/acq/picklist/brief_record?ur=" + reqId + "&prepop=" + encodeURIComponent(js2JSON({
+               "1": aur_obj.title() || aur_obj.article_title() || aur_obj.volume(),
+               "2": aur_obj.author(),
+               "5": aur_obj.isxn(),
+               "9": aur_obj.publisher(),
+               "10": aur_obj.pubdate()
+           }));
+       }
+       
+       function setNoHold() {
+           // reqId, from detail view
+           fieldmapper.standardRequest(
+               [ 'open-ils.acq', 'open-ils.acq.user_request.set_no_hold.batch' ],
+               {   async: true,
+                   params: [openils_User.authtoken, [reqId]],
+                   oncomplete: function(r) {
+                       location.href = location.href; // kludge to reload the interface
+                   }
+               }
+           );
+       }
+       
+       // format the title data as id:title
+       function getTitle(idx, item) {
+           if(item) {
+               return this.grid.store.getValue(item, 'id') + ':' + 
+               this.grid.store.getValue(item, 'title');
+           }
+           return '';
+       }
+       
+       // turn id:title into a url
+       function formatTitle(value) {
+           if(value) {
+               var parts = value.split(/:/);
+               return '<a href="' + oilsBasePath + 
+                   '/acq/picklist/user_request/' + parts[0] + '">' + parts[1] + '</a>';
+           }
+       }
+       
+       function drawList() {
+           buildGrid();
+       
+           var connect = function() {
+               dojo.connect(contextOrgSelector, 'onChange',
+                   function() {
+                       contextOrg = this.attr('value');
+                       rGrid.resetStore();
+                       buildGrid();
+                   }
+               );
+           };
+       
+           new openils_User().buildPermOrgSelector(
+               'CREATE_PICKLIST', contextOrgSelector, null, connect);
+       }
+       
+       function buildGrid() {
+       
+           if(contextOrg == null)
+               contextOrg = openils_User.user.ws_ou();
+       
+           var query = {
+               cancel_reason : null,
+               '+au' : {
+                   home_ou : fieldmapper.aou.descendantNodeList(contextOrg).map(
+                   function(item) { return item.id(); })
+               }
+           };
+       
+           if (contextUsr) {
+               delete query['+au']['home_ou'];
+               query['+au']['id'] = contextUsr;
+           }
+       
+           if (contextEg_bib) {
+               query['eg_bib'] = contextEg_bib;
+           }
+       
+           if (contextLI) {
+               query['lineitem'] = contextLI;
+           }
+       
+           rGrid.resetStore();
+           rGrid.loadAll(
+               {   order_by : {aur : 'request_date'},
+                   join : 'au' 
+               },
+               query
+           );
+       }
+       
+       function changeBib(value) {
+           contextEg_bib = value;
+           rGrid.overrideEditWidgets.eg_bib = new dijit.form.TextBox({"disabled": true});
+           rGrid.overrideEditWidgets.eg_bib.shove = { create : contextEg_bib };
+       }
+       
+       function changeLI(value,display_value) {
+           contextLI = value;
+           contextLITextbox.setValue( contextLI );
+           contextLITextbox.setDisplayedValue( display_value || contextLI );
+           rGrid.overrideEditWidgets.lineitem = new dijit.form.TextBox({"disabled": true});
+           rGrid.overrideEditWidgets.lineitem.shove = { create : contextLI };
+       }
+       
+       function changeLIPrompt() {
+           var lineitem = window.prompt(localeStrings.UR_FILTER_LINEITEM);
+           if(lineitem != '' && (lineitem == null || Number(lineitem) == NaN)) {
+               return;
+           }
+           changeLI(lineitem);
+           buildGrid();
+       }
+       
+       function changeUser(value,display_value) {
+           contextUsr = value;
+           contextUsrTextbox.setValue( contextUsr );
+           contextUsrTextbox.setDisplayedValue( display_value || contextUsr );
+           rGrid.overrideEditWidgets.usr = new dijit.form.TextBox({"disabled": true});
+           rGrid.overrideEditWidgets.usr.shove = { create : contextUsr };
+       }
+       
+       function changeUserPrompt() {
+           var barcode = window.prompt(localeStrings.UR_FILTER_USER);
+           if(barcode == null) {
+               return;
+           }
+           if(typeof xulG != 'undefined' && xulG.get_barcode) {
+               // We have a "complete the barcode" function, call it (actor = users only)
+               var new_barcode = xulG.get_barcode(window, 'actor', barcode);
+               // If we got a result (boolean false is "no result") check it
+               if(new_barcode) {
+                   // user_false string means they picked "None of the above"
+                   // Abort before any other events can fire
+                   if(new_barcode == "user_false") return;
+                   // No error means we have a (hopefully valid) completed barcode to use.
+                   // Otherwise, fall through to other methods of checking
+                   if(typeof new_barcode.ilsevent == 'undefined')
+                       barcode = new_barcode.barcode;
+               }
+           }
+           if (barcode == '') {
+               contextUsrObj = null;
+               changeUser('','');
+           } else {
+               var usr_obj = fieldmapper.standardRequest(
+                   [
+                       'open-ils.actor',
+                       'open-ils.actor.user.fleshed.retrieve_by_barcode.authoritative'
+                   ],
+                   {
+                       params: [openils_User.authtoken, barcode]
+                   }
+               );
+               if (typeof usr_obj.textcode != 'undefined') {
+                   alert(usr_obj.textcode + ' : ' + usr_obj.desc);
+                   return;
+               } else {
+                   contextUsrObj = usr_obj;
+                   changeUser(usr_obj.id(),usr_obj.card().barcode());
+               }
+           }
+           buildGrid();
+       }
+       
+       function createRequest() {
+           if (!contextUsr) {
+               changeUserPrompt();
+           }
+           if (contextUsr) {
+               rGrid.overrideEditWidgets.pickup_lib = new dijit.form.TextBox({"disabled": true});
+               rGrid.overrideEditWidgets.pickup_lib.shove = { create : contextUsrObj.home_ou() };
+               rGrid.showCreateDialog();
+           }
+       }
+       
+       openils_Util.addOnLoad(setup);
+       
+       
+       
+
+});
\ No newline at end of file
index 7cbc0c5..f631d35 100644 (file)
-dojo.require('dojo.date.stamp');
-dojo.require('dojo.date.locale');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require('dijit.layout.ContentPane');
-
-var plist;
-var plOffset = 0;
-var plLimit = 20;
-var liTable;
-
-
-function load() {
-    liTable = new AcqLiTable();
-    liTable.isPL = plId;
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
-        {   async: true,
-            params: [openils.User.authtoken, plId, 
-                {flesh_lineitem_count:true, flesh_owner:true}],
-            oncomplete: function(r) {
-                plist = openils.Util.readResponse(r);
-                drawPl(plist);
-            }
-        }
-    );
-
-}
-
-function drawPl() {
-
-    dojo.byId("oils-acq-picklist-name").innerHTML = plist.name();
-    dojo.byId("oils-acq-picklist-attr-owner").innerHTML = plist.owner().usrname();
-    dojo.byId("oils-acq-picklist-attr-count").innerHTML = plist.entry_count();
-
-    dojo.byId("oils-acq-picklist-attr-cdate").innerHTML =
-         dojo.date.locale.format(
-            dojo.date.stamp.fromISOString(plist.create_time()), 
-            {selector:'date'}
-        );
-
-    dojo.byId("oils-acq-picklist-attr-edate").innerHTML = 
-         dojo.date.locale.format(
-            dojo.date.stamp.fromISOString(plist.edit_time()), 
-            {selector:'date'}
-        );
-
-    loadLIs();
-}
-
-function loadLIs() {
-    liTable.reset();
-
-    if(plist.entry_count() > (plOffset + plLimit)) {
-        liTable.setNext(
-            function() { 
-                plOffset += plLimit;
-                loadLIs();
-            }
-        );
-    } else {
-        liTable.setNext(null);
-    }
-
-    if(plOffset > 0) {
-        liTable.setPrev(
-            function() { 
-                plOffset -= plLimit;
-                loadLIs();
-            }
-        );
-    } else {
-        liTable.setPrev(null);
-    }
-
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem.picklist.retrieve'],
-        {   async: true,
-            params: [openils.User.authtoken, plId, 
-                {flesh_notes:true, flesh_cancel_reason:true, flesh_attrs:true, clear_marc:true, offset:plOffset, limit:plLimit}],
-            onresponse: function(r) {
-                var li = openils.Util.readResponse(r);
-                if (li) { /* Not every response is an LI (for some reason) */
-                    liTable.addLineitem(li);
-                    liTable.show('list');
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(load);
-
-
+require([
+       "dojo/date/stamp",
+       "dojo/date/locale",
+       "openils/User",
+       "openils/Util",
+       "dijit/layout/ContentPane"
+       ],
+function(dojo_date_stamp,
+       dojo_date_locale,
+       openils_User,
+       openils_Util,
+       dijit_layout_ContentPane){
+       
+       var plist;
+       var plOffset = 0;
+       var plLimit = 20;
+       var liTable;
+       
+       
+       function load() {
+           liTable = new AcqLiTable();
+           liTable.isPL = plId;
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
+               {   async: true,
+                   params: [openils_User.authtoken, plId, 
+                       {flesh_lineitem_count:true, flesh_owner:true}],
+                   oncomplete: function(r) {
+                       plist = openils_Util.readResponse(r);
+                       drawPl(plist);
+                   }
+               }
+           );
+       
+       }
+       
+       function drawPl() {
+       
+           dojo.byId("oils-acq-picklist-name").innerHTML = plist.name();
+           dojo.byId("oils-acq-picklist-attr-owner").innerHTML = plist.owner().usrname();
+           dojo.byId("oils-acq-picklist-attr-count").innerHTML = plist.entry_count();
+       
+           dojo.byId("oils-acq-picklist-attr-cdate").innerHTML =
+                dojo_date_locale.format(
+                   dojo_date_stamp.fromISOString(plist.create_time()), 
+                   {selector:'date'}
+               );
+       
+           dojo.byId("oils-acq-picklist-attr-edate").innerHTML = 
+                dojo_date_locale.format(
+                   dojo_date_stamp.fromISOString(plist.edit_time()), 
+                   {selector:'date'}
+               );
+       
+           loadLIs();
+       }
+       
+       function loadLIs() {
+           liTable.reset();
+       
+           if(plist.entry_count() > (plOffset + plLimit)) {
+               liTable.setNext(
+                   function() { 
+                       plOffset += plLimit;
+                       loadLIs();
+                   }
+               );
+           } else {
+               liTable.setNext(null);
+           }
+       
+           if(plOffset > 0) {
+               liTable.setPrev(
+                   function() { 
+                       plOffset -= plLimit;
+                       loadLIs();
+                   }
+               );
+           } else {
+               liTable.setPrev(null);
+           }
+       
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem.picklist.retrieve'],
+               {   async: true,
+                   params: [openils_User.authtoken, plId, 
+                       {flesh_notes:true, flesh_cancel_reason:true, flesh_attrs:true, clear_marc:true, offset:plOffset, limit:plLimit}],
+                   onresponse: function(r) {
+                       var li = openils_Util.readResponse(r);
+                       if (li) { /* Not every response is an LI (for some reason) */
+                           liTable.addLineitem(li);
+                           liTable.show('list');
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(load);
+       
+       
+       
+
+});
\ No newline at end of file
index 17b2e92..6ba5d63 100644 (file)
-//dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.Dialog');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.Button');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('openils.acq.Picklist');
-dojo.require('openils.Util');
-dojo.require('openils.widget.ProgressDialog');
-
-var listAll = false;
-var plCache = {};
-
-function loadGrid() {
-    dojo.connect(plMergeDialog, 'onOpen', function(){loadLeadPlSelector();});
-    plListGrid.dataLoader = gridDataLoader;    
-    gridDataLoader();    
-}
-
-function gridDataLoader() {
-
-    var method = 'open-ils.acq.picklist.user.retrieve';
-    if(listAll)
-        method = method.replace(/user/, 'user.all');
-
-    plListGrid.showLoadProgressIndicator();
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', method],
-        {   async: true,
-            params: [
-                openils.User.authtoken, 
-                {
-                    flesh_lineitem_count:1, 
-                    flesh_owner:1,
-                    offset : plListGrid.displayOffset,
-                    limit : plListGrid.displayLimit,
-                }
-            ],
-            onresponse : function(r) {
-                var pl = openils.Util.readResponse(r);
-                if(pl) {
-                    plCache[pl.id()] = pl;
-                    plListGrid.store.newItem(acqpl.toStoreItem(pl));
-                }
-            }, 
-            oncomplete : function() {
-                plListGrid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-function getOwnerName(rowIndex, item) {
-    if(!item) return ''; 
-    var id= this.grid.store.getValue(item, 'id'); 
-    var pl = plCache[id];
-    return pl.owner().usrname();
-}
-
-function createPL(fields) {
-    if(fields.name == '') return;
-
-    openils.acq.Picklist.create(fields,
-
-        function(plId) {
-            fieldmapper.standardRequest(
-
-                ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
-                {   async: true,
-                    params: [openils.User.authtoken, plId,
-                        {flesh_lineitem_count:1, flesh_owner:1}],
-
-                    oncomplete: function(r) {
-                        if(pl = openils.Util.readResponse(r)) {
-                           plCache[pl.id()] = pl;
-                           plListGrid.store.newItem(acqpl.toStoreData([pl]).items[0]);
-                        }
-                    }
-                }
-            );
-        }
-    );
-}
-
-function getDateTimeField(rowIndex, item) {
-    if(!item) return '';
-    var data = this.grid.store.getValue(item, this.field);
-    var date = dojo.date.stamp.fromISOString(data);
-    return dojo.date.locale.format(date, {formatLength:'short'});
-}
-function deleteFromGrid() {
-    progressDialog.show(true);
-    var list = [];
-    dojo.forEach(
-        plListGrid.getSelectedItems(), 
-        function(item) {
-            list.push(plListGrid.store.getValue(item, 'id'));
-            plListGrid.store.deleteItem(item);
-        }
-    );
-    openils.acq.Picklist.deleteList(list, function(){progressDialog.hide();});
-}
-
-function cloneSelectedPl(fields) {
-
-    var item = plListGrid.getSelectedItems()[0];
-    if(!item) return;
-
-    var plId = plListGrid.store.getValue(item, 'id');
-    var entryCount = Number(plListGrid.store.getValue(item, 'entry_count'));
-
-    progressDialog.show();
-    progressDialog.update({maximum:entryCount, progress:0});
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.picklist.clone'],
-        {   async: true,
-            params: [openils.User.authtoken, plId, fields.name],
-
-            onresponse : function(r) {
-                var resp = openils.Util.readResponse(r);
-                if(!resp) return;
-                progressDialog.update({progress:resp.li});
-
-                if(resp.complete) {
-                    progressDialog.hide();
-                    var pl = resp.picklist;
-                    plCache[pl.id()] = pl;
-                    pl.owner(openils.User.user);
-                    pl.entry_count(entryCount);
-                    plListGrid.store.newItem(fieldmapper.acqpl.toStoreItem(pl));
-                }
-            }
-        }
-    );
-}
-
-function loadLeadPlSelector() {
-    var store = new dojo.data.ItemFileWriteStore({data:acqpl.initStoreData()}); 
-    dojo.forEach(
-        plListGrid.getSelectedItems(),
-        function(item) { 
-            var pl = plCache[plListGrid.store.getValue(item, 'id')];
-            store.newItem(fieldmapper.acqpl.toStoreItem(pl));
-        }
-    );
-    plMergeLeadSelector.store = store;
-    plMergeLeadSelector.startup();
-}
-
-function mergeSelectedPl(fields) {
-    if(!fields.lead) return;
-
-    var ids = [];
-    var totalLi = 0;
-    var leadPl = plCache[fields.lead];
-    var leadPlItem;
-
-    dojo.forEach(
-        plListGrid.getSelectedItems(),
-        function(item) { 
-            var id = plListGrid.store.getValue(item, 'id');
-            if(id == fields.lead) {
-                leadPlItem = item;
-                return;
-            }
-            totalLi +=  new Number(plListGrid.store.getValue(item, 'entry_count'));
-            ids.push(id);
-        }
-    );
-
-    progressDialog.show();
-    progressDialog.update({maximum:totalLi, progress:0});
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.picklist.merge'],
-        {   async: true,
-            params: [openils.User.authtoken, fields.lead, ids],
-            onresponse : function(r) {
-                var resp = openils.Util.readResponse(r);
-                if(!resp) return;
-                progressDialog.update({progress:resp.li});
-
-                if(resp.complete) {
-                    progressDialog.hide();
-                    leadPl.entry_count( leadPl.entry_count() + totalLi );
-                    plListGrid.store.setValue(leadPlItem, 'entry_count', leadPl.entry_count());
-
-                    // remove the deleted lists from the grid
-                    dojo.forEach(
-                        plListGrid.getSelectedItems(),
-                        function(item) { 
-                            var id = plListGrid.store.getValue(item, 'id');
-                            if(id != fields.lead)
-                                plListGrid.store.deleteItem(item);
-                        }
-                    );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(loadGrid);
-
-
+require([
+       "dojox/grid/DataGrid",  //dojo.require('dojox.grid.DataGrid');
+       "openils/widget/AutoGrid",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/Dialog",
+       "dijit/form/Button",
+       "dijit/form/TextBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Button",
+       "dojox/grid/cells/dijit",
+       "openils/acq/Picklist",
+       "openils/Util",
+       "openils/widget/ProgressDialog"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojo_data_ItemFileWriteStore,
+       dijit_Dialog,
+       dijit_form_Button,
+       dijit_form_TextBox,
+       dijit_form_FilteringSelect,
+       dijit_form_Button,
+       dojox_grid_cells_dijit,
+       openils_acq_Picklist,
+       openils_Util,
+       openils_widget_ProgressDialog){
+       
+       var listAll = false;
+       var plCache = {};
+       
+       function loadGrid() {
+           dojo.connect(plMergeDialog, 'onOpen', function(){loadLeadPlSelector();});
+           plListGrid.dataLoader = gridDataLoader;    
+           gridDataLoader();    
+       }
+       
+       function gridDataLoader() {
+       
+           var method = 'open-ils.acq.picklist.user.retrieve';
+           if(listAll)
+               method = method.replace(/user/, 'user.all');
+       
+           plListGrid.showLoadProgressIndicator();
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', method],
+               {   async: true,
+                   params: [
+                       openils.User.authtoken, 
+                       {
+                           flesh_lineitem_count:1, 
+                           flesh_owner:1,
+                           offset : plListGrid.displayOffset,
+                           limit : plListGrid.displayLimit,
+                       }
+                   ],
+                   onresponse : function(r) {
+                       var pl = openils_Util.readResponse(r);
+                       if(pl) {
+                           plCache[pl.id()] = pl;
+                           plListGrid.store.newItem(acqpl.toStoreItem(pl));
+                       }
+                   }, 
+                   oncomplete : function() {
+                       plListGrid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       function getOwnerName(rowIndex, item) {
+           if(!item) return ''; 
+           var id= this.grid.store.getValue(item, 'id'); 
+           var pl = plCache[id];
+           return pl.owner().usrname();
+       }
+       
+       function createPL(fields) {
+           if(fields.name == '') return;
+       
+           openils_acq_Picklist.create(fields,
+       
+               function(plId) {
+                   fieldmapper.standardRequest(
+       
+                       ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
+                       {   async: true,
+                           params: [openils.User.authtoken, plId,
+                               {flesh_lineitem_count:1, flesh_owner:1}],
+       
+                           oncomplete: function(r) {
+                               if(pl = openils_Util.readResponse(r)) {
+                                  plCache[pl.id()] = pl;
+                                  plListGrid.store.newItem(acqpl.toStoreData([pl]).items[0]);
+                               }
+                           }
+                       }
+                   );
+               }
+           );
+       }
+       
+       function getDateTimeField(rowIndex, item) {
+           if(!item) return '';
+           var data = this.grid.store.getValue(item, this.field);
+           var date = dojo.date.stamp.fromISOString(data);
+           return dojo.date.locale.format(date, {formatLength:'short'});
+       }
+       function deleteFromGrid() {
+           progressDialog.show(true);
+           var list = [];
+           dojo.forEach(
+               plListGrid.getSelectedItems(), 
+               function(item) {
+                   list.push(plListGrid.store.getValue(item, 'id'));
+                   plListGrid.store.deleteItem(item);
+               }
+           );
+           openils_acq_Picklist.deleteList(list, function(){progressDialog.hide();});
+       }
+       
+       function cloneSelectedPl(fields) {
+       
+           var item = plListGrid.getSelectedItems()[0];
+           if(!item) return;
+       
+           var plId = plListGrid.store.getValue(item, 'id');
+           var entryCount = Number(plListGrid.store.getValue(item, 'entry_count'));
+       
+           progressDialog.show();
+           progressDialog.update({maximum:entryCount, progress:0});
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.picklist.clone'],
+               {   async: true,
+                   params: [openils.User.authtoken, plId, fields.name],
+       
+                   onresponse : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       if(!resp) return;
+                       progressDialog.update({progress:resp.li});
+       
+                       if(resp.complete) {
+                           progressDialog.hide();
+                           var pl = resp.picklist;
+                           plCache[pl.id()] = pl;
+                           pl.owner(openils.User.user);
+                           pl.entry_count(entryCount);
+                           plListGrid.store.newItem(fieldmapper.acqpl.toStoreItem(pl));
+                       }
+                   }
+               }
+           );
+       }
+       
+       function loadLeadPlSelector() {
+           var store = new dojo_data_ItemFileWriteStore({data:acqpl.initStoreData()}); 
+           dojo.forEach(
+               plListGrid.getSelectedItems(),
+               function(item) { 
+                   var pl = plCache[plListGrid.store.getValue(item, 'id')];
+                   store.newItem(fieldmapper.acqpl.toStoreItem(pl));
+               }
+           );
+           plMergeLeadSelector.store = store;
+           plMergeLeadSelector.startup();
+       }
+       
+       function mergeSelectedPl(fields) {
+           if(!fields.lead) return;
+       
+           var ids = [];
+           var totalLi = 0;
+           var leadPl = plCache[fields.lead];
+           var leadPlItem;
+       
+           dojo.forEach(
+               plListGrid.getSelectedItems(),
+               function(item) { 
+                   var id = plListGrid.store.getValue(item, 'id');
+                   if(id == fields.lead) {
+                       leadPlItem = item;
+                       return;
+                   }
+                   totalLi +=  new Number(plListGrid.store.getValue(item, 'entry_count'));
+                   ids.push(id);
+               }
+           );
+       
+           progressDialog.show();
+           progressDialog.update({maximum:totalLi, progress:0});
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.picklist.merge'],
+               {   async: true,
+                   params: [openils.User.authtoken, fields.lead, ids],
+                   onresponse : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       if(!resp) return;
+                       progressDialog.update({progress:resp.li});
+       
+                       if(resp.complete) {
+                           progressDialog.hide();
+                           leadPl.entry_count( leadPl.entry_count() + totalLi );
+                           plListGrid.store.setValue(leadPlItem, 'entry_count', leadPl.entry_count());
+       
+                           // remove the deleted lists from the grid
+                           dojo.forEach(
+                               plListGrid.getSelectedItems(),
+                               function(item) { 
+                                   var id = plListGrid.store.getValue(item, 'id');
+                                   if(id != fields.lead)
+                                       plListGrid.store.deleteItem(item);
+                               }
+                           );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(loadGrid);
+       
+       
+       
+
+});
\ No newline at end of file
index 181fc11..5b1c968 100644 (file)
@@ -1,58 +1,65 @@
-dojo.require("openils.widget.EditDialog");
-dojo.require("openils.widget.EditPane");
+require([
+       "openils/widget/EditDialog",
+       "openils/widget/EditPane"
+       ],
+function(openils_widget_EditDialog,
+       openils_widget_EditPane){
+       
+       var editDialog;
+       
+       function toPoListing() {
+           location.href = oilsBasePath + "/acq/search/unified?ca=po";
+       }
+       
+       function toOnePo(id) {
+           location.href = oilsBasePath + "/acq/po/view/" + id;
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               editDialog = new openils_widget_EditDialog({
+                   "editPane": new openils_widget_EditPane({
+                       "fmObject": new acqpo(),
+                       /* After realizing how many fields should be excluded from this
+                        * interface because users shouldn't set them arbitrarily,
+                        * it hardly seems like using these Edit widgets gives much
+                        * much advantage over a hardcoded interface. */
+                       "suppressFields": [
+                           "create_time", "edit_time", "editor", "order_date",
+                           "owner", "cancel_reason", "creator", "state", "name"
+                       ],
+                       "fieldOrder": ["ordering_agency", "provider"],
+                       "mode": "create",
+                       overrideWidgetArgs : {
+                           provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
+                           ordering_agency : { orgDefaultsToWs : true }
+                       },
+                       "onSubmit": function(po) {
+                           fieldmapper.standardRequest(
+                               ["open-ils.acq", "open-ils.acq.purchase_order.create"],{
+                                   "async": false,
+                                   "params": [openils.User.authtoken, po],
+                                   "onresponse": function(r) {
+                                       toOnePo(
+                                           openils.Util.readResponse(r).
+                                           purchase_order.id()
+                                       );
+                                   }
+                               }
+                           );
+                       },
+                       "onCancel": function() {
+                           editDialog.hide();
+                           toPoListing();
+                           /* I'd rather do window.close() or xulG.close_tab(),
+                            * but neither of those seem to work here. */
+                       }
+                   })
+               });
+               editDialog.startup();
+               editDialog.show();
+           }
+       );
+       
 
-var editDialog;
-
-function toPoListing() {
-    location.href = oilsBasePath + "/acq/search/unified?ca=po";
-}
-
-function toOnePo(id) {
-    location.href = oilsBasePath + "/acq/po/view/" + id;
-}
-
-openils.Util.addOnLoad(
-    function() {
-        editDialog = new openils.widget.EditDialog({
-            "editPane": new openils.widget.EditPane({
-                "fmObject": new acqpo(),
-                /* After realizing how many fields should be excluded from this
-                 * interface because users shouldn't set them arbitrarily,
-                 * it hardly seems like using these Edit widgets gives much
-                 * much advantage over a hardcoded interface. */
-                "suppressFields": [
-                    "create_time", "edit_time", "editor", "order_date",
-                    "owner", "cancel_reason", "creator", "state", "name"
-                ],
-                "fieldOrder": ["ordering_agency", "provider"],
-                "mode": "create",
-                overrideWidgetArgs : {
-                    provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
-                    ordering_agency : { orgDefaultsToWs : true }
-                },
-                "onSubmit": function(po) {
-                    fieldmapper.standardRequest(
-                        ["open-ils.acq", "open-ils.acq.purchase_order.create"],{
-                            "async": false,
-                            "params": [openils.User.authtoken, po],
-                            "onresponse": function(r) {
-                                toOnePo(
-                                    openils.Util.readResponse(r).
-                                    purchase_order.id()
-                                );
-                            }
-                        }
-                    );
-                },
-                "onCancel": function() {
-                    editDialog.hide();
-                    toPoListing();
-                    /* I'd rather do window.close() or xulG.close_tab(),
-                     * but neither of those seem to work here. */
-                }
-            })
-        });
-        editDialog.startup();
-        editDialog.show();
-    }
-);
+});
\ No newline at end of file
index aaddfc5..55714e8 100644 (file)
-dojo.require('openils.widget.AutoGrid');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('dijit.form.DateTextBox');
-dojo.require('dojo.date.stamp');
-
-var eventState;
-var eventContextOrg;
-var eventList;
-var eventStartDateRange;
-var eventEndDateRange;
-var po_map = {};
-
-function eventInit() {
-    try {
-        buildStateSelector();
-        buildOrgSelector();
-        buildDatePickers();
-        buildEventGrid();
-
-        eventGrid.cancelSelected = function() { doSelected('open-ils.acq.purchase_order.event.cancel.batch') };
-        eventGrid.resetSelected = function() { doSelected('open-ils.acq.purchase_order.event.reset.batch') };
-        eventGrid.doSearch = function() {
-            buildEventGrid();
-        }
-
-    } catch(E) {
-        //dump('Error in acq/events.js, eventInit(): ' + E);
-        throw(E);
-    }
-}
-
-function buildDatePickers() {
-    var today = new Date(); 
-    var yesterday = new Date( today.getFullYear(), today.getMonth(), today.getDate() - 1);
-    eventStartDatePicker.constraints.max = today;
-    eventStartDatePicker.attr( 'value', yesterday );
-    eventStartDateRange = eventStartDatePicker.attr('value');
-    eventEndDatePicker.constraints.max = today;
-    eventEndDatePicker.attr( 'value', today );
-    eventEndDateRange = eventEndDatePicker.attr('value');
-    dojo.connect(
-        eventStartDatePicker,
-        'onChange',
-        function() {
-            var new_date = arguments[0];
-            if (new_date > eventEndDatePicker.attr('value')) {
-                var swap = eventEndDatePicker.attr('value');
-                eventEndDatePicker.attr( 'value', new_date );
-                this.attr( 'value', swap );
-            }
-            eventStartDateRange = this.attr('value');
-        }
-    );
-    dojo.connect(
-        eventEndDatePicker,
-        'onChange',
-        function() {
-            var new_date = arguments[0];
-            if (new_date < eventStartDatePicker.attr('value')) {
-                var swap = eventStartDatePicker.attr('value');
-                eventStartDatePicker.attr( 'value', new_date );
-                this.attr( 'value', swap );
-            }
-            eventEndDateRange = this.attr('value');
-        }
-    );
-
-}
-
-function buildStateSelector() {
-    try {
-        eventStateSelect.store = new dojo.data.ItemFileReadStore({
-            data : {
-                identifier:"value",
-                label: "name",
-                items: [
-                    /* FIXME: I18N? */
-                    {name:"Pending", value:'pending'},
-                    {name:"Complete", value:'complete'},
-                    {name:"Error", value:'error'}
-                ]
-            }
-        });
-        eventStateSelect.attr( 'value','pending' );
-        dojo.connect(
-            eventStateSelect, 
-            'onChange',
-            function() {
-                try {
-                     eventState = this.attr('value');
-                } catch(E) {
-                    //dump('Error in acq/events.js, eventInit, connect, onChange: ' + E);
-                    throw(E);
-                }
-            }
-        );
-
-    } catch(E) {
-        //dump('Error in acq/events.js, buildStateSelector(): ' + E);
-        throw(E);
-    }
-}
-
-function buildOrgSelector() {
-    try {
-        var connect = function() {
-            try {
-                dojo.connect(
-                    eventContextOrgSelect, 
-                    'onChange',
-                    function() {
-                        try {
-                             eventContextOrg = this.attr('value');
-                        } catch(E) {
-                            //dump('Error in acq/events.js, eventInit, connect, onChange: ' + E);
-                            throw(E);
-                        }
-                    }
-                );
-            } catch(E) {
-                //dump('Error in acq/events.js, eventInit, connect: ' + E);
-                throw(E);
-            }
-        };
-        new openils.User().buildPermOrgSelector('STAFF_LOGIN', eventContextOrgSelect, null, connect);
-
-    } catch(E) {
-        //dump('Error in acq/events.js, buildOrgSelector(): ' + E);
-        throw(E);
-    }
-}
-
-function doSelected(method) {
-    try {
-        var ids = [];
-        dojo.forEach(
-            eventGrid.getSelectedItems(),
-            function(item) {
-                ids.push( eventGrid.store.getValue(item,'id') );
-            }
-        );
-        fieldmapper.standardRequest(
-            [ 'open-ils.acq', method ],
-            {   async: true,
-                params: [openils.User.authtoken, ids],
-                onresponse: function(r) {
-                    try {
-                        var result = openils.Util.readResponse(r);
-                        if (typeof result.ilsevent != 'undefined') { throw(result); }
-                    } catch(E) {
-                        //dump('Error in acq/events.js, doSelected(), onresponse(): ' + E);
-                        throw(E);
-                    }
-                },
-                onerror: function(r) {
-                    try {
-                        var result = openils.Util.readResponse(r);
-                        throw(result);
-                    } catch(E) {
-                        //dump('Error in acq/events.js, doSelected(), onerror(): ' + E);
-                        throw(E);
-                    }
-                },
-                oncomplete: function(r) {
-                    try {
-                        var result = openils.Util.readResponse(r);
-                        buildEventGrid();
-                    } catch(E) {
-                        //dump('Error in acq/events.js, doSelected(), oncomplete(): ' + E);
-                        throw(E);
-                    }
-                }
-            }
-        );
-    } catch(E) {
-        //dump('Error in acq/events.js, doSelected(): ' + E);
-        throw(E);
-    }
-}
-
-function buildEventGrid() {
-    eventGrid.resetStore();
-    if(eventContextOrg == null) {
-        eventContextOrg = openils.User.user.ws_ou();
-    }
-    if(eventState == null) {
-        eventState = 'pending';
-    }
-    var filter = {"state":eventState, "order_by":[{"class":"atev", "field":"run_time", "direction":"desc"}]};
-    if(eventStartDateRange != null) {
-        /* the dijit appears to always provide 00:00:00 for the timestamp component */
-        var end_of_day = eventEndDateRange; end_of_day.setDate( end_of_day.getDate() + 1 ); 
-        filter['start_time'] = {
-            'between' : [
-                dojo.date.stamp.toISOString( eventStartDateRange ),
-                dojo.date.stamp.toISOString( end_of_day )
-            ]
-        }
-    }
-    po_map = {};
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.purchase_order.events.ordering_agency'],
-        {   async: true,
-            params: [openils.User.authtoken, eventContextOrg, filter],
-            onresponse: function(r) {
-                try {
-                    if(eventObject = openils.Util.readResponse(r)) {
-                        po_map[ eventObject.target().id() ] = eventObject.target();
-                        eventObject.target( eventObject.target().id() );
-                        eventGrid.store.newItem(atev.toStoreItem(eventObject));
-                    }
-                } catch(E) {
-                    //dump('Error in acq/events.js, buildEventGrid, onresponse: ' + E);
-                    throw(E);
-                }
-            }
-        }
-    );
-}
-
-function format_po_link(value) {
-    if (value) {
-        // FIXME -- how do you escape the value from .name() ?
-        return '<a href="' + oilsBasePath + '/acq/po/view/' + value + '">' + po_map[ value ].name() + '</a>';
-    }
-}
-
-openils.Util.addOnLoad(eventInit);
-
-
+require([
+       "openils/widget/AutoGrid",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect",
+       "dijit/form/DateTextBox",
+       "dojo/date/stamp"
+       ],
+function(openils_widget_AutoGrid,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect,
+       dijit_form_DateTextBox,
+       dojo_date_stamp){
+       
+       var eventState;
+       var eventContextOrg;
+       var eventList;
+       var eventStartDateRange;
+       var eventEndDateRange;
+       var po_map = {};
+       
+       function eventInit() {
+           try {
+               buildStateSelector();
+               buildOrgSelector();
+               buildDatePickers();
+               buildEventGrid();
+       
+               eventGrid.cancelSelected = function() { doSelected('open-ils.acq.purchase_order.event.cancel.batch') };
+               eventGrid.resetSelected = function() { doSelected('open-ils.acq.purchase_order.event.reset.batch') };
+               eventGrid.doSearch = function() {
+                   buildEventGrid();
+               }
+       
+           } catch(E) {
+               //dump('Error in acq/events.js, eventInit(): ' + E);
+               throw(E);
+           }
+       }
+       
+       function buildDatePickers() {
+           var today = new Date(); 
+           var yesterday = new Date( today.getFullYear(), today.getMonth(), today.getDate() - 1);
+           eventStartDatePicker.constraints.max = today;
+           eventStartDatePicker.attr( 'value', yesterday );
+           eventStartDateRange = eventStartDatePicker.attr('value');
+           eventEndDatePicker.constraints.max = today;
+           eventEndDatePicker.attr( 'value', today );
+           eventEndDateRange = eventEndDatePicker.attr('value');
+           dojo.connect(
+               eventStartDatePicker,
+               'onChange',
+               function() {
+                   var new_date = arguments[0];
+                   if (new_date > eventEndDatePicker.attr('value')) {
+                       var swap = eventEndDatePicker.attr('value');
+                       eventEndDatePicker.attr( 'value', new_date );
+                       this.attr( 'value', swap );
+                   }
+                   eventStartDateRange = this.attr('value');
+               }
+           );
+           dojo.connect(
+               eventEndDatePicker,
+               'onChange',
+               function() {
+                   var new_date = arguments[0];
+                   if (new_date < eventStartDatePicker.attr('value')) {
+                       var swap = eventStartDatePicker.attr('value');
+                       eventStartDatePicker.attr( 'value', new_date );
+                       this.attr( 'value', swap );
+                   }
+                   eventEndDateRange = this.attr('value');
+               }
+           );
+       
+       }
+       
+       function buildStateSelector() {
+           try {
+               eventStateSelect.store = new dojo.data.ItemFileReadStore({
+                   data : {
+                       identifier:"value",
+                       label: "name",
+                       items: [
+                           /* FIXME: I18N? */
+                           {name:"Pending", value:'pending'},
+                           {name:"Complete", value:'complete'},
+                           {name:"Error", value:'error'}
+                       ]
+                   }
+               });
+               eventStateSelect.attr( 'value','pending' );
+               dojo.connect(
+                   eventStateSelect, 
+                   'onChange',
+                   function() {
+                       try {
+                            eventState = this.attr('value');
+                       } catch(E) {
+                           //dump('Error in acq/events.js, eventInit, connect, onChange: ' + E);
+                           throw(E);
+                       }
+                   }
+               );
+       
+           } catch(E) {
+               //dump('Error in acq/events.js, buildStateSelector(): ' + E);
+               throw(E);
+           }
+       }
+       
+       function buildOrgSelector() {
+           try {
+               var connect = function() {
+                   try {
+                       dojo.connect(
+                           eventContextOrgSelect, 
+                           'onChange',
+                           function() {
+                               try {
+                                    eventContextOrg = this.attr('value');
+                               } catch(E) {
+                                   //dump('Error in acq/events.js, eventInit, connect, onChange: ' + E);
+                                   throw(E);
+                               }
+                           }
+                       );
+                   } catch(E) {
+                       //dump('Error in acq/events.js, eventInit, connect: ' + E);
+                       throw(E);
+                   }
+               };
+               new openils.User().buildPermOrgSelector('STAFF_LOGIN', eventContextOrgSelect, null, connect);
+       
+           } catch(E) {
+               //dump('Error in acq/events.js, buildOrgSelector(): ' + E);
+               throw(E);
+           }
+       }
+       
+       function doSelected(method) {
+           try {
+               var ids = [];
+               dojo.forEach(
+                   eventGrid.getSelectedItems(),
+                   function(item) {
+                       ids.push( eventGrid.store.getValue(item,'id') );
+                   }
+               );
+               fieldmapper.standardRequest(
+                   [ 'open-ils.acq', method ],
+                   {   async: true,
+                       params: [openils.User.authtoken, ids],
+                       onresponse: function(r) {
+                           try {
+                               var result = openils.Util.readResponse(r);
+                               if (typeof result.ilsevent != 'undefined') { throw(result); }
+                           } catch(E) {
+                               //dump('Error in acq/events.js, doSelected(), onresponse(): ' + E);
+                               throw(E);
+                           }
+                       },
+                       onerror: function(r) {
+                           try {
+                               var result = openils.Util.readResponse(r);
+                               throw(result);
+                           } catch(E) {
+                               //dump('Error in acq/events.js, doSelected(), onerror(): ' + E);
+                               throw(E);
+                           }
+                       },
+                       oncomplete: function(r) {
+                           try {
+                               var result = openils.Util.readResponse(r);
+                               buildEventGrid();
+                           } catch(E) {
+                               //dump('Error in acq/events.js, doSelected(), oncomplete(): ' + E);
+                               throw(E);
+                           }
+                       }
+                   }
+               );
+           } catch(E) {
+               //dump('Error in acq/events.js, doSelected(): ' + E);
+               throw(E);
+           }
+       }
+       
+       function buildEventGrid() {
+           eventGrid.resetStore();
+           if(eventContextOrg == null) {
+               eventContextOrg = openils.User.user.ws_ou();
+           }
+           if(eventState == null) {
+               eventState = 'pending';
+           }
+           var filter = {"state":eventState, "order_by":[{"class":"atev", "field":"run_time", "direction":"desc"}]};
+           if(eventStartDateRange != null) {
+               /* the dijit appears to always provide 00:00:00 for the timestamp component */
+               var end_of_day = eventEndDateRange; end_of_day.setDate( end_of_day.getDate() + 1 ); 
+               filter['start_time'] = {
+                   'between' : [
+                       dojo_date_stamp.toISOString( eventStartDateRange ),
+                       dojo_date_stamp.toISOString( end_of_day )
+                   ]
+               }
+           }
+           po_map = {};
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.purchase_order.events.ordering_agency'],
+               {   async: true,
+                   params: [openils.User.authtoken, eventContextOrg, filter],
+                   onresponse: function(r) {
+                       try {
+                           if(eventObject = openils.Util.readResponse(r)) {
+                               po_map[ eventObject.target().id() ] = eventObject.target();
+                               eventObject.target( eventObject.target().id() );
+                               eventGrid.store.newItem(atev.toStoreItem(eventObject));
+                           }
+                       } catch(E) {
+                           //dump('Error in acq/events.js, buildEventGrid, onresponse: ' + E);
+                           throw(E);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function format_po_link(value) {
+           if (value) {
+               // FIXME -- how do you escape the value from .name() ?
+               return '<a href="' + oilsBasePath + '/acq/po/view/' + value + '">' + po_map[ value ].name() + '</a>';
+           }
+       }
+       
+       openils.Util.addOnLoad(eventInit);
+       
+       
+       
+
+});
\ No newline at end of file
index 5ef4d24..bdfac96 100644 (file)
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.NumberTextBox');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dojo.date.locale');
-dojo.require('dojo.date.stamp');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.PermaCrud');
+require([
+       "dijit/form/Form",
+       "dijit/form/Button",
+       "dijit/form/CheckBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/NumberTextBox",
+       "dojo/data/ItemFileWriteStore",
+       "dojo/date/locale",
+       "dojo/date/stamp",
+       "openils/User",
+       "openils/Util",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/PermaCrud"
+       ],
+function(dijit_form_Form,
+       dijit_form_Button,
+       dijit_form_CheckBox,
+       dijit_form_FilteringSelect,
+       dijit_form_NumberTextBox,
+       dojo_data_ItemFileWriteStore,
+       dojo_date_locale,
+       dojo_date_stamp,
+       openils_User,
+       openils_Util,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_PermaCrud){
+       
+       var metaPO;
+       var _last_fields;
+       var general_po_search_opts = {"order_by": {"acqpo": "edit_time DESC"}};
+       
+       function getPOOwner(rowIndex, item) {
+           if(!item) return '';
+           var data = this.grid.store.getValue(item, 'owner');
+           return new openils_User({id:data}).user.usrname();
+       }
+       
+       function doSearch(fields) {
+           _last_fields = dojo.clone(fields); /* Save for re-use */
+           var metapo_view = false;
+       
+           /* Remove the metapo_view field from 'fields'... we'll use it later */
+           if (fields.metapo_view && fields.metapo_view[0]) {
+               metapo_view = true;
+               delete fields.metapo_view;
+           }
+       
+           if (
+               !(fields.id && fields.id.constructor.name == 'Array') && 
+               isNaN(fields.id)
+           ) {
+               delete fields.id;
+               for(var k in fields) {
+                   if(fields[k] == '' || fields[k] == null)
+                       delete fields[k];
+               }
+           } else {
+               // ID search trumps other searches
+               fields = {id:fields.id};
+           }
+       
+           // no search fields
+           var some = false;
+           for(var k in fields) some = true;
+           if(!some) fields.id = {'!=' : null};
+       
+       
+           if (metapo_view) {
+               openils_Util.hide("holds_po_grid");
+               loadMetaPO(fields);
+           } else {
+               if (metaPO) metaPO.myHide();
+               openils_Util.show("holds_po_grid");
+               poGrid.resetStore();
+               poGrid.loadAll(general_po_search_opts, fields);
+           }
+       }
+       
+       function loadForm() {
+       
+           new openils_widget_AutoFieldWidget({
+               fmClass : 'acqpo', 
+               fmField : 'provider', 
+               parentNode : dojo.byId('po-search-provider-selector'),
+               orgLimitPerms : ['VIEW_PURCHASE_ORDER'],
+               dijitArgs : {name:'provider', required:false}
+           }).build();
+       
+           new openils_widget_AutoFieldWidget({
+               fmClass : 'acqpo', 
+               fmField : 'ordering_agency', 
+               parentNode : dojo.byId('po-search-agency-selector'),
+               orgLimitPerms : ['VIEW_PURCHASE_ORDER'],
+               dijitArgs : {name:'ordering_agency', required:false}
+           }).build();
+       
+           if (poIds && poIds.length > 0) {
+               dijit.byId("metapo_view").attr("checked", true);
+               doSearch({"id": poIds, "metapo_view": [true] /* [sic] */});
+           } else {
+               doSearch({"ordering_agency": openils_User.user.ws_ou()});
+           }
+       }
+       
+       function loadMetaPO(fields) {
+           var pcrud = new openils_PermaCrud();
+           var po_list = pcrud.search("acqpo", fields, general_po_search_opts);
+           if (!po_list || !po_list.length) {
+               alert(localeStrings.NO_PO_RESULTS);
+           } else {
+               if (!metaPO) {
+                   metaPO = new AcqLiTable();
+       
+                   /* We need to know the width (in cells) of the template row for
+                    * the LI table, and we don't want to hardcode it here. */
+                   metaPO.n_cells = dojo.query("> td", metaPO.rowTemplate).length;
+       
+                   metaPO._copy_count_cb = function(liId, count) {
+                       var poId = this.liCache[liId].purchase_order();
+       
+                       if (this.copy_counts[poId] == undefined)
+                           this.copy_counts[poId] = {};
+                       this.copy_counts[poId][liId] = count;
+       
+                       this.renderCopyCounts(poId);
+                       this.renderSummary("copies");
+                   };
+                   metaPO.myHide = function() {
+                       this.hide();
+                       openils_Util.hide("oils-acq-holds-metapo-summary");
+                   };
+                   metaPO.renderSummary = function(part) {
+                       var self = this;
+                       /* The idea here will be that if "part" is defined, we'll
+                        * just update that part of the metaPO summary, otherwise,
+                        * the whole thing. */
+                       if (part != undefined) {
+                           var target = dojo.byId("oils-acq-metapo-summary-" + part);
+                           switch (part) {
+                               case "copies":
+                                   target.innerHTML = self.copiesTotal();
+                                   break;
+                               case "po":
+                                   target.innerHTML = self.working_po_list.length;
+                                   break;
+                               /* Any numeric fields should be named here. */
+                               case "amount_encumbered":
+                               case "amount_spent":
+                                   target.innerHTML = self.numericFieldTotal(part);
+                                   break;
+                               default:
+                                   /* assume a field on the acqpo's themselves */
+                                   target.innerHTML = self.anyFieldTotal(part);
+                                   break;
+                           }
+                       } else {
+                           openils_Util.show("oils-acq-holds-metapo-summary");
+                           self.totalable_fields.forEach(
+                               function(f) { self.renderSummary(f); }
+                           );
+                       }
+                   };
+                   metaPO.numericFieldTotal = function(field) {
+                       var self = this;
+                       var pennies = self.working_po_list.reduce(
+                           /* working_po_list contains unfleshed acqpo's, so we must
+                            * find the same PO in the poCache */
+                           function(p, c) {
+                               c = self.poCache[c.id()][field]();
+                               return p + Number(c) * 100;
+                           }, 0
+                       );
+                       return pennies / 100;
+                   };
+                   metaPO.anyFieldTotal = function(field) {
+                       var self = this;
+                       return self.working_po_list.reduce(
+                           /* working_po_list contains unfleshed acqpo's, so we must
+                            * find the same PO in the poCache */
+                           function(p, c) {
+                               c = self.poCache[c.id()][field]();
+                               return p + Number(c);
+                           }, 0
+                       );
+                   };
+                   metaPO.renderCopyCounts = function(poId) {
+                       try {
+                           dojo.query("td#oils-acq-po-heading-" + poId +
+                               ' span span[attr="copies"]')[0].innerHTML =
+                                   this.copiesByPOId(poId);
+                       } catch (E) {
+                           ;
+                       }
+                   };
+                   metaPO.sectionHeadingById = function(id) {
+                       var headings = dojo.query("#po-heading-" + id, this.tbody);
+                       if (headings.length != 1) {
+                           alert(localeStrings.PO_HEADING_ERROR);
+                           return undefined;
+                       } else {
+                           return headings[0];
+                       }
+                   };
+                   metaPO.sectionHeadingByPOId = function(poId) {
+                       return this.sectionHeadingById(this.sections_by_poid[poId]);
+                   };
+                   metaPO.addSection = function(po) {
+                       var s = this.sections_by_poid[po.id()] = this.sections++;
+       
+                       this.tbody.appendChild(
+                           dojo.create("tr", {
+                               "class": "acq-lit-po-heading", "id": "po-heading-" + s
+                           })
+                       );
+       
+                       return s;
+                   };
+                   metaPO.addLineitemToSection = function(li, section) {
+                       dojo.place(
+                           this.addLineitem(li, true /* skip_final_placement */),
+                           this.sectionHeadingById(section),
+                           "after"
+                       );
+                   };
+                   metaPO.generateActivator = function(id) {
+                       return function() {
+                           progressDialog.show(true);
+                           try {
+                               fieldmapper.standardRequest(
+                                   ["open-ils.acq",
+                                       "open-ils.acq.purchase_order.activate"], {
+                                       "async": true,
+                                       "params": [openils_User.authtoken, id],
+                                       "oncomplete": function() {
+                                           progressDialog.hide();
+                                           doSearch(_last_fields);
+                                       }
+                                   }
+                               );
+                           } catch (E) {
+                               progressDialog.hide();
+                               alert(E); /* XXX */
+                           }
+                       };
+                   };
+                   metaPO.renderHeading = function(poId) {
+                       var self = this;
+                       var td = dojo.create("td", {"colspan": self.n_cells});
+                       td.id = "oils-acq-po-heading-" + poId;
+       
+                       /* Build our HTML structure from the template... */
+                       dojo.query("> span", "oils-acq-po-heading-template").forEach(
+                           function(s) { td.appendChild(s.cloneNode(true)); }
+                       );
+       
+                       /* Some fields straight from the PO object... */
+                       self.po_fields_for_display.forEach(
+                           function(f) {
+                               dojo.query('[attr="' + f + '"]', td)[0].innerHTML =
+                                   self.poCache[poId][f]();
+                           }
+                       );
+       
+                       /* The name field needs special treatment: it's a link */
+                       dojo.attr(
+                           dojo.query('a[attr="name"]', td)[0],
+                           "href",
+                           oilsBasePath + '/acq/po/view/' + poId
+                       );
+       
+                       /* Show an "activate" link, or not, based on "state"... */
+                       var a = dojo.query('a[attr="activator"]', td)[0];
+                       if (self.poCache[poId].state() == "pending") {
+                           a.onclick = self.generateActivator(poId);
+                           openils_Util.show(a, "inline");
+                       } else {
+                           openils_Util.hide(a);
+                       }
+       
+                       /* Put the new heading cell in place... */
+                       dojo.place(td, self.sectionHeadingByPOId(poId), "only");
+       
+                       /* And finally, render copy info (must happen _after_ heading
+                        * is attached to the DOM tree */
+                       this.renderCopyCounts(poId);
+                   };
+                   metaPO.copiesByPOId = function(poId) {
+                       if (!this.copy_counts[poId]) return undefined;
+                       var total = 0;
+                       for (var liId in this.copy_counts[poId]) {
+                           total += this.copy_counts[poId][liId];
+                       }
+                       return total;
+                   };
+                   metaPO.copiesTotal = function() {
+                       var total = 0;
+                       for (var poId in this.copy_counts)
+                           total += this.copiesByPOId(poId);
+                       return total;
+                   };
+                   metaPO.myReset = function() {
+                       this.isMeta = true;
+                       this.sections = 0;
+                       this.sections_by_poid = {};
+                       this.copy_counts = {};
+                       this.po_fields_for_display = [
+                           "name", "lineitem_count", "amount_encumbered",
+                           "amount_spent", "state"
+                       ];
+                       this.totalable_fields = [
+                           "po", "lineitem_count", "copies",
+                           "amount_encumbered", "amount_spent"
+                       ];
+                       openils_Util.hide("oils-acq-holds-metapo-summary");
+                   };
+                   metaPO.populate = function(list) {
+                       var self = this;
+                       var done = 0;
+       
+                       self.working_po_list = [];
+       
+                       progressDialog.show(true);
+                       list.forEach(function(po) {
+                           var sec = self.addSection(po);
+                           fieldmapper.standardRequest(
+                               ["open-ils.acq", "open-ils.acq.lineitem.search"], {
+                                   "async": true,
+                                   "params": [
+                                       openils_User.authtoken,
+                                       {"purchase_order": po.id()},
+                                       {"flesh_attrs": true, "flesh_notes": true}
+                                   ],
+                                   "onresponse": function(r) {
+                                       var li = openils_Util.readResponse(r);
+                                       if (li) /* sometimes empty string: disregard */
+                                           self.addLineitemToSection(li, sec);
+                                   },
+                                   "oncomplete": function(r) {
+                                       self.working_po_list.push(po);
+                                       self.renderHeading(po.id());
+                                       self.renderSummary();
+                                       /* This mechanism avoids calling .show() too
+                                        * often or before results are ready, and
+                                        * thus smooths out DOM rendering glitches. */
+                                       if (++done >= list.length) {
+                                           done = -1;
+                                           self.show("list");
+                                           progressDialog.hide();
+                                       }
+                                   }
+                               }
+                           );
+                       });
+                       /* This mechanism sees to it that we call .show() at least once
+                        * even if the search result population seems to be timing
+                        * out or failing. */
+                       setTimeout(
+                           function() {
+                               if (done != -1) {
+                                   self.show("list");
+                                   progressDialog.hide();
+                               }
+                           }, 10000    /* 10 seconds: make this configurable? */
+                       );
+                   };
+               }
+       
+               metaPO.reset();
+               metaPO.myReset();
+               metaPO.populate(po_list);
+           }
+       }
+       
+       openils_Util.addOnLoad(loadForm);
+       
 
-var metaPO;
-var _last_fields;
-var general_po_search_opts = {"order_by": {"acqpo": "edit_time DESC"}};
-
-function getPOOwner(rowIndex, item) {
-    if(!item) return '';
-    var data = this.grid.store.getValue(item, 'owner');
-    return new openils.User({id:data}).user.usrname();
-}
-
-function doSearch(fields) {
-    _last_fields = dojo.clone(fields); /* Save for re-use */
-    var metapo_view = false;
-
-    /* Remove the metapo_view field from 'fields'... we'll use it later */
-    if (fields.metapo_view && fields.metapo_view[0]) {
-        metapo_view = true;
-        delete fields.metapo_view;
-    }
-
-    if (
-        !(fields.id && fields.id.constructor.name == 'Array') && 
-        isNaN(fields.id)
-    ) {
-        delete fields.id;
-        for(var k in fields) {
-            if(fields[k] == '' || fields[k] == null)
-                delete fields[k];
-        }
-    } else {
-        // ID search trumps other searches
-        fields = {id:fields.id};
-    }
-
-    // no search fields
-    var some = false;
-    for(var k in fields) some = true;
-    if(!some) fields.id = {'!=' : null};
-
-
-    if (metapo_view) {
-        openils.Util.hide("holds_po_grid");
-        loadMetaPO(fields);
-    } else {
-        if (metaPO) metaPO.myHide();
-        openils.Util.show("holds_po_grid");
-        poGrid.resetStore();
-        poGrid.loadAll(general_po_search_opts, fields);
-    }
-}
-
-function loadForm() {
-
-    new openils.widget.AutoFieldWidget({
-        fmClass : 'acqpo', 
-        fmField : 'provider', 
-        parentNode : dojo.byId('po-search-provider-selector'),
-        orgLimitPerms : ['VIEW_PURCHASE_ORDER'],
-        dijitArgs : {name:'provider', required:false}
-    }).build();
-
-    new openils.widget.AutoFieldWidget({
-        fmClass : 'acqpo', 
-        fmField : 'ordering_agency', 
-        parentNode : dojo.byId('po-search-agency-selector'),
-        orgLimitPerms : ['VIEW_PURCHASE_ORDER'],
-        dijitArgs : {name:'ordering_agency', required:false}
-    }).build();
-
-    if (poIds && poIds.length > 0) {
-        dijit.byId("metapo_view").attr("checked", true);
-        doSearch({"id": poIds, "metapo_view": [true] /* [sic] */});
-    } else {
-        doSearch({"ordering_agency": openils.User.user.ws_ou()});
-    }
-}
-
-function loadMetaPO(fields) {
-    var pcrud = new openils.PermaCrud();
-    var po_list = pcrud.search("acqpo", fields, general_po_search_opts);
-    if (!po_list || !po_list.length) {
-        alert(localeStrings.NO_PO_RESULTS);
-    } else {
-        if (!metaPO) {
-            metaPO = new AcqLiTable();
-
-            /* We need to know the width (in cells) of the template row for
-             * the LI table, and we don't want to hardcode it here. */
-            metaPO.n_cells = dojo.query("> td", metaPO.rowTemplate).length;
-
-            metaPO._copy_count_cb = function(liId, count) {
-                var poId = this.liCache[liId].purchase_order();
-
-                if (this.copy_counts[poId] == undefined)
-                    this.copy_counts[poId] = {};
-                this.copy_counts[poId][liId] = count;
-
-                this.renderCopyCounts(poId);
-                this.renderSummary("copies");
-            };
-            metaPO.myHide = function() {
-                this.hide();
-                openils.Util.hide("oils-acq-holds-metapo-summary");
-            };
-            metaPO.renderSummary = function(part) {
-                var self = this;
-                /* The idea here will be that if "part" is defined, we'll
-                 * just update that part of the metaPO summary, otherwise,
-                 * the whole thing. */
-                if (part != undefined) {
-                    var target = dojo.byId("oils-acq-metapo-summary-" + part);
-                    switch (part) {
-                        case "copies":
-                            target.innerHTML = self.copiesTotal();
-                            break;
-                        case "po":
-                            target.innerHTML = self.working_po_list.length;
-                            break;
-                        /* Any numeric fields should be named here. */
-                        case "amount_encumbered":
-                        case "amount_spent":
-                            target.innerHTML = self.numericFieldTotal(part);
-                            break;
-                        default:
-                            /* assume a field on the acqpo's themselves */
-                            target.innerHTML = self.anyFieldTotal(part);
-                            break;
-                    }
-                } else {
-                    openils.Util.show("oils-acq-holds-metapo-summary");
-                    self.totalable_fields.forEach(
-                        function(f) { self.renderSummary(f); }
-                    );
-                }
-            };
-            metaPO.numericFieldTotal = function(field) {
-                var self = this;
-                var pennies = self.working_po_list.reduce(
-                    /* working_po_list contains unfleshed acqpo's, so we must
-                     * find the same PO in the poCache */
-                    function(p, c) {
-                        c = self.poCache[c.id()][field]();
-                        return p + Number(c) * 100;
-                    }, 0
-                );
-                return pennies / 100;
-            };
-            metaPO.anyFieldTotal = function(field) {
-                var self = this;
-                return self.working_po_list.reduce(
-                    /* working_po_list contains unfleshed acqpo's, so we must
-                     * find the same PO in the poCache */
-                    function(p, c) {
-                        c = self.poCache[c.id()][field]();
-                        return p + Number(c);
-                    }, 0
-                );
-            };
-            metaPO.renderCopyCounts = function(poId) {
-                try {
-                    dojo.query("td#oils-acq-po-heading-" + poId +
-                        ' span span[attr="copies"]')[0].innerHTML =
-                            this.copiesByPOId(poId);
-                } catch (E) {
-                    ;
-                }
-            };
-            metaPO.sectionHeadingById = function(id) {
-                var headings = dojo.query("#po-heading-" + id, this.tbody);
-                if (headings.length != 1) {
-                    alert(localeStrings.PO_HEADING_ERROR);
-                    return undefined;
-                } else {
-                    return headings[0];
-                }
-            };
-            metaPO.sectionHeadingByPOId = function(poId) {
-                return this.sectionHeadingById(this.sections_by_poid[poId]);
-            };
-            metaPO.addSection = function(po) {
-                var s = this.sections_by_poid[po.id()] = this.sections++;
-
-                this.tbody.appendChild(
-                    dojo.create("tr", {
-                        "class": "acq-lit-po-heading", "id": "po-heading-" + s
-                    })
-                );
-
-                return s;
-            };
-            metaPO.addLineitemToSection = function(li, section) {
-                dojo.place(
-                    this.addLineitem(li, true /* skip_final_placement */),
-                    this.sectionHeadingById(section),
-                    "after"
-                );
-            };
-            metaPO.generateActivator = function(id) {
-                return function() {
-                    progressDialog.show(true);
-                    try {
-                        fieldmapper.standardRequest(
-                            ["open-ils.acq",
-                                "open-ils.acq.purchase_order.activate"], {
-                                "async": true,
-                                "params": [openils.User.authtoken, id],
-                                "oncomplete": function() {
-                                    progressDialog.hide();
-                                    doSearch(_last_fields);
-                                }
-                            }
-                        );
-                    } catch (E) {
-                        progressDialog.hide();
-                        alert(E); /* XXX */
-                    }
-                };
-            };
-            metaPO.renderHeading = function(poId) {
-                var self = this;
-                var td = dojo.create("td", {"colspan": self.n_cells});
-                td.id = "oils-acq-po-heading-" + poId;
-
-                /* Build our HTML structure from the template... */
-                dojo.query("> span", "oils-acq-po-heading-template").forEach(
-                    function(s) { td.appendChild(s.cloneNode(true)); }
-                );
-
-                /* Some fields straight from the PO object... */
-                self.po_fields_for_display.forEach(
-                    function(f) {
-                        dojo.query('[attr="' + f + '"]', td)[0].innerHTML =
-                            self.poCache[poId][f]();
-                    }
-                );
-
-                /* The name field needs special treatment: it's a link */
-                dojo.attr(
-                    dojo.query('a[attr="name"]', td)[0],
-                    "href",
-                    oilsBasePath + '/acq/po/view/' + poId
-                );
-
-                /* Show an "activate" link, or not, based on "state"... */
-                var a = dojo.query('a[attr="activator"]', td)[0];
-                if (self.poCache[poId].state() == "pending") {
-                    a.onclick = self.generateActivator(poId);
-                    openils.Util.show(a, "inline");
-                } else {
-                    openils.Util.hide(a);
-                }
-
-                /* Put the new heading cell in place... */
-                dojo.place(td, self.sectionHeadingByPOId(poId), "only");
-
-                /* And finally, render copy info (must happen _after_ heading
-                 * is attached to the DOM tree */
-                this.renderCopyCounts(poId);
-            };
-            metaPO.copiesByPOId = function(poId) {
-                if (!this.copy_counts[poId]) return undefined;
-                var total = 0;
-                for (var liId in this.copy_counts[poId]) {
-                    total += this.copy_counts[poId][liId];
-                }
-                return total;
-            };
-            metaPO.copiesTotal = function() {
-                var total = 0;
-                for (var poId in this.copy_counts)
-                    total += this.copiesByPOId(poId);
-                return total;
-            };
-            metaPO.myReset = function() {
-                this.isMeta = true;
-                this.sections = 0;
-                this.sections_by_poid = {};
-                this.copy_counts = {};
-                this.po_fields_for_display = [
-                    "name", "lineitem_count", "amount_encumbered",
-                    "amount_spent", "state"
-                ];
-                this.totalable_fields = [
-                    "po", "lineitem_count", "copies",
-                    "amount_encumbered", "amount_spent"
-                ];
-                openils.Util.hide("oils-acq-holds-metapo-summary");
-            };
-            metaPO.populate = function(list) {
-                var self = this;
-                var done = 0;
-
-                self.working_po_list = [];
-
-                progressDialog.show(true);
-                list.forEach(function(po) {
-                    var sec = self.addSection(po);
-                    fieldmapper.standardRequest(
-                        ["open-ils.acq", "open-ils.acq.lineitem.search"], {
-                            "async": true,
-                            "params": [
-                                openils.User.authtoken,
-                                {"purchase_order": po.id()},
-                                {"flesh_attrs": true, "flesh_notes": true}
-                            ],
-                            "onresponse": function(r) {
-                                var li = openils.Util.readResponse(r);
-                                if (li) /* sometimes empty string: disregard */
-                                    self.addLineitemToSection(li, sec);
-                            },
-                            "oncomplete": function(r) {
-                                self.working_po_list.push(po);
-                                self.renderHeading(po.id());
-                                self.renderSummary();
-                                /* This mechanism avoids calling .show() too
-                                 * often or before results are ready, and
-                                 * thus smooths out DOM rendering glitches. */
-                                if (++done >= list.length) {
-                                    done = -1;
-                                    self.show("list");
-                                    progressDialog.hide();
-                                }
-                            }
-                        }
-                    );
-                });
-                /* This mechanism sees to it that we call .show() at least once
-                 * even if the search result population seems to be timing
-                 * out or failing. */
-                setTimeout(
-                    function() {
-                        if (done != -1) {
-                            self.show("list");
-                            progressDialog.hide();
-                        }
-                    }, 10000    /* 10 seconds: make this configurable? */
-                );
-            };
-        }
-
-        metaPO.reset();
-        metaPO.myReset();
-        metaPO.populate(po_list);
-    }
-}
-
-openils.Util.addOnLoad(loadForm);
+});
\ No newline at end of file
index ed4d55e..12baba2 100644 (file)
-dojo.require("dojo.string");
-dojo.require('dijit.layout.ContentPane');
-dojo.require('openils.PermaCrud');
-
-var pcrud = new openils.PermaCrud();
-var PO = null;
-var liTable;
-var poItemTable;
-var poNoteTable;
-var invoiceLinkDialogManager;
-
-function AcqPoNoteTable() {
-    var self = this;
-
-    this.notesTbody = dojo.byId("acq-po-notes-tbody");
-    this.notesRow = this.notesTbody.removeChild(dojo.byId("acq-po-notes-row"));
-
-    dojo.byId("acq-po-notes-back-button").onclick = function() { self.hide(); };
-    dojo.byId("acq-po-view-notes").onclick = function() { self.show(); };
-
-    /* widgets' event properties are cased likeThis */
-    acqPoCreateNoteSubmit.onClick = function() {
-        if (!acqPoCreateNoteText.attr("value")) return;
-
-        /* prep new note */
-        var note = new acqpon();
-        note.vendor_public(
-            Boolean(acqPoCreateNoteVendorPublic.attr('checked'))
-        );
-        note.value(acqPoCreateNoteText.attr("value"));
-        note.purchase_order(PO.id());
-        note.isnew(true);
-
-        /* save it */
-        self.updatePoNotes(note);
-
-        /* reset fields for next use */
-        acqPoCreateNoteText.attr("value", "");
-        acqPoCreateNoteVendorPublic.attr("checked", false);
-    };
-
-    this.drawPoNote = function(note) {
-        if (note.isdeleted())
-            return;
-
-        var row = dojo.clone(this.notesRow);
-
-        nodeByName("value", row).innerHTML = note.value();
-
-        if (openils.Util.isTrue(note.vendor_public()))
-            nodeByName("vendor_public", row).innerHTML =
-                localeStrings.VENDOR_PUBLIC;
-
-        nodeByName("delete", row).onclick = function() {
-            note.isdeleted(true);
-            self.notesTbody.removeChild(row);
-            self.updatePoNotes();
-        };
-
-        if (note.edit_time()) {
-            nodeByName("edit_time", row).innerHTML =
-                dojo.date.locale.format(
-                    dojo.date.stamp.fromISOString(note.edit_time()),
-                    {"formatLength": "short"}
-                );
-        }
-
-        self.notesTbody.appendChild(row);
-    };
-
-    this.drawPoNotes = function() {
-        /* sort */
-        PO.notes(
-            PO.notes().sort(
-                function(a, b) {
-                    return (a.edit_time() < b.edit_time()) ? 1 : -1;
-                }
-            )
-        );
-
-        /* remove old renderings of notes */
-        dojo.empty(this.notesTbody);
-
-        PO.notes().forEach(function(o) { self.drawPoNote(o); });
-    };
-
-    this.updatePoNotesCount = function() {
-        dojo.byId("acq-po-view-notes").innerHTML =
-            "(" + PO.notes().length + ")";
-    };
-
-    this.updatePoNotes = function(newNote) {
-        var notes = newNote ?
-            [newNote] :
-            PO.notes().filter(
-                function(o) {
-                    if (o.ischanged() || o.isnew() || o.isdeleted())
-                        return o;
-                }
-            );
-
-        if (notes.length < 1)
-            return;
-
-        progressDialog.show();
-
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {
-                "async": true,
-                "params": [openils.User.authtoken, notes],
-                "onresponse": function(r) {
-                    var resp = openils.Util.readResponse(r);
-                    if (resp) {
-                        progressDialog.update(resp);
-
-                        if (!resp.note.isdeleted()) {
-                            resp.note.isnew(false);
-                            resp.note.ischanged(false);
-                            PO.notes().push(resp.note);
-                        }
-                    }
-                },
-                "oncomplete": function() {
-                    if (!newNote) {
-                        /* remove the old changed notes */
-                        var list = [];
-                        PO.notes(
-                            PO.notes().filter(
-                                function(o) {
-                                    return (!(
-                                        o.ischanged() || o.isnew() ||
-                                        o.isdeleted()
-                                    ));
-                                }
-                            )
-                        );
-                    }
-
-                    progressDialog.hide();
-                    self.updatePoNotesCount();
-                    self.drawPoNotes();
-                }
-            }
-        );
-    };
-
-    this.hide = function() {
-        openils.Util.hide("acq-po-notes-div");
-        liTable.show("list");
-        poItemTable.show();
-    };
-
-    this.show = function() {
-        liTable.hide();
-        poItemTable.hide();
-        self.drawPoNotes();
-        openils.Util.show("acq-po-notes-div");
-    };
-}
-
-function updatePoState(po_info) {
-    var data = po_info[PO.id()];
-    if (data) {
-        for (var key in data)
-            PO[key](data[key]);
-        renderPo();
-    }
-}
-
-function cancellationUpdater(r) {
-    var r = openils.Util.readResponse(r);
-    if (r) {
-        if (r.po) updatePoState(r.po);
-        if (r.li) {
-            for (var id in r.li) {
-                liTable.liCache[id].state(r.li[id].state);
-                liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);
-                liTable.updateLiState(liTable.liCache[id]);
-            }
-        }
-        if (r.lid && liTable.copyCache) {
-            for (var id in r.lid) {
-                if (liTable.copyCache[id]) {
-                    liTable.copyCache[id].cancel_reason(
-                        r.lid[id].cancel_reason
-                    );
-                    liTable.updateLidState(liTable.copyCache[id]);
-                }
-            }
-        }
-    }
-}
-
-function makeProviderLink(node, provider) {
-    return dojo.create(
-        "a", {
-            "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),
-            "innerHTML": provider.name() + " (" + provider.code() + ")",
-        },
-        node,
-        "only"
-    );
-}
-function makePrepayWidget(node, prepay) {
-    if (prepay) {
-        openils.Util.addCSSClass(node, "oils-acq-po-prepay");
-        node.innerHTML = localeStrings.YES;
-    } else {
-        openils.Util.removeCSSClass(node, "oils-acq-po-prepay");
-        node.innerHTML = localeStrings.NO;
-    }
-}
-
-function makeCancelWidget(node, labelnode) {
-    openils.Util.hide("acq-po-choose-cancel-reason");
-
-    if (PO.cancel_reason()) {
-        labelnode.innerHTML = localeStrings.CANCEL_REASON;
-        node.innerHTML = PO.cancel_reason().description() + " (" +
-            PO.cancel_reason().label() + ")";
-    } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {
-        dojo.destroy(this.oldTip);
-        labelnode.innerHTML = "";
-        node.innerHTML = "";
-    } else {
-        dojo.destroy(this.oldTip);
-        labelnode.innerHTML = localeStrings.CANCEL;
-        node.innerHTML = "";
-        if (!acqPoCancelReasonSubmit._prepared) {
-            var widget = new openils.widget.AutoFieldWidget({
-                "fmField": "cancel_reason",
-                "fmClass": "acqpo",
-                "parentNode": dojo.byId("acq-po-cancel-reason"),
-                "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
-                "forceSync": true
-            });
-            widget.build(
-                function(w, ww) {
-                    acqPoCancelReasonSubmit.onClick = function() {
-                        if (w.attr("value")) {
-                            if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {
-                                fieldmapper.standardRequest(
-                                    ["open-ils.acq",
-                                        "open-ils.acq.purchase_order.cancel"],
-                                    {
-                                        "params": [
-                                            openils.User.authtoken,
-                                            PO.id(), 
-                                            w.attr("value")
-                                        ],
-                                        "async": true,
-                                        "oncomplete": cancellationUpdater
-                                    }
-                                );
-                            }
-                        }
-                    };
-                    acqPoCancelReasonSubmit._prepared = true;
-                }
-            );
-        }
-        openils.Util.show("acq-po-choose-cancel-reason", "inline");
-    }
-}
-
-function prepareInvoiceFeatures() {
-    /* show the count of related invoices on the "view invoices" button */
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {
-            "params": [
-                openils.User.authtoken,
-                {"acqpo":[{"id": PO.id()}]},
-                null,
-                null,
-                {"id_list": true}
-            ],
-            "async": true,
-            "oncomplete": function(r) {
-                dojo.byId("acq-po-view-invoice-count").innerHTML =
-                    openils.Util.readResponse(r).length;
-            }
-        }
-    );
-
-    /* view invoices button */
-    dijit.byId("acq-po-view-invoice-link").onClick = function() {
-        location.href = oilsBasePath + "/acq/search/unified?so=" +
-            base64Encode({"acqpo":[{"id": PO.id()}]}) +
-            "&rt=invoice";
-    };
-
-    /* create invoice button */
-    dijit.byId("acq-po-create-invoice-link").onClick = function() {
-        location.href = oilsBasePath +
-            "/acq/invoice/view?create=1&attach_po=" + PO.id();
-    };
-
-    openils.Util.show("acq-po-invoice-stuff", "table-cell");
-}
-
-function renderPo() {
-    dojo.byId("acq-po-view-id").innerHTML = PO.id();
-    dojo.byId("acq-po-view-name").innerHTML = PO.name();
-    makeProviderLink(
-        dojo.byId("acq-po-view-provider"),
-        PO.provider()
-    );
-    dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
-    dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);
-    dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);
-    dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
-
-    if(PO.order_date()) {
-        openils.Util.show('acq-po-activated-on', 'inline');
-        dojo.byId('acq-po-activated-on').innerHTML = 
-            dojo.string.substitute(
-                localeStrings.PO_ACTIVATED_ON, [
-                    openils.Util.timeStamp(PO.order_date(), {formatLength:'short'})
-                ]
-            );
-
-    }
-
-    makePrepayWidget(
-        dojo.byId("acq-po-view-prepay"),
-        openils.Util.isTrue(PO.prepayment_required())
-    );
-    makeCancelWidget(
-        dojo.byId("acq-po-view-cancel-reason"),
-        dojo.byId("acq-po-cancel-label")
-    );
-    // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
-    poNoteTable.updatePoNotesCount();
-
-    if (PO.state() == "pending") {
-        checkCouldActivatePo();
-        if (PO.lineitem_count() > 1)
-            openils.Util.show("acq-po-split");
-    } else {
-        dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.NO;
-    }
-
-    // XXX we probably don't *always* need to do this...
-    poItemTable.reset();
-    PO.po_items().forEach(
-        function(po_item) { poItemTable.addItem(po_item); }
-    );
-    poItemTable.show();
-
-    dojo.attr(
-        "acq-po-view-history", "href",
-        oilsBasePath + "/acq/po/history/" + PO.id()
-    );
-    openils.Util.show("acq-po-view-history", "inline");
-
-    prepareInvoiceFeatures();
-}
-
-
-function init() {
-    /* set up li table */
-    liTable = new AcqLiTable();
-    liTable.reset();
-    liTable.isPO = poId;
-    liTable.poUpdateCallback = updatePoState;
-
-    /* set up po notes table */
-    poNoteTable = new AcqPoNoteTable();
-
-    /* retrieve data and populate */
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
-        {   async: true,
-            params: [openils.User.authtoken, poId, {
-                "flesh_provider": true,
-                "flesh_price_summary": true,
-                "flesh_lineitem_count": true,
-                "flesh_notes": true,
-                "flesh_po_items": true
-            }],
-            oncomplete: function(r) {
-                PO = openils.Util.readResponse(r); /* save PO globally */
-
-                /* po item table */
-                poItemTable = new PoItemTable(PO, pcrud);
-
-                renderPo();
-            }
-        }
-    );
-
-    var totalEstimated = 0;
-    var zeroLi = true;
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem.search'],
-        {   async: true,
-            params: [
-                openils.User.authtoken, 
-                [{purchase_order:poId}, {"order_by": {"jub": "id ASC"}}], 
-                {flesh_attrs:true, flesh_notes:true, flesh_cancel_reason:true, clear_marc:true}
-            ],
-            onresponse: function(r) {
-                zeroLi = false;
-                liTable.show('list');
-                var li = openils.Util.readResponse(r);
-                // TODO: Add po_item's to total estimated amount
-                totalEstimated += (Number(li.item_count() || 0) * Number(li.estimated_unit_price() || 0));
-                liTable.addLineitem(li);
-            },
-
-            oncomplete : function() {
-                dojo.byId("acq-po-view-total-estimated").innerHTML = totalEstimated.toFixed(2);
-                if (liFocus) liTable.drawCopies(liFocus);
-                if(zeroLi) openils.Util.show('acq-po-no-lineitems');
-            }
-        }
-    );
-
-    pcrud.search(
-        'acqedim', 
-        {purchase_order : poId}, 
-        {
-            id_list : true,
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                // TODO: I18n
-                if(resp) {
-                    dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';
-                    dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);
-                } else {
-                    dojo.byId('acq-po-view-edi-messages').innerHTML = '0';
-                    dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');
-                }
-            }
-        }
-    );
-}
-
-function checkCouldActivatePo() {
-    var d = dojo.byId("acq-po-activate-checking");
-    var a = dojo.byId("acq-po-activate-link");
-    d.innerHTML = localeStrings.PO_CHECKING;
-    var warnings = [];
-    var stops = [];
-    var other = [];
-
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {
-            "params": [openils.User.authtoken, PO.id()],
-            "async": true,
-            "onresponse": function(r) {
-                if ((r = openils.Util.readResponse(r, true /* eventOk */))) {
-                    if (typeof(r.textcode) != "undefined") {
-                        switch(r.textcode) {
-                            case "ACQ_FUND_EXCEEDS_STOP_PERCENT":
-                                stops.push(r);
-                                break;
-                            case "ACQ_FUND_EXCEEDS_WARN_PERCENT":
-                                warnings.push(r);
-                                break;
-                            default:
-                                other.push(r);
-                        }
-                    }
-                }
-            },
-            "oncomplete": function() {
-                /* XXX in the future, this might be tweaked to display info
-                 * about more than one stop or warning event from the ML. */
-                if (!(warnings.length || stops.length || other.length)) {
-                    d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
-                    openils.Util.show(a, "inline");
-                } else {
-                    if (other.length) {
-                        /* XXX make the textcode part a tooltip one day */
-                        d.innerHTML = localeStrings.NO + ": " +
-                            other[0].desc + " (" + other[0].textcode + ")";
-                        openils.Util.hide(a);
-                    } else if (stops.length) {
-                        d.innerHTML =
-                            dojo.string.substitute(
-                                localeStrings.PO_STOP_BLOCKS_ACTIVATION, [
-                                    stops[0].payload.fund.code(),
-                                    stops[0].payload.fund.year()
-                                ]
-                            );
-                        openils.Util.hide(a);
-                    } else {
-                        PO._warning_hack = true;
-                        d.innerHTML =
-                            dojo.string.substitute(
-                                localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [
-                                    warnings[0].payload.fund.code(),
-                                    warnings[0].payload.fund.year()
-                                ]
-                            );
-                        openils.Util.show(a, "inline");
-                    }
-                }
-            }
-        }
-    );
-}
-
-function activatePo() {
-    if (openils.Util.isTrue(PO.prepayment_required())) {
-        if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))
-            return false;
-    }
-
-    if (PO._warning_hack) {
-        if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))
-            return false;
-    }
-
-    liTable.showAssetCreator(activatePoStage2);
-}
-
-function activatePoStage2() {
-
-    var want_refresh = false;
-    progressDialog.show(true);
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {
-            "async": true,
-            "params": [openils.User.authtoken, PO.id()],
-            "onresponse": function(r) {
-                want_refresh = Boolean(openils.Util.readResponse(r));
-            },
-            "oncomplete": function() {
-                progressDialog.hide();
-                if (want_refresh)
-                    location.href = location.href;
-            }
-        }
-    );
-}
-
-function splitPo() {
-    progressDialog.show(true);
-    try {
-        var list;
-        fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
-            {   async: true,
-                params: [openils.User.authtoken, PO.id()],
-                onresponse : function(r) {
-                    list = openils.Util.readResponse(r);
-                },
-                oncomplete : function() {
-                    progressDialog.hide();
-                    if (list) {
-                        location.href = oilsBasePath + '/acq/po/search/' +
-                            list.join(",");
-                    }
-                }
-            }
-        );
-    } catch(E) {
-        progressDialog.hide();
-        alert(E);
-    }
-}
-
-function updatePoName() {
-    var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
-    if(!value || value == PO.name()) return;
-    PO.name(value);
-    pcrud.update(PO, {
-        oncomplete : function(r, cudResults) {
-            var stat = cudResults[0];
-            if(stat)
-                dojo.byId('acq-po-view-name').innerHTML = value;
-        }
-    });
-}
-
-openils.Util.addOnLoad(init);
+require([
+       "dojo/string",
+       "dijit/layout/ContentPane",
+       "openils/PermaCrud"
+       ],
+function(dojo_string,
+       dijit_layout_ContentPane,
+       openils_PermaCrud){
+       
+       var pcrud = new openils_PermaCrud();
+       var PO = null;
+       var liTable;
+       var poItemTable;
+       var poNoteTable;
+       var invoiceLinkDialogManager;
+       
+       function AcqPoNoteTable() {
+           var self = this;
+       
+           this.notesTbody = dojo.byId("acq-po-notes-tbody");
+           this.notesRow = this.notesTbody.removeChild(dojo.byId("acq-po-notes-row"));
+       
+           dojo.byId("acq-po-notes-back-button").onclick = function() { self.hide(); };
+           dojo.byId("acq-po-view-notes").onclick = function() { self.show(); };
+       
+           /* widgets' event properties are cased likeThis */
+           acqPoCreateNoteSubmit.onClick = function() {
+               if (!acqPoCreateNoteText.attr("value")) return;
+       
+               /* prep new note */
+               var note = new acqpon();
+               note.vendor_public(
+                   Boolean(acqPoCreateNoteVendorPublic.attr('checked'))
+               );
+               note.value(acqPoCreateNoteText.attr("value"));
+               note.purchase_order(PO.id());
+               note.isnew(true);
+       
+               /* save it */
+               self.updatePoNotes(note);
+       
+               /* reset fields for next use */
+               acqPoCreateNoteText.attr("value", "");
+               acqPoCreateNoteVendorPublic.attr("checked", false);
+           };
+       
+           this.drawPoNote = function(note) {
+               if (note.isdeleted())
+                   return;
+       
+               var row = dojo.clone(this.notesRow);
+       
+               nodeByName("value", row).innerHTML = note.value();
+       
+               if (openils.Util.isTrue(note.vendor_public()))
+                   nodeByName("vendor_public", row).innerHTML =
+                       localeStrings.VENDOR_PUBLIC;
+       
+               nodeByName("delete", row).onclick = function() {
+                   note.isdeleted(true);
+                   self.notesTbody.removeChild(row);
+                   self.updatePoNotes();
+               };
+       
+               if (note.edit_time()) {
+                   nodeByName("edit_time", row).innerHTML =
+                       dojo.date.locale.format(
+                           dojo.date.stamp.fromISOString(note.edit_time()),
+                           {"formatLength": "short"}
+                       );
+               }
+       
+               self.notesTbody.appendChild(row);
+           };
+       
+           this.drawPoNotes = function() {
+               /* sort */
+               PO.notes(
+                   PO.notes().sort(
+                       function(a, b) {
+                           return (a.edit_time() < b.edit_time()) ? 1 : -1;
+                       }
+                   )
+               );
+       
+               /* remove old renderings of notes */
+               dojo.empty(this.notesTbody);
+       
+               PO.notes().forEach(function(o) { self.drawPoNote(o); });
+           };
+       
+           this.updatePoNotesCount = function() {
+               dojo.byId("acq-po-view-notes").innerHTML =
+                   "(" + PO.notes().length + ")";
+           };
+       
+           this.updatePoNotes = function(newNote) {
+               var notes = newNote ?
+                   [newNote] :
+                   PO.notes().filter(
+                       function(o) {
+                           if (o.ischanged() || o.isnew() || o.isdeleted())
+                               return o;
+                       }
+                   );
+       
+               if (notes.length < 1)
+                   return;
+       
+               progressDialog.show();
+       
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {
+                       "async": true,
+                       "params": [openils.User.authtoken, notes],
+                       "onresponse": function(r) {
+                           var resp = openils.Util.readResponse(r);
+                           if (resp) {
+                               progressDialog.update(resp);
+       
+                               if (!resp.note.isdeleted()) {
+                                   resp.note.isnew(false);
+                                   resp.note.ischanged(false);
+                                   PO.notes().push(resp.note);
+                               }
+                           }
+                       },
+                       "oncomplete": function() {
+                           if (!newNote) {
+                               /* remove the old changed notes */
+                               var list = [];
+                               PO.notes(
+                                   PO.notes().filter(
+                                       function(o) {
+                                           return (!(
+                                               o.ischanged() || o.isnew() ||
+                                               o.isdeleted()
+                                           ));
+                                       }
+                                   )
+                               );
+                           }
+       
+                           progressDialog.hide();
+                           self.updatePoNotesCount();
+                           self.drawPoNotes();
+                       }
+                   }
+               );
+           };
+       
+           this.hide = function() {
+               openils.Util.hide("acq-po-notes-div");
+               liTable.show("list");
+               poItemTable.show();
+           };
+       
+           this.show = function() {
+               liTable.hide();
+               poItemTable.hide();
+               self.drawPoNotes();
+               openils.Util.show("acq-po-notes-div");
+           };
+       }
+       
+       function updatePoState(po_info) {
+           var data = po_info[PO.id()];
+           if (data) {
+               for (var key in data)
+                   PO[key](data[key]);
+               renderPo();
+           }
+       }
+       
+       function cancellationUpdater(r) {
+           var r = openils.Util.readResponse(r);
+           if (r) {
+               if (r.po) updatePoState(r.po);
+               if (r.li) {
+                   for (var id in r.li) {
+                       liTable.liCache[id].state(r.li[id].state);
+                       liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);
+                       liTable.updateLiState(liTable.liCache[id]);
+                   }
+               }
+               if (r.lid && liTable.copyCache) {
+                   for (var id in r.lid) {
+                       if (liTable.copyCache[id]) {
+                           liTable.copyCache[id].cancel_reason(
+                               r.lid[id].cancel_reason
+                           );
+                           liTable.updateLidState(liTable.copyCache[id]);
+                       }
+                   }
+               }
+           }
+       }
+       
+       function makeProviderLink(node, provider) {
+           return dojo.create(
+               "a", {
+                   "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),
+                   "innerHTML": provider.name() + " (" + provider.code() + ")",
+               },
+               node,
+               "only"
+           );
+       }
+       function makePrepayWidget(node, prepay) {
+           if (prepay) {
+               openils.Util.addCSSClass(node, "oils-acq-po-prepay");
+               node.innerHTML = localeStrings.YES;
+           } else {
+               openils.Util.removeCSSClass(node, "oils-acq-po-prepay");
+               node.innerHTML = localeStrings.NO;
+           }
+       }
+       
+       function makeCancelWidget(node, labelnode) {
+           openils.Util.hide("acq-po-choose-cancel-reason");
+       
+           if (PO.cancel_reason()) {
+               labelnode.innerHTML = localeStrings.CANCEL_REASON;
+               node.innerHTML = PO.cancel_reason().description() + " (" +
+                   PO.cancel_reason().label() + ")";
+           } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {
+               dojo.destroy(this.oldTip);
+               labelnode.innerHTML = "";
+               node.innerHTML = "";
+           } else {
+               dojo.destroy(this.oldTip);
+               labelnode.innerHTML = localeStrings.CANCEL;
+               node.innerHTML = "";
+               if (!acqPoCancelReasonSubmit._prepared) {
+                   var widget = new openils.widget.AutoFieldWidget({
+                       "fmField": "cancel_reason",
+                       "fmClass": "acqpo",
+                       "parentNode": dojo.byId("acq-po-cancel-reason"),
+                       "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
+                       "forceSync": true
+                   });
+                   widget.build(
+                       function(w, ww) {
+                           acqPoCancelReasonSubmit.onClick = function() {
+                               if (w.attr("value")) {
+                                   if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {
+                                       fieldmapper.standardRequest(
+                                           ["open-ils.acq",
+                                               "open-ils.acq.purchase_order.cancel"],
+                                           {
+                                               "params": [
+                                                   openils.User.authtoken,
+                                                   PO.id(), 
+                                                   w.attr("value")
+                                               ],
+                                               "async": true,
+                                               "oncomplete": cancellationUpdater
+                                           }
+                                       );
+                                   }
+                               }
+                           };
+                           acqPoCancelReasonSubmit._prepared = true;
+                       }
+                   );
+               }
+               openils.Util.show("acq-po-choose-cancel-reason", "inline");
+           }
+       }
+       
+       function prepareInvoiceFeatures() {
+           /* show the count of related invoices on the "view invoices" button */
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {
+                   "params": [
+                       openils.User.authtoken,
+                       {"acqpo":[{"id": PO.id()}]},
+                       null,
+                       null,
+                       {"id_list": true}
+                   ],
+                   "async": true,
+                   "oncomplete": function(r) {
+                       dojo.byId("acq-po-view-invoice-count").innerHTML =
+                           openils.Util.readResponse(r).length;
+                   }
+               }
+           );
+       
+           /* view invoices button */
+           dijit.byId("acq-po-view-invoice-link").onClick = function() {
+               location.href = oilsBasePath + "/acq/search/unified?so=" +
+                   base64Encode({"acqpo":[{"id": PO.id()}]}) +
+                   "&rt=invoice";
+           };
+       
+           /* create invoice button */
+           dijit.byId("acq-po-create-invoice-link").onClick = function() {
+               location.href = oilsBasePath +
+                   "/acq/invoice/view?create=1&attach_po=" + PO.id();
+           };
+       
+           openils.Util.show("acq-po-invoice-stuff", "table-cell");
+       }
+       
+       function renderPo() {
+           dojo.byId("acq-po-view-id").innerHTML = PO.id();
+           dojo.byId("acq-po-view-name").innerHTML = PO.name();
+           makeProviderLink(
+               dojo.byId("acq-po-view-provider"),
+               PO.provider()
+           );
+           dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();
+           dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);
+           dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);
+           dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n
+       
+           if(PO.order_date()) {
+               openils.Util.show('acq-po-activated-on', 'inline');
+               dojo.byId('acq-po-activated-on').innerHTML = 
+                   dojo_string.substitute(
+                       localeStrings.PO_ACTIVATED_ON, [
+                           openils.Util.timeStamp(PO.order_date(), {formatLength:'short'})
+                       ]
+                   );
+       
+           }
+       
+           makePrepayWidget(
+               dojo.byId("acq-po-view-prepay"),
+               openils.Util.isTrue(PO.prepayment_required())
+           );
+           makeCancelWidget(
+               dojo.byId("acq-po-view-cancel-reason"),
+               dojo.byId("acq-po-cancel-label")
+           );
+           // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
+           poNoteTable.updatePoNotesCount();
+       
+           if (PO.state() == "pending") {
+               checkCouldActivatePo();
+               if (PO.lineitem_count() > 1)
+                   openils.Util.show("acq-po-split");
+           } else {
+               dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.NO;
+           }
+       
+           // XXX we probably don't *always* need to do this...
+           poItemTable.reset();
+           PO.po_items().forEach(
+               function(po_item) { poItemTable.addItem(po_item); }
+           );
+           poItemTable.show();
+       
+           dojo.attr(
+               "acq-po-view-history", "href",
+               oilsBasePath + "/acq/po/history/" + PO.id()
+           );
+           openils.Util.show("acq-po-view-history", "inline");
+       
+           prepareInvoiceFeatures();
+       }
+       
+       
+       function init() {
+           /* set up li table */
+           liTable = new AcqLiTable();
+           liTable.reset();
+           liTable.isPO = poId;
+           liTable.poUpdateCallback = updatePoState;
+       
+           /* set up po notes table */
+           poNoteTable = new AcqPoNoteTable();
+       
+           /* retrieve data and populate */
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
+               {   async: true,
+                   params: [openils.User.authtoken, poId, {
+                       "flesh_provider": true,
+                       "flesh_price_summary": true,
+                       "flesh_lineitem_count": true,
+                       "flesh_notes": true,
+                       "flesh_po_items": true
+                   }],
+                   oncomplete: function(r) {
+                       PO = openils.Util.readResponse(r); /* save PO globally */
+       
+                       /* po item table */
+                       poItemTable = new PoItemTable(PO, pcrud);
+       
+                       renderPo();
+                   }
+               }
+           );
+       
+           var totalEstimated = 0;
+           var zeroLi = true;
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem.search'],
+               {   async: true,
+                   params: [
+                       openils.User.authtoken, 
+                       [{purchase_order:poId}, {"order_by": {"jub": "id ASC"}}], 
+                       {flesh_attrs:true, flesh_notes:true, flesh_cancel_reason:true, clear_marc:true}
+                   ],
+                   onresponse: function(r) {
+                       zeroLi = false;
+                       liTable.show('list');
+                       var li = openils.Util.readResponse(r);
+                       // TODO: Add po_item's to total estimated amount
+                       totalEstimated += (Number(li.item_count() || 0) * Number(li.estimated_unit_price() || 0));
+                       liTable.addLineitem(li);
+                   },
+       
+                   oncomplete : function() {
+                       dojo.byId("acq-po-view-total-estimated").innerHTML = totalEstimated.toFixed(2);
+                       if (liFocus) liTable.drawCopies(liFocus);
+                       if(zeroLi) openils.Util.show('acq-po-no-lineitems');
+                   }
+               }
+           );
+       
+           pcrud.search(
+               'acqedim', 
+               {purchase_order : poId}, 
+               {
+                   id_list : true,
+                   oncomplete : function(r) {
+                       var resp = openils.Util.readResponse(r);
+                       // TODO: I18n
+                       if(resp) {
+                           dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';
+                           dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);
+                       } else {
+                           dojo.byId('acq-po-view-edi-messages').innerHTML = '0';
+                           dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');
+                       }
+                   }
+               }
+           );
+       }
+       
+       function checkCouldActivatePo() {
+           var d = dojo.byId("acq-po-activate-checking");
+           var a = dojo.byId("acq-po-activate-link");
+           d.innerHTML = localeStrings.PO_CHECKING;
+           var warnings = [];
+           var stops = [];
+           var other = [];
+       
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {
+                   "params": [openils.User.authtoken, PO.id()],
+                   "async": true,
+                   "onresponse": function(r) {
+                       if ((r = openils.Util.readResponse(r, true /* eventOk */))) {
+                           if (typeof(r.textcode) != "undefined") {
+                               switch(r.textcode) {
+                                   case "ACQ_FUND_EXCEEDS_STOP_PERCENT":
+                                       stops.push(r);
+                                       break;
+                                   case "ACQ_FUND_EXCEEDS_WARN_PERCENT":
+                                       warnings.push(r);
+                                       break;
+                                   default:
+                                       other.push(r);
+                               }
+                           }
+                       }
+                   },
+                   "oncomplete": function() {
+                       /* XXX in the future, this might be tweaked to display info
+                        * about more than one stop or warning event from the ML. */
+                       if (!(warnings.length || stops.length || other.length)) {
+                           d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
+                           openils.Util.show(a, "inline");
+                       } else {
+                           if (other.length) {
+                               /* XXX make the textcode part a tooltip one day */
+                               d.innerHTML = localeStrings.NO + ": " +
+                                   other[0].desc + " (" + other[0].textcode + ")";
+                               openils.Util.hide(a);
+                           } else if (stops.length) {
+                               d.innerHTML =
+                                   dojo_string.substitute(
+                                       localeStrings.PO_STOP_BLOCKS_ACTIVATION, [
+                                           stops[0].payload.fund.code(),
+                                           stops[0].payload.fund.year()
+                                       ]
+                                   );
+                               openils.Util.hide(a);
+                           } else {
+                               PO._warning_hack = true;
+                               d.innerHTML =
+                                   dojo_string.substitute(
+                                       localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [
+                                           warnings[0].payload.fund.code(),
+                                           warnings[0].payload.fund.year()
+                                       ]
+                                   );
+                               openils.Util.show(a, "inline");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       function activatePo() {
+           if (openils.Util.isTrue(PO.prepayment_required())) {
+               if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))
+                   return false;
+           }
+       
+           if (PO._warning_hack) {
+               if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))
+                   return false;
+           }
+       
+           liTable.showAssetCreator(activatePoStage2);
+       }
+       
+       function activatePoStage2() {
+       
+           var want_refresh = false;
+           progressDialog.show(true);
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {
+                   "async": true,
+                   "params": [openils.User.authtoken, PO.id()],
+                   "onresponse": function(r) {
+                       want_refresh = Boolean(openils.Util.readResponse(r));
+                   },
+                   "oncomplete": function() {
+                       progressDialog.hide();
+                       if (want_refresh)
+                           location.href = location.href;
+                   }
+               }
+           );
+       }
+       
+       function splitPo() {
+           progressDialog.show(true);
+           try {
+               var list;
+               fieldmapper.standardRequest(
+                   ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
+                   {   async: true,
+                       params: [openils.User.authtoken, PO.id()],
+                       onresponse : function(r) {
+                           list = openils.Util.readResponse(r);
+                       },
+                       oncomplete : function() {
+                           progressDialog.hide();
+                           if (list) {
+                               location.href = oilsBasePath + '/acq/po/search/' +
+                                   list.join(",");
+                           }
+                       }
+                   }
+               );
+           } catch(E) {
+               progressDialog.hide();
+               alert(E);
+           }
+       }
+       
+       function updatePoName() {
+           var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
+           if(!value || value == PO.name()) return;
+           PO.name(value);
+           pcrud.update(PO, {
+               oncomplete : function(r, cudResults) {
+                   var stat = cudResults[0];
+                   if(stat)
+                       dojo.byId('acq-po-view-name').innerHTML = value;
+               }
+           });
+       }
+       
+       openils.Util.addOnLoad(init);
+       
+
+});
\ No newline at end of file
index 8c7a8b8..968ec1d 100644 (file)
@@ -1,62 +1,79 @@
-dojo.require('fieldmapper.Fieldmapper');
-dojo.require('dijit.ProgressBar');
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.Button');
-dojo.require("dijit.Dialog");
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.acq.Lineitem');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var lineitems = [];
-
-function drawForm() {
-    new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', orderingAgencySelect);
-}
-
-var liReceived;
-function doSearch(values) {
-
-    var search = {
-        attr_values : [values.identifier],
-        po_agencies : (values.ordering_agency) ? [values.ordering_agency] : null,
-        li_states : ['in-process']
-    };
-
-    options = {clear_marc:1, flesh_attrs:1};
-    liReceived = 0;
-    dojo.style('searchProgress', 'visibility', 'visible');
-
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.lineitem.search.ident'],
-        {   async: true,
-            params: [openils.User.authtoken, search, options],
-            onresponse: handleResult,
-            oncomplete: viewList
-        }
-    );
-}
-
-var searchLimit = 10; // ?
-function handleResult(r) {
-    var result = r.recv().content();
-    searchProgress.update({maximum: searchLimit, progress: ++liReceived});
-    lineitems.push(result);
-}
-
-function viewList() {
-    dojo.style('searchProgress', 'visibility', 'hidden');
-    dojo.style('oils-acq-recv-grid', 'visibility', 'visible');
-    var store = new dojo.data.ItemFileWriteStore(
-        {data:jub.toStoreData(lineitems, null, 
-            {virtualFields:['estimated_price', 'actual_price']})});
-    var model = new dojox.grid.data.DojoData(
-        null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
-    JUBGrid.populate(liGrid, model, lineitems);
-}
-
-openils.Util.addOnLoad(drawForm);
+require([
+       "fieldmapper/Fieldmapper",
+       "dijit/ProgressBar",
+       "dijit/form/Form",
+       "dijit/form/TextBox",
+       "dijit/form/CheckBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Button",
+       "dijit/Dialog",
+       "openils/Event",
+       "openils/Util",
+       "openils/acq/Lineitem",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(fieldmapper_Fieldmapper,
+       dijit_ProgressBar,
+       dijit_form_Form,
+       dijit_form_TextBox,
+       dijit_form_CheckBox,
+       dijit_form_FilteringSelect,
+       dijit_form_Button,
+       dijit_Dialog,
+       openils_Event,
+       openils_Util,
+       openils_acq_Lineitem,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var lineitems = [];
+       
+       function drawForm() {
+           new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', orderingAgencySelect);
+       }
+       
+       var liReceived;
+       function doSearch(values) {
+       
+           var search = {
+               attr_values : [values.identifier],
+               po_agencies : (values.ordering_agency) ? [values.ordering_agency] : null,
+               li_states : ['in-process']
+           };
+       
+           options = {clear_marc:1, flesh_attrs:1};
+           liReceived = 0;
+           dojo.style('searchProgress', 'visibility', 'visible');
+       
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.lineitem.search.ident'],
+               {   async: true,
+                   params: [openils.User.authtoken, search, options],
+                   onresponse: handleResult,
+                   oncomplete: viewList
+               }
+           );
+       }
+       
+       var searchLimit = 10; // ?
+       function handleResult(r) {
+           var result = r.recv().content();
+           searchProgress.update({maximum: searchLimit, progress: ++liReceived});
+           lineitems.push(result);
+       }
+       
+       function viewList() {
+           dojo.style('searchProgress', 'visibility', 'hidden');
+           dojo.style('oils-acq-recv-grid', 'visibility', 'visible');
+           var store = new dojo.data.ItemFileWriteStore(
+               {data:jub.toStoreData(lineitems, null, 
+                   {virtualFields:['estimated_price', 'actual_price']})});
+           var model = new dojox.grid.data.DojoData(
+               null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+           JUBGrid.populate(liGrid, model, lineitems);
+       }
+       
+       openils_Util.addOnLoad(drawForm);
+       
+       
 
+});
\ No newline at end of file
index a5dc460..5f7e420 100644 (file)
@@ -1,50 +1,56 @@
-dojo.require('openils.widget.ProgressDialog');
+require([
+       "openils/widget/ProgressDialog"
+       ],
+function(openils_widget_ProgressDialog){
+       
+       function getInvIdent(rowIndex, item) {
+           if (item) {
+               return {
+                   "inv_ident": this.grid.store.getValue(item, "inv_ident") ||
+                       this.grid.store.getValue(item, "id"),
+                   "id": this.grid.store.getValue(item, "id")
+               };
+           }
+       }
+       
+       function formatInvIdent(inv) {
+           if (inv) {
+               return "<a href='" + oilsBasePath + "/acq/invoice/view/" +
+                   inv.id + "'>" + inv.inv_ident + "</a>";
+           }
+       }
+       
+       function printInvoiceVouchers() {
+           var inv_ids = dijit.byId("acq-unified-inv-grid").
+               getSelectedItems().map(function(o) {return o.id[0];});
+       
+           progressDialog.show(true);
+       
+           var html;
+           if (inv_ids.length) {
+               var win = null;
+               fieldmapper.standardRequest(
+                   ["open-ils.acq", "open-ils.acq.invoice.print.html"], {
+                       "params": [openils.User.authtoken, inv_ids],
+                       "async": true,
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               if(!html) {
+                                   html = "<style type='text/css'>.acq-invoice-" +
+                                       "voucher {page-break-after:always;}" +
+                                       "</style>\n";
+                               }
+                               html += r.template_output().data();
+                           }
+                       },
+                       "oncomplete": function() { 
+                           progressDialog.hide();
+                           openils.Util.printHtmlString(html);
+                       }
+                   }
+               );
+           }
+       }
+       
 
-function getInvIdent(rowIndex, item) {
-    if (item) {
-        return {
-            "inv_ident": this.grid.store.getValue(item, "inv_ident") ||
-                this.grid.store.getValue(item, "id"),
-            "id": this.grid.store.getValue(item, "id")
-        };
-    }
-}
-
-function formatInvIdent(inv) {
-    if (inv) {
-        return "<a href='" + oilsBasePath + "/acq/invoice/view/" +
-            inv.id + "'>" + inv.inv_ident + "</a>";
-    }
-}
-
-function printInvoiceVouchers() {
-    var inv_ids = dijit.byId("acq-unified-inv-grid").
-        getSelectedItems().map(function(o) {return o.id[0];});
-
-    progressDialog.show(true);
-
-    var html;
-    if (inv_ids.length) {
-        var win = null;
-        fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.invoice.print.html"], {
-                "params": [openils.User.authtoken, inv_ids],
-                "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        if(!html) {
-                            html = "<style type='text/css'>.acq-invoice-" +
-                                "voucher {page-break-after:always;}" +
-                                "</style>\n";
-                        }
-                        html += r.template_output().data();
-                    }
-                },
-                "oncomplete": function() { 
-                    progressDialog.hide();
-                    openils.Util.printHtmlString(html);
-                }
-            }
-        );
-    }
-}
+});
\ No newline at end of file
index 61da13f..cdf7913 100644 (file)
-dojo.require("dojo.data.ItemFileWriteStore");
-dojo.require("dijit.Dialog");
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.TextBox");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("dijit.form.Button");
-dojo.require("dojox.grid.cells.dijit");
-dojo.require("openils.acq.Picklist");
-dojo.require("openils.widget.ProgressDialog");
-
-function getPlOwnerName(rowIndex, item) {
-    try {
-        return resultManager.plCache[this.grid.store.getValue(item, "id")].
-            owner().usrname();
-    } catch (E) {
-        return "";
-    }
-}
-
-function formatPlName(pl) {
-    if (pl) {
-        return "<a href='" + oilsBasePath + "/acq/picklist/view/" +
-            pl.id + "'>" + pl.name + "</a>";
-    }
-}
-
-function deleteSelectedPl() {
-    var grid = resultManager.result_types.picklist.interface;
-
-    progressDialog.show(true);
-
-    openils.acq.Picklist.deleteList(
-        grid.getSelectedItems().map(
-            function(item) {
-                var id = grid.store.getValue(item, "id");
-                grid.store.deleteItem(item);
-                return id;
-            }
-        ), function() { progressDialog.hide(); }
-    );
-}
-
-function cloneSelectedPl(fields) {
-    var grid = resultManager.result_types.picklist.interface;
-
-    var item = grid.getSelectedItems()[0];
-    if (!item) return;
-
-    var plId = grid.store.getValue(item, "id");
-    var entryCount = Number(grid.store.getValue(item, "entry_count"));
-
-    progressDialog.show();
-    progressDialog.update({"maximum": entryCount, "progress": 0});
-
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.picklist.clone"], {
-            "async": true,
-            "params": [openils.User.authtoken, plId, fields.name],
-            "onresponse": function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp) {
-                    progressDialog.update({"progress": resp.li});
-
-                    if (resp.complete) {
-                        progressDialog.hide();
-                        var pl = resp.picklist;
-                        pl.owner(openils.User.user);
-                        pl.entry_count(entryCount);
-                        resultManager.plCache[pl.id()] = pl;
-                        grid.store.newItem(fieldmapper.acqpl.toStoreItem(pl));
-                    }
-                }
-            }
-        }
-    );
-}
-
-function loadLeadPlSelector() {
-    var grid = resultManager.result_types.picklist.interface;
-    var data = acqpl.initStoreData();
-    var store = new dojo.data.ItemFileWriteStore({"data": data});
-
-    grid.getSelectedItems().forEach(
-        function(item) {
-            store.newItem(
-                fieldmapper.acqpl.toStoreItem(
-                    resultManager.plCache[grid.store.getValue(item, "id")]
-                )
-            );
-        }
-    );
-
-    plMergeLeadSelector.store = store;
-    plMergeLeadSelector.startup();
-}
-
-function mergeSelectedPl(fields) {
-    var grid = resultManager.result_types.picklist.interface;
-
-    if (!fields.lead) return;
-
-    var ids = [];
-    var totalLi = 0;
-    var leadPl = resultManager.plCache[fields.lead];
-    var leadPlItem;
-
-    grid.getSelectedItems().forEach(
-        function(item) {
-            var id = grid.store.getValue(item, "id");
-            if (id == fields.lead) {
-                leadPlItem = item;
-                return;
-            }
-            totalLi +=  new Number(grid.store.getValue(item, "entry_count"));
-            ids.push(id);
-        }
-    );
-
-    progressDialog.show();
-    progressDialog.update({"maximum": totalLi, "progress": 0});
-
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.picklist.merge"], {
-            "async": true,
-            "params": [openils.User.authtoken, fields.lead, ids],
-            "onresponse": function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp) {
-                    if (resp.li)
-                        progressDialog.update({"progress": resp.li});
-
-                    if (resp.complete) {
-                        progressDialog.hide();
-                        leadPl.entry_count(leadPl.entry_count() + totalLi);
-
-                        grid.store.setValue(
-                            leadPlItem, "entry_count", leadPl.entry_count()
-                        );
-                        if (resp.picklist) {
-                            grid.store.setValue(
-                                leadPlItem, "edit_time",
-                                resp.picklist.edit_time()
-                            );
-                        }
-
-                        // remove the deleted lists from the grid
-                        grid.getSelectedItems().filter(
-                            function(o) {
-                                return grid.store.getValue(o, "id") !=
-                                    fields.lead;
-                            }
-                        ).forEach(function(o) { grid.store.deleteItem(o); });
-                    }
-                }
-            }
-        }
-    );
-}
-
-function createPl(fields) {
-    if (fields.name == '') return;
-
-    var grid = resultManager.result_types.picklist.interface;
-
-    openils.acq.Picklist.create(fields,
-        function(plId) {
-            fieldmapper.standardRequest(
-                ["open-ils.acq", "open-ils.acq.picklist.retrieve.authoritative"], {
-                    "async": true,
-                    "params": [
-                        openils.User.authtoken, plId,
-                        {"flesh_lineitem_count": 1, "flesh_owner": 1}
-                    ],
-                    "oncomplete": function(r) {
-                        var pl = openils.Util.readResponse(r);
-                        if (pl) {
-                            resultManager.plCache[pl.id()] = pl;
-                            grid.store.newItem(
-                                acqpl.toStoreData([pl]).items[0]
-                            );
-                        }
-                    }
-                }
-            );
-        }
-    );
-}
-
+require([
+       "dojo/data/ItemFileWriteStore",
+       "dijit/Dialog",
+       "dijit/form/Button",
+       "dijit/form/TextBox",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Button",
+       "dojox/grid/cells/dijit",
+       "openils/acq/Picklist",
+       "openils/widget/ProgressDialog"
+       ],
+function(dojo_data_ItemFileWriteStore,
+       dijit_Dialog,
+       dijit_form_Button,
+       dijit_form_TextBox,
+       dijit_form_FilteringSelect,
+       dijit_form_Button,
+       dojox_grid_cells_dijit,
+       openils_acq_Picklist,
+       openils_widget_ProgressDialog){
+       
+       function getPlOwnerName(rowIndex, item) {
+           try {
+               return resultManager.plCache[this.grid.store.getValue(item, "id")].
+                   owner().usrname();
+           } catch (E) {
+               return "";
+           }
+       }
+       
+       function formatPlName(pl) {
+           if (pl) {
+               return "<a href='" + oilsBasePath + "/acq/picklist/view/" +
+                   pl.id + "'>" + pl.name + "</a>";
+           }
+       }
+       
+       function deleteSelectedPl() {
+           var grid = resultManager.result_types.picklist.interface;
+       
+           progressDialog.show(true);
+       
+           openils_acq_Picklist.deleteList(
+               grid.getSelectedItems().map(
+                   function(item) {
+                       var id = grid.store.getValue(item, "id");
+                       grid.store.deleteItem(item);
+                       return id;
+                   }
+               ), function() { progressDialog.hide(); }
+           );
+       }
+       
+       function cloneSelectedPl(fields) {
+           var grid = resultManager.result_types.picklist.interface;
+       
+           var item = grid.getSelectedItems()[0];
+           if (!item) return;
+       
+           var plId = grid.store.getValue(item, "id");
+           var entryCount = Number(grid.store.getValue(item, "entry_count"));
+       
+           progressDialog.show();
+           progressDialog.update({"maximum": entryCount, "progress": 0});
+       
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.picklist.clone"], {
+                   "async": true,
+                   "params": [openils.User.authtoken, plId, fields.name],
+                   "onresponse": function(r) {
+                       var resp = openils.Util.readResponse(r);
+                       if (resp) {
+                           progressDialog.update({"progress": resp.li});
+       
+                           if (resp.complete) {
+                               progressDialog.hide();
+                               var pl = resp.picklist;
+                               pl.owner(openils.User.user);
+                               pl.entry_count(entryCount);
+                               resultManager.plCache[pl.id()] = pl;
+                               grid.store.newItem(fieldmapper.acqpl.toStoreItem(pl));
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       function loadLeadPlSelector() {
+           var grid = resultManager.result_types.picklist.interface;
+           var data = acqpl.initStoreData();
+           var store = new dojo_data_ItemFileWriteStore({"data": data});
+       
+           grid.getSelectedItems().forEach(
+               function(item) {
+                   store.newItem(
+                       fieldmapper.acqpl.toStoreItem(
+                           resultManager.plCache[grid.store.getValue(item, "id")]
+                       )
+                   );
+               }
+           );
+       
+           plMergeLeadSelector.store = store;
+           plMergeLeadSelector.startup();
+       }
+       
+       function mergeSelectedPl(fields) {
+           var grid = resultManager.result_types.picklist.interface;
+       
+           if (!fields.lead) return;
+       
+           var ids = [];
+           var totalLi = 0;
+           var leadPl = resultManager.plCache[fields.lead];
+           var leadPlItem;
+       
+           grid.getSelectedItems().forEach(
+               function(item) {
+                   var id = grid.store.getValue(item, "id");
+                   if (id == fields.lead) {
+                       leadPlItem = item;
+                       return;
+                   }
+                   totalLi +=  new Number(grid.store.getValue(item, "entry_count"));
+                   ids.push(id);
+               }
+           );
+       
+           progressDialog.show();
+           progressDialog.update({"maximum": totalLi, "progress": 0});
+       
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.picklist.merge"], {
+                   "async": true,
+                   "params": [openils.User.authtoken, fields.lead, ids],
+                   "onresponse": function(r) {
+                       var resp = openils.Util.readResponse(r);
+                       if (resp) {
+                           if (resp.li)
+                               progressDialog.update({"progress": resp.li});
+       
+                           if (resp.complete) {
+                               progressDialog.hide();
+                               leadPl.entry_count(leadPl.entry_count() + totalLi);
+       
+                               grid.store.setValue(
+                                   leadPlItem, "entry_count", leadPl.entry_count()
+                               );
+                               if (resp.picklist) {
+                                   grid.store.setValue(
+                                       leadPlItem, "edit_time",
+                                       resp.picklist.edit_time()
+                                   );
+                               }
+       
+                               // remove the deleted lists from the grid
+                               grid.getSelectedItems().filter(
+                                   function(o) {
+                                       return grid.store.getValue(o, "id") !=
+                                           fields.lead;
+                                   }
+                               ).forEach(function(o) { grid.store.deleteItem(o); });
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       function createPl(fields) {
+           if (fields.name == '') return;
+       
+           var grid = resultManager.result_types.picklist.interface;
+       
+           openils_acq_Picklist.create(fields,
+               function(plId) {
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq", "open-ils.acq.picklist.retrieve.authoritative"], {
+                           "async": true,
+                           "params": [
+                               openils.User.authtoken, plId,
+                               {"flesh_lineitem_count": 1, "flesh_owner": 1}
+                           ],
+                           "oncomplete": function(r) {
+                               var pl = openils.Util.readResponse(r);
+                               if (pl) {
+                                   resultManager.plCache[pl.id()] = pl;
+                                   grid.store.newItem(
+                                       acqpl.toStoreData([pl]).items[0]
+                                   );
+                               }
+                           }
+                       }
+                   );
+               }
+           );
+       }
+       
+       
+
+});
\ No newline at end of file
index 02e1d68..3c68d5c 100644 (file)
-dojo.require("dojo.date.stamp");
-dojo.require("dojox.encoding.base64");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.AutoWidget");
-dojo.require("openils.widget.XULTermLoader");
-dojo.require("openils.PermaCrud");
-
-if (!localeStrings) {   /* we can do this because javascript doesn't have block 
-                           scope */
-    dojo.requireLocalization('openils.acq', 'acq');
-    var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
-}
-
-var termSelectorFactory;
-var termManager;
-var resultManager;
-var uriManager;
-var pcrud = new openils.PermaCrud();
-var cgi = new openils.CGI();
-
-/* typing save: add {get,set}Value() to all HTML <select> elements */
-HTMLSelectElement.prototype.getValue = function() {
-    return this.options[this.selectedIndex].value;
-}
-
-/* only sets the selected value if such an option is actually available */
-HTMLSelectElement.prototype.setValue = function(s) {
-    for (var i = 0; i < this.options.length; i++) {
-        if (s == this.options[i].value) {
-            this.selectedIndex = i;
-            break;
-        }
-    }
-}
-
-/* minor formatting function used by autogrids in unified.tt2 */
-function getName(rowIndex, item) {
-    if (item) {
-        return {
-            "name": this.grid.store.getValue(item, "name") ||
-                localeStrings.UNNAMED,
-            "id": this.grid.store.getValue(item, "id")
-        };
-    }
-}
-
-/* quickly find elements by the value of a "name" attribute */
-function nodeByName(name, root) {
-    return dojo.query("[name='" + name + "']", root)[0];
-}
-
-function hideForm() {
-    openils.Util.hide("acq-unified-hide-form");
-    openils.Util.show("acq-unified-reveal-form", "inline");
-    openils.Util.hide("acq-unified-form");
-}
-
-function revealForm() {
-    openils.Util.hide("acq-unified-reveal-form");
-    openils.Util.show("acq-unified-hide-form", "inline");
-    openils.Util.show("acq-unified-form");
-}
-
-/* The TermSelectorFactory will be instantiated by the TermManager. It
- * provides HTML select controls whose options are all the searchable
- * fields.  Selecting a field from one of these controls will create the
- * appopriate type of corresponding widget for the user to enter a search
- * term against the selected field.
- */
-function TermSelectorFactory(terms) {
-    var self = this;
-    this.terms = terms;
-    this.onlyBibFriendly = false;
-
-    this.template = dojo.create("select");
-    this.template.appendChild(
-        dojo.create("option", {
-            "disabled": "disabled",
-            "selected": "selected",
-            "value": "",
-            "innerHTML": localeStrings.SELECT_SEARCH_FIELD
-        })
-    );
-
-    /* Create abbreviations for class names to make field categories
-     * more readable in field selector control. */
-    this._abbreviate = function(s) {
-        var last, result;
-        for (var i = 0; i < s.length; i++) {
-            if (s[i] != " ") {
-                if (!i) result = s[i];
-                else if (last == " ") result += s[i];
-            }
-            last = s[i];
-        }
-        return result;
-    };
-
-    var selectorMethods = {
-        /* Important: within the following functions, "this" refers to one
-         * HTMLSelect object, and "self" refers to the TermSelectorFactory. */
-        "getTerm": function() { return this.valueToTerm(this.getValue()); },
-        "valueToTerm": function(value) {
-            var parts = value.split(":");
-            if (!parts || parts.length != 2) return null;
-            return dojo.mixin(
-                self.terms[parts[0]][parts[1]],
-                {"hint": parts[0], "field": parts[1]}
-            );
-        },
-        "onlyBibFriendly": function(yes) {
-            if (yes) {
-                for (var i = 0; i < this.options.length; i++) {
-                    if (this.options[i].value) {
-                        var term = this.valueToTerm(this.options[i].value);
-                        this.options[i].disabled = !term.bib_friendly;
-                    }
-                }
-            } else {
-                for (var i = 0; i < this.options.length; i++) {
-                    if (this.options[i].value)
-                        this.options[i].disabled = false;
-                }
-            }
-        },
-        "makeWidget": function(
-            parentNode, wStore, matchHow, value, noFocus, callback
-        ) {
-            var term = this.getTerm();
-            var widgetKey = this.uniq;
-            if (matchHow.getValue() == "__in") {
-                new openils.widget.XULTermLoader({
-                    "parentNode": parentNode
-                }).build(
-                    function(w) {
-                        wStore[widgetKey] = w;
-                        if (typeof(callback) == "function")
-                            callback(term, widgetKey);
-                        if (typeof(value) != "undefined")
-                            w.attr("value", value);
-                        /* I would love for the following call not to be
-                         * necessary, so that updating the value of the dijit
-                         * would lead to this automatically, but I can't yet
-                         * figure out the correct way to do this in Dojo.
-                         */
-                        w.updateCount();
-                    }
-                );
-            } else if (term.hint == "acqlia" ||
-                (term.hint == "jub" && term.field == "eg_bib_id")) {
-                /* The test for jub.eg_bib_id is a special case to prevent
-                 * AutoFieldWidget from trying to render a ridiculous dropdown
-                 * of every bib record ID in the system. */
-                wStore[widgetKey] = dojo.create(
-                    "input", {"type": "text"}, parentNode, "only"
-                );
-                if (typeof(value) != "undefined")
-                    wStore[widgetKey].value = value;
-                if (!noFocus)
-                    wStore[widgetKey].focus();
-                if (typeof(callback) == "function")
-                    callback(term, widgetKey);
-            } else {
-                new openils.widget.AutoFieldWidget({
-                    "fmClass": term.hint,
-                    "fmField": term.field,
-                    "noDisablePkey": true,
-                    "parentNode": dojo.create("span", null, parentNode, "only")
-                }).build(
-                    function(w) {
-                        wStore[widgetKey] = w;
-                        if (typeof(value) != "undefined") {
-                            if (w.declaredClass.match(/Check/))
-                                w.attr("checked", value == "t");
-                            else
-                                w.attr("value", value);
-                        }
-                        if (!noFocus)
-                            w.focus();
-                        if (typeof(callback) == "function")
-                            callback(term, widgetKey);
-
-                        // submit on enter
-                        openils.Util.registerEnterHandler(w.domNode,
-                            function() { 
-                                resultManager.go(termManager.buildSearchObject());
-                            }
-                        );
-                    }
-                );
-            }
-        }
-    }
-
-    for (var hint in this.terms) {
-        var optgroup = dojo.create(
-            "optgroup", {"value": "", "label": this.terms[hint].__label}
-        );
-        var prefix = this._abbreviate(this.terms[hint].__label);
-
-        for (var field in this.terms[hint]) {
-            if (!/^__/.test(field)) {
-                optgroup.appendChild(
-                    dojo.create("option", {
-                        "class": "acq-unified-option-regular",
-                        "value": hint + ":" + field,
-                        "innerHTML": prefix + " - " +
-                            this.terms[hint][field].label
-                    })
-                );
-            }
-        }
-
-        this.template.appendChild(optgroup);
-    }
-
-    this.make = function(n) {
-        var node = dojo.clone(this.template);
-        node.uniq = n;
-        dojo.attr(node, "id", "term-" + n);
-        for (var name in selectorMethods)
-            node[name] = selectorMethods[name];
-        if (this.onlyBibFriendly)
-            node.onlyBibFriendly(true);
-        return node;
-    };
-}
-
-/* The term manager retrieves information from the IDL about all the fields
- * in the classes that we consider searchable for our purpose.  It maintains
- * a dynamic HTML table of search terms, using the TermSelectorFactory
- * to generate search field selectors, which in turn provide appropriate
- * widgets for entering search terms.  The TermManager provides search term
- * modifiers (fuzzy searching, not searching). The TermManager also handles
- * adding and removing rows of search terms, as well as building the search
- * query to pass to the middle layer from the search term widgets.
- */
-function TermManager() {
-    var self = this;
-
-    /* All the keys in this object are bib-search-friendly attributes, but the
-     * boolean values indicate whether they should be searched by their
-     * field name as such, or simply mapped to "keyword". */
-    this.bibFriendlyAttrNames = {
-        "author": true, "title": true,
-        "isbn": false, "issn": false, "upc": false
-    };
-
-    this.terms = {};
-    ["jub", "acqpl", "acqpo", "acqinv"].forEach(
-        function(hint) {
-            var o = {};
-            o.__label = fieldmapper.IDL.fmclasses[hint].label;
-            fieldmapper.IDL.fmclasses[hint].fields.forEach(
-                function(field) {
-                    if (!field.virtual) {
-                        o[field.name] = {
-                            "label": field.label, "datatype": field.datatype
-                        };
-                    }
-                }
-            );
-            self.terms[hint] = o;
-        }
-    );
-
-    this.terms.acqlia = {"__label": fieldmapper.IDL.fmclasses.acqlia.label};
-    pcrud.retrieveAll("acqliad", {"order_by": {"acqliad": "id"}}).forEach(
-        function(def) {
-            self.terms.acqlia[def.id()] = {
-                "label": def.description(),
-                "datatype": "text",
-                "bib_friendly":
-                    (typeof(self.bibFriendlyAttrNames[def.code()]) !=
-                        "undefined"),
-                "bib_attr_name":
-                    self.bibFriendlyAttrNames[def.code()] ?
-                        def.code() : "keyword"
-            };
-        }
-    );
-
-    this.selectorFactory = new TermSelectorFactory(this.terms);
-    this.template = dojo.byId("acq-unified-terms-tbody").
-        removeChild(dojo.byId("acq-unified-terms-row-tmpl"));
-    dojo.attr(this.template, "id");
-
-    this.lastResultType = null;
-
-    this.rowId = 0;
-    this.widgets = {};
-
-    dojo.byId("acq-unified-result-type").onchange = function() {
-        self.resultTypeChange(this.getValue());
-    };
-
-    this.allRowIds = function() {
-        return dojo.query("tr[id^='term-row-']", "acq-unified-terms-tbody").
-            map(function(o) { return o.id.match(/^term-row-(\d+)$/)[1]; });
-    };
-
-    this._row = function(id) { return dojo.byId("term-row-" + id); };
-    this._selector = function(id) { return dojo.byId("term-" + id); };
-    this._match_how = function(id) { return dojo.byId("term-match-" + id); };
-
-    this._updateMatchHowForField = function(term, key) {
-        /* NOTE important to use self, not this, in this function.
-         *
-         * Based on the selected field (its datatype and the kind of widget
-         * that AutoFieldWidget provides for it) we update the possible
-         * choices in the mach_how selector.
-         */
-        var w = self.widgets[key];
-        var can_do_fuzzy, can_do_in;
-        if (term.datatype == "id") {
-            can_do_fuzzy = false;
-            can_do_in = true;
-        } else if (term.datatype == "link") {
-            var target = self.getLinkTarget(term);
-            can_do_fuzzy = (target == "au");
-            can_do_in = (target == "bre"); /* XXX might revise later */
-        } else if (typeof(w.declaredClass) != "undefined") {
-            can_do_fuzzy = can_do_in =
-                Boolean(w.declaredClass.match(/form\.Text|XULT/));
-        } else {
-            var type = dojo.attr(w, "type");
-            if (type)
-                can_do_fuzzy = can_do_in = (type == "text");
-            else
-                can_do_fuzzy = can_do_in = false;
-        }
-
-        self.matchHowAllow(key, "__fuzzy", can_do_fuzzy);
-        self.matchHowAllow(key, "__in", can_do_in);
-
-        var inequalities = (term.datatype == "timestamp");
-        self.matchHowAllow(key, "__gte", inequalities);
-        self.matchHowAllow(key, "__lte", inequalities);
-    };
-
-    this.removerButton = function(n) {
-        return dojo.create("button", {
-            "innerHTML": "X",
-            "class": "acq-unified-remover",
-            "onclick": function() { self.removeRow(n); }
-        });
-    };
-
-    this.matchHowAllow = function(where, what, which, exact) {
-        dojo.query(
-            "option[value" + (exact ? "" : "*") + "='" + what + "']",
-            typeof(where) == "object" ? where : this._match_how(where)
-        ).forEach(function(o) { o.disabled = !which; });
-    };
-
-    this.getLinkTarget = function(term) {
-        return fieldmapper.IDL.fmclasses[term.hint].
-            field_map[term.field]["class"];
-    };
-
-    this.updateRowWidget = function(id, value, noFocus) {
-        var where = nodeByName("widget", this._row(id));
-
-        delete this.widgets[id];
-        dojo.empty(where);
-
-        this._selector(id).makeWidget(
-            where, this.widgets, this._match_how(id), value, noFocus,
-            this._updateMatchHowForField
-        );
-    };
-
-    this.resultTypeChange = function(resultType) {
-        if (
-            this.lastResultType == "lineitem_and_bib" &&
-            resultType != "lineitem_and_bib"
-        ) {
-            /* Re-enable all non-bib-friendly fields in all search term
-             * field selectors. */
-            this.allRowIds().forEach(
-                function(id) {
-                    self._selector(id).onlyBibFriendly(false);
-                    self.matchHowAllow(id, "", true, /* exact */ true);
-                    self.matchHowAllow(id, "__not", true, /* exact */ true);
-                }
-            );
-            /* Tell the selector factory to create new search term field
-             * selectors with all fields, not just bib-friendly ones. */
-            this.selectorFactory.onlyBibFriendly = false;
-        } else if (
-            this.lastResultType != "lineitem_and_bib" &&
-            resultType == "lineitem_and_bib"
-        ) {
-            /* Remove all search term rows set to non-bib-friendly fields. */
-            this.allRowIds().forEach(
-                function(id) {
-                    var term = self._selector(id).getTerm();
-                    if (term &&
-                        !self.terms[term.hint][term.field].bib_friendly) {
-                        self.removeRow(id);
-                    }
-                }
-            );
-            /* Disable all non-bib-friendly fields in all remaining search term
-             * field selectors. */
-            this.allRowIds().forEach(
-                function(id) {
-                    self._selector(id).onlyBibFriendly(true);
-                    self.matchHowAllow(id, "", false, /* exact */ true);
-                    self.matchHowAllow(id, "__not", false, /* exact */ true);
-                }
-            );
-            /* Tell the selector factory to create new search term field
-             * selectors with only bib friendly options. */
-            this.selectorFactory.onlyBibFriendly = true;
-        }
-        this.lastResultType = resultType;
-    };
-
-    /* this method is particularly kludgy... puts back together a string
-     * based on object properties that might arrive in indeterminate order. */
-    this._term_reverse_match_how = function(term) {
-        /* only two-key combination we use */
-        if (term.__not && term.__fuzzy)
-            return "__not,__fuzzy";
-
-        /* only other possibilities are single-key or no key */
-        for (var key in term) {
-            if (/^__/.test(key))
-                return key;
-        }
-
-        return null;
-    };
-
-
-    this._term_reverse_selector_field = function(term) {
-        for (var key in term) {
-            if (!/^__/.test(key))
-                return key;
-        }
-        return null;
-    };
-
-    this._term_reverse_selector_value = function(term) {
-        for (var key in term) {
-            if (!/^__/.test(key))
-                return term[key];
-        }
-        return null;
-    };
-
-    this.addRow = function(term, hint) {
-        var uniq = (this.rowId)++;
-
-        var row = dojo.clone(this.template);
-        dojo.attr(row, "id", "term-row-" + uniq);
-
-        var selector = this.selectorFactory.make(uniq);
-        dojo.attr(
-            selector, "onchange", function() { self.updateRowWidget(uniq); }
-        );
-
-        var match_how = dojo.query("select", nodeByName("match", row))[0];
-        dojo.attr(match_how, "id", "term-match-" + uniq);
-        dojo.attr(match_how, "selectedIndex", 0);
-        dojo.attr(
-            match_how, "onchange",
-            function() {
-                if (this.getValue() == "__in") {
-                    self.updateRowWidget(uniq);
-                    this.was_in = true;
-                } else if (this.was_in) {
-                    self.updateRowWidget(uniq);
-                    this.was_in = false;
-                }
-                if (self.widgets[uniq]) self.widgets[uniq].focus();
-            }
-        );
-
-        /* Kind of inelegant; could be improved: this section turns off
-         * match-type options that don't apply to bib searching. */
-        this.matchHowAllow(
-            match_how, "",
-            !this.selectorFactory.onlyBibFriendly, /* exact */ true
-        );
-        this.matchHowAllow(
-            match_how, "__not",
-            !this.selectorFactory.onlyBibFriendly, /* exact */ true
-        );
-        if (this.selectorFactory.onlyBibFriendly)
-            match_how.setValue("__fuzzy");
-
-        nodeByName("selector", row).appendChild(selector);
-        nodeByName("remove", row).appendChild(this.removerButton(uniq));
-
-        dojo.place(row, "acq-unified-terms-tbody", "last");
-
-        if (term && hint) {
-            var attr = this._term_reverse_selector_field(term);
-            var field = hint + ":" + attr;
-            selector.setValue(field);
-
-            var match_how_value = this._term_reverse_match_how(term);
-            if (match_how_value)
-                match_how.setValue(match_how_value);
-
-            var value = this._term_reverse_selector_value(term);
-            if (this.terms[hint][attr].datatype == "timestamp")
-                value = dojo.date.stamp.fromISOString(value);
-            this.updateRowWidget(uniq, value, /* noFocus */ true);
-
-        }
-    }
-
-    this.removeRow = function(id) {
-        delete this.widgets[id];
-        dojo.destroy(this._row(id));
-    };
-
-    this.reflect = function(search_object) {
-        for (var hint in search_object) {
-            search_object[hint].forEach(
-                function(term) { self.addRow(term, hint); }
-            );
-        }
-    };
-
-    this.buildSearchObject = function() {
-        var so = {};
-
-        for (var id in this.widgets) {
-            var kvlist = this._selector(id).getValue().split(":");
-            var hint = kvlist[0];
-            var attr = kvlist[1];
-            if (!(hint && attr)) continue;
-
-            var match_how =
-                this._match_how(id).getValue().split(",").filter(Boolean);
-
-            var value;
-            if (typeof(this.widgets[id].declaredClass) != "undefined") {
-                if (this.widgets[id].declaredClass.match(/Date/)) {
-                    value =
-                        dojo.date.stamp.toISOString(this.widgets[id].value).
-                            split("T")[0];
-                } else {
-                    value = this.widgets[id].attr("value");
-                    if (this.widgets[id].declaredClass.match(/Check/))
-                        value = (value == "on") ? "t" : "f";
-                }
-            } else {
-                value = this.widgets[id].value;
-            }
-
-            if (!so[hint])
-                so[hint] = [];
-
-            var unit = {};
-            unit[attr] = value;
-            match_how.forEach(function(key) { unit[key] = true; });
-            if (this.terms[hint][attr].datatype == "timestamp")
-                unit.__castdate = true;
-
-            so[hint].push(unit);
-        }
-        return so;
-    };
-
-    this.buildBibSearchString = function() {
-        var conj = {"and": " ", "or": " || "}[
-            dojo.byId("acq-unified-conjunction").getValue()
-        ];
-
-        var sso = {};
-        /* Notice that below we use conj in two places and a constant " || "
-         * in one. That constant " || " is applied for the "file of terms"
-         * search term type, which is in itself always an or search. */
-        for (var id in this.widgets) {
-            var term = this._selector(id).getTerm();
-            var attr = term.bib_attr_name;
-            var match_how = this._match_how(id).getValue();
-            var widget = this.widgets[id];
-
-            if (!sso[attr]) sso[attr] = [];
-            var  value = (
-                typeof(widget.attr) == "function" ?
-                    widget.attr("value") : widget.value
-            );
-            if (typeof(value) != "string")
-                value = value.join(" || ");
-            sso[attr].push(
-                (match_how.indexOf("__not") == -1 ? "" : "-") + value
-            );
-        }
-        var ssa = [];
-        for (var attr in sso)
-            ssa.push(attr + ": " + sso[attr].join(conj));
-        return "(" + ssa.join(conj) + ")";
-    };
-}
-
-/* The result manager is used primarily when the users submits a search.  It
- * consults the termManager to get the search query to send to the middl
- * layer, and it chooses which ML method to call as well as what widgets to use
- * to display the results.
- */
-function ResultManager(liPager, poGrid, plGrid, invGrid) {
-    var self = this;
-
-    this.liPager = liPager;
-
-    this.poGrid = poGrid;
-    this.plGrid = plGrid;
-    this.invGrid = invGrid;
-    this.poCache = {};
-    this.plCache = {};
-    this.invCache = {};
-
-    this.result_types = {
-        "lineitem": {
-            "search_options": {
-                "flesh_attrs": true,
-                "flesh_cancel_reason": true,
-                "flesh_notes": true
-            },
-            "revealer": function() {
-                self.liPager.show();
-                progressDialog.show(true);
-            },
-            "finisher": function() {
-                self.liPager.batch_length = self.count_results;
-                self.liPager.relabelControls();
-                self.liPager.enableControls(true);
-                progressDialog.hide();
-            },
-            "adder": function(li) {
-                self.liPager.liTable.addLineitem(li);
-            },
-            "interface": self.liPager
-        },
-        "purchase_order": {
-            "search_options": {
-                "no_flesh_cancel_reason": true
-            },
-            "revealer": function() {
-                self.poGrid.resetStore();
-                self.poGrid.showLoadProgressIndicator();
-                self.poCache = {};
-            },
-            "finisher": function() {
-                self.poGrid.hideLoadProgressIndicator();
-            },
-            "adder": function(po) {
-                self.poCache[po.id()] = po;
-                self.poGrid.store.newItem(acqpo.toStoreItem(po));
-            },
-            "interface": self.poGrid
-        },
-        "picklist": {
-            "search_options": {
-                "flesh_lineitem_count": true,
-                "flesh_owner": true
-            },
-            "revealer": function() {
-                self.plGrid.resetStore();
-                self.plGrid.showLoadProgressIndicator();
-                self.plCache = {};
-            },
-            "finisher": function() {
-                self.plGrid.hideLoadProgressIndicator();
-            },
-            "adder": function(pl) {
-                self.plCache[pl.id()] = pl;
-                self.plGrid.store.newItem(acqpl.toStoreItem(pl));
-            },
-            "interface": self.plGrid
-        },
-        "invoice": {
-            "search_options": {
-                "no_flesh_misc": true
-            },
-            "finisher": function() {
-                self.invGrid.hideLoadProgressIndicator();
-            },
-            "revealer": function() {
-                self.invGrid.resetStore();
-                self.invCache = {};
-            },
-            "adder": function(inv) {
-                self.invCache[inv.id()] = inv;
-                self.invGrid.store.newItem(acqinv.toStoreItem(inv));
-            },
-            "interface": self.invGrid
-        },
-        "no_results": {
-            "revealer": function() { alert(localeStrings.NO_RESULTS); }
-        }
-    };
-
-    this._dataLoader = function(opts) {
-        /* This function must contain references to "self" only, not "this." */
-        var grid = self.result_types[self.result_type].interface;
-
-        if (!opts)
-            opts = {};
-
-        self.count_results = 0;
-
-        var use_params = dojo.clone(self.params);   /* need copy, not ref */
-
-        if (!opts.skip_paging) {
-            use_params[4].offset = grid.displayOffset;
-            use_params[4].limit = grid.displayLimit;
-        }
-
-        var method = self.method_name;
-        if (opts.atomic)
-            method += ".atomic";
-
-        if (opts.id_list)
-            use_params[4].id_list = true;
-
-        var request_options = {
-            "params": use_params,
-            "async": true
-        };
-
-        if (typeof opts.onresponse != "undefined") {
-            request_options.onresponse = opts.onresponse;
-        } else {
-            /* normal onresponse handler for most times we call this method */
-            request_options.onresponse = function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    if (!self.count_results++)
-                        self.show(self.result_type);
-                    self.add(self.result_type, r);
-                }
-            };
-        }
-
-        if (typeof opts.oncomplete != "undefined") {
-            request_options.oncomplete = opts.oncomplete;
-        } else {
-            /* normal oncomplete handler for most times we call this method */
-            request_options.oncomplete = function() { self.resultsComplete(); };
-        }
-
-        fieldmapper.standardRequest(["open-ils.acq", method], request_options);
-    };
-
-    this.add = function(which, what) {
-        var f = this.result_types[which].adder;
-        if (f) f(what);
-    };
-
-    this.finish = function(which) {
-        var f = this.result_types[which].finisher;
-        if (f) f();
-    };
-
-    this.show = function(which) {
-        openils.Util.objectProperties(this.result_types).forEach(
-            function(rt) {
-                openils.Util[rt == which ? "show" : "hide"](
-                    "acq-unified-results-" + rt
-                );
-            }
-        );
-        this.result_types[which].revealer();
-    };
-
-    this.resultsComplete = function() {
-        if (!this.count_results)
-            this.show("no_results");
-        else this.finish(this.result_type);
-    };
-
-    this.go = function(search_object) {
-        location.href = oilsBasePath + "/acq/search/unified?" +
-            "so=" + base64Encode(search_object) +
-            "&rt=" + dojo.byId("acq-unified-result-type").getValue() +
-            "&c=" + dojo.byId("acq-unified-conjunction").getValue();
-    };
-
-    this.search = function(uriManager, termManager) {
-        var bib_search_string = null;
-        this.count_results = 0;
-        this.result_type = dojo.byId("acq-unified-result-type").getValue();
-
-        /* lineitem_and_bib: a special case */
-        if (this.result_type == "lineitem_and_bib") {
-            this.result_type = "lineitem";
-            bib_search_string = termManager.buildBibSearchString();
-        }
-
-        this.method_name = "open-ils.acq." + this.result_type +
-            ".unified_search";
-        /* Except for building the API method name that we want to call,
-         * we want to treat lineitem_and_bib the same way as lineitem from
-         * here forward. */
-
-        this.params = [
-            openils.User.authtoken,
-            null, null, null,
-            this.result_types[this.result_type].search_options
-        ];
-
-        this.params[
-            dojo.byId("acq-unified-conjunction").getValue() == "and" ? 1 : 2
-        ] = uriManager.search_object;
-        if (uriManager.order_by)
-            this.params[4].order_by = uriManager.order_by;
-
-        var interface = this.result_types[this.result_type].interface;
-        interface.dataLoader = this._dataLoader;
-
-        if (bib_search_string) {
-            /* Have the ML do the bib search first, which incidentally has the
-             * side effect of creating line items that will show up when
-             * we do the LI part of the search (so we don't actually want
-             * to display these results directly). */
-            fieldmapper.standardRequest(
-                ["open-ils.acq", "open-ils.acq.biblio.wrapped_search.atomic"], {
-                    "params": [
-                        openils.User.authtoken, bib_search_string, {
-                            "clear_marc": true
-                        }
-                    ],
-                    "onresponse": function(r) {
-                        r = openils.Util.readResponse(r, false, true);
-                    }
-                }
-            );
-        }
-
-        interface.dataLoader();
-    };
-}
-
-function URIManager() {
-    var self = this;
-    this.cannedSearches = {
-        "po": {
-            "search_object": {
-                "acqpo": [
-                    {"ordering_agency": openils.User.user.ws_ou()},
-                    {"state": "on-order"}
-                ]
-            },
-            "half_search": true,
-            "result_type": "purchase_order",
-            "conjunction": "and",
-            "order_by": [
-                {"class": "acqpo", "field": "edit_time", "direction": "desc"}
-            ]
-        },
-        "pl": {
-            "search_object": {
-                "acqpl": [
-                    {"owner": openils.User.user.usrname()}
-                ]
-            },
-            "result_type": "picklist",
-            "conjunction": "and",
-            "order_by": [
-                {"class": "acqpl", "field": "edit_time", "direction": "desc"}
-            ]
-        },
-        "inv": {
-            "search_object": {
-                "acqinv": [
-                    {"complete": "f"},
-                    {"receiver": openils.User.user.ws_ou()}
-                ]
-            },
-            "half_search": true,
-            "result_type": "invoice",
-            "conjunction": "and",
-            "order_by": [
-                {"class": "acqinv", "field": "recv_date", "direction": "desc"}
-            ]
-        }
-    };
-
-    if (this.canned = cgi.param("ca")) { /* assignment */
-        dojo.mixin(this, this.cannedSearches[this.canned]);
-        dojo.byId("acq-unified-result-type").setValue(this.result_type);
-        dojo.byId("acq-unified-result-type").onchange();
-        dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
-    } else {
-        this.search_object = cgi.param("so");
-        if (this.search_object)
-            this.search_object = base64Decode(this.search_object);
-
-        this.result_type = cgi.param("rt");
-        if (this.result_type) {
-            dojo.byId("acq-unified-result-type").setValue(this.result_type);
-            dojo.byId("acq-unified-result-type").onchange();
-        }
-
-        this.conjunction = cgi.param("c");
-        if (this.conjunction)
-            dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
-    }
-}
-
-/* onload */
-openils.Util.addOnLoad(
-    function() {
-        termManager = new TermManager();
-        resultManager = new ResultManager(
-            new LiTablePager(null, new AcqLiTable()),
-            dijit.byId("acq-unified-po-grid"),
-            dijit.byId("acq-unified-pl-grid"),
-            dijit.byId("acq-unified-inv-grid")
-        );
-
-        uriManager = new URIManager();
-        if (uriManager.search_object) {
-            if (!uriManager.half_search)
-                hideForm();
-            openils.Util.show("acq-unified-body");
-            termManager.reflect(uriManager.search_object);
-
-            if (!uriManager.half_search)
-                resultManager.search(uriManager, termManager);
-        } else {
-            termManager.addRow();
-            openils.Util.show("acq-unified-body");
-        }
-    }
-);
+require([
+       "dojo/date/stamp",
+       "dojox/encoding/base64",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoWidget",
+       "openils/widget/XULTermLoader",
+       "openils/PermaCrud"
+       ],
+function(dojo_date_stamp,
+       dojox_encoding_base64,
+       openils_widget_AutoGrid,
+       openils_widget_AutoWidget,
+       openils_widget_XULTermLoader,
+       openils_PermaCrud){
+       
+       if (!localeStrings) {   /* we can do this because javascript doesn't have block 
+                                  scope */
+           dojo.requireLocalization('openils.acq', 'acq');
+           var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+       }
+       
+       var termSelectorFactory;
+       var termManager;
+       var resultManager;
+       var uriManager;
+       var pcrud = new openils_PermaCrud();
+       var cgi = new openils.CGI();
+       
+       /* typing save: add {get,set}Value() to all HTML <select> elements */
+       HTMLSelectElement.prototype.getValue = function() {
+           return this.options[this.selectedIndex].value;
+       }
+       
+       /* only sets the selected value if such an option is actually available */
+       HTMLSelectElement.prototype.setValue = function(s) {
+           for (var i = 0; i < this.options.length; i++) {
+               if (s == this.options[i].value) {
+                   this.selectedIndex = i;
+                   break;
+               }
+           }
+       }
+       
+       /* minor formatting function used by autogrids in unified.tt2 */
+       function getName(rowIndex, item) {
+           if (item) {
+               return {
+                   "name": this.grid.store.getValue(item, "name") ||
+                       localeStrings.UNNAMED,
+                   "id": this.grid.store.getValue(item, "id")
+               };
+           }
+       }
+       
+       /* quickly find elements by the value of a "name" attribute */
+       function nodeByName(name, root) {
+           return dojo.query("[name='" + name + "']", root)[0];
+       }
+       
+       function hideForm() {
+           openils.Util.hide("acq-unified-hide-form");
+           openils.Util.show("acq-unified-reveal-form", "inline");
+           openils.Util.hide("acq-unified-form");
+       }
+       
+       function revealForm() {
+           openils.Util.hide("acq-unified-reveal-form");
+           openils.Util.show("acq-unified-hide-form", "inline");
+           openils.Util.show("acq-unified-form");
+       }
+       
+       /* The TermSelectorFactory will be instantiated by the TermManager. It
+        * provides HTML select controls whose options are all the searchable
+        * fields.  Selecting a field from one of these controls will create the
+        * appopriate type of corresponding widget for the user to enter a search
+        * term against the selected field.
+        */
+       function TermSelectorFactory(terms) {
+           var self = this;
+           this.terms = terms;
+           this.onlyBibFriendly = false;
+       
+           this.template = dojo.create("select");
+           this.template.appendChild(
+               dojo.create("option", {
+                   "disabled": "disabled",
+                   "selected": "selected",
+                   "value": "",
+                   "innerHTML": localeStrings.SELECT_SEARCH_FIELD
+               })
+           );
+       
+           /* Create abbreviations for class names to make field categories
+            * more readable in field selector control. */
+           this._abbreviate = function(s) {
+               var last, result;
+               for (var i = 0; i < s.length; i++) {
+                   if (s[i] != " ") {
+                       if (!i) result = s[i];
+                       else if (last == " ") result += s[i];
+                   }
+                   last = s[i];
+               }
+               return result;
+           };
+       
+           var selectorMethods = {
+               /* Important: within the following functions, "this" refers to one
+                * HTMLSelect object, and "self" refers to the TermSelectorFactory. */
+               "getTerm": function() { return this.valueToTerm(this.getValue()); },
+               "valueToTerm": function(value) {
+                   var parts = value.split(":");
+                   if (!parts || parts.length != 2) return null;
+                   return dojo.mixin(
+                       self.terms[parts[0]][parts[1]],
+                       {"hint": parts[0], "field": parts[1]}
+                   );
+               },
+               "onlyBibFriendly": function(yes) {
+                   if (yes) {
+                       for (var i = 0; i < this.options.length; i++) {
+                           if (this.options[i].value) {
+                               var term = this.valueToTerm(this.options[i].value);
+                               this.options[i].disabled = !term.bib_friendly;
+                           }
+                       }
+                   } else {
+                       for (var i = 0; i < this.options.length; i++) {
+                           if (this.options[i].value)
+                               this.options[i].disabled = false;
+                       }
+                   }
+               },
+               "makeWidget": function(
+                   parentNode, wStore, matchHow, value, noFocus, callback
+               ) {
+                   var term = this.getTerm();
+                   var widgetKey = this.uniq;
+                   if (matchHow.getValue() == "__in") {
+                       new openils_widget_XULTermLoader({
+                           "parentNode": parentNode
+                       }).build(
+                           function(w) {
+                               wStore[widgetKey] = w;
+                               if (typeof(callback) == "function")
+                                   callback(term, widgetKey);
+                               if (typeof(value) != "undefined")
+                                   w.attr("value", value);
+                               /* I would love for the following call not to be
+                                * necessary, so that updating the value of the dijit
+                                * would lead to this automatically, but I can't yet
+                                * figure out the correct way to do this in Dojo.
+                                */
+                               w.updateCount();
+                           }
+                       );
+                   } else if (term.hint == "acqlia" ||
+                       (term.hint == "jub" && term.field == "eg_bib_id")) {
+                       /* The test for jub.eg_bib_id is a special case to prevent
+                        * AutoFieldWidget from trying to render a ridiculous dropdown
+                        * of every bib record ID in the system. */
+                       wStore[widgetKey] = dojo.create(
+                           "input", {"type": "text"}, parentNode, "only"
+                       );
+                       if (typeof(value) != "undefined")
+                           wStore[widgetKey].value = value;
+                       if (!noFocus)
+                           wStore[widgetKey].focus();
+                       if (typeof(callback) == "function")
+                           callback(term, widgetKey);
+                   } else {
+                       new openils.widget.AutoFieldWidget({
+                           "fmClass": term.hint,
+                           "fmField": term.field,
+                           "noDisablePkey": true,
+                           "parentNode": dojo.create("span", null, parentNode, "only")
+                       }).build(
+                           function(w) {
+                               wStore[widgetKey] = w;
+                               if (typeof(value) != "undefined") {
+                                   if (w.declaredClass.match(/Check/))
+                                       w.attr("checked", value == "t");
+                                   else
+                                       w.attr("value", value);
+                               }
+                               if (!noFocus)
+                                   w.focus();
+                               if (typeof(callback) == "function")
+                                   callback(term, widgetKey);
+       
+                               // submit on enter
+                               openils.Util.registerEnterHandler(w.domNode,
+                                   function() { 
+                                       resultManager.go(termManager.buildSearchObject());
+                                   }
+                               );
+                           }
+                       );
+                   }
+               }
+           }
+       
+           for (var hint in this.terms) {
+               var optgroup = dojo.create(
+                   "optgroup", {"value": "", "label": this.terms[hint].__label}
+               );
+               var prefix = this._abbreviate(this.terms[hint].__label);
+       
+               for (var field in this.terms[hint]) {
+                   if (!/^__/.test(field)) {
+                       optgroup.appendChild(
+                           dojo.create("option", {
+                               "class": "acq-unified-option-regular",
+                               "value": hint + ":" + field,
+                               "innerHTML": prefix + " - " +
+                                   this.terms[hint][field].label
+                           })
+                       );
+                   }
+               }
+       
+               this.template.appendChild(optgroup);
+           }
+       
+           this.make = function(n) {
+               var node = dojo.clone(this.template);
+               node.uniq = n;
+               dojo.attr(node, "id", "term-" + n);
+               for (var name in selectorMethods)
+                   node[name] = selectorMethods[name];
+               if (this.onlyBibFriendly)
+                   node.onlyBibFriendly(true);
+               return node;
+           };
+       }
+       
+       /* The term manager retrieves information from the IDL about all the fields
+        * in the classes that we consider searchable for our purpose.  It maintains
+        * a dynamic HTML table of search terms, using the TermSelectorFactory
+        * to generate search field selectors, which in turn provide appropriate
+        * widgets for entering search terms.  The TermManager provides search term
+        * modifiers (fuzzy searching, not searching). The TermManager also handles
+        * adding and removing rows of search terms, as well as building the search
+        * query to pass to the middle layer from the search term widgets.
+        */
+       function TermManager() {
+           var self = this;
+       
+           /* All the keys in this object are bib-search-friendly attributes, but the
+            * boolean values indicate whether they should be searched by their
+            * field name as such, or simply mapped to "keyword". */
+           this.bibFriendlyAttrNames = {
+               "author": true, "title": true,
+               "isbn": false, "issn": false, "upc": false
+           };
+       
+           this.terms = {};
+           ["jub", "acqpl", "acqpo", "acqinv"].forEach(
+               function(hint) {
+                   var o = {};
+                   o.__label = fieldmapper.IDL.fmclasses[hint].label;
+                   fieldmapper.IDL.fmclasses[hint].fields.forEach(
+                       function(field) {
+                           if (!field.virtual) {
+                               o[field.name] = {
+                                   "label": field.label, "datatype": field.datatype
+                               };
+                           }
+                       }
+                   );
+                   self.terms[hint] = o;
+               }
+           );
+       
+           this.terms.acqlia = {"__label": fieldmapper.IDL.fmclasses.acqlia.label};
+           pcrud.retrieveAll("acqliad", {"order_by": {"acqliad": "id"}}).forEach(
+               function(def) {
+                   self.terms.acqlia[def.id()] = {
+                       "label": def.description(),
+                       "datatype": "text",
+                       "bib_friendly":
+                           (typeof(self.bibFriendlyAttrNames[def.code()]) !=
+                               "undefined"),
+                       "bib_attr_name":
+                           self.bibFriendlyAttrNames[def.code()] ?
+                               def.code() : "keyword"
+                   };
+               }
+           );
+       
+           this.selectorFactory = new TermSelectorFactory(this.terms);
+           this.template = dojo.byId("acq-unified-terms-tbody").
+               removeChild(dojo.byId("acq-unified-terms-row-tmpl"));
+           dojo.attr(this.template, "id");
+       
+           this.lastResultType = null;
+       
+           this.rowId = 0;
+           this.widgets = {};
+       
+           dojo.byId("acq-unified-result-type").onchange = function() {
+               self.resultTypeChange(this.getValue());
+           };
+       
+           this.allRowIds = function() {
+               return dojo.query("tr[id^='term-row-']", "acq-unified-terms-tbody").
+                   map(function(o) { return o.id.match(/^term-row-(\d+)$/)[1]; });
+           };
+       
+           this._row = function(id) { return dojo.byId("term-row-" + id); };
+           this._selector = function(id) { return dojo.byId("term-" + id); };
+           this._match_how = function(id) { return dojo.byId("term-match-" + id); };
+       
+           this._updateMatchHowForField = function(term, key) {
+               /* NOTE important to use self, not this, in this function.
+                *
+                * Based on the selected field (its datatype and the kind of widget
+                * that AutoFieldWidget provides for it) we update the possible
+                * choices in the mach_how selector.
+                */
+               var w = self.widgets[key];
+               var can_do_fuzzy, can_do_in;
+               if (term.datatype == "id") {
+                   can_do_fuzzy = false;
+                   can_do_in = true;
+               } else if (term.datatype == "link") {
+                   var target = self.getLinkTarget(term);
+                   can_do_fuzzy = (target == "au");
+                   can_do_in = (target == "bre"); /* XXX might revise later */
+               } else if (typeof(w.declaredClass) != "undefined") {
+                   can_do_fuzzy = can_do_in =
+                       Boolean(w.declaredClass.match(/form\.Text|XULT/));
+               } else {
+                   var type = dojo.attr(w, "type");
+                   if (type)
+                       can_do_fuzzy = can_do_in = (type == "text");
+                   else
+                       can_do_fuzzy = can_do_in = false;
+               }
+       
+               self.matchHowAllow(key, "__fuzzy", can_do_fuzzy);
+               self.matchHowAllow(key, "__in", can_do_in);
+       
+               var inequalities = (term.datatype == "timestamp");
+               self.matchHowAllow(key, "__gte", inequalities);
+               self.matchHowAllow(key, "__lte", inequalities);
+           };
+       
+           this.removerButton = function(n) {
+               return dojo.create("button", {
+                   "innerHTML": "X",
+                   "class": "acq-unified-remover",
+                   "onclick": function() { self.removeRow(n); }
+               });
+           };
+       
+           this.matchHowAllow = function(where, what, which, exact) {
+               dojo.query(
+                   "option[value" + (exact ? "" : "*") + "='" + what + "']",
+                   typeof(where) == "object" ? where : this._match_how(where)
+               ).forEach(function(o) { o.disabled = !which; });
+           };
+       
+           this.getLinkTarget = function(term) {
+               return fieldmapper.IDL.fmclasses[term.hint].
+                   field_map[term.field]["class"];
+           };
+       
+           this.updateRowWidget = function(id, value, noFocus) {
+               var where = nodeByName("widget", this._row(id));
+       
+               delete this.widgets[id];
+               dojo.empty(where);
+       
+               this._selector(id).makeWidget(
+                   where, this.widgets, this._match_how(id), value, noFocus,
+                   this._updateMatchHowForField
+               );
+           };
+       
+           this.resultTypeChange = function(resultType) {
+               if (
+                   this.lastResultType == "lineitem_and_bib" &&
+                   resultType != "lineitem_and_bib"
+               ) {
+                   /* Re-enable all non-bib-friendly fields in all search term
+                    * field selectors. */
+                   this.allRowIds().forEach(
+                       function(id) {
+                           self._selector(id).onlyBibFriendly(false);
+                           self.matchHowAllow(id, "", true, /* exact */ true);
+                           self.matchHowAllow(id, "__not", true, /* exact */ true);
+                       }
+                   );
+                   /* Tell the selector factory to create new search term field
+                    * selectors with all fields, not just bib-friendly ones. */
+                   this.selectorFactory.onlyBibFriendly = false;
+               } else if (
+                   this.lastResultType != "lineitem_and_bib" &&
+                   resultType == "lineitem_and_bib"
+               ) {
+                   /* Remove all search term rows set to non-bib-friendly fields. */
+                   this.allRowIds().forEach(
+                       function(id) {
+                           var term = self._selector(id).getTerm();
+                           if (term &&
+                               !self.terms[term.hint][term.field].bib_friendly) {
+                               self.removeRow(id);
+                           }
+                       }
+                   );
+                   /* Disable all non-bib-friendly fields in all remaining search term
+                    * field selectors. */
+                   this.allRowIds().forEach(
+                       function(id) {
+                           self._selector(id).onlyBibFriendly(true);
+                           self.matchHowAllow(id, "", false, /* exact */ true);
+                           self.matchHowAllow(id, "__not", false, /* exact */ true);
+                       }
+                   );
+                   /* Tell the selector factory to create new search term field
+                    * selectors with only bib friendly options. */
+                   this.selectorFactory.onlyBibFriendly = true;
+               }
+               this.lastResultType = resultType;
+           };
+       
+           /* this method is particularly kludgy... puts back together a string
+            * based on object properties that might arrive in indeterminate order. */
+           this._term_reverse_match_how = function(term) {
+               /* only two-key combination we use */
+               if (term.__not && term.__fuzzy)
+                   return "__not,__fuzzy";
+       
+               /* only other possibilities are single-key or no key */
+               for (var key in term) {
+                   if (/^__/.test(key))
+                       return key;
+               }
+       
+               return null;
+           };
+       
+       
+           this._term_reverse_selector_field = function(term) {
+               for (var key in term) {
+                   if (!/^__/.test(key))
+                       return key;
+               }
+               return null;
+           };
+       
+           this._term_reverse_selector_value = function(term) {
+               for (var key in term) {
+                   if (!/^__/.test(key))
+                       return term[key];
+               }
+               return null;
+           };
+       
+           this.addRow = function(term, hint) {
+               var uniq = (this.rowId)++;
+       
+               var row = dojo.clone(this.template);
+               dojo.attr(row, "id", "term-row-" + uniq);
+       
+               var selector = this.selectorFactory.make(uniq);
+               dojo.attr(
+                   selector, "onchange", function() { self.updateRowWidget(uniq); }
+               );
+       
+               var match_how = dojo.query("select", nodeByName("match", row))[0];
+               dojo.attr(match_how, "id", "term-match-" + uniq);
+               dojo.attr(match_how, "selectedIndex", 0);
+               dojo.attr(
+                   match_how, "onchange",
+                   function() {
+                       if (this.getValue() == "__in") {
+                           self.updateRowWidget(uniq);
+                           this.was_in = true;
+                       } else if (this.was_in) {
+                           self.updateRowWidget(uniq);
+                           this.was_in = false;
+                       }
+                       if (self.widgets[uniq]) self.widgets[uniq].focus();
+                   }
+               );
+       
+               /* Kind of inelegant; could be improved: this section turns off
+                * match-type options that don't apply to bib searching. */
+               this.matchHowAllow(
+                   match_how, "",
+                   !this.selectorFactory.onlyBibFriendly, /* exact */ true
+               );
+               this.matchHowAllow(
+                   match_how, "__not",
+                   !this.selectorFactory.onlyBibFriendly, /* exact */ true
+               );
+               if (this.selectorFactory.onlyBibFriendly)
+                   match_how.setValue("__fuzzy");
+       
+               nodeByName("selector", row).appendChild(selector);
+               nodeByName("remove", row).appendChild(this.removerButton(uniq));
+       
+               dojo.place(row, "acq-unified-terms-tbody", "last");
+       
+               if (term && hint) {
+                   var attr = this._term_reverse_selector_field(term);
+                   var field = hint + ":" + attr;
+                   selector.setValue(field);
+       
+                   var match_how_value = this._term_reverse_match_how(term);
+                   if (match_how_value)
+                       match_how.setValue(match_how_value);
+       
+                   var value = this._term_reverse_selector_value(term);
+                   if (this.terms[hint][attr].datatype == "timestamp")
+                       value = dojo_date_stamp.fromISOString(value);
+                   this.updateRowWidget(uniq, value, /* noFocus */ true);
+       
+               }
+           }
+       
+           this.removeRow = function(id) {
+               delete this.widgets[id];
+               dojo.destroy(this._row(id));
+           };
+       
+           this.reflect = function(search_object) {
+               for (var hint in search_object) {
+                   search_object[hint].forEach(
+                       function(term) { self.addRow(term, hint); }
+                   );
+               }
+           };
+       
+           this.buildSearchObject = function() {
+               var so = {};
+       
+               for (var id in this.widgets) {
+                   var kvlist = this._selector(id).getValue().split(":");
+                   var hint = kvlist[0];
+                   var attr = kvlist[1];
+                   if (!(hint && attr)) continue;
+       
+                   var match_how =
+                       this._match_how(id).getValue().split(",").filter(Boolean);
+       
+                   var value;
+                   if (typeof(this.widgets[id].declaredClass) != "undefined") {
+                       if (this.widgets[id].declaredClass.match(/Date/)) {
+                           value =
+                               dojo_date_stamp.toISOString(this.widgets[id].value).
+                                   split("T")[0];
+                       } else {
+                           value = this.widgets[id].attr("value");
+                           if (this.widgets[id].declaredClass.match(/Check/))
+                               value = (value == "on") ? "t" : "f";
+                       }
+                   } else {
+                       value = this.widgets[id].value;
+                   }
+       
+                   if (!so[hint])
+                       so[hint] = [];
+       
+                   var unit = {};
+                   unit[attr] = value;
+                   match_how.forEach(function(key) { unit[key] = true; });
+                   if (this.terms[hint][attr].datatype == "timestamp")
+                       unit.__castdate = true;
+       
+                   so[hint].push(unit);
+               }
+               return so;
+           };
+       
+           this.buildBibSearchString = function() {
+               var conj = {"and": " ", "or": " || "}[
+                   dojo.byId("acq-unified-conjunction").getValue()
+               ];
+       
+               var sso = {};
+               /* Notice that below we use conj in two places and a constant " || "
+                * in one. That constant " || " is applied for the "file of terms"
+                * search term type, which is in itself always an or search. */
+               for (var id in this.widgets) {
+                   var term = this._selector(id).getTerm();
+                   var attr = term.bib_attr_name;
+                   var match_how = this._match_how(id).getValue();
+                   var widget = this.widgets[id];
+       
+                   if (!sso[attr]) sso[attr] = [];
+                   var  value = (
+                       typeof(widget.attr) == "function" ?
+                           widget.attr("value") : widget.value
+                   );
+                   if (typeof(value) != "string")
+                       value = value.join(" || ");
+                   sso[attr].push(
+                       (match_how.indexOf("__not") == -1 ? "" : "-") + value
+                   );
+               }
+               var ssa = [];
+               for (var attr in sso)
+                   ssa.push(attr + ": " + sso[attr].join(conj));
+               return "(" + ssa.join(conj) + ")";
+           };
+       }
+       
+       /* The result manager is used primarily when the users submits a search.  It
+        * consults the termManager to get the search query to send to the middl
+        * layer, and it chooses which ML method to call as well as what widgets to use
+        * to display the results.
+        */
+       function ResultManager(liPager, poGrid, plGrid, invGrid) {
+           var self = this;
+       
+           this.liPager = liPager;
+       
+           this.poGrid = poGrid;
+           this.plGrid = plGrid;
+           this.invGrid = invGrid;
+           this.poCache = {};
+           this.plCache = {};
+           this.invCache = {};
+       
+           this.result_types = {
+               "lineitem": {
+                   "search_options": {
+                       "flesh_attrs": true,
+                       "flesh_cancel_reason": true,
+                       "flesh_notes": true
+                   },
+                   "revealer": function() {
+                       self.liPager.show();
+                       progressDialog.show(true);
+                   },
+                   "finisher": function() {
+                       self.liPager.batch_length = self.count_results;
+                       self.liPager.relabelControls();
+                       self.liPager.enableControls(true);
+                       progressDialog.hide();
+                   },
+                   "adder": function(li) {
+                       self.liPager.liTable.addLineitem(li);
+                   },
+                   "interface": self.liPager
+               },
+               "purchase_order": {
+                   "search_options": {
+                       "no_flesh_cancel_reason": true
+                   },
+                   "revealer": function() {
+                       self.poGrid.resetStore();
+                       self.poGrid.showLoadProgressIndicator();
+                       self.poCache = {};
+                   },
+                   "finisher": function() {
+                       self.poGrid.hideLoadProgressIndicator();
+                   },
+                   "adder": function(po) {
+                       self.poCache[po.id()] = po;
+                       self.poGrid.store.newItem(acqpo.toStoreItem(po));
+                   },
+                   "interface": self.poGrid
+               },
+               "picklist": {
+                   "search_options": {
+                       "flesh_lineitem_count": true,
+                       "flesh_owner": true
+                   },
+                   "revealer": function() {
+                       self.plGrid.resetStore();
+                       self.plGrid.showLoadProgressIndicator();
+                       self.plCache = {};
+                   },
+                   "finisher": function() {
+                       self.plGrid.hideLoadProgressIndicator();
+                   },
+                   "adder": function(pl) {
+                       self.plCache[pl.id()] = pl;
+                       self.plGrid.store.newItem(acqpl.toStoreItem(pl));
+                   },
+                   "interface": self.plGrid
+               },
+               "invoice": {
+                   "search_options": {
+                       "no_flesh_misc": true
+                   },
+                   "finisher": function() {
+                       self.invGrid.hideLoadProgressIndicator();
+                   },
+                   "revealer": function() {
+                       self.invGrid.resetStore();
+                       self.invCache = {};
+                   },
+                   "adder": function(inv) {
+                       self.invCache[inv.id()] = inv;
+                       self.invGrid.store.newItem(acqinv.toStoreItem(inv));
+                   },
+                   "interface": self.invGrid
+               },
+               "no_results": {
+                   "revealer": function() { alert(localeStrings.NO_RESULTS); }
+               }
+           };
+       
+           this._dataLoader = function(opts) {
+               /* This function must contain references to "self" only, not "this." */
+               var grid = self.result_types[self.result_type].interface;
+       
+               if (!opts)
+                   opts = {};
+       
+               self.count_results = 0;
+       
+               var use_params = dojo.clone(self.params);   /* need copy, not ref */
+       
+               if (!opts.skip_paging) {
+                   use_params[4].offset = grid.displayOffset;
+                   use_params[4].limit = grid.displayLimit;
+               }
+       
+               var method = self.method_name;
+               if (opts.atomic)
+                   method += ".atomic";
+       
+               if (opts.id_list)
+                   use_params[4].id_list = true;
+       
+               var request_options = {
+                   "params": use_params,
+                   "async": true
+               };
+       
+               if (typeof opts.onresponse != "undefined") {
+                   request_options.onresponse = opts.onresponse;
+               } else {
+                   /* normal onresponse handler for most times we call this method */
+                   request_options.onresponse = function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           if (!self.count_results++)
+                               self.show(self.result_type);
+                           self.add(self.result_type, r);
+                       }
+                   };
+               }
+       
+               if (typeof opts.oncomplete != "undefined") {
+                   request_options.oncomplete = opts.oncomplete;
+               } else {
+                   /* normal oncomplete handler for most times we call this method */
+                   request_options.oncomplete = function() { self.resultsComplete(); };
+               }
+       
+               fieldmapper.standardRequest(["open-ils.acq", method], request_options);
+           };
+       
+           this.add = function(which, what) {
+               var f = this.result_types[which].adder;
+               if (f) f(what);
+           };
+       
+           this.finish = function(which) {
+               var f = this.result_types[which].finisher;
+               if (f) f();
+           };
+       
+           this.show = function(which) {
+               openils.Util.objectProperties(this.result_types).forEach(
+                   function(rt) {
+                       openils.Util[rt == which ? "show" : "hide"](
+                           "acq-unified-results-" + rt
+                       );
+                   }
+               );
+               this.result_types[which].revealer();
+           };
+       
+           this.resultsComplete = function() {
+               if (!this.count_results)
+                   this.show("no_results");
+               else this.finish(this.result_type);
+           };
+       
+           this.go = function(search_object) {
+               location.href = oilsBasePath + "/acq/search/unified?" +
+                   "so=" + base64Encode(search_object) +
+                   "&rt=" + dojo.byId("acq-unified-result-type").getValue() +
+                   "&c=" + dojo.byId("acq-unified-conjunction").getValue();
+           };
+       
+           this.search = function(uriManager, termManager) {
+               var bib_search_string = null;
+               this.count_results = 0;
+               this.result_type = dojo.byId("acq-unified-result-type").getValue();
+       
+               /* lineitem_and_bib: a special case */
+               if (this.result_type == "lineitem_and_bib") {
+                   this.result_type = "lineitem";
+                   bib_search_string = termManager.buildBibSearchString();
+               }
+       
+               this.method_name = "open-ils.acq." + this.result_type +
+                   ".unified_search";
+               /* Except for building the API method name that we want to call,
+                * we want to treat lineitem_and_bib the same way as lineitem from
+                * here forward. */
+       
+               this.params = [
+                   openils.User.authtoken,
+                   null, null, null,
+                   this.result_types[this.result_type].search_options
+               ];
+       
+               this.params[
+                   dojo.byId("acq-unified-conjunction").getValue() == "and" ? 1 : 2
+               ] = uriManager.search_object;
+               if (uriManager.order_by)
+                   this.params[4].order_by = uriManager.order_by;
+       
+               var interface = this.result_types[this.result_type].interface;
+               interface.dataLoader = this._dataLoader;
+       
+               if (bib_search_string) {
+                   /* Have the ML do the bib search first, which incidentally has the
+                    * side effect of creating line items that will show up when
+                    * we do the LI part of the search (so we don't actually want
+                    * to display these results directly). */
+                   fieldmapper.standardRequest(
+                       ["open-ils.acq", "open-ils.acq.biblio.wrapped_search.atomic"], {
+                           "params": [
+                               openils.User.authtoken, bib_search_string, {
+                                   "clear_marc": true
+                               }
+                           ],
+                           "onresponse": function(r) {
+                               r = openils.Util.readResponse(r, false, true);
+                           }
+                       }
+                   );
+               }
+       
+               interface.dataLoader();
+           };
+       }
+       
+       function URIManager() {
+           var self = this;
+           this.cannedSearches = {
+               "po": {
+                   "search_object": {
+                       "acqpo": [
+                           {"ordering_agency": openils.User.user.ws_ou()},
+                           {"state": "on-order"}
+                       ]
+                   },
+                   "half_search": true,
+                   "result_type": "purchase_order",
+                   "conjunction": "and",
+                   "order_by": [
+                       {"class": "acqpo", "field": "edit_time", "direction": "desc"}
+                   ]
+               },
+               "pl": {
+                   "search_object": {
+                       "acqpl": [
+                           {"owner": openils.User.user.usrname()}
+                       ]
+                   },
+                   "result_type": "picklist",
+                   "conjunction": "and",
+                   "order_by": [
+                       {"class": "acqpl", "field": "edit_time", "direction": "desc"}
+                   ]
+               },
+               "inv": {
+                   "search_object": {
+                       "acqinv": [
+                           {"complete": "f"},
+                           {"receiver": openils.User.user.ws_ou()}
+                       ]
+                   },
+                   "half_search": true,
+                   "result_type": "invoice",
+                   "conjunction": "and",
+                   "order_by": [
+                       {"class": "acqinv", "field": "recv_date", "direction": "desc"}
+                   ]
+               }
+           };
+       
+           if (this.canned = cgi.param("ca")) { /* assignment */
+               dojo.mixin(this, this.cannedSearches[this.canned]);
+               dojo.byId("acq-unified-result-type").setValue(this.result_type);
+               dojo.byId("acq-unified-result-type").onchange();
+               dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
+           } else {
+               this.search_object = cgi.param("so");
+               if (this.search_object)
+                   this.search_object = base64Decode(this.search_object);
+       
+               this.result_type = cgi.param("rt");
+               if (this.result_type) {
+                   dojo.byId("acq-unified-result-type").setValue(this.result_type);
+                   dojo.byId("acq-unified-result-type").onchange();
+               }
+       
+               this.conjunction = cgi.param("c");
+               if (this.conjunction)
+                   dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
+           }
+       }
+       
+       /* onload */
+       openils.Util.addOnLoad(
+           function() {
+               termManager = new TermManager();
+               resultManager = new ResultManager(
+                   new LiTablePager(null, new AcqLiTable()),
+                   dijit.byId("acq-unified-po-grid"),
+                   dijit.byId("acq-unified-pl-grid"),
+                   dijit.byId("acq-unified-inv-grid")
+               );
+       
+               uriManager = new URIManager();
+               if (uriManager.search_object) {
+                   if (!uriManager.half_search)
+                       hideForm();
+                   openils.Util.show("acq-unified-body");
+                   termManager.reflect(uriManager.search_object);
+       
+                   if (!uriManager.half_search)
+                       resultManager.search(uriManager, termManager);
+               } else {
+                   termManager.addRow();
+                   openils.Util.show("acq-unified-body");
+               }
+           }
+       );
+       
+
+});
\ No newline at end of file
index 1ed56a4..3d10080 100644 (file)
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.Textarea');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.ComboBox');
-dojo.require('dijit.form.NumberSpinner');
-dojo.require('fieldmapper.IDL');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.Button');
-dojo.require('dojo.date');
-dojo.require('openils.CGI');
-dojo.require('openils.XUL');
-dojo.require('openils.Util');
-dojo.require('openils.Event');
-
-dojo.requireLocalization('openils.actor', 'register');
-var localeStrings = dojo.i18n.getLocalization('openils.actor', 'register');
-
-
-var pcrud;
-var fmClasses = ['au', 'ac', 'aua', 'actsc', 'asv', 'asvq', 'asva'];
-var fieldDoc = {};
-var statCats;
-var statCatTemplate;
-var surveys;
-var staff;
-var patron;
-var uEditUsePhonePw = false;
-var widgetPile = [];
-var uEditCardVirtId = -1;
-var uEditAddrVirtId = -1;
-var orgSettings = {};
-var userSettings = {};
-var userSettingsToUpdate = {};
-var userSettingTypes;
-var tbody;
-var addrTemplateRows;
-var cgi;
-var cloneUser;
-var cloneUserObj;
-var stageUser;
-var optInSettings;
-var allCardsTemplate;
-var uEditCloneCopyAddr; // if true, copy addrs on clone instead of link
-var homeOuTypes = {};
-var holdPickupTypes = {};
-var cardPerms = {};
-var editCard;
-var prevBillingAddress;
-var prevMailingAddress;
-
-var dupeUsrname = false;
-var dupeBarcode = false;
-
-// allow for a pause after typing before sending address alert queries
-var addressAlertTimeout = 2000; 
-var addressAlertFields = 
-    ['street1', 'street2', 'city', 'state', 'county', 'country', 'post_code'];
-
-if(!window.xulG) var xulG = null;
-var lock_ready = false;
-var already_locked = false;
-
-function load() {
-    staff = new openils.User().user;
-    pcrud = new openils.PermaCrud();
-    cgi = new openils.CGI();
-    cloneUser = cgi.param('clone');
-    var userId = cgi.param('usr');
-    var stageUname = cgi.param('stage');
-
-    saveButton.attr("label", localeStrings.SAVE);
-    saveCloneButton.attr("label", localeStrings.SAVE_CLONE);
-    replaceBarcode.attr("label", localeStrings.REPLACE_BARCODE);
-    dojo.byId('uedit-show-required').innerHTML = localeStrings.SHOW_REQUIRED;
-    dojo.byId('uedit-show-suggested').innerHTML = localeStrings.SHOW_SUGGESTED;
-    dojo.byId('uedit-show-all').innerHTML = localeStrings.SHOW_ALL;
-    dojo.byId('uedit-dupe-barcode-warning').innerHTML = localeStrings.BARCODE_IN_USE;
-    allCards.attr("label", localeStrings.SEE_ALL);
-    dojo.byId('uedit-dupe-username-warning').innerHTML = localeStrings.DUPE_USERNAME;
-    generatePassword.attr("label", localeStrings.RESET_PASSWORD);
-    dojo.byId('verifyPassword').innerHTML = localeStrings.VERIFY_PASSWORD;
-    dojo.byId('parentGuardian').innerHTML = localeStrings.PARENT_OR_GUARDIAN;
-    dojo.byId('userSettings').innerHTML = localeStrings.USER_SETTINGS;
-    dojo.byId('statCats').innerHTML = localeStrings.STAT_CATS;
-    dojo.byId('uedit-all-cards-barcode').innerHTML = localeStrings.ALL_CARDS_BARCODE;
-    dojo.byId('uedit-all-cards-active').innerHTML = localeStrings.ALL_CARDS_ACTIVE;
-    dojo.byId('uedit-all-cards-primary').innerHTML = localeStrings.ALL_CARDS_PRIMARY;
-    allCardsClose.attr("label", localeStrings.ALL_CARDS_CLOSE);
-    allCardsApply.attr("label", localeStrings.ALL_CARDS_APPLY);
-
-    dojo.query("td[name='addressHeader']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_HEADER; });
-    dojo.query("span[name='mailingAddress']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_MAILING; });
-    dojo.query("span[name='billingAddress']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_BILLING; });
-    dojo.query("span[name='addressPending']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_PENDING; });
-    dojo.query("button[name='approve-button']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_APPROVE; });
-    dojo.query("span[name='address-already-owned']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_OWNED; });
-    dojo.query("button[name='addressNew']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_NEW; });
-
-    if(xulG) {
-           if(xulG.ses) openils.User.authtoken = xulG.ses;
-           if(typeof xulG.clone != 'undefined') cloneUser = xulG.clone;
-        if(typeof xulG.usr != 'undefined') userId = xulG.usr
-        if(typeof xulG.params != 'undefined') {
-            var parms = xulG.params;
-               if(typeof parms.ses != 'undefined') 
-                openils.User.authtoken = parms.ses;
-               if(typeof parms.clone != 'undefined') 
-                cloneUser = parms.clone;
-            if(typeof parms.usr != 'undefined')
-                userId = parms.usr;
-            if(typeof parms.stage != 'undefined')
-                stageUname = parms.stage
-        }
-    }
-
-    orgSettings = fieldmapper.aou.fetchOrgSettingBatch(staff.ws_ou(), [
-        'global.password_regex',
-        'global.juvenile_age_threshold',
-        'patron.password.use_phone',
-        'ui.patron.default_inet_access_level',
-        'ui.patron.default_ident_type',
-        'ui.patron.default_country',
-        'ui.patron.registration.require_address',
-        'circ.holds.behind_desk_pickup_supported',
-        'circ.patron_edit.clone.copy_address',
-        'ui.patron.edit.au.prefix.require',
-        'ui.patron.edit.au.prefix.show',
-        'ui.patron.edit.au.prefix.suggest',
-        'ui.patron.edit.au.second_given_name.show',
-        'ui.patron.edit.au.second_given_name.suggest',
-        'ui.patron.edit.au.suffix.show',
-        'ui.patron.edit.au.suffix.suggest',
-        'ui.patron.edit.au.alias.show',
-        'ui.patron.edit.au.alias.suggest',
-        'ui.patron.edit.au.dob.require',
-        'ui.patron.edit.au.dob.show',
-        'ui.patron.edit.au.dob.suggest',
-        'ui.patron.edit.au.dob.calendar',
-        'ui.patron.edit.au.juvenile.show',
-        'ui.patron.edit.au.juvenile.suggest',
-        'ui.patron.edit.au.ident_value.show',
-        'ui.patron.edit.au.ident_value.suggest',
-        'ui.patron.edit.au.ident_value2.show',
-        'ui.patron.edit.au.ident_value2.suggest',
-        'ui.patron.edit.au.email.require',
-        'ui.patron.edit.au.email.show',
-        'ui.patron.edit.au.email.suggest',
-        'ui.patron.edit.au.email.regex',
-        'ui.patron.edit.au.email.example',
-        'ui.patron.edit.au.day_phone.require',
-        'ui.patron.edit.au.day_phone.show',
-        'ui.patron.edit.au.day_phone.suggest',
-        'ui.patron.edit.au.day_phone.regex',
-        'ui.patron.edit.au.day_phone.example',
-        'ui.patron.edit.au.evening_phone.require',
-        'ui.patron.edit.au.evening_phone.show',
-        'ui.patron.edit.au.evening_phone.suggest',
-        'ui.patron.edit.au.evening_phone.regex',
-        'ui.patron.edit.au.evening_phone.example',
-        'ui.patron.edit.au.other_phone.require',
-        'ui.patron.edit.au.other_phone.show',
-        'ui.patron.edit.au.other_phone.suggest',
-        'ui.patron.edit.au.other_phone.regex',
-        'ui.patron.edit.au.other_phone.example',
-        'ui.patron.edit.phone.regex',
-        'ui.patron.edit.phone.example',
-        'ui.patron.edit.au.active.show',
-        'ui.patron.edit.au.active.suggest',
-        'ui.patron.edit.au.barred.show',
-        'ui.patron.edit.au.barred.suggest',
-        'ui.patron.edit.au.master_account.show',
-        'ui.patron.edit.au.master_account.suggest',
-        'ui.patron.edit.au.claims_returned_count.show',
-        'ui.patron.edit.au.claims_returned_count.suggest',
-        'ui.patron.edit.au.claims_never_checked_out_count.show',
-        'ui.patron.edit.au.claims_never_checked_out_count.suggest',
-        'ui.patron.edit.au.alert_message.show',
-        'ui.patron.edit.au.alert_message.suggest',
-        'ui.patron.edit.aua.post_code.regex',
-        'ui.patron.edit.aua.post_code.example',
-        'ui.patron.edit.aua.county.require',
-        'format.date',
-        'ui.patron.edit.default_suggested',
-        'opac.barcode_regex',
-        'opac.username_regex',
-        'sms.enable'
-    ]);
-
-    for(k in orgSettings)
-        if(orgSettings[k])
-            orgSettings[k] = orgSettings[k].value;
-
-    uEditCloneCopyAddr = orgSettings['circ.patron_edit.clone.copy_address'];
-    uEditUsePhonePw = orgSettings['patron.password.use_phone'];
-    uEditFetchUserSettings(userId);
-
-    if(userId) {
-        patron = uEditLoadUser(userId);
-    } else {
-        if(stageUname) {
-            patron = uEditLoadStageUser(stageUname);
-        } else {
-            patron = uEditNewPatron();
-            if(cloneUser) 
-                uEditCopyCloneData(patron);
-        }
-    }
-
-
-    var list = pcrud.search('fdoc', {fm_class:fmClasses});
-    for(var i in list) {
-        var doc = list[i];
-        if(!fieldDoc[doc.fm_class()])
-            fieldDoc[doc.fm_class()] = {};
-        fieldDoc[doc.fm_class()][doc.field()] = doc;
-    }
-
-    list = pcrud.search('aout', {can_have_users: 'true'});
-    for(var i in list) {
-        var type = list[i];
-        homeOuTypes[type.id()] = true;
-    }
-    list = pcrud.search('aout', {can_have_vols: 'true'});
-    for(var i in list) {
-        var type = list[i];
-        holdPickupTypes[type.id()] = true;
-    }
-
-    tbody = dojo.byId('uedit-tbody');
-
-    if(orgSettings['ui.patron.edit.default_suggested'])
-        uEditToggleRequired(2);
-
-    addrTemplateRows = dojo.query('tr[type=addr-template]', tbody);
-    dojo.forEach(addrTemplateRows, function(row) { row.parentNode.removeChild(row); } );
-    statCatTemplate = tbody.removeChild(dojo.byId('stat-cat-row-template'));
-    surveyTemplate = tbody.removeChild(dojo.byId('survey-row-template'));
-    surveyQuestionTemplate = tbody.removeChild(dojo.byId('survey-question-row-template'));
-
-    checkGrpAppPerm(); // to do the initial load
-    loadStaticFields();
-
-
-    if(patron.isnew() && patron.addresses().length == 0) 
-        uEditNewAddr(null, uEditAddrVirtId, true);
-    else loadAllAddrs();
-    loadStatCats();
-    loadSurveys();
-    checkClaimsReturnCountPerm();
-    checkClaimsNoCheckoutCountPerm();
-
-    dojo.connect(replaceBarcode, 'onClick', replaceCardHandler);
-    dojo.connect(allCards, 'onClick', drawAllCards);
-    if(patron.isnew()) {
-        dojo.addClass(dojo.byId('uedit-all-barcodes'), 'hidden');
-    } else if(checkGrpAppPerm(patron.profile())) {
-        new openils.User().getPermOrgList(
-            'UPDATE_PATRON_ACTIVE_CARD',
-            function(orgList) { 
-                if(orgList.indexOf(patron.home_ou()) != -1) 
-                    cardPerms['UPDATE_PATRON_ACTIVE_CARD'] = true;
-            },
-            true, 
-            true
-        );
-        new openils.User().getPermOrgList(
-            'UPDATE_PATRON_PRIMARY_CARD',
-            function(orgList) { 
-                if(orgList.indexOf(patron.home_ou()) != -1) 
-                    cardPerms['UPDATE_PATRON_PRIMARY_CARD'] = true;
-            },
-            true, 
-            true
-        );
-    }
-
-    var input = findWidget('ac', 'barcode');
-    if (patron.isnew()) {
-        replaceBarcode.attr('disabled', true);
-    } else {
-        input.widget.attr('disabled', true).attr('readOnly', true);
-    }
-
-       dojo.connect(generatePassword, 'onClick', generatePasswordHandler);
-
-    if(!patron.isnew() && !checkGrpAppPerm(patron.profile()) && patron.id() != openils.User.user.id()) {
-        // we are not allowed to edit this user, so disable the save option
-        saveButton.attr('disabled', true);
-        saveCloneButton.attr('disabled', true);
-    }
-        
-    uUpdateContactInvalidators();
-    lock_ready = true;
-}
-
-var permGroups;
-var noPermGroups = [];
-// Returns true if the user is allowed to edit the selected group
-function checkGrpAppPerm(grpId) {
-
-    if(!permGroups) {
-
-        // get the groups
-        permGroups = new openils.PermaCrud().retrieveAll('pgt');
-        var permGroupPerms = []
-
-        // collect the group permissions
-        dojo.forEach(permGroups, 
-            function(grp) {
-                if(grp.application_perm())
-                    permGroupPerms.push(grp.application_perm());
-            }
-        );
-
-        // see which of the group application perms I do not have
-        var myPerms = fieldmapper.standardRequest(
-            ['open-ils.actor', 'open-ils.actor.user.has_work_perm_at.batch'],
-            [openils.User.authtoken, permGroupPerms]
-        );
-
-        var failedPerms = [];
-        for(var p in myPerms) { 
-            if(myPerms[p].length == 0) 
-                failedPerms.push(p); 
-        }
-
-        // identify which groups I cannot edit because I do not have permisssion
-
-        function checkTree(grp, failed) {
-            failed = failed || failedPerms.indexOf(grp.application_perm()) > -1;
-            if(failed) noPermGroups.push(grp.id()+'');
-            dojo.forEach(
-                permGroups.filter(function(g) { return g.parent() == grp.id() } ),
-                function(child) {
-                    checkTree(child, failed);
-                }
-            );
-        }
-
-        checkTree(permGroups.filter(function(g) { return g.parent() == null })[0]);
-    }
-
-    return noPermGroups.indexOf(grpId+'') == -1;
-}
-
-
-function drawAllCards() {
-
-    var tbody = dojo.byId('uedit-all-cards-tbody');
-    if(!allCardsTemplate) {
-        allCardsTemplate = tbody.removeChild(dojo.byId('uedit-all-cards-tr-template'));
-    } else {
-        while(tbody.childNodes[0])
-            tbody.removeChild(tbody.childNodes[0]);
-    }
-
-    if(cardPerms['UPDATE_PATRON_ACTIVE_CARD'] || cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
-        dojo.removeClass(dojo.byId('uedit-apply-card-changes'), 'hidden');
-    } else {
-        dojo.addClass(dojo.byId('uedit-apply-card-changes'), 'hidden');
-    }
-
-    var first = true;
-    dojo.forEach(
-        patron.cards().filter(function(c) { return c.id() == patron.card().id(); }).concat(patron.cards()), // grab the main card first
-        function(card) {
-            if(!first) {
-                if(card.id() == patron.card().id())
-                    return;
-            }
-            var row = allCardsTemplate.cloneNode(true);
-            row.setAttribute("cardid", card.id());
-            row.card = card;
-            getByName(row, 'barcode').innerHTML = card.barcode();
-            if(cardPerms['UPDATE_PATRON_ACTIVE_CARD']) {
-                row.active_checkbox = new dijit.form.CheckBox({
-                    scrollOnFocus:false,
-                    checked: openils.Util.isTrue(card.active())
-                }, getByName(row, 'active'));
-            } else {
-                getByName(row, 'active').appendChild(
-                    openils.Util.isTrue(card.active()) ? 
-                        dojo.byId('true').cloneNode(true) :
-                        dojo.byId('false').cloneNode(true)
-                );
-            }
-            if(cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
-                row.primary_radiobutton = new dijit.form.RadioButton({
-                    scrollOnFocus:false,
-                    checked: card.id() == patron.card().id(),
-                    value: card.id(),
-                    name: 'card_primary'
-                }, getByName(row, 'primary'));
-            } else {
-                getByName(row, 'primary').appendChild(
-                    openils.Util.isTrue(card.id() == patron.card().id()) ? 
-                        dojo.byId('true').cloneNode(true) :
-                        dojo.byId('false').cloneNode(true)
-                );
-            }
-            tbody.appendChild(row);
-            first = false;
-        }
-    );
-
-    allCardsDialog.show();
-}
-
-function applyCardChanges() {
-    var cardrows = dojo.query('[cardid]', allCardsDialog.domNode);
-    var changed = false;
-    dojo.forEach(cardrows,
-        function(row) {
-            if(cardPerms['UPDATE_PATRON_ACTIVE_CARD']) {
-                var active = row.active_checkbox.checked ? 't' : 'f'
-                if(row.card.active() != active) {
-                    row.card.active(active);
-                    row.card.ischanged(1);
-                    changed = true;
-                }
-            }
-            if(cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
-                if(row.primary_radiobutton.checked && row.card.id() != patron.card().id()) {
-                    patron.card(row.card);
-                    changed = true;
-                }
-            }
-        }
-    );
-    if(changed && lock_ready && xulG && typeof xulG.lock_tab == 'function' && !already_locked) {
-        xulG.lock_tab();
-        already_locked = true;
-    }
-    allCardsDialog.hide();
-}
-
-/**
- * Mark the current card inactive, create a new primary card
- */
-function replaceCardHandler() {
-    var input = findWidget('ac', 'barcode');
-    input.widget.attr('disabled', false).attr('readOnly', false).attr('value', null).focus();
-    replaceBarcode.attr('disabled', true);
-    
-    // pull old card off the cards list so we don't have a dupe sitting in there
-    if (patron.cards().length > 0) {
-        var old = patron.cards().filter(function(c){return (c.id() == patron.card().id())})[0];
-        old.active('f');
-        old.ischanged(1);
-    }
-
-    var newc = new fieldmapper.ac();
-    newc.id(uEditCardVirtId--);
-    newc.isnew(1);
-    newc.active('t');
-    patron.card(newc);
-    editCard = newc;
-    var t = patron.cards();
-        if (!t) { t = []; }
-        t.push(newc);
-        patron.cards(t);
-}
-
-/**
- * Generate a random password for the patron.
- */
-function generatePasswordHandler() {
-       uEditMakeRandomPw(patron);
-       var f = findWidget('au', 'passwd');
-       f.widget.attr('value', patron.passwd());
-       f = findWidget('au', 'passwd2');
-       f.widget.attr('value', patron.passwd());
-}
-
-/**
- * Loads a staged user and turns them into something the editor can understand
- */
-function uEditLoadStageUser(stageUname) {
-
-    var data = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.stage.retrieve.by_username'],
-        { params : [openils.User.authtoken, stageUname] }
-    );
-
-    stageUser = data.user;
-    patron = uEditNewPatron();
-
-    if(!stageUser) 
-        return patron;
-
-    // copy the data into our new user object
-    for(var key in fieldmapper.IDL.fmclasses.stgu.field_map) {
-        if(fieldmapper.IDL.fmclasses.au.field_map[key] && !fieldmapper.IDL.fmclasses.stgu.field_map[key].virtual) {
-            if(data.user[key]() !== null)
-                patron[key]( data.user[key]() );
-        }
-    }
-
-    // copy the data into our new address objects
-    // TODO: uses the first mailing address only
-    if(data.mailing_addresses.length) {
-
-        var mail_addr = new fieldmapper.aua();
-        mail_addr.id(-1); // virtual ID
-        mail_addr.usr(-1);
-        mail_addr.isnew(1);
-        patron.mailing_address(mail_addr);
-        var t = patron.addresses();
-            if (!t) { t = []; }
-            t.push(mail_addr);
-            patron.addresses(t);
-
-        for(var key in fieldmapper.IDL.fmclasses.stgma.field_map) {
-            if(fieldmapper.IDL.fmclasses.aua.field_map[key] && !fieldmapper.IDL.fmclasses.stgma.field_map[key].virtual) {
-                if(data.mailing_addresses[0][key]() !== null)
-                    mail_addr[key]( data.mailing_addresses[0][key]() );
-            }
-        }
-    }
-    
-    // copy the data into our new address objects
-    // TODO uses the first billing address only
-    if(data.billing_addresses.length) {
-
-        var bill_addr = new fieldmapper.aua();
-        bill_addr.id(-2); // virtual ID
-        bill_addr.usr(-1);
-        bill_addr.isnew(1);
-        patron.billing_address(bill_addr);
-        var t = patron.addresses();
-            if (!t) { t = []; }
-            t.push(bill_addr);
-            patron.addresses(t);
-
-        for(var key in fieldmapper.IDL.fmclasses.stgba.field_map) {
-            if(fieldmapper.IDL.fmclasses.aua.field_map[key] && !fieldmapper.IDL.fmclasses.stgba.field_map[key].virtual) {
-                if(data.billing_addresses[0][key]() !== null)
-                    bill_addr[key]( data.billing_addresses[0][key]() );
-            }
-        }
-    }
-
-    // TODO: uses the first card only
-    if(data.cards.length) {
-        var card = new fieldmapper.ac();
-        card.id(-1); // virtual ID
-        patron.card().barcode(data.cards[0].barcode());
-    }
-
-    return patron;
-}
-
-/*
- * clone the home org, phone numbers, and billing/mailing address
- */
-function uEditCopyCloneData(patron) {
-    cloneUserObj = uEditLoadUser(cloneUser);
-
-    var cloneFields = [
-        'home_ou', 
-        'day_phone', 
-        'evening_phone', 
-        'other_phone',
-        'usrgroup'
-    ];
-
-    if(!uEditCloneCopyAddr) 
-        cloneFields = cloneFields.concat(['mailing_address', 'billing_address']);
-
-    dojo.forEach(
-        cloneFields, 
-        function(field) {
-            patron[field](cloneUserObj[field]());
-        }
-    );
-
-    if(uEditCloneCopyAddr) {
-        var billAddr, mailAddr;
-
-        // copy the billing and mailing addresses into new addresses
-        function cloneAddr(addr) {
-            var newAddr = addr.clone();
-            newAddr.isnew(true);
-            newAddr.id(uEditAddrVirtId--);
-            newAddr.usr(patron.id());
-            patron.addresses().push(newAddr);
-            return newAddr;
-        }
-
-        if(billAddr = cloneUserObj.billing_address()) 
-            patron.billing_address(cloneAddr(billAddr));
-
-        if(mailAddr = cloneUserObj.mailing_address()) {
-            if (billAddr && billAddr.id() == mailAddr.id()) {
-                patron.mailing_address(patron.billing_address());
-            } else {
-                patron.mailing_address(cloneAddr(mailAddr));
-            }
-        }
-
-        if(!billAddr) // if there was no billing addr, use the mailing addr
-            patron.billing_address(patron.mailing_address());
-
-    } else {
-
-        // link the billing and mailing addresses
-        if(patron.billing_address()) {
-            var t = patron.addresses();
-                if (!t) { t = []; }
-                t.push(patron.billing_address());
-                patron.addresses(t);
-        }
-
-        if(patron.mailing_address() && (
-                patron.addresses().length == 0 || 
-                patron.mailing_address().id() != patron.billing_address().id()) ) {
-            var t = patron.addresses();
-                if (!t) { t = []; }
-                t.push(patron.mailing_address());
-                patron.addresses(t);
-        }
-    }
-}
-
-
-function uEditFetchUserSettings(userId) {
-    
-    var baseNode = fieldmapper.aou.findOrgUnit(staff.ws_ou());
-    var orgs = fieldmapper.aou.orgNodeTrail(baseNode);
-    orgs = orgs.map(function(node) { return node.id(); });
-
-    /* fetch any user setting types we need + any that offer opt-in */
-    userSettingTypes = pcrud.search('cust', {
-        '-or' : [
-            {name:['circ.holds_behind_desk', 'circ.collections.exempt', 'opac.hold_notify', 'opac.default_phone', 'opac.default_pickup_location', 'opac.default_sms_carrier', 'opac.default_sms_notify']}, 
-            {name : {
-                'in': {
-                    select : {atevdef : ['opt_in_setting']}, 
-                    from : 'atevdef',
-                    // we only care about opt-in settings for event_defs our users encounter
-                    where : {'+atevdef' : {owner : orgs}}
-                }
-            }}
-        ]
-    });
-
-    var names = userSettingTypes.map(function(obj) { return obj.name() });
-
-    /* fetch any values set for this user */
-    if(userId) {
-        userSettings = fieldmapper.standardRequest(
-            ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve.authoritative'],
-            {params : [openils.User.authtoken, userId, names]});
-    }
-}
-
-
-function uEditLoadUser(userId) {
-    var patron = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
-        {params : [openils.User.authtoken, userId]}
-    );
-    openils.Event.parse_and_raise(patron);
-    return patron;
-}
-
-function loadStaticFields() {
-    for(var idx = 0; tbody.childNodes[idx]; idx++) {
-        var row = tbody.childNodes[idx];
-        if(row.nodeType != row.ELEMENT_NODE) continue;
-        var fmcls = row.getAttribute('fmclass');
-        if(fmcls) {
-            fleshFMRow(row, fmcls);
-        } else {
-
-            if(row.id == 'uedit-settings-divider') {
-
-                var template = tbody.removeChild(dojo.byId('uedit-user-setting-template'));
-                dojo.forEach(userSettingTypes, function(type) { uEditDrawSettingRow(tbody, row, template, type); } );
-
-                if(userSettingTypes.length > 1 || orgSettings['circ.holds.behind_desk_pickup_supported']) {
-                    openils.Util.show('uedit-settings-divider', 'table-row');
-                }
-            }
-        }
-    }
-}
-
-function uEditDrawSettingRow(tbody, dividerRow, template, stype) {
-    var row = template.cloneNode(true);
-    row.setAttribute('user_setting', stype.name());
-    getByName(row, 'label').innerHTML = stype.label();
-    switch(stype.name()) {
-        case 'opac.hold_notify':
-            var template = localeStrings.HOLD_NOTIFY_PHONE + '<span name="hold_phone"></span>&nbsp;'
-                + localeStrings.HOLD_NOTIFY_EMAIL + '<span name="hold_email"></span>';
-            if(orgSettings['sms.enable']) {
-                template += '&nbsp;' + localeStrings.HOLD_NOTIFY_SMS + '<span name="hold_sms"></span>';
-            }
-            getByName(row, 'widget').innerHTML = template;
-            var setting = userSettings['opac.hold_notify'];
-            if(setting == null) setting = 'phone:email';
-            var cb_phone = new dijit.form.CheckBox({scrollOnFocus:false}, getByName(row, 'hold_phone'));
-            cb_phone.attr('value', setting.indexOf('phone') != -1);
-            var cb_email = new dijit.form.CheckBox({scrollOnFocus:false}, getByName(row, 'hold_email'));
-            cb_email.attr('value', setting.indexOf('email') != -1);
-            var cb_sms = null;
-            if(orgSettings['sms.enable']) {
-                cb_sms = new dijit.form.CheckBox({scrollOnFocus:false}, getByName(row, 'hold_sms'));
-                cb_sms.attr('value', setting.indexOf('sms') != -1);
-            }
-            var func = function() {
-                var newVal = '';
-                var splitter = '';
-                if(cb_phone.checked) {
-                    newVal+= splitter + 'phone';
-                    splitter = ':';
-                }
-                if(cb_email.checked) {
-                    newVal+= splitter + 'email';
-                    splitter = ':';
-                }
-                if(orgSettings['sms.enable'] && cb_sms.checked) {
-                    newVal+= splitter + 'sms';
-                    splitter = ':';
-                }
-                userSettingsToUpdate['opac.hold_notify'] = newVal;
-            };
-            dojo.connect(cb_phone, 'onChange', func);
-            dojo.connect(cb_email, 'onChange', func);
-            if(cb_sms) dojo.connect(cb_sms, 'onChange', func);
-            break;
-        case 'opac.default_pickup_location':
-            var sb = new openils.widget.FilteringTreeSelect({
-                scrollOnFocus: false,
-                labelAttr: 'name',
-                searchAttr: 'name',
-                parentField: 'parent_ou',
-                }, getByName(row, 'widget'));
-            sb.tree = fieldmapper.aou.globalOrgTree;
-            sb.startup();
-            sb.attr('value', userSettings[stype.name()]);
-
-            sb.isValid = function() {
-                if(this.item) {
-                    if(holdPickupTypes[this.store.getValue(this.item, 'ou_type')]) {
-                        return true;
-                    }
-                    return false;
-                }
-                return true;
-            };
-
-            dojo.connect(sb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
-            break;
-        case 'opac.default_sms_carrier':
-            if(!orgSettings['sms.enable']) return; // Skip when SMS is disabled
-            var carriers = pcrud.search('csc', {active: 'true'}, {'order_by':[{'class':'csc', 'field':'name'},{'class':'csc', 'field':'region'}]});
-            var storedata = fieldmapper.csc.toStoreData(carriers);
-            for(var i in storedata.items) storedata.items[i].label = storedata.items[i].name + ' (' + storedata.items[i].region + ')';
-            var store = new dojo.data.ItemFileReadStore({data:storedata});
-            var select = new dijit.form.FilteringSelect({store:store,scrollOnFocus:false,labelAttr:'label',searchAttr:'label'}, getByName(row, 'widget'));
-            select.attr('value', userSettings[stype.name()]);
-            select.isValid = function() { return true; };
-            dojo.connect(select, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
-            break;
-        case 'opac.default_sms_notify':
-            if(!orgSettings['sms.enable']) return; // Skip when SMS is disabled
-        case 'opac.default_phone':
-            var tb = new dijit.form.TextBox({scrollOnFocus:false}, getByName(row, 'widget'));
-            tb.attr('value', userSettings[stype.name()]);
-            dojo.connect(tb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
-            break;
-        default:
-            var cb = new dijit.form.CheckBox({scrollOnFocus:false}, getByName(row, 'widget'));
-            cb.attr('value', userSettings[stype.name()]);
-            dojo.connect(cb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
-            if(stype.name() == 'circ.collections.exempt') {
-                checkCollectionsExemptPerm(cb);
-            }
-    }
-    tbody.insertBefore(row, dividerRow.nextSibling);
-    openils.Util.show(row, 'table-row');
-}
-
-function uEditUpdateUserSettings(userId) {
-    return fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.patron.settings.update'],
-        {params : [openils.User.authtoken, userId, userSettingsToUpdate]});
-}
-
-function loadAllAddrs() {
-    dojo.forEach(patron.addresses(),
-        function(addr) {
-            uEditNewAddr(null, addr.id());
-        }
-    );
-}
-
-function loadStatCats() {
-
-    statCats = fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.stat_cat.actor.retrieve.all'],
-        {params : [openils.User.authtoken, staff.ws_ou()]}
-    );
-
-    // draw stat cats
-    for(var idx in statCats) {
-        var stat = statCats[idx];
-        var row = statCatTemplate.cloneNode(true);
-        row.id = 'stat-cat-row-' + idx;
-        row.setAttribute('stat_cat_owner',stat.owner());
-        row.setAttribute('stat_cat_name',stat.name());
-        row.setAttribute('stat_cat_id',stat.id());
-        tbody.appendChild(row);
-        getByName(row, 'name').innerHTML = stat.name();
-        var valtd = getByName(row, 'widget');
-        var span = valtd.appendChild(document.createElement('span'));
-        var store = new dojo.data.ItemFileReadStore(
-                {data:fieldmapper.actsc.toStoreData(stat.entries())});
-        var comboBox = new dijit.form.ComboBox({store:store,scrollOnFocus:false,fetchProperties:{sort:[{attribute: 'value'}]}}, span);
-        comboBox.labelAttr = 'value';
-        comboBox.searchAttr = 'value';
-
-        comboBox._wtype = 'statcat';
-        comboBox._statcat = stat.id();
-        widgetPile.push(comboBox); 
-
-        // populate existing cats
-        var map = patron.stat_cat_entries().filter(
-            function(mp) { return (mp.stat_cat() == stat.id()) })[0];
-        if(map) comboBox.attr('value', map.stat_cat_entry()); 
-
-    }
-}
-
-function loadSurveys() {
-
-    surveys = fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.survey.retrieve.all'],
-        {params : [openils.User.authtoken]}
-    );
-
-    // draw surveys
-    for(var idx in surveys) {
-        var survey = surveys[idx];
-        var required = openils.Util.isTrue(survey.required());
-        var srow = surveyTemplate.cloneNode(true);
-        if(required) srow.setAttribute('required','required');
-        tbody.appendChild(srow);
-        getByName(srow, 'name').innerHTML = survey.name();
-
-        for(var q in survey.questions()) {
-            var quest = survey.questions()[q];
-            var qrow = surveyQuestionTemplate.cloneNode(true);
-            if(required) qrow.setAttribute('required','required');
-            tbody.appendChild(qrow);
-            getByName(qrow, 'question').innerHTML = quest.question();
-
-            var span = getByName(qrow, 'answers').appendChild(document.createElement('span'));
-            var store = new dojo.data.ItemFileReadStore(
-                {data:fieldmapper.asva.toStoreData(quest.answers())});
-            var select = new dijit.form.FilteringSelect({store:store,scrollOnFocus:false}, span);
-            if (! required ) {
-                select.isValid = function() { return true; };
-            }
-            select.labelAttr = 'answer';
-            select.searchAttr = 'answer';
-
-            select._wtype = 'survey';
-            select._survey = survey.id();
-            select._question = quest.id();
-            widgetPile.push(select); 
-        }
-    }
-}
-
-
-function fleshFMRow(row, fmcls, args) {
-    var fmfield = row.getAttribute('fmfield');
-    var wclass = row.getAttribute('wclass');
-    var wstyle = row.getAttribute('wstyle');
-    var wconstraints = row.getAttribute('wconstraints');
-    /* use CSS to set the zindex for widgets you want to disable. */
-    var disabled = dojo.style(row, 'zIndex') == -1 ? true : false;
-    var isphone = (fmcls == 'au') && (fmfield.search('_phone') != -1);
-
-    var isPasswd2 = (fmfield == 'passwd2');
-    if(isPasswd2) fmfield = 'passwd';
-    var fieldIdl = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield];
-    if(!args) args = {};
-
-    var existing = dojo.query('td', row);
-    var htd = existing[0] || row.appendChild(document.createElement('td'));
-    var ltd = existing[1] || row.appendChild(document.createElement('td'));
-    var wtd = existing[2] || row.appendChild(document.createElement('td'));
-    var ftd = existing[3] || row.appendChild(document.createElement('td'));
-
-    openils.Util.addCSSClass(htd, 'uedit-help');
-    if(fieldDoc[fmcls] && fieldDoc[fmcls][fmfield]) {
-        var link = dojo.byId('uedit-help-template').cloneNode(true);
-        link.id = '';
-        link.onclick = function() { ueLoadContextHelp(fmcls, fmfield) };
-        openils.Util.removeCSSClass(link, 'hidden');
-        htd.appendChild(link);
-    }
-
-    if(!ltd.textContent) {
-        ltd.appendChild(document.createTextNode(fieldIdl.label));
-    }
-
-    if(!ftd.textContent) {
-        if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']) {
-            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']));
-        }
-        else if(isphone && orgSettings['ui.patron.edit.phone.example']) {
-            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.phone.example']));
-        }
-        else if(fieldIdl.datatype == 'timestamp') {
-            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + dojo.date.locale.format(new Date(1970,0,31),{selector: "date", fullYear: true, datePattern: (orgSettings['format.date'] ? orgSettings['format.date'] : null)})));
-        }
-
-        if (fmcls == "au" && (isphone || fmfield == "email")) {
-            var span = dojo.create(
-                "span", {
-                    "className": "hidden",
-                    "id": "wrap_invalidate_" + fmfield
-                }
-            );
-            uGenerateInvalidatorWidget(span, fmfield);
-            ftd.appendChild(span);
-        }
-    }
-
-    var span = document.createElement('span');
-    wtd.appendChild(span);
-
-    var fmObject = null;
-    switch(fmcls) {
-        case 'au' :
-            fmObject = patron;
-            if(fmfield == 'barred') {
-                // Are we allowed to touch the barred state?
-                var permission = 'BAR_PATRON';
-                if(fmObject.barred() == 't') {
-                    permission = 'UNBAR_PATRON';
-                }
-                var ou = staff.ws_ou();
-                if(fmObject.home_ou() != null) {
-                    ou = fmObject.home_ou();
-                }
-                var resp = fieldmapper.standardRequest(
-                    ['open-ils.actor', 'open-ils.actor.user.perm.check'],
-                    { params : [openils.User.authtoken, staff.id(), ou, [permission] ] }
-                );
-                if(resp[0]) { // No permission to adjust barred state from current
-                    disabled = true;
-                }
-            }
-            break;
-        case 'ac' : if(!editCard) editCard = patron.card(); fmObject = editCard; break;
-        case 'aua' : 
-            fmObject = patron.addresses().filter(
-                function(i) { return (i.id() == args.addr) })[0];
-            if(fmObject && fmObject.usr() != patron.id())
-                disabled = true;
-            break;
-    }
-
-    // Adjust required value by org settings
-    var curRequired = row.getAttribute('required');
-    var required = curRequired == 'required';
-    if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.require']) {
-        row.setAttribute('required', 'required');
-        required = true;
-    }
-    else if (curRequired != 'required' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.show']) {
-        row.setAttribute('required', 'show');
-    }
-    else if (curRequired != 'required' && curRequired != 'show' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.suggest']) {
-        row.setAttribute('required', 'suggested');
-    }
-
-    // password data is not fetched/required/displayed for existing users
-    if(!patron.isnew() && 'passwd' == fmfield)
-        required = false;
-
-    var dijitArgs = {
-        style: wstyle, 
-        required : required,
-        constraints : (wconstraints) ? eval('('+wconstraints+')') : {}, // the ()'s prevent Invalid Label errors with eval
-        disabled : disabled
-    };
-
-    // Org settings provided regex?
-    if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex']) {
-        dijitArgs.regExp = orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex'];
-    }
-    else if(isphone && orgSettings['ui.patron.edit.phone.regex']) {
-        dijitArgs.regExp = orgSettings['ui.patron.edit.phone.regex'];
-    }
-
-    if(fmcls == 'au' && fmfield == 'passwd') {
-        if (orgSettings['global.password_regex']) {
-            dijitArgs.regExp = orgSettings['global.password_regex'];
-        }
-    }
-
-    if(fmcls == 'au' && fmfield == 'dob' && !orgSettings['ui.patron.edit.au.dob.calendar'])
-        dijitArgs.popupClass = "";
-
-    var value = row.getAttribute('wvalue');
-    if(value !== null)
-        dijitArgs.value = value;
-
-    var wargs = {
-        idlField : fieldIdl,
-        fmObject : fmObject,
-        fmClass : fmcls,
-        parentNode : span,
-        widgetClass : wclass,
-        dijitArgs : dijitArgs,
-        orgDefaultsToWs : true,
-        orgLimitPerms : ['UPDATE_USER'],
-    };
-
-    if(fmfield == 'profile') {
-        // fetch profile groups non-async so existing expire_date is
-        // not overwritten when the profile groups arrive and update
-        wargs.forceSync = true;
-        wargs.disableQuery = {usergroup : 'f'};
-    } else {
-        wargs.forceSync = false;
-    }
-
-    if(fmcls == 'au' && fmfield == 'home_ou'){
-       wargs.labelAttr = 'name';
-       wargs.searchAttr = 'name';
-    }
-
-    var widget = new openils.widget.AutoFieldWidget(wargs);
-    widget.build(
-        function(w, ww) {
-            if(fmfield == 'profile') {
-                trimGrpTree(ww);
-                if(!patron.isnew() && !checkGrpAppPerm(patron.profile())){
-                    w.attr('disabled', true);
-                }
-            }
-        }
-    );
-
-    // now put it back before we register the widget
-    if(isPasswd2) fmfield = 'passwd2';
-
-    widget._wtype = fmcls;
-    widget._fmfield = fmfield;
-    widget._addr = args.addr;
-    widgetPile.push(widget);
-    attachWidgetEvents(fmcls, fmfield, widget);
-    return widget;
-}
-
-function trimGrpTree(autoWidget) {
-    var store = autoWidget.widget.store;
-    if(!store) return;
-    // remove all groups that this user are not allowed to edit, 
-    // except the profile group of an existing user
-    store.fetch({onItem : 
-        function(item) {
-            if(!checkGrpAppPerm(item.id[0]) && patron.profile() != item.id[0])
-                store.deleteItem(item);
-        }
-    });
-}
-
-function findWidget(wtype, fmfield, callback) {
-    return widgetPile.filter(
-        function(i){
-            if(i._wtype == wtype && i._fmfield == fmfield) {
-                if(callback) return callback(i);
-                return true;
-            }
-        }
-    ).pop();
-}
-
-/**
- * if the user does not have the UPDATE_PATRON_CLAIM_RETURN_COUNT, 
- * they are not allowed to directly alter the claim return count. 
- * This function checks the perm and disable/enables the widget.
- */
-function checkClaimsReturnCountPerm() {
-    new openils.User().getPermOrgList(
-        'UPDATE_PATRON_CLAIM_RETURN_COUNT',
-        function(orgList) { 
-            var cr = findWidget('au', 'claims_returned_count');
-            if(orgList.indexOf(patron.home_ou()) == -1) 
-                cr.widget.attr('disabled', true);
-            else
-                cr.widget.attr('disabled', false);
-        },
-        true, 
-        true
-    );
-}
-
-
-function checkClaimsNoCheckoutCountPerm() {
-    new openils.User().getPermOrgList(
-        'UPDATE_PATRON_CLAIM_NEVER_CHECKED_OUT_COUNT',
-        function(orgList) { 
-            var cr = findWidget('au', 'claims_never_checked_out_count');
-            if(orgList.indexOf(patron.home_ou()) == -1) 
-                cr.widget.attr('disabled', true);
-            else
-                cr.widget.attr('disabled', false);
-        },
-        true, 
-        true
-    );
-}
-
-var collectExemptCBox;
-function checkCollectionsExemptPerm(cbox) {
-    if(cbox) collectExemptCBox = cbox;
-    new openils.User().getPermOrgList(
-        'UPDATE_PATRON_COLLECTIONS_EXEMPT',
-        function(orgList) { 
-            if(orgList.indexOf(patron.home_ou()) == -1) 
-                collectExemptCBox.attr('disabled', true);
-            else
-                collectExemptCBox.attr('disabled', false);
-        },
-        true, 
-        true
-    );
-}
-
-function usePhonePw(newVal) {
-    var newPw = false;
-    if(this.regExp) {
-        matches = RegExp(this.regExp).exec(newVal);
-        if(matches.length > 1) newPw = matches[1];
-    }
-    if(!newPw && newVal && newVal.length >= 4) {
-        newPw = newVal.substring(newVal.length - 4);
-    }
-    if(newPw) {
-        var p1 = findWidget('au', 'passwd');
-        var p2 = findWidget('au', 'passwd2');
-        if (p1 && p2) {
-            p1.widget.attr('value', newPw);
-            p2.widget.attr('value', newPw);
-        }
-        return newPw;
-    } else {
-        return null;
-    }
-}
-
-function attachWidgetEvents(fmcls, fmfield, widget) {
-
-    dojo.connect(
-        widget.widget,
-        'onKeyPress',
-        function(ev){
-            netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-            if (!(ev.altKey || ev.ctrlKey || ev.metaKey)) {
-                if (lock_ready && xulG && typeof xulG.lock_tab == 'function') {
-                    if (! already_locked) {
-                        xulG.lock_tab();
-                        already_locked = true;
-                    }
-                }
-            }
-        }
-    );
-    dojo.connect(
-        widget.widget,
-        'onChange',
-        function(){
-            if (lock_ready && xulG && typeof xulG.lock_tab == 'function') {
-                if (! already_locked) {
-                    xulG.lock_tab();
-                    already_locked = true;
-                }
-            }
-        }
-    );
-
-
-    if(fmcls == 'ac') {
-        if(fmfield == 'barcode') {
-            dojo.connect(widget.widget, 'onChange',
-                function() {
-                    var barcode = this.attr('value');
-                    dupeBarcode = false;
-                    dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
-                    fieldmapper.standardRequest(
-                        ['open-ils.actor', 'open-ils.actor.barcode.exists'],
-                        {
-                            params: [openils.User.authtoken, barcode],
-                            oncomplete : function(r) {
-                                var res = openils.Util.readResponse(r);
-                                if(res == '1') {
-                                    dupeBarcode = true;
-                                    dojo.removeClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
-                                } else {
-                                    dupeBarcode = false;
-                                    dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
-                                    editCard.barcode(barcode); // Keep the "All" interface up to date
-                                    var un = findWidget('au', 'usrname');
-                                    if(!un.widget.attr('value'))
-                                        un.widget.attr('value', barcode);
-                                }
-                            }
-                        }
-                    );
-                }
-            );
-            return;
-        }
-    }
-
-    if(fmcls == 'au') {
-        switch(fmfield) {
-
-            case 'usrname':
-                widget.widget.isValid = function() {
-                    // No spaces
-                    if(this.attr("value").match(/\s/)) {
-                        return false;
-                    }
-                    // Can look like a barcode (for initial value)
-                    if(orgSettings['opac.barcode_regex']) {
-                        var test_regexp = new RegExp(orgSettings['opac.barcode_regex']);
-                        if(test_regexp.test(this.attr("value"))) {
-                            return true;
-                        }
-                    }
-                    // Can look like a username
-                    if(orgSettings['opac.username_regex']) {
-                        var test_regexp = new RegExp(orgSettings['opac.username_regex']);
-                        if(test_regexp.test(this.attr("value"))) {
-                            return true;
-                        }
-                    }
-                    // If we know what a barcode and username look like and we got here, reject
-                    if(orgSettings['opac.barcode_regex'] && orgSettings['opac.username_regex'])
-                        return false;
-                    // Otherwise we don't have enough info to say either way, let it through.
-                    return true;
-                }
-                dojo.connect(widget.widget, 'onChange', 
-                    function() {
-                        var input = findWidget('au', 'usrname');
-                        var usrname = input.widget.attr('value');
-
-                        if(!usrname) {
-                            dupeUsrname = false;
-                            dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
-                            return;
-                        }
-
-                        fieldmapper.standardRequest(
-                            ['open-ils.actor', 'open-ils.actor.username.exists'],
-                            {
-                                params: [openils.User.authtoken, usrname],
-                                oncomplete : function(r) {
-                                    var res = openils.Util.readResponse(r);
-                                    if(res) {
-                                        dupeUsrname = true;
-                                        dojo.removeClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
-                                    } else {
-                                        dupeUsrname = false;
-                                        dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
-                                    }
-                                }
-                            }
-                        );
-                    }   
-                );
-
-                return;
-
-            case 'profile': // when the profile changes, update the expire date
-                dojo.connect(widget.widget, 'onChange', 
-                    function() {
-                        var self = this;
-                        var expireWidget = findWidget('au', 'expire_date');
-                        function found(items) {
-                            if(items.length == 0) return;
-                            var item = items[0];
-                            var interval = self.store.getValue(item, 'perm_interval');
-                            expireWidget.widget.attr('value', dojo.date.add(new Date(), 
-                                'second', openils.Util.intervalToSeconds(interval)));
-                        }
-                        this.store.fetch({onComplete:found, query:{id:this.attr('value')}});
-                    }
-                );
-                return;
-
-            case 'dob':
-                widget.widget.isValid = function() {
-                    return this.attr("value") < new Date();
-                };
-                dojo.connect(widget.widget, 'onChange',
-                    function(newDob) {
-                        if(!newDob) return;
-                        var oldDob = patron.dob();
-                        if(dojo.date.stamp.fromISOString(oldDob) == newDob) return;
-
-                        var juvInterval = orgSettings['global.juvenile_age_threshold'] || '18 years';
-                        var juvWidget = findWidget('au', 'juvenile');
-                        var base = new Date();
-                        base.setTime(base.getTime() - Number(openils.Util.intervalToSeconds(juvInterval) + '000'));
-
-                        if(newDob <= base) // older than global.juvenile_age_threshold
-                            juvWidget.widget.attr('value', false);
-                        else
-                            juvWidget.widget.attr('value', true);
-                    }
-                );
-                return;
-
-            case 'first_given_name':
-            case 'family_name':
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) { uEditDupeSearch('name', newVal); });
-                return;
-
-            case 'email':
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) { uEditDupeSearch('email', newVal); });
-                return;
-
-            case 'ident_value':
-            case 'ident_value2':
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) { uEditDupeSearch('ident', newVal); });
-                return;
-
-            case 'day_phone':
-                // if configured, use the last four digits of the day phone number as the password
-                // Alt, use the first capture group of the validator regex
-                if(uEditUsePhonePw && patron.isnew()) {
-                    dojo.connect(widget.widget, 'onChange', widget.widget, usePhonePw);
-                    if (patron.day_phone()) {
-                        usePhonePw(patron.day_phone());
-                    }
-                }
-            case 'evening_phone':
-            case 'other_phone':
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) { uEditDupeSearch('phone', newVal); });
-                return;
-
-            case 'home_ou':
-                widget.widget.isValid = function() {
-                    if(this.item) {
-                        if(homeOuTypes[this.store.getValue(this.item, 'ou_type')]) {
-                            return true;
-                        }
-                        return false;
-                    }
-                    return true;
-                };
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) { 
-                        checkClaimsReturnCountPerm(); 
-                        checkClaimsNoCheckoutCountPerm();
-                        checkCollectionsExemptPerm();
-                    }
-                );
-                return;
-
-            case 'passwd':
-                dojo.connect(widget.widget, 'onChange',
-                    function(newVal) {
-                        var pw1 = findWidget('au', 'passwd').widget;
-                        var pw2 = findWidget('au', 'passwd2').widget;
-                        var preserved_value = pw2.attr('value');
-                        // Ensure that the pw2 field match the pw1 field to validate
-                        pw2.regExp = newVal.replace(/([.\\^$*+?\(\)\[\]\{\}])/g, '\\$1');
-                        pw2.reset();
-                        pw2.attr('value',preserved_value);
-                    });
-                return;
-        }
-    }
-
-    if(fmclass = 'aua') {
-
-        // map post code to city, state, and county
-        if (fmfield == 'post_code') {
-            dojo.connect(widget.widget, 'onChange',
-                function(e) { 
-                    fieldmapper.standardRequest(
-                        ['open-ils.search', 'open-ils.search.zip'],
-                        {   async: true,
-                            params: [e],
-                            oncomplete : function(r) {
-                                var res = openils.Util.readResponse(r);
-                                if(!res) return;
-                                var callback = function(w) { return w._addr == widget._addr; };
-                                if(res.city) findWidget('aua', 'city', callback).widget.attr('value', res.city);
-                                if(res.state) findWidget('aua', 'state', callback).widget.attr('value', res.state);
-                                if(res.county) findWidget('aua', 'county', callback).widget.attr('value', res.county);
-                                if(res.alert) alert(res.alert);
-                            }
-                        }
-                    );
-                }
-            );
-        }
-
-        // duplicate address search
-        if (['street1', 'street2', 'city'].indexOf(fmfield) > -1) {
-            dojo.connect(widget.widget, 'onChange',
-                function(e) {
-                    var callback = function(w) { return w._addr == widget._addr; };
-                    var args = {
-                        street1 : findWidget('aua', 'street1', callback).widget.attr('value'),
-                        street2 : findWidget('aua', 'street2', callback).widget.attr('value'),
-                        city : findWidget('aua', 'city', callback).widget.attr('value'),
-                        post_code : findWidget('aua', 'post_code', callback).widget.attr('value')
-                    };
-                    if(args.street1 && args.city && args.post_code)
-                        uEditDupeSearch('address', args); 
-                }
-            ); 
-        }
-
-        if (addressAlertFields.indexOf(fmfield) > -1) {
-            dojo.connect(
-                widget.widget, 'onChange', 
-                function() { uEditAddressAlertMarshal(widget._addr) }
-            );
-        }
-    }
-}
-
-function uEditAddressAlertMarshal(addrId, changeBilling, changeMailing) {
-
-    if (changeBilling) {
-        uEditAddressAlertMarshal(prevBillingAddress);
-        prevBillingAddress = addrId;
-    }
-    
-    if (changeMailing) {
-        uEditAddressAlertMarshal(prevMailingAddress);
-        prevMailingAddress = addrId;
-    }
-
-    var callback = function(w) { return w._addr == addrId; };
-    var args = {};
-    dojo.forEach(addressAlertFields,
-        function(field) {
-            args[field] = findWidget('aua', field, callback).widget.attr('value')
-        }
-    );
-    args.mailing_address = dojo.byId('uedit-mailing-address-' + addrId).checked;
-    args.billing_address = dojo.byId('uedit-billing-address-' + addrId).checked;
-    uEditAddressAlertSearch(args, addrId);
-}
-
-var _addrAlertTimeout = {};
-function uEditAddressAlertSearch(args, addrId) {
-
-    _addrAlertTimeout[addrId] = setTimeout(
-        function() {
-            if (_addrAlertTimeout[addrId]) 
-                clearTimeout(_addrAlertTimeout[addrId]);
-
-            console.log('creating addr alert search for ' + addrId);
-
-            fieldmapper.standardRequest(
-                ['open-ils.actor', 'open-ils.actor.address_alert.test'],
-                {   async: true,
-                    params: [openils.User.authtoken, staff.ws_ou(), args],
-                    oncomplete : function(r) {
-                        var alerts = openils.Util.readResponse(r);
-                        var msgNode = dojo.byId('uedit-address-alert-message');
-                        var headerRow = dojo.filter(
-                            dojo.query('[name=uedit-addr-divider]'),
-                            function(row) { return row.getAttribute('addr') == addrId })[0]
-
-                        msgNode.innerHTML = '';
-
-                        if (alerts.length) {
-
-                            // show the alert box
-                            openils.Util.hide('uedit-help-div');
-                            openils.Util.hide('uedit-dupe-div');
-                            openils.Util.show('uedit-address-alert');
-
-                            // style the address header row
-                            openils.Util.addCSSClass(headerRow, 'uedit-address-alert-divider');
-
-                            dojo.forEach(alerts,
-                                function(addr) {
-                                    msgNode.innerHTML += addr.alert_message() + '<br/>';
-                                }
-                            );
-
-                        } else { 
-                            openils.Util.hide('uedit-address-alert');
-                            openils.Util.removeCSSClass(headerRow, 'uedit-address-alert-divider');
-                        }
-                    }
-                }
-            );
-        }, 
-        addressAlertTimeout
-    );
-}
-
-function uEditDupeSearch(type, value) {
-    if(!value) return;
-    var search;
-    switch(type) {
-
-        case 'name':
-            openils.Util.hide('uedit-dupe-names-link');
-            var fname = findWidget('au', 'first_given_name').widget.attr('value');
-            var lname = findWidget('au', 'family_name').widget.attr('value');
-            if( !(fname && lname) ) return;
-            search = {
-                first_given_name : {value : fname, group : 0},
-                family_name : {value : lname, group : 0},
-            };
-            break;
-
-        case 'email':
-            openils.Util.hide('uedit-dupe-email-link');
-            search = {email : {value : value, group : 0}};
-            break;
-
-        case 'ident':
-            openils.Util.hide('uedit-dupe-ident-link');
-            search = {ident : {value : value, group : 2}};
-            break;
-
-        case 'phone':
-            openils.Util.hide('uedit-dupe-phone-link');
-            search = {phone : {value : value, group : 2}};
-            break;
-
-        case 'address':
-            openils.Util.hide('uedit-dupe-address-link');
-            search = {};
-            dojo.forEach(['street1', 'street2', 'city', 'post_code'],
-                function(field) {
-                    if(value[field])
-                        search[field] = {value : value[field], group: 1};
-                }
-            );
-            break;
-    }
-
-    // find possible duplicate patrons
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.patron.search.advanced'],
-        {   async: true,
-            params: [openils.User.authtoken, search],
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                resp = resp.filter(function(id) { return (id != patron.id()); });
-
-                if(resp && resp.length > 0) {
-
-                    openils.Util.hide('uedit-help-div');
-                    openils.Util.hide('uedit-address-alert');
-                    openils.Util.show('uedit-dupe-div');
-                    var link;
-
-                    switch(type) {
-                        case 'name':
-                            link = dojo.byId('uedit-dupe-names-link');
-                            link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_NAME, [resp.length]);
-                            break;
-                        case 'email':
-                            link = dojo.byId('uedit-dupe-email-link');
-                            link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_EMAIL, [resp.length]);
-                            break;
-                        case 'ident':
-                            link = dojo.byId('uedit-dupe-ident-link');
-                            link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_IDENT, [resp.length]);
-                            break;
-                        case 'phone':
-                            link = dojo.byId('uedit-dupe-phone-link');
-                            link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_PHONE, [resp.length]);
-                            break;
-                        case 'address':
-                            link = dojo.byId('uedit-dupe-address-link');
-                            link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_ADDR, [resp.length]);
-                            break;
-                    }
-
-                    openils.Util.show(link);
-                    link.onclick = function() {
-                        search.search_sort = js2JSON(["penalties", "family_name", "first_given_name"]);
-                        if(window.xulG)
-                            window.xulG.spawn_search(search);
-                        else
-                            console.log("running XUL patron search " + js2JSON(search));
-                    }
-                }
-            }
-        }
-    );
-}
-
-function getByName(node, name) {
-    return dojo.query('[name='+name+']', node)[0];
-}
-
-
-function ueLoadContextHelp(fmcls, fmfield) {
-    openils.Util.hide('uedit-dupe-div');
-    openils.Util.hide('uedit-dupe-div');
-    openils.Util.show('uedit-help-div');
-    dojo.byId('uedit-help-field').innerHTML = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield].label;
-    dojo.byId('uedit-help-text').innerHTML = fieldDoc[fmcls][fmfield].string();
-}
-
-
-/* creates a new patron object with card attached */
-function uEditNewPatron() {
-    patron = new au();
-    patron.isnew(1);
-    patron.id(-1);
-    card = new ac();
-    card.id(uEditCardVirtId--);
-    card.isnew(1);
-    patron.active(1);
-    patron.card(card);
-    patron.cards([card]);
-    patron.net_access_level(orgSettings['ui.patron.default_inet_access_level'] || 1);
-    patron.ident_type(orgSettings['ui.patron.default_ident_type']);
-    patron.stat_cat_entries([]);
-    patron.survey_responses([]);
-    patron.addresses([]);
-    uEditMakeRandomPw(patron);
-    return patron;
-}
-
-function uEditMakeRandomPw(patron) {
-    var rand  = Math.random();
-    rand = parseInt(rand * 10000) + '';
-    while(rand.length < 4) rand += '0';
-/*
-    appendClear($('ue_password_plain'),text(rand));
-    unHideMe($('ue_password_gen'));
-*/
-    patron.passwd(rand);
-    return rand;
-}
-
-function uEditWidgetVal(w) {
-    var val = (w.getFormattedValue) ? w.getFormattedValue() : w.attr('value');
-    if(val === '') val = null;
-    return val;
-}
-
-function uEditSave() { _uEditSave(); }
-function uEditSaveClone() { _uEditSave(true); }
-
-function _uEditSave(doClone) {
-
-    if ( (! myForm.isValid()) || dupeUsrname || dupeBarcode ) {
-        alert(localeStrings.INVALID_FORM);
-        return;
-    }
-
-    for(var idx in widgetPile) {
-        var w = widgetPile[idx];
-        var val = uEditWidgetVal(w);
-
-        switch(w._wtype) {
-            case 'au':
-                if(w._fmfield != 'passwd2')
-                    patron[w._fmfield](val);
-                break;
-
-            case 'ac':
-                if(!editCard) editCard = patron.card();
-                editCard[w._fmfield](val);
-                break;
-
-            case 'aua':
-                var addr = patron.addresses().filter(function(i){return (i.id() == w._addr)})[0];
-                if(!addr) {
-                    addr = new fieldmapper.aua();
-                    addr.id(w._addr);
-                    addr.isnew(1);
-                    addr.usr(patron.id());
-                    addr.country(orgSettings['ui.patron.default_country']);
-                    var t = patron.addresses();
-                        if (!t) { t = []; }
-                        t.push(addr);
-                        patron.addresses(t);
-                } else {
-                    if(addr[w._fmfield]() != val)
-                        addr.ischanged(1);
-                }
-                addr[w._fmfield](val);
-
-                if(dojo.byId('uedit-billing-address-' + addr.id()).checked) 
-                    patron.billing_address(addr.id());
-
-                if(dojo.byId('uedit-mailing-address-' + addr.id()).checked)
-                    patron.mailing_address(addr.id());
-
-                break;
-
-            case 'survey':
-                if(val == null) break;
-                var resp = new fieldmapper.asvr();
-                resp.isnew(1);
-                resp.survey(w._survey)
-                resp.usr(patron.id());
-                resp.question(w._question)
-                resp.answer(val);
-                var t = patron.survey_responses();
-                    if (!t) { t = []; }
-                    t.push(resp);
-                    patron.survey_responses(t);
-                break;
-
-            case 'statcat':
-                var map = patron.stat_cat_entries().filter(
-                    function(m){
-                        return (m.stat_cat() == w._statcat) })[0];
-
-                if(map) {
-                    if(map.stat_cat_entry() == val) 
-                        break;
-                    if(val == null) {
-                        val = '';
-                        map.isdeleted(1);
-                    } else {
-                        map.ischanged(1);
-                    }
-                } else {
-                    if(val == null)
-                        break;
-                    map = new fieldmapper.actscecm();
-                    map.isnew(1);
-                }
-
-                map.stat_cat(w._statcat);
-                map.stat_cat_entry(val);
-                map.target_usr(patron.id());
-                var t = patron.stat_cat_entries();
-                    if (!t) { t = []; }
-                    t.push(map);
-                    patron.stat_cat_entries(t);
-                break;
-        }
-    }
-
-    patron.ischanged(1);
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.patron.update'],
-        {   async: true,
-            params: [openils.User.authtoken, patron],
-            oncomplete: function(r) {
-                lock_ready = false;
-                if (xulG && typeof xulG.unlock_tab == 'function') {
-                    xulG.unlock_tab();
-                    already_locked = false;
-                }
-                /* There's something that seems to just make the form reload
-                 * on all saves, so this uUpdate... isn't needed here after
-                 * all. */
-                //uUpdateContactInvalidators();
-
-                newPatron = openils.Util.readResponse(r);
-                if(newPatron) {
-                    uEditUpdateUserSettings(newPatron.id());
-                    if(stageUser) uEditRemoveStage();
-                    uEditFinishSave(newPatron, doClone);
-                }
-            }
-        }
-    );
-}
-
-function uUpdateContactInvalidators() {
-    /* show invalidator buttons for fields that having anything in them */
-    ["email", "day_phone", "evening_phone", "other_phone"].forEach(
-        function(f) {
-            openils.Util[patron[f]() ? "show" : "hide"]("wrap_invalidate_" + f);
-        }
-    );
-}
-
-function uGenerateInvalidatorWidget(container_node, field) {
-    new dijit.form.Button(
-        {
-            "label": localeStrings.INVALIDATE,
-            "scrollOnFocus": false,
-            "onClick": function() {
-                progressDialog.show(true);
-                fieldmapper.standardRequest(
-                    ["open-ils.actor", "open-ils.actor.invalidate." + field], {
-                        "async": true,
-                        "params": [openils.User.authtoken, patron.id(), null, patron.home_ou()],
-                        "oncomplete": function(r) {
-                            progressDialog.hide();
-                            // alerts on non-success event
-                            var res = openils.Util.readResponse(r);
-
-                            if (res.payload.last_xact_id) {
-                                for (var id in res.payload.last_xact_id) {
-                                    if (patron.id() == id)
-                                        patron.last_xact_id(
-                                            res.payload.last_xact_id[id]
-                                        );
-                                }
-
-                                findWidget("au",field).widget.attr("value","");
-                                openils.Util.hide(container_node);
-                            }
-                        }
-                    }
-                );
-            }
-        }, dojo.create("span", null, container_node, "only")
-    );
-}
-
-function uEditRemoveStage() {
-    var resp = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.stage.delete'],
-        { params : [openils.User.authtoken, stageUser.row_id()] }
-    )
-}
-
-function uEditFinishSave(newPatron, doClone) {
-
-    if(doClone && cloneUser == null)
-        cloneUser = newPatron.id();
-
-       if( doClone ) {
-
-               if(xulG && typeof xulG.spawn_editor == 'function' && !patron.isnew() ) {
-            window.xulG.spawn_editor({ses:openils.User.authtoken,clone:cloneUser});
-            uEditRefresh();
-
+require([
+       "dojo/data/ItemFileReadStore",
+       "dijit/form/Form",
+       "dijit/form/Textarea",
+       "dijit/form/FilteringSelect",
+       "dijit/form/ComboBox",
+       "dijit/form/NumberSpinner",
+       "fieldmapper/IDL",
+       "fieldmapper/OrgUtils",
+       "openils/PermaCrud",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/widget/ProgressDialog",
+       "dijit/form/CheckBox",
+       "dijit/form/Button",
+       "dojo/date",
+       "openils/CGI",
+       "openils/XUL",
+       "openils/Util",
+       "openils/Event"
+       ],
+function(dojo_data_ItemFileReadStore,
+       dijit_form_Form,
+       dijit_form_Textarea,
+       dijit_form_FilteringSelect,
+       dijit_form_ComboBox,
+       dijit_form_NumberSpinner,
+       fieldmapper_IDL,
+       fieldmapper_OrgUtils,
+       openils_PermaCrud,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_widget_ProgressDialog,
+       dijit_form_CheckBox,
+       dijit_form_Button,
+       dojo_date,
+       openils_CGI,
+       openils_XUL,
+       openils_Util,
+       openils_Event){
+       
+       dojo.requireLocalization('openils.actor', 'register');
+       var localeStrings = dojo.i18n.getLocalization('openils.actor', 'register');
+       
+       
+       var pcrud;
+       var fmClasses = ['au', 'ac', 'aua', 'actsc', 'asv', 'asvq', 'asva'];
+       var fieldDoc = {};
+       var statCats;
+       var statCatTemplate;
+       var surveys;
+       var staff;
+       var patron;
+       var uEditUsePhonePw = false;
+       var widgetPile = [];
+       var uEditCardVirtId = -1;
+       var uEditAddrVirtId = -1;
+       var orgSettings = {};
+       var userSettings = {};
+       var userSettingsToUpdate = {};
+       var userSettingTypes;
+       var tbody;
+       var addrTemplateRows;
+       var cgi;
+       var cloneUser;
+       var cloneUserObj;
+       var stageUser;
+       var optInSettings;
+       var allCardsTemplate;
+       var uEditCloneCopyAddr; // if true, copy addrs on clone instead of link
+       var homeOuTypes = {};
+       var holdPickupTypes = {};
+       var cardPerms = {};
+       var editCard;
+       var prevBillingAddress;
+       var prevMailingAddress;
+       
+       var dupeUsrname = false;
+       var dupeBarcode = false;
+       
+       // allow for a pause after typing before sending address alert queries
+       var addressAlertTimeout = 2000; 
+       var addressAlertFields = 
+           ['street1', 'street2', 'city', 'state', 'county', 'country', 'post_code'];
+       
+       if(!window.xulG) var xulG = null;
+       var lock_ready = false;
+       var already_locked = false;
+       
+       function load() {
+           staff = new openils.User().user;
+           pcrud = new openils_PermaCrud();
+           cgi = new openils_CGI();
+           cloneUser = cgi.param('clone');
+           var userId = cgi.param('usr');
+           var stageUname = cgi.param('stage');
+       
+           saveButton.attr("label", localeStrings.SAVE);
+           saveCloneButton.attr("label", localeStrings.SAVE_CLONE);
+           replaceBarcode.attr("label", localeStrings.REPLACE_BARCODE);
+           dojo.byId('uedit-show-required').innerHTML = localeStrings.SHOW_REQUIRED;
+           dojo.byId('uedit-show-suggested').innerHTML = localeStrings.SHOW_SUGGESTED;
+           dojo.byId('uedit-show-all').innerHTML = localeStrings.SHOW_ALL;
+           dojo.byId('uedit-dupe-barcode-warning').innerHTML = localeStrings.BARCODE_IN_USE;
+           allCards.attr("label", localeStrings.SEE_ALL);
+           dojo.byId('uedit-dupe-username-warning').innerHTML = localeStrings.DUPE_USERNAME;
+           generatePassword.attr("label", localeStrings.RESET_PASSWORD);
+           dojo.byId('verifyPassword').innerHTML = localeStrings.VERIFY_PASSWORD;
+           dojo.byId('parentGuardian').innerHTML = localeStrings.PARENT_OR_GUARDIAN;
+           dojo.byId('userSettings').innerHTML = localeStrings.USER_SETTINGS;
+           dojo.byId('statCats').innerHTML = localeStrings.STAT_CATS;
+           dojo.byId('uedit-all-cards-barcode').innerHTML = localeStrings.ALL_CARDS_BARCODE;
+           dojo.byId('uedit-all-cards-active').innerHTML = localeStrings.ALL_CARDS_ACTIVE;
+           dojo.byId('uedit-all-cards-primary').innerHTML = localeStrings.ALL_CARDS_PRIMARY;
+           allCardsClose.attr("label", localeStrings.ALL_CARDS_CLOSE);
+           allCardsApply.attr("label", localeStrings.ALL_CARDS_APPLY);
+       
+           dojo.query("td[name='addressHeader']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_HEADER; });
+           dojo.query("span[name='mailingAddress']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_MAILING; });
+           dojo.query("span[name='billingAddress']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_BILLING; });
+           dojo.query("span[name='addressPending']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_PENDING; });
+           dojo.query("button[name='approve-button']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_APPROVE; });
+           dojo.query("span[name='address-already-owned']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_OWNED; });
+           dojo.query("button[name='addressNew']").forEach( function(item) { item.innerHTML = localeStrings.ADDRESS_NEW; });
+       
+           if(xulG) {
+                   if(xulG.ses) openils.User.authtoken = xulG.ses;
+                   if(typeof xulG.clone != 'undefined') cloneUser = xulG.clone;
+               if(typeof xulG.usr != 'undefined') userId = xulG.usr
+               if(typeof xulG.params != 'undefined') {
+                   var parms = xulG.params;
+                       if(typeof parms.ses != 'undefined') 
+                       openils.User.authtoken = parms.ses;
+                       if(typeof parms.clone != 'undefined') 
+                       cloneUser = parms.clone;
+                   if(typeof parms.usr != 'undefined')
+                       userId = parms.usr;
+                   if(typeof parms.stage != 'undefined')
+                       stageUname = parms.stage
+               }
+           }
+       
+           orgSettings = fieldmapper.aou.fetchOrgSettingBatch(staff.ws_ou(), [
+               'global.password_regex',
+               'global.juvenile_age_threshold',
+               'patron.password.use_phone',
+               'ui.patron.default_inet_access_level',
+               'ui.patron.default_ident_type',
+               'ui.patron.default_country',
+               'ui.patron.registration.require_address',
+               'circ.holds.behind_desk_pickup_supported',
+               'circ.patron_edit.clone.copy_address',
+               'ui.patron.edit.au.prefix.require',
+               'ui.patron.edit.au.prefix.show',
+               'ui.patron.edit.au.prefix.suggest',
+               'ui.patron.edit.au.second_given_name.show',
+               'ui.patron.edit.au.second_given_name.suggest',
+               'ui.patron.edit.au.suffix.show',
+               'ui.patron.edit.au.suffix.suggest',
+               'ui.patron.edit.au.alias.show',
+               'ui.patron.edit.au.alias.suggest',
+               'ui.patron.edit.au.dob.require',
+               'ui.patron.edit.au.dob.show',
+               'ui.patron.edit.au.dob.suggest',
+               'ui.patron.edit.au.dob.calendar',
+               'ui.patron.edit.au.juvenile.show',
+               'ui.patron.edit.au.juvenile.suggest',
+               'ui.patron.edit.au.ident_value.show',
+               'ui.patron.edit.au.ident_value.suggest',
+               'ui.patron.edit.au.ident_value2.show',
+               'ui.patron.edit.au.ident_value2.suggest',
+               'ui.patron.edit.au.email.require',
+               'ui.patron.edit.au.email.show',
+               'ui.patron.edit.au.email.suggest',
+               'ui.patron.edit.au.email.regex',
+               'ui.patron.edit.au.email.example',
+               'ui.patron.edit.au.day_phone.require',
+               'ui.patron.edit.au.day_phone.show',
+               'ui.patron.edit.au.day_phone.suggest',
+               'ui.patron.edit.au.day_phone.regex',
+               'ui.patron.edit.au.day_phone.example',
+               'ui.patron.edit.au.evening_phone.require',
+               'ui.patron.edit.au.evening_phone.show',
+               'ui.patron.edit.au.evening_phone.suggest',
+               'ui.patron.edit.au.evening_phone.regex',
+               'ui.patron.edit.au.evening_phone.example',
+               'ui.patron.edit.au.other_phone.require',
+               'ui.patron.edit.au.other_phone.show',
+               'ui.patron.edit.au.other_phone.suggest',
+               'ui.patron.edit.au.other_phone.regex',
+               'ui.patron.edit.au.other_phone.example',
+               'ui.patron.edit.phone.regex',
+               'ui.patron.edit.phone.example',
+               'ui.patron.edit.au.active.show',
+               'ui.patron.edit.au.active.suggest',
+               'ui.patron.edit.au.barred.show',
+               'ui.patron.edit.au.barred.suggest',
+               'ui.patron.edit.au.master_account.show',
+               'ui.patron.edit.au.master_account.suggest',
+               'ui.patron.edit.au.claims_returned_count.show',
+               'ui.patron.edit.au.claims_returned_count.suggest',
+               'ui.patron.edit.au.claims_never_checked_out_count.show',
+               'ui.patron.edit.au.claims_never_checked_out_count.suggest',
+               'ui.patron.edit.au.alert_message.show',
+               'ui.patron.edit.au.alert_message.suggest',
+               'ui.patron.edit.aua.post_code.regex',
+               'ui.patron.edit.aua.post_code.example',
+               'ui.patron.edit.aua.county.require',
+               'format.date',
+               'ui.patron.edit.default_suggested',
+               'opac.barcode_regex',
+               'opac.username_regex',
+               'sms.enable'
+           ]);
+       
+           for(k in orgSettings)
+               if(orgSettings[k])
+                   orgSettings[k] = orgSettings[k].value;
+       
+           uEditCloneCopyAddr = orgSettings['circ.patron_edit.clone.copy_address'];
+           uEditUsePhonePw = orgSettings['patron.password.use_phone'];
+           uEditFetchUserSettings(userId);
+       
+           if(userId) {
+               patron = uEditLoadUser(userId);
+           } else {
+               if(stageUname) {
+                   patron = uEditLoadStageUser(stageUname);
+               } else {
+                   patron = uEditNewPatron();
+                   if(cloneUser) 
+                       uEditCopyCloneData(patron);
+               }
+           }
+       
+       
+           var list = pcrud.search('fdoc', {fm_class:fmClasses});
+           for(var i in list) {
+               var doc = list[i];
+               if(!fieldDoc[doc.fm_class()])
+                   fieldDoc[doc.fm_class()] = {};
+               fieldDoc[doc.fm_class()][doc.field()] = doc;
+           }
+       
+           list = pcrud.search('aout', {can_have_users: 'true'});
+           for(var i in list) {
+               var type = list[i];
+               homeOuTypes[type.id()] = true;
+           }
+           list = pcrud.search('aout', {can_have_vols: 'true'});
+           for(var i in list) {
+               var type = list[i];
+               holdPickupTypes[type.id()] = true;
+           }
+       
+           tbody = dojo.byId('uedit-tbody');
+       
+           if(orgSettings['ui.patron.edit.default_suggested'])
+               uEditToggleRequired(2);
+       
+           addrTemplateRows = dojo.query('tr[type=addr-template]', tbody);
+           dojo.forEach(addrTemplateRows, function(row) { row.parentNode.removeChild(row); } );
+           statCatTemplate = tbody.removeChild(dojo.byId('stat-cat-row-template'));
+           surveyTemplate = tbody.removeChild(dojo.byId('survey-row-template'));
+           surveyQuestionTemplate = tbody.removeChild(dojo.byId('survey-question-row-template'));
+       
+           checkGrpAppPerm(); // to do the initial load
+           loadStaticFields();
+       
+       
+           if(patron.isnew() && patron.addresses().length == 0) 
+               uEditNewAddr(null, uEditAddrVirtId, true);
+           else loadAllAddrs();
+           loadStatCats();
+           loadSurveys();
+           checkClaimsReturnCountPerm();
+           checkClaimsNoCheckoutCountPerm();
+       
+           dojo.connect(replaceBarcode, 'onClick', replaceCardHandler);
+           dojo.connect(allCards, 'onClick', drawAllCards);
+           if(patron.isnew()) {
+               dojo.addClass(dojo.byId('uedit-all-barcodes'), 'hidden');
+           } else if(checkGrpAppPerm(patron.profile())) {
+               new openils.User().getPermOrgList(
+                   'UPDATE_PATRON_ACTIVE_CARD',
+                   function(orgList) { 
+                       if(orgList.indexOf(patron.home_ou()) != -1) 
+                           cardPerms['UPDATE_PATRON_ACTIVE_CARD'] = true;
+                   },
+                   true, 
+                   true
+               );
+               new openils.User().getPermOrgList(
+                   'UPDATE_PATRON_PRIMARY_CARD',
+                   function(orgList) { 
+                       if(orgList.indexOf(patron.home_ou()) != -1) 
+                           cardPerms['UPDATE_PATRON_PRIMARY_CARD'] = true;
+                   },
+                   true, 
+                   true
+               );
+           }
+       
+           var input = findWidget('ac', 'barcode');
+           if (patron.isnew()) {
+               replaceBarcode.attr('disabled', true);
+           } else {
+               input.widget.attr('disabled', true).attr('readOnly', true);
+           }
+       
+               dojo.connect(generatePassword, 'onClick', generatePasswordHandler);
+       
+           if(!patron.isnew() && !checkGrpAppPerm(patron.profile()) && patron.id() != openils.User.user.id()) {
+               // we are not allowed to edit this user, so disable the save option
+               saveButton.attr('disabled', true);
+               saveCloneButton.attr('disabled', true);
+           }
+               
+           uUpdateContactInvalidators();
+           lock_ready = true;
+       }
+       
+       var permGroups;
+       var noPermGroups = [];
+       // Returns true if the user is allowed to edit the selected group
+       function checkGrpAppPerm(grpId) {
+       
+           if(!permGroups) {
+       
+               // get the groups
+               permGroups = new openils_PermaCrud().retrieveAll('pgt');
+               var permGroupPerms = []
+       
+               // collect the group permissions
+               dojo.forEach(permGroups, 
+                   function(grp) {
+                       if(grp.application_perm())
+                           permGroupPerms.push(grp.application_perm());
+                   }
+               );
+       
+               // see which of the group application perms I do not have
+               var myPerms = fieldmapper.standardRequest(
+                   ['open-ils.actor', 'open-ils.actor.user.has_work_perm_at.batch'],
+                   [openils.User.authtoken, permGroupPerms]
+               );
+       
+               var failedPerms = [];
+               for(var p in myPerms) { 
+                   if(myPerms[p].length == 0) 
+                       failedPerms.push(p); 
+               }
+       
+               // identify which groups I cannot edit because I do not have permisssion
+       
+               function checkTree(grp, failed) {
+                   failed = failed || failedPerms.indexOf(grp.application_perm()) > -1;
+                   if(failed) noPermGroups.push(grp.id()+'');
+                   dojo.forEach(
+                       permGroups.filter(function(g) { return g.parent() == grp.id() } ),
+                       function(child) {
+                           checkTree(child, failed);
+                       }
+                   );
+               }
+       
+               checkTree(permGroups.filter(function(g) { return g.parent() == null })[0]);
+           }
+       
+           return noPermGroups.indexOf(grpId+'') == -1;
+       }
+       
+       
+       function drawAllCards() {
+       
+           var tbody = dojo.byId('uedit-all-cards-tbody');
+           if(!allCardsTemplate) {
+               allCardsTemplate = tbody.removeChild(dojo.byId('uedit-all-cards-tr-template'));
+           } else {
+               while(tbody.childNodes[0])
+                   tbody.removeChild(tbody.childNodes[0]);
+           }
+       
+           if(cardPerms['UPDATE_PATRON_ACTIVE_CARD'] || cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
+               dojo.removeClass(dojo.byId('uedit-apply-card-changes'), 'hidden');
+           } else {
+               dojo.addClass(dojo.byId('uedit-apply-card-changes'), 'hidden');
+           }
+       
+           var first = true;
+           dojo.forEach(
+               patron.cards().filter(function(c) { return c.id() == patron.card().id(); }).concat(patron.cards()), // grab the main card first
+               function(card) {
+                   if(!first) {
+                       if(card.id() == patron.card().id())
+                           return;
+                   }
+                   var row = allCardsTemplate.cloneNode(true);
+                   row.setAttribute("cardid", card.id());
+                   row.card = card;
+                   getByName(row, 'barcode').innerHTML = card.barcode();
+                   if(cardPerms['UPDATE_PATRON_ACTIVE_CARD']) {
+                       row.active_checkbox = new dijit_form_CheckBox({
+                           scrollOnFocus:false,
+                           checked: openils_Util.isTrue(card.active())
+                       }, getByName(row, 'active'));
+                   } else {
+                       getByName(row, 'active').appendChild(
+                           openils_Util.isTrue(card.active()) ? 
+                               dojo.byId('true').cloneNode(true) :
+                               dojo.byId('false').cloneNode(true)
+                       );
+                   }
+                   if(cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
+                       row.primary_radiobutton = new dijit.form.RadioButton({
+                           scrollOnFocus:false,
+                           checked: card.id() == patron.card().id(),
+                           value: card.id(),
+                           name: 'card_primary'
+                       }, getByName(row, 'primary'));
+                   } else {
+                       getByName(row, 'primary').appendChild(
+                           openils_Util.isTrue(card.id() == patron.card().id()) ? 
+                               dojo.byId('true').cloneNode(true) :
+                               dojo.byId('false').cloneNode(true)
+                       );
+                   }
+                   tbody.appendChild(row);
+                   first = false;
+               }
+           );
+       
+           allCardsDialog.show();
+       }
+       
+       function applyCardChanges() {
+           var cardrows = dojo.query('[cardid]', allCardsDialog.domNode);
+           var changed = false;
+           dojo.forEach(cardrows,
+               function(row) {
+                   if(cardPerms['UPDATE_PATRON_ACTIVE_CARD']) {
+                       var active = row.active_checkbox.checked ? 't' : 'f'
+                       if(row.card.active() != active) {
+                           row.card.active(active);
+                           row.card.ischanged(1);
+                           changed = true;
+                       }
+                   }
+                   if(cardPerms['UPDATE_PATRON_PRIMARY_CARD']) {
+                       if(row.primary_radiobutton.checked && row.card.id() != patron.card().id()) {
+                           patron.card(row.card);
+                           changed = true;
+                       }
+                   }
+               }
+           );
+           if(changed && lock_ready && xulG && typeof xulG.lock_tab == 'function' && !already_locked) {
+               xulG.lock_tab();
+               already_locked = true;
+           }
+           allCardsDialog.hide();
+       }
+       
+       /**
+        * Mark the current card inactive, create a new primary card
+        */
+       function replaceCardHandler() {
+           var input = findWidget('ac', 'barcode');
+           input.widget.attr('disabled', false).attr('readOnly', false).attr('value', null).focus();
+           replaceBarcode.attr('disabled', true);
+           
+           // pull old card off the cards list so we don't have a dupe sitting in there
+           if (patron.cards().length > 0) {
+               var old = patron.cards().filter(function(c){return (c.id() == patron.card().id())})[0];
+               old.active('f');
+               old.ischanged(1);
+           }
+       
+           var newc = new fieldmapper.ac();
+           newc.id(uEditCardVirtId--);
+           newc.isnew(1);
+           newc.active('t');
+           patron.card(newc);
+           editCard = newc;
+           var t = patron.cards();
+               if (!t) { t = []; }
+               t.push(newc);
+               patron.cards(t);
+       }
+       
+       /**
+        * Generate a random password for the patron.
+        */
+       function generatePasswordHandler() {
+               uEditMakeRandomPw(patron);
+               var f = findWidget('au', 'passwd');
+               f.widget.attr('value', patron.passwd());
+               f = findWidget('au', 'passwd2');
+               f.widget.attr('value', patron.passwd());
+       }
+       
+       /**
+        * Loads a staged user and turns them into something the editor can understand
+        */
+       function uEditLoadStageUser(stageUname) {
+       
+           var data = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.stage.retrieve.by_username'],
+               { params : [openils.User.authtoken, stageUname] }
+           );
+       
+           stageUser = data.user;
+           patron = uEditNewPatron();
+       
+           if(!stageUser) 
+               return patron;
+       
+           // copy the data into our new user object
+           for(var key in fieldmapper_IDL.fmclasses.stgu.field_map) {
+               if(fieldmapper_IDL.fmclasses.au.field_map[key] && !fieldmapper_IDL.fmclasses.stgu.field_map[key].virtual) {
+                   if(data.user[key]() !== null)
+                       patron[key]( data.user[key]() );
+               }
+           }
+       
+           // copy the data into our new address objects
+           // TODO: uses the first mailing address only
+           if(data.mailing_addresses.length) {
+       
+               var mail_addr = new fieldmapper.aua();
+               mail_addr.id(-1); // virtual ID
+               mail_addr.usr(-1);
+               mail_addr.isnew(1);
+               patron.mailing_address(mail_addr);
+               var t = patron.addresses();
+                   if (!t) { t = []; }
+                   t.push(mail_addr);
+                   patron.addresses(t);
+       
+               for(var key in fieldmapper_IDL.fmclasses.stgma.field_map) {
+                   if(fieldmapper_IDL.fmclasses.aua.field_map[key] && !fieldmapper_IDL.fmclasses.stgma.field_map[key].virtual) {
+                       if(data.mailing_addresses[0][key]() !== null)
+                           mail_addr[key]( data.mailing_addresses[0][key]() );
+                   }
+               }
+           }
+           
+           // copy the data into our new address objects
+           // TODO uses the first billing address only
+           if(data.billing_addresses.length) {
+       
+               var bill_addr = new fieldmapper.aua();
+               bill_addr.id(-2); // virtual ID
+               bill_addr.usr(-1);
+               bill_addr.isnew(1);
+               patron.billing_address(bill_addr);
+               var t = patron.addresses();
+                   if (!t) { t = []; }
+                   t.push(bill_addr);
+                   patron.addresses(t);
+       
+               for(var key in fieldmapper_IDL.fmclasses.stgba.field_map) {
+                   if(fieldmapper_IDL.fmclasses.aua.field_map[key] && !fieldmapper_IDL.fmclasses.stgba.field_map[key].virtual) {
+                       if(data.billing_addresses[0][key]() !== null)
+                           bill_addr[key]( data.billing_addresses[0][key]() );
+                   }
+               }
+           }
+       
+           // TODO: uses the first card only
+           if(data.cards.length) {
+               var card = new fieldmapper.ac();
+               card.id(-1); // virtual ID
+               patron.card().barcode(data.cards[0].barcode());
+           }
+       
+           return patron;
+       }
+       
+       /*
+        * clone the home org, phone numbers, and billing/mailing address
+        */
+       function uEditCopyCloneData(patron) {
+           cloneUserObj = uEditLoadUser(cloneUser);
+       
+           var cloneFields = [
+               'home_ou', 
+               'day_phone', 
+               'evening_phone', 
+               'other_phone',
+               'usrgroup'
+           ];
+       
+           if(!uEditCloneCopyAddr) 
+               cloneFields = cloneFields.concat(['mailing_address', 'billing_address']);
+       
+           dojo.forEach(
+               cloneFields, 
+               function(field) {
+                   patron[field](cloneUserObj[field]());
+               }
+           );
+       
+           if(uEditCloneCopyAddr) {
+               var billAddr, mailAddr;
+       
+               // copy the billing and mailing addresses into new addresses
+               function cloneAddr(addr) {
+                   var newAddr = addr.clone();
+                   newAddr.isnew(true);
+                   newAddr.id(uEditAddrVirtId--);
+                   newAddr.usr(patron.id());
+                   patron.addresses().push(newAddr);
+                   return newAddr;
+               }
+       
+               if(billAddr = cloneUserObj.billing_address()) 
+                   patron.billing_address(cloneAddr(billAddr));
+       
+               if(mailAddr = cloneUserObj.mailing_address()) {
+                   if (billAddr && billAddr.id() == mailAddr.id()) {
+                       patron.mailing_address(patron.billing_address());
+                   } else {
+                       patron.mailing_address(cloneAddr(mailAddr));
+                   }
+               }
+       
+               if(!billAddr) // if there was no billing addr, use the mailing addr
+                   patron.billing_address(patron.mailing_address());
+       
+           } else {
+       
+               // link the billing and mailing addresses
+               if(patron.billing_address()) {
+                   var t = patron.addresses();
+                       if (!t) { t = []; }
+                       t.push(patron.billing_address());
+                       patron.addresses(t);
+               }
+       
+               if(patron.mailing_address() && (
+                       patron.addresses().length == 0 || 
+                       patron.mailing_address().id() != patron.billing_address().id()) ) {
+                   var t = patron.addresses();
+                       if (!t) { t = []; }
+                       t.push(patron.mailing_address());
+                       patron.addresses(t);
+               }
+           }
+       }
+       
+       
+       function uEditFetchUserSettings(userId) {
+           
+           var baseNode = fieldmapper.aou.findOrgUnit(staff.ws_ou());
+           var orgs = fieldmapper.aou.orgNodeTrail(baseNode);
+           orgs = orgs.map(function(node) { return node.id(); });
+       
+           /* fetch any user setting types we need + any that offer opt-in */
+           userSettingTypes = pcrud.search('cust', {
+               '-or' : [
+                   {name:['circ.holds_behind_desk', 'circ.collections.exempt', 'opac.hold_notify', 'opac.default_phone', 'opac.default_pickup_location', 'opac.default_sms_carrier', 'opac.default_sms_notify']}, 
+                   {name : {
+                       'in': {
+                           select : {atevdef : ['opt_in_setting']}, 
+                           from : 'atevdef',
+                           // we only care about opt-in settings for event_defs our users encounter
+                           where : {'+atevdef' : {owner : orgs}}
+                       }
+                   }}
+               ]
+           });
+       
+           var names = userSettingTypes.map(function(obj) { return obj.name() });
+       
+           /* fetch any values set for this user */
+           if(userId) {
+               userSettings = fieldmapper.standardRequest(
+                   ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve.authoritative'],
+                   {params : [openils.User.authtoken, userId, names]});
+           }
+       }
+       
+       
+       function uEditLoadUser(userId) {
+           var patron = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
+               {params : [openils.User.authtoken, userId]}
+           );
+           openils_Event.parse_and_raise(patron);
+           return patron;
+       }
+       
+       function loadStaticFields() {
+           for(var idx = 0; tbody.childNodes[idx]; idx++) {
+               var row = tbody.childNodes[idx];
+               if(row.nodeType != row.ELEMENT_NODE) continue;
+               var fmcls = row.getAttribute('fmclass');
+               if(fmcls) {
+                   fleshFMRow(row, fmcls);
+               } else {
+       
+                   if(row.id == 'uedit-settings-divider') {
+       
+                       var template = tbody.removeChild(dojo.byId('uedit-user-setting-template'));
+                       dojo.forEach(userSettingTypes, function(type) { uEditDrawSettingRow(tbody, row, template, type); } );
+       
+                       if(userSettingTypes.length > 1 || orgSettings['circ.holds.behind_desk_pickup_supported']) {
+                           openils_Util.show('uedit-settings-divider', 'table-row');
+                       }
+                   }
+               }
+           }
+       }
+       
+       function uEditDrawSettingRow(tbody, dividerRow, template, stype) {
+           var row = template.cloneNode(true);
+           row.setAttribute('user_setting', stype.name());
+           getByName(row, 'label').innerHTML = stype.label();
+           switch(stype.name()) {
+               case 'opac.hold_notify':
+                   var template = localeStrings.HOLD_NOTIFY_PHONE + '<span name="hold_phone"></span>&nbsp;'
+                       + localeStrings.HOLD_NOTIFY_EMAIL + '<span name="hold_email"></span>';
+                   if(orgSettings['sms.enable']) {
+                       template += '&nbsp;' + localeStrings.HOLD_NOTIFY_SMS + '<span name="hold_sms"></span>';
+                   }
+                   getByName(row, 'widget').innerHTML = template;
+                   var setting = userSettings['opac.hold_notify'];
+                   if(setting == null) setting = 'phone:email';
+                   var cb_phone = new dijit_form_CheckBox({scrollOnFocus:false}, getByName(row, 'hold_phone'));
+                   cb_phone.attr('value', setting.indexOf('phone') != -1);
+                   var cb_email = new dijit_form_CheckBox({scrollOnFocus:false}, getByName(row, 'hold_email'));
+                   cb_email.attr('value', setting.indexOf('email') != -1);
+                   var cb_sms = null;
+                   if(orgSettings['sms.enable']) {
+                       cb_sms = new dijit_form_CheckBox({scrollOnFocus:false}, getByName(row, 'hold_sms'));
+                       cb_sms.attr('value', setting.indexOf('sms') != -1);
+                   }
+                   var func = function() {
+                       var newVal = '';
+                       var splitter = '';
+                       if(cb_phone.checked) {
+                           newVal+= splitter + 'phone';
+                           splitter = ':';
+                       }
+                       if(cb_email.checked) {
+                           newVal+= splitter + 'email';
+                           splitter = ':';
+                       }
+                       if(orgSettings['sms.enable'] && cb_sms.checked) {
+                           newVal+= splitter + 'sms';
+                           splitter = ':';
+                       }
+                       userSettingsToUpdate['opac.hold_notify'] = newVal;
+                   };
+                   dojo.connect(cb_phone, 'onChange', func);
+                   dojo.connect(cb_email, 'onChange', func);
+                   if(cb_sms) dojo.connect(cb_sms, 'onChange', func);
+                   break;
+               case 'opac.default_pickup_location':
+                   var sb = new openils.widget.FilteringTreeSelect({
+                       scrollOnFocus: false,
+                       labelAttr: 'name',
+                       searchAttr: 'name',
+                       parentField: 'parent_ou',
+                       }, getByName(row, 'widget'));
+                   sb.tree = fieldmapper.aou.globalOrgTree;
+                   sb.startup();
+                   sb.attr('value', userSettings[stype.name()]);
+       
+                   sb.isValid = function() {
+                       if(this.item) {
+                           if(holdPickupTypes[this.store.getValue(this.item, 'ou_type')]) {
+                               return true;
+                           }
+                           return false;
+                       }
+                       return true;
+                   };
+       
+                   dojo.connect(sb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
+                   break;
+               case 'opac.default_sms_carrier':
+                   if(!orgSettings['sms.enable']) return; // Skip when SMS is disabled
+                   var carriers = pcrud.search('csc', {active: 'true'}, {'order_by':[{'class':'csc', 'field':'name'},{'class':'csc', 'field':'region'}]});
+                   var storedata = fieldmapper.csc.toStoreData(carriers);
+                   for(var i in storedata.items) storedata.items[i].label = storedata.items[i].name + ' (' + storedata.items[i].region + ')';
+                   var store = new dojo_data_ItemFileReadStore({data:storedata});
+                   var select = new dijit_form_FilteringSelect({store:store,scrollOnFocus:false,labelAttr:'label',searchAttr:'label'}, getByName(row, 'widget'));
+                   select.attr('value', userSettings[stype.name()]);
+                   select.isValid = function() { return true; };
+                   dojo.connect(select, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
+                   break;
+               case 'opac.default_sms_notify':
+                   if(!orgSettings['sms.enable']) return; // Skip when SMS is disabled
+               case 'opac.default_phone':
+                   var tb = new dijit.form.TextBox({scrollOnFocus:false}, getByName(row, 'widget'));
+                   tb.attr('value', userSettings[stype.name()]);
+                   dojo.connect(tb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
+                   break;
+               default:
+                   var cb = new dijit_form_CheckBox({scrollOnFocus:false}, getByName(row, 'widget'));
+                   cb.attr('value', userSettings[stype.name()]);
+                   dojo.connect(cb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
+                   if(stype.name() == 'circ.collections.exempt') {
+                       checkCollectionsExemptPerm(cb);
+                   }
+           }
+           tbody.insertBefore(row, dividerRow.nextSibling);
+           openils_Util.show(row, 'table-row');
+       }
+       
+       function uEditUpdateUserSettings(userId) {
+           return fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.patron.settings.update'],
+               {params : [openils.User.authtoken, userId, userSettingsToUpdate]});
+       }
+       
+       function loadAllAddrs() {
+           dojo.forEach(patron.addresses(),
+               function(addr) {
+                   uEditNewAddr(null, addr.id());
+               }
+           );
+       }
+       
+       function loadStatCats() {
+       
+           statCats = fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.stat_cat.actor.retrieve.all'],
+               {params : [openils.User.authtoken, staff.ws_ou()]}
+           );
+       
+           // draw stat cats
+           for(var idx in statCats) {
+               var stat = statCats[idx];
+               var row = statCatTemplate.cloneNode(true);
+               row.id = 'stat-cat-row-' + idx;
+               row.setAttribute('stat_cat_owner',stat.owner());
+               row.setAttribute('stat_cat_name',stat.name());
+               row.setAttribute('stat_cat_id',stat.id());
+               tbody.appendChild(row);
+               getByName(row, 'name').innerHTML = stat.name();
+               var valtd = getByName(row, 'widget');
+               var span = valtd.appendChild(document.createElement('span'));
+               var store = new dojo_data_ItemFileReadStore(
+                       {data:fieldmapper.actsc.toStoreData(stat.entries())});
+               var comboBox = new dijit_form_ComboBox({store:store,scrollOnFocus:false,fetchProperties:{sort:[{attribute: 'value'}]}}, span);
+               comboBox.labelAttr = 'value';
+               comboBox.searchAttr = 'value';
+       
+               comboBox._wtype = 'statcat';
+               comboBox._statcat = stat.id();
+               widgetPile.push(comboBox); 
+       
+               // populate existing cats
+               var map = patron.stat_cat_entries().filter(
+                   function(mp) { return (mp.stat_cat() == stat.id()) })[0];
+               if(map) comboBox.attr('value', map.stat_cat_entry()); 
+       
+           }
+       }
+       
+       function loadSurveys() {
+       
+           surveys = fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.survey.retrieve.all'],
+               {params : [openils.User.authtoken]}
+           );
+       
+           // draw surveys
+           for(var idx in surveys) {
+               var survey = surveys[idx];
+               var required = openils_Util.isTrue(survey.required());
+               var srow = surveyTemplate.cloneNode(true);
+               if(required) srow.setAttribute('required','required');
+               tbody.appendChild(srow);
+               getByName(srow, 'name').innerHTML = survey.name();
+       
+               for(var q in survey.questions()) {
+                   var quest = survey.questions()[q];
+                   var qrow = surveyQuestionTemplate.cloneNode(true);
+                   if(required) qrow.setAttribute('required','required');
+                   tbody.appendChild(qrow);
+                   getByName(qrow, 'question').innerHTML = quest.question();
+       
+                   var span = getByName(qrow, 'answers').appendChild(document.createElement('span'));
+                   var store = new dojo_data_ItemFileReadStore(
+                       {data:fieldmapper.asva.toStoreData(quest.answers())});
+                   var select = new dijit_form_FilteringSelect({store:store,scrollOnFocus:false}, span);
+                   if (! required ) {
+                       select.isValid = function() { return true; };
+                   }
+                   select.labelAttr = 'answer';
+                   select.searchAttr = 'answer';
+       
+                   select._wtype = 'survey';
+                   select._survey = survey.id();
+                   select._question = quest.id();
+                   widgetPile.push(select); 
+               }
+           }
+       }
+       
+       
+       function fleshFMRow(row, fmcls, args) {
+           var fmfield = row.getAttribute('fmfield');
+           var wclass = row.getAttribute('wclass');
+           var wstyle = row.getAttribute('wstyle');
+           var wconstraints = row.getAttribute('wconstraints');
+           /* use CSS to set the zindex for widgets you want to disable. */
+           var disabled = dojo.style(row, 'zIndex') == -1 ? true : false;
+           var isphone = (fmcls == 'au') && (fmfield.search('_phone') != -1);
+       
+           var isPasswd2 = (fmfield == 'passwd2');
+           if(isPasswd2) fmfield = 'passwd';
+           var fieldIdl = fieldmapper_IDL.fmclasses[fmcls].field_map[fmfield];
+           if(!args) args = {};
+       
+           var existing = dojo.query('td', row);
+           var htd = existing[0] || row.appendChild(document.createElement('td'));
+           var ltd = existing[1] || row.appendChild(document.createElement('td'));
+           var wtd = existing[2] || row.appendChild(document.createElement('td'));
+           var ftd = existing[3] || row.appendChild(document.createElement('td'));
+       
+           openils_Util.addCSSClass(htd, 'uedit-help');
+           if(fieldDoc[fmcls] && fieldDoc[fmcls][fmfield]) {
+               var link = dojo.byId('uedit-help-template').cloneNode(true);
+               link.id = '';
+               link.onclick = function() { ueLoadContextHelp(fmcls, fmfield) };
+               openils_Util.removeCSSClass(link, 'hidden');
+               htd.appendChild(link);
+           }
+       
+           if(!ltd.textContent) {
+               ltd.appendChild(document.createTextNode(fieldIdl.label));
+           }
+       
+           if(!ftd.textContent) {
+               if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']) {
+                   ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']));
+               }
+               else if(isphone && orgSettings['ui.patron.edit.phone.example']) {
+                   ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.phone.example']));
+               }
+               else if(fieldIdl.datatype == 'timestamp') {
+                   ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + dojo_date.locale.format(new Date(1970,0,31),{selector: "date", fullYear: true, datePattern: (orgSettings['format.date'] ? orgSettings['format.date'] : null)})));
+               }
+       
+               if (fmcls == "au" && (isphone || fmfield == "email")) {
+                   var span = dojo.create(
+                       "span", {
+                           "className": "hidden",
+                           "id": "wrap_invalidate_" + fmfield
+                       }
+                   );
+                   uGenerateInvalidatorWidget(span, fmfield);
+                   ftd.appendChild(span);
+               }
+           }
+       
+           var span = document.createElement('span');
+           wtd.appendChild(span);
+       
+           var fmObject = null;
+           switch(fmcls) {
+               case 'au' :
+                   fmObject = patron;
+                   if(fmfield == 'barred') {
+                       // Are we allowed to touch the barred state?
+                       var permission = 'BAR_PATRON';
+                       if(fmObject.barred() == 't') {
+                           permission = 'UNBAR_PATRON';
+                       }
+                       var ou = staff.ws_ou();
+                       if(fmObject.home_ou() != null) {
+                           ou = fmObject.home_ou();
+                       }
+                       var resp = fieldmapper.standardRequest(
+                           ['open-ils.actor', 'open-ils.actor.user.perm.check'],
+                           { params : [openils.User.authtoken, staff.id(), ou, [permission] ] }
+                       );
+                       if(resp[0]) { // No permission to adjust barred state from current
+                           disabled = true;
+                       }
+                   }
+                   break;
+               case 'ac' : if(!editCard) editCard = patron.card(); fmObject = editCard; break;
+               case 'aua' : 
+                   fmObject = patron.addresses().filter(
+                       function(i) { return (i.id() == args.addr) })[0];
+                   if(fmObject && fmObject.usr() != patron.id())
+                       disabled = true;
+                   break;
+           }
+       
+           // Adjust required value by org settings
+           var curRequired = row.getAttribute('required');
+           var required = curRequired == 'required';
+           if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.require']) {
+               row.setAttribute('required', 'required');
+               required = true;
+           }
+           else if (curRequired != 'required' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.show']) {
+               row.setAttribute('required', 'show');
+           }
+           else if (curRequired != 'required' && curRequired != 'show' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.suggest']) {
+               row.setAttribute('required', 'suggested');
+           }
+       
+           // password data is not fetched/required/displayed for existing users
+           if(!patron.isnew() && 'passwd' == fmfield)
+               required = false;
+       
+           var dijitArgs = {
+               style: wstyle, 
+               required : required,
+               constraints : (wconstraints) ? eval('('+wconstraints+')') : {}, // the ()'s prevent Invalid Label errors with eval
+               disabled : disabled
+           };
+       
+           // Org settings provided regex?
+           if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex']) {
+               dijitArgs.regExp = orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex'];
+           }
+           else if(isphone && orgSettings['ui.patron.edit.phone.regex']) {
+               dijitArgs.regExp = orgSettings['ui.patron.edit.phone.regex'];
+           }
+       
+           if(fmcls == 'au' && fmfield == 'passwd') {
+               if (orgSettings['global.password_regex']) {
+                   dijitArgs.regExp = orgSettings['global.password_regex'];
+               }
+           }
+       
+           if(fmcls == 'au' && fmfield == 'dob' && !orgSettings['ui.patron.edit.au.dob.calendar'])
+               dijitArgs.popupClass = "";
+       
+           var value = row.getAttribute('wvalue');
+           if(value !== null)
+               dijitArgs.value = value;
+       
+           var wargs = {
+               idlField : fieldIdl,
+               fmObject : fmObject,
+               fmClass : fmcls,
+               parentNode : span,
+               widgetClass : wclass,
+               dijitArgs : dijitArgs,
+               orgDefaultsToWs : true,
+               orgLimitPerms : ['UPDATE_USER'],
+           };
+       
+           if(fmfield == 'profile') {
+               // fetch profile groups non-async so existing expire_date is
+               // not overwritten when the profile groups arrive and update
+               wargs.forceSync = true;
+               wargs.disableQuery = {usergroup : 'f'};
+           } else {
+               wargs.forceSync = false;
+           }
+       
+           if(fmcls == 'au' && fmfield == 'home_ou'){
+               wargs.labelAttr = 'name';
+               wargs.searchAttr = 'name';
+           }
+       
+           var widget = new openils_widget_AutoFieldWidget(wargs);
+           widget.build(
+               function(w, ww) {
+                   if(fmfield == 'profile') {
+                       trimGrpTree(ww);
+                       if(!patron.isnew() && !checkGrpAppPerm(patron.profile())){
+                           w.attr('disabled', true);
+                       }
+                   }
+               }
+           );
+       
+           // now put it back before we register the widget
+           if(isPasswd2) fmfield = 'passwd2';
+       
+           widget._wtype = fmcls;
+           widget._fmfield = fmfield;
+           widget._addr = args.addr;
+           widgetPile.push(widget);
+           attachWidgetEvents(fmcls, fmfield, widget);
+           return widget;
+       }
+       
+       function trimGrpTree(autoWidget) {
+           var store = autoWidget.widget.store;
+           if(!store) return;
+           // remove all groups that this user are not allowed to edit, 
+           // except the profile group of an existing user
+           store.fetch({onItem : 
+               function(item) {
+                   if(!checkGrpAppPerm(item.id[0]) && patron.profile() != item.id[0])
+                       store.deleteItem(item);
+               }
+           });
+       }
+       
+       function findWidget(wtype, fmfield, callback) {
+           return widgetPile.filter(
+               function(i){
+                   if(i._wtype == wtype && i._fmfield == fmfield) {
+                       if(callback) return callback(i);
+                       return true;
+                   }
+               }
+           ).pop();
+       }
+       
+       /**
+        * if the user does not have the UPDATE_PATRON_CLAIM_RETURN_COUNT, 
+        * they are not allowed to directly alter the claim return count. 
+        * This function checks the perm and disable/enables the widget.
+        */
+       function checkClaimsReturnCountPerm() {
+           new openils.User().getPermOrgList(
+               'UPDATE_PATRON_CLAIM_RETURN_COUNT',
+               function(orgList) { 
+                   var cr = findWidget('au', 'claims_returned_count');
+                   if(orgList.indexOf(patron.home_ou()) == -1) 
+                       cr.widget.attr('disabled', true);
+                   else
+                       cr.widget.attr('disabled', false);
+               },
+               true, 
+               true
+           );
+       }
+       
+       
+       function checkClaimsNoCheckoutCountPerm() {
+           new openils.User().getPermOrgList(
+               'UPDATE_PATRON_CLAIM_NEVER_CHECKED_OUT_COUNT',
+               function(orgList) { 
+                   var cr = findWidget('au', 'claims_never_checked_out_count');
+                   if(orgList.indexOf(patron.home_ou()) == -1) 
+                       cr.widget.attr('disabled', true);
+                   else
+                       cr.widget.attr('disabled', false);
+               },
+               true, 
+               true
+           );
+       }
+       
+       var collectExemptCBox;
+       function checkCollectionsExemptPerm(cbox) {
+           if(cbox) collectExemptCBox = cbox;
+           new openils.User().getPermOrgList(
+               'UPDATE_PATRON_COLLECTIONS_EXEMPT',
+               function(orgList) { 
+                   if(orgList.indexOf(patron.home_ou()) == -1) 
+                       collectExemptCBox.attr('disabled', true);
+                   else
+                       collectExemptCBox.attr('disabled', false);
+               },
+               true, 
+               true
+           );
+       }
+       
+       function usePhonePw(newVal) {
+           var newPw = false;
+           if(this.regExp) {
+               matches = RegExp(this.regExp).exec(newVal);
+               if(matches.length > 1) newPw = matches[1];
+           }
+           if(!newPw && newVal && newVal.length >= 4) {
+               newPw = newVal.substring(newVal.length - 4);
+           }
+           if(newPw) {
+               var p1 = findWidget('au', 'passwd');
+               var p2 = findWidget('au', 'passwd2');
+               if (p1 && p2) {
+                   p1.widget.attr('value', newPw);
+                   p2.widget.attr('value', newPw);
+               }
+               return newPw;
+           } else {
+               return null;
+           }
+       }
+       
+       function attachWidgetEvents(fmcls, fmfield, widget) {
+       
+           dojo.connect(
+               widget.widget,
+               'onKeyPress',
+               function(ev){
+                   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+                   if (!(ev.altKey || ev.ctrlKey || ev.metaKey)) {
+                       if (lock_ready && xulG && typeof xulG.lock_tab == 'function') {
+                           if (! already_locked) {
+                               xulG.lock_tab();
+                               already_locked = true;
+                           }
+                       }
+                   }
+               }
+           );
+           dojo.connect(
+               widget.widget,
+               'onChange',
+               function(){
+                   if (lock_ready && xulG && typeof xulG.lock_tab == 'function') {
+                       if (! already_locked) {
+                           xulG.lock_tab();
+                           already_locked = true;
+                       }
+                   }
+               }
+           );
+       
+       
+           if(fmcls == 'ac') {
+               if(fmfield == 'barcode') {
+                   dojo.connect(widget.widget, 'onChange',
+                       function() {
+                           var barcode = this.attr('value');
+                           dupeBarcode = false;
+                           dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
+                           fieldmapper.standardRequest(
+                               ['open-ils.actor', 'open-ils.actor.barcode.exists'],
+                               {
+                                   params: [openils.User.authtoken, barcode],
+                                   oncomplete : function(r) {
+                                       var res = openils_Util.readResponse(r);
+                                       if(res == '1') {
+                                           dupeBarcode = true;
+                                           dojo.removeClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
+                                       } else {
+                                           dupeBarcode = false;
+                                           dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
+                                           editCard.barcode(barcode); // Keep the "All" interface up to date
+                                           var un = findWidget('au', 'usrname');
+                                           if(!un.widget.attr('value'))
+                                               un.widget.attr('value', barcode);
+                                       }
+                                   }
+                               }
+                           );
+                       }
+                   );
+                   return;
+               }
+           }
+       
+           if(fmcls == 'au') {
+               switch(fmfield) {
+       
+                   case 'usrname':
+                       widget.widget.isValid = function() {
+                           // No spaces
+                           if(this.attr("value").match(/\s/)) {
+                               return false;
+                           }
+                           // Can look like a barcode (for initial value)
+                           if(orgSettings['opac.barcode_regex']) {
+                               var test_regexp = new RegExp(orgSettings['opac.barcode_regex']);
+                               if(test_regexp.test(this.attr("value"))) {
+                                   return true;
+                               }
+                           }
+                           // Can look like a username
+                           if(orgSettings['opac.username_regex']) {
+                               var test_regexp = new RegExp(orgSettings['opac.username_regex']);
+                               if(test_regexp.test(this.attr("value"))) {
+                                   return true;
+                               }
+                           }
+                           // If we know what a barcode and username look like and we got here, reject
+                           if(orgSettings['opac.barcode_regex'] && orgSettings['opac.username_regex'])
+                               return false;
+                           // Otherwise we don't have enough info to say either way, let it through.
+                           return true;
+                       }
+                       dojo.connect(widget.widget, 'onChange', 
+                           function() {
+                               var input = findWidget('au', 'usrname');
+                               var usrname = input.widget.attr('value');
+       
+                               if(!usrname) {
+                                   dupeUsrname = false;
+                                   dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
+                                   return;
+                               }
+       
+                               fieldmapper.standardRequest(
+                                   ['open-ils.actor', 'open-ils.actor.username.exists'],
+                                   {
+                                       params: [openils.User.authtoken, usrname],
+                                       oncomplete : function(r) {
+                                           var res = openils_Util.readResponse(r);
+                                           if(res) {
+                                               dupeUsrname = true;
+                                               dojo.removeClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
+                                           } else {
+                                               dupeUsrname = false;
+                                               dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
+                                           }
+                                       }
+                                   }
+                               );
+                           }   
+                       );
+       
+                       return;
+       
+                   case 'profile': // when the profile changes, update the expire date
+                       dojo.connect(widget.widget, 'onChange', 
+                           function() {
+                               var self = this;
+                               var expireWidget = findWidget('au', 'expire_date');
+                               function found(items) {
+                                   if(items.length == 0) return;
+                                   var item = items[0];
+                                   var interval = self.store.getValue(item, 'perm_interval');
+                                   expireWidget.widget.attr('value', dojo_date.add(new Date(), 
+                                       'second', openils_Util.intervalToSeconds(interval)));
+                               }
+                               this.store.fetch({onComplete:found, query:{id:this.attr('value')}});
+                           }
+                       );
+                       return;
+       
+                   case 'dob':
+                       widget.widget.isValid = function() {
+                           return this.attr("value") < new Date();
+                       };
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newDob) {
+                               if(!newDob) return;
+                               var oldDob = patron.dob();
+                               if(dojo_date.stamp.fromISOString(oldDob) == newDob) return;
+       
+                               var juvInterval = orgSettings['global.juvenile_age_threshold'] || '18 years';
+                               var juvWidget = findWidget('au', 'juvenile');
+                               var base = new Date();
+                               base.setTime(base.getTime() - Number(openils_Util.intervalToSeconds(juvInterval) + '000'));
+       
+                               if(newDob <= base) // older than global.juvenile_age_threshold
+                                   juvWidget.widget.attr('value', false);
+                               else
+                                   juvWidget.widget.attr('value', true);
+                           }
+                       );
+                       return;
+       
+                   case 'first_given_name':
+                   case 'family_name':
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) { uEditDupeSearch('name', newVal); });
+                       return;
+       
+                   case 'email':
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) { uEditDupeSearch('email', newVal); });
+                       return;
+       
+                   case 'ident_value':
+                   case 'ident_value2':
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) { uEditDupeSearch('ident', newVal); });
+                       return;
+       
+                   case 'day_phone':
+                       // if configured, use the last four digits of the day phone number as the password
+                       // Alt, use the first capture group of the validator regex
+                       if(uEditUsePhonePw && patron.isnew()) {
+                           dojo.connect(widget.widget, 'onChange', widget.widget, usePhonePw);
+                           if (patron.day_phone()) {
+                               usePhonePw(patron.day_phone());
+                           }
+                       }
+                   case 'evening_phone':
+                   case 'other_phone':
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) { uEditDupeSearch('phone', newVal); });
+                       return;
+       
+                   case 'home_ou':
+                       widget.widget.isValid = function() {
+                           if(this.item) {
+                               if(homeOuTypes[this.store.getValue(this.item, 'ou_type')]) {
+                                   return true;
+                               }
+                               return false;
+                           }
+                           return true;
+                       };
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) { 
+                               checkClaimsReturnCountPerm(); 
+                               checkClaimsNoCheckoutCountPerm();
+                               checkCollectionsExemptPerm();
+                           }
+                       );
+                       return;
+       
+                   case 'passwd':
+                       dojo.connect(widget.widget, 'onChange',
+                           function(newVal) {
+                               var pw1 = findWidget('au', 'passwd').widget;
+                               var pw2 = findWidget('au', 'passwd2').widget;
+                               var preserved_value = pw2.attr('value');
+                               // Ensure that the pw2 field match the pw1 field to validate
+                               pw2.regExp = newVal.replace(/([.\\^$*+?\(\)\[\]\{\}])/g, '\\$1');
+                               pw2.reset();
+                               pw2.attr('value',preserved_value);
+                           });
+                       return;
+               }
+           }
+       
+           if(fmclass = 'aua') {
+       
+               // map post code to city, state, and county
+               if (fmfield == 'post_code') {
+                   dojo.connect(widget.widget, 'onChange',
+                       function(e) { 
+                           fieldmapper.standardRequest(
+                               ['open-ils.search', 'open-ils.search.zip'],
+                               {   async: true,
+                                   params: [e],
+                                   oncomplete : function(r) {
+                                       var res = openils_Util.readResponse(r);
+                                       if(!res) return;
+                                       var callback = function(w) { return w._addr == widget._addr; };
+                                       if(res.city) findWidget('aua', 'city', callback).widget.attr('value', res.city);
+                                       if(res.state) findWidget('aua', 'state', callback).widget.attr('value', res.state);
+                                       if(res.county) findWidget('aua', 'county', callback).widget.attr('value', res.county);
+                                       if(res.alert) alert(res.alert);
+                                   }
+                               }
+                           );
+                       }
+                   );
+               }
+       
+               // duplicate address search
+               if (['street1', 'street2', 'city'].indexOf(fmfield) > -1) {
+                   dojo.connect(widget.widget, 'onChange',
+                       function(e) {
+                           var callback = function(w) { return w._addr == widget._addr; };
+                           var args = {
+                               street1 : findWidget('aua', 'street1', callback).widget.attr('value'),
+                               street2 : findWidget('aua', 'street2', callback).widget.attr('value'),
+                               city : findWidget('aua', 'city', callback).widget.attr('value'),
+                               post_code : findWidget('aua', 'post_code', callback).widget.attr('value')
+                           };
+                           if(args.street1 && args.city && args.post_code)
+                               uEditDupeSearch('address', args); 
+                       }
+                   ); 
+               }
+       
+               if (addressAlertFields.indexOf(fmfield) > -1) {
+                   dojo.connect(
+                       widget.widget, 'onChange', 
+                       function() { uEditAddressAlertMarshal(widget._addr) }
+                   );
+               }
+           }
+       }
+       
+       function uEditAddressAlertMarshal(addrId, changeBilling, changeMailing) {
+       
+           if (changeBilling) {
+               uEditAddressAlertMarshal(prevBillingAddress);
+               prevBillingAddress = addrId;
+           }
+           
+           if (changeMailing) {
+               uEditAddressAlertMarshal(prevMailingAddress);
+               prevMailingAddress = addrId;
+           }
+       
+           var callback = function(w) { return w._addr == addrId; };
+           var args = {};
+           dojo.forEach(addressAlertFields,
+               function(field) {
+                   args[field] = findWidget('aua', field, callback).widget.attr('value')
+               }
+           );
+           args.mailing_address = dojo.byId('uedit-mailing-address-' + addrId).checked;
+           args.billing_address = dojo.byId('uedit-billing-address-' + addrId).checked;
+           uEditAddressAlertSearch(args, addrId);
+       }
+       
+       var _addrAlertTimeout = {};
+       function uEditAddressAlertSearch(args, addrId) {
+       
+           _addrAlertTimeout[addrId] = setTimeout(
+               function() {
+                   if (_addrAlertTimeout[addrId]) 
+                       clearTimeout(_addrAlertTimeout[addrId]);
+       
+                   console.log('creating addr alert search for ' + addrId);
+       
+                   fieldmapper.standardRequest(
+                       ['open-ils.actor', 'open-ils.actor.address_alert.test'],
+                       {   async: true,
+                           params: [openils.User.authtoken, staff.ws_ou(), args],
+                           oncomplete : function(r) {
+                               var alerts = openils_Util.readResponse(r);
+                               var msgNode = dojo.byId('uedit-address-alert-message');
+                               var headerRow = dojo.filter(
+                                   dojo.query('[name=uedit-addr-divider]'),
+                                   function(row) { return row.getAttribute('addr') == addrId })[0]
+       
+                               msgNode.innerHTML = '';
+       
+                               if (alerts.length) {
+       
+                                   // show the alert box
+                                   openils_Util.hide('uedit-help-div');
+                                   openils_Util.hide('uedit-dupe-div');
+                                   openils_Util.show('uedit-address-alert');
+       
+                                   // style the address header row
+                                   openils_Util.addCSSClass(headerRow, 'uedit-address-alert-divider');
+       
+                                   dojo.forEach(alerts,
+                                       function(addr) {
+                                           msgNode.innerHTML += addr.alert_message() + '<br/>';
+                                       }
+                                   );
+       
+                               } else { 
+                                   openils_Util.hide('uedit-address-alert');
+                                   openils_Util.removeCSSClass(headerRow, 'uedit-address-alert-divider');
+                               }
+                           }
+                       }
+                   );
+               }, 
+               addressAlertTimeout
+           );
+       }
+       
+       function uEditDupeSearch(type, value) {
+           if(!value) return;
+           var search;
+           switch(type) {
+       
+               case 'name':
+                   openils_Util.hide('uedit-dupe-names-link');
+                   var fname = findWidget('au', 'first_given_name').widget.attr('value');
+                   var lname = findWidget('au', 'family_name').widget.attr('value');
+                   if( !(fname && lname) ) return;
+                   search = {
+                       first_given_name : {value : fname, group : 0},
+                       family_name : {value : lname, group : 0},
+                   };
+                   break;
+       
+               case 'email':
+                   openils_Util.hide('uedit-dupe-email-link');
+                   search = {email : {value : value, group : 0}};
+                   break;
+       
+               case 'ident':
+                   openils_Util.hide('uedit-dupe-ident-link');
+                   search = {ident : {value : value, group : 2}};
+                   break;
+       
+               case 'phone':
+                   openils_Util.hide('uedit-dupe-phone-link');
+                   search = {phone : {value : value, group : 2}};
+                   break;
+       
+               case 'address':
+                   openils_Util.hide('uedit-dupe-address-link');
+                   search = {};
+                   dojo.forEach(['street1', 'street2', 'city', 'post_code'],
+                       function(field) {
+                           if(value[field])
+                               search[field] = {value : value[field], group: 1};
+                       }
+                   );
+                   break;
+           }
+       
+           // find possible duplicate patrons
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.patron.search.advanced'],
+               {   async: true,
+                   params: [openils.User.authtoken, search],
+                   oncomplete : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       resp = resp.filter(function(id) { return (id != patron.id()); });
+       
+                       if(resp && resp.length > 0) {
+       
+                           openils_Util.hide('uedit-help-div');
+                           openils_Util.hide('uedit-address-alert');
+                           openils_Util.show('uedit-dupe-div');
+                           var link;
+       
+                           switch(type) {
+                               case 'name':
+                                   link = dojo.byId('uedit-dupe-names-link');
+                                   link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_NAME, [resp.length]);
+                                   break;
+                               case 'email':
+                                   link = dojo.byId('uedit-dupe-email-link');
+                                   link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_EMAIL, [resp.length]);
+                                   break;
+                               case 'ident':
+                                   link = dojo.byId('uedit-dupe-ident-link');
+                                   link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_IDENT, [resp.length]);
+                                   break;
+                               case 'phone':
+                                   link = dojo.byId('uedit-dupe-phone-link');
+                                   link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_PHONE, [resp.length]);
+                                   break;
+                               case 'address':
+                                   link = dojo.byId('uedit-dupe-address-link');
+                                   link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_ADDR, [resp.length]);
+                                   break;
+                           }
+       
+                           openils_Util.show(link);
+                           link.onclick = function() {
+                               search.search_sort = js2JSON(["penalties", "family_name", "first_given_name"]);
+                               if(window.xulG)
+                                   window.xulG.spawn_search(search);
+                               else
+                                   console.log("running XUL patron search " + js2JSON(search));
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       function getByName(node, name) {
+           return dojo.query('[name='+name+']', node)[0];
+       }
+       
+       
+       function ueLoadContextHelp(fmcls, fmfield) {
+           openils_Util.hide('uedit-dupe-div');
+           openils_Util.hide('uedit-dupe-div');
+           openils_Util.show('uedit-help-div');
+           dojo.byId('uedit-help-field').innerHTML = fieldmapper_IDL.fmclasses[fmcls].field_map[fmfield].label;
+           dojo.byId('uedit-help-text').innerHTML = fieldDoc[fmcls][fmfield].string();
+       }
+       
+       
+       /* creates a new patron object with card attached */
+       function uEditNewPatron() {
+           patron = new au();
+           patron.isnew(1);
+           patron.id(-1);
+           card = new ac();
+           card.id(uEditCardVirtId--);
+           card.isnew(1);
+           patron.active(1);
+           patron.card(card);
+           patron.cards([card]);
+           patron.net_access_level(orgSettings['ui.patron.default_inet_access_level'] || 1);
+           patron.ident_type(orgSettings['ui.patron.default_ident_type']);
+           patron.stat_cat_entries([]);
+           patron.survey_responses([]);
+           patron.addresses([]);
+           uEditMakeRandomPw(patron);
+           return patron;
+       }
+       
+       function uEditMakeRandomPw(patron) {
+           var rand  = Math.random();
+           rand = parseInt(rand * 10000) + '';
+           while(rand.length < 4) rand += '0';
+       /*
+           appendClear($('ue_password_plain'),text(rand));
+           unHideMe($('ue_password_gen'));
+       */
+           patron.passwd(rand);
+           return rand;
+       }
+       
+       function uEditWidgetVal(w) {
+           var val = (w.getFormattedValue) ? w.getFormattedValue() : w.attr('value');
+           if(val === '') val = null;
+           return val;
+       }
+       
+       function uEditSave() { _uEditSave(); }
+       function uEditSaveClone() { _uEditSave(true); }
+       
+       function _uEditSave(doClone) {
+       
+           if ( (! myForm.isValid()) || dupeUsrname || dupeBarcode ) {
+               alert(localeStrings.INVALID_FORM);
+               return;
+           }
+       
+           for(var idx in widgetPile) {
+               var w = widgetPile[idx];
+               var val = uEditWidgetVal(w);
+       
+               switch(w._wtype) {
+                   case 'au':
+                       if(w._fmfield != 'passwd2')
+                           patron[w._fmfield](val);
+                       break;
+       
+                   case 'ac':
+                       if(!editCard) editCard = patron.card();
+                       editCard[w._fmfield](val);
+                       break;
+       
+                   case 'aua':
+                       var addr = patron.addresses().filter(function(i){return (i.id() == w._addr)})[0];
+                       if(!addr) {
+                           addr = new fieldmapper.aua();
+                           addr.id(w._addr);
+                           addr.isnew(1);
+                           addr.usr(patron.id());
+                           addr.country(orgSettings['ui.patron.default_country']);
+                           var t = patron.addresses();
+                               if (!t) { t = []; }
+                               t.push(addr);
+                               patron.addresses(t);
+                       } else {
+                           if(addr[w._fmfield]() != val)
+                               addr.ischanged(1);
+                       }
+                       addr[w._fmfield](val);
+       
+                       if(dojo.byId('uedit-billing-address-' + addr.id()).checked) 
+                           patron.billing_address(addr.id());
+       
+                       if(dojo.byId('uedit-mailing-address-' + addr.id()).checked)
+                           patron.mailing_address(addr.id());
+       
+                       break;
+       
+                   case 'survey':
+                       if(val == null) break;
+                       var resp = new fieldmapper.asvr();
+                       resp.isnew(1);
+                       resp.survey(w._survey)
+                       resp.usr(patron.id());
+                       resp.question(w._question)
+                       resp.answer(val);
+                       var t = patron.survey_responses();
+                           if (!t) { t = []; }
+                           t.push(resp);
+                           patron.survey_responses(t);
+                       break;
+       
+                   case 'statcat':
+                       var map = patron.stat_cat_entries().filter(
+                           function(m){
+                               return (m.stat_cat() == w._statcat) })[0];
+       
+                       if(map) {
+                           if(map.stat_cat_entry() == val) 
+                               break;
+                           if(val == null) {
+                               val = '';
+                               map.isdeleted(1);
+                           } else {
+                               map.ischanged(1);
+                           }
+                       } else {
+                           if(val == null)
+                               break;
+                           map = new fieldmapper.actscecm();
+                           map.isnew(1);
+                       }
+       
+                       map.stat_cat(w._statcat);
+                       map.stat_cat_entry(val);
+                       map.target_usr(patron.id());
+                       var t = patron.stat_cat_entries();
+                           if (!t) { t = []; }
+                           t.push(map);
+                           patron.stat_cat_entries(t);
+                       break;
+               }
+           }
+       
+           patron.ischanged(1);
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.patron.update'],
+               {   async: true,
+                   params: [openils.User.authtoken, patron],
+                   oncomplete: function(r) {
+                       lock_ready = false;
+                       if (xulG && typeof xulG.unlock_tab == 'function') {
+                           xulG.unlock_tab();
+                           already_locked = false;
+                       }
+                       /* There's something that seems to just make the form reload
+                        * on all saves, so this uUpdate... isn't needed here after
+                        * all. */
+                       //uUpdateContactInvalidators();
+       
+                       newPatron = openils_Util.readResponse(r);
+                       if(newPatron) {
+                           uEditUpdateUserSettings(newPatron.id());
+                           if(stageUser) uEditRemoveStage();
+                           uEditFinishSave(newPatron, doClone);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function uUpdateContactInvalidators() {
+           /* show invalidator buttons for fields that having anything in them */
+           ["email", "day_phone", "evening_phone", "other_phone"].forEach(
+               function(f) {
+                   openils_Util[patron[f]() ? "show" : "hide"]("wrap_invalidate_" + f);
+               }
+           );
+       }
+       
+       function uGenerateInvalidatorWidget(container_node, field) {
+           new dijit_form_Button(
+               {
+                   "label": localeStrings.INVALIDATE,
+                   "scrollOnFocus": false,
+                   "onClick": function() {
+                       progressDialog.show(true);
+                       fieldmapper.standardRequest(
+                           ["open-ils.actor", "open-ils.actor.invalidate." + field], {
+                               "async": true,
+                               "params": [openils.User.authtoken, patron.id(), null, patron.home_ou()],
+                               "oncomplete": function(r) {
+                                   progressDialog.hide();
+                                   // alerts on non-success event
+                                   var res = openils_Util.readResponse(r);
+       
+                                   if (res.payload.last_xact_id) {
+                                       for (var id in res.payload.last_xact_id) {
+                                           if (patron.id() == id)
+                                               patron.last_xact_id(
+                                                   res.payload.last_xact_id[id]
+                                               );
+                                       }
+       
+                                       findWidget("au",field).widget.attr("value","");
+                                       openils_Util.hide(container_node);
+                                   }
+                               }
+                           }
+                       );
+                   }
+               }, dojo.create("span", null, container_node, "only")
+           );
+       }
+       
+       function uEditRemoveStage() {
+           var resp = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.stage.delete'],
+               { params : [openils.User.authtoken, stageUser.row_id()] }
+           )
+       }
+       
+       function uEditFinishSave(newPatron, doClone) {
+       
+           if(doClone && cloneUser == null)
+               cloneUser = newPatron.id();
+       
+               if( doClone ) {
+       
+                       if(xulG && typeof xulG.spawn_editor == 'function' && !patron.isnew() ) {
+                   window.xulG.spawn_editor({ses:openils.User.authtoken,clone:cloneUser});
+                   uEditRefresh();
+       
+                       } else {
+                               location.href = location.href.replace(/\?.*/, '') + '?clone=' + cloneUser;
+                       }
+       
                } else {
-                       location.href = location.href.replace(/\?.*/, '') + '?clone=' + cloneUser;
+       
+                       uEditRefresh();
                }
-
-       } else {
-
-               uEditRefresh();
+       
+               uEditRefreshXUL(newPatron);
        }
+       
+       function uEditRefresh() {
+           var usr = cgi.param('usr');
+           var href = location.href.replace(/\?.*/, '');
+           href += ((usr) ? '?usr=' + usr : '');
+           location.href = href;
+       }
+       
+       function uEditRefreshXUL(newuser) {
+               if (window.xulG && typeof window.xulG.on_save == 'function') 
+                       window.xulG.on_save(newuser);
+       }
+       
+       
+       /**
+        * Create a new address and insert it into the DOM
+        * @param evt ignored
+        * @param id The address id
+        * @param mkLinks If true, set the new address as the 
+        *  mailing/billing address for the user
+        */
+       function uEditNewAddr(evt, id, mkLinks) {
+       
+           if(id == null) 
+               id = --uEditAddrVirtId; // new address
+       
+           var addr =  patron.addresses().filter(
+               function(i) { return (i.id() == id) })[0];
+       
+           dojo.forEach(addrTemplateRows, 
+               function(row) {
+       
+                   row = tbody.insertBefore(row.cloneNode(true), dojo.byId('new-addr-row'));
+                   row.setAttribute('type', '');
+                   row.setAttribute('addr', id+'');
+       
+                   if(row.getAttribute('fmclass')) {
+                       var widget = fleshFMRow(row, 'aua', {addr:id});
+       
+                       // make new addresses a default address type
+                       if(id < 0 && row.getAttribute('fmfield') == 'address_type') 
+                           widget.widget.attr('value', localeStrings.DEFAULT_ADDRESS_TYPE); 
+       
+                       // make new addresses valid by default
+                       if(id < 0 && row.getAttribute('fmfield') == 'valid') 
+                           widget.widget.attr('value', true); 
+       
+                       // make new addresses use the org setting for default country 
+                       if(id < 0 && row.getAttribute('fmfield') == 'country') 
+                           widget.widget.attr('value',orgSettings['ui.patron.default_country']);
+       
+                   } else if(row.getAttribute('name') == 'uedit-addr-pending-row') {
+       
+                       // if it's a pending address, show the 'approve' button
+                       if(addr && openils_Util.isTrue(addr.pending())) {
+                           openils_Util.show(row, 'table-row');
+                           dojo.query('[name=approve-button]', row)[0].onclick = 
+                               function() { uEditApproveAddress(addr); };
+       
+                           if(addr.replaces()) {
+                               var div = dojo.query('[name=replaced-addr]', row)[0]
+                               var replaced =  patron.addresses().filter(
+                                   function(i) { return (i.id() == addr.replaces()) })[0];
+       
+                               div.innerHTML = dojo.string.substitute(localeStrings.REPLACED_ADDRESS, [
+                                   replaced.address_type() || '',
+                                   replaced.street1() || '',
+                                   replaced.street2() || '',
+                                   replaced.city() || '',
+                                   replaced.state() || '',
+                                   replaced.post_code() || ''
+                               ]);
+       
+                           } else {
+                               openils_Util.hide(dojo.query('[name=replaced-addr-div]', row)[0]);
+                           }
+                       }
+       
+                   } else if(row.getAttribute('name') == 'uedit-addr-owner-row') {
+                       // address is owned by someone else.  provide option to load the
+                       // user in a different tab
+                       
+                       if(addr && addr.usr() != patron.id()) {
+                           openils_Util.show(row, 'table-row');
+                           var link = getByName(row, 'addr-owner');
+       
+                           // fetch the linked user so we can present their name in the UI
+                           var addrUser;
+                           if(cloneUserObj && cloneUserObj.id() == addr.usr()) {
+                               addrUser = [
+                                   cloneUserObj.first_given_name(), 
+                                   cloneUserObj.second_given_name(), 
+                                   cloneUserObj.family_name()
+                               ];
+                           } else {
+                               addrUser = fieldmapper.standardRequest(
+                                   ['open-ils.actor', 'open-ils.actor.user.retrieve.parts'],
+                                   {params: [
+                                       openils.User.authtoken, 
+                                       addr.usr(), 
+                                       ['first_given_name', 'second_given_name', 'family_name']
+                                   ]}
+                               );
+                           }
+       
+                           link.innerHTML = (addrUser.map(function(name) { return (name) ? name+' ' : '' })+'').replace(/,/g,''); // TODO i18n
+                           link.onclick = function() {
+                               if(openils_XUL.isXUL()) { 
+                                   window.xulG.spawn_editor({ses:openils.User.authtoken, usr:addr.usr()})
+                               } else {
+                                   parent.location.href = location.href.replace(/clone=\d+/, 'usr=' + addr.usr());
+                               }
+                           }
+                       }
+       
+                   } else if(row.getAttribute('name') == 'uedit-addr-divider') {
+                       // link up the billing/mailing address and give the inputs IDs so we can access the later
+                       
+                       // billing address
+                       var ba = getByName(row, 'billing_address');
+                       ba.id = 'uedit-billing-address-' + id;
+                       if(mkLinks || (patron.billing_address() && patron.billing_address().id() == id)) {
+                           ba.checked = true;
+                           prevBillingAddress = id;
+                       }
+       
+                       // mailing address
+                       var ma = getByName(row, 'mailing_address');
+                       ma.id = 'uedit-mailing-address-' + id;
+                       if(mkLinks || (patron.mailing_address() && patron.mailing_address().id() == id)) {
+                           ma.checked = true;
+                           prevMailingAddress = id;
+                       }
+       
+                       ba.onclick = function() { console.log('ba.onchange ' + id); uEditAddressAlertMarshal(id, true) };
+                       ma.onclick = function() { uEditAddressAlertMarshal(id, false, true) };
+                       
+                       var btn = dojo.query('[name=delete-button]', row)[0];
+                       if(btn) btn.onclick = function(){ uEditDeleteAddr(id) };
+                   }
+               }
+           );
+       }
+       
+       function uEditApproveAddress(addr) {
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.pending_address.approve'],
+               {   async: true,
+                   params:  [openils.User.authtoken, addr],
+       
+                   oncomplete : function(r) {
+                       var oldId = openils_Util.readResponse(r);
+                           
+                       // remove addrs from UI
+                       dojo.forEach(
+                           patron.addresses(), 
+                           function(addr) { uEditDeleteAddr(addr.id(), true); }
+                       );
+       
+                       if(oldId != null) {
+                           
+                           // remove the replaced address 
+                           if(oldId != addr.id()) {
+                                       patron.addresses(
+                                   patron.addresses().filter(
+                                                       function(i) { return (i.id() != oldId); }
+                                               )
+                                       );
+                           }
+                           
+                           // fix the the new address
+                           addr.id(oldId);
+                           addr.replaces(null);
+                           addr.pending('f');
+       
+                       }
+       
+                       // redraw addrs
+                       loadAllAddrs();
+                   }
+               }
+           );
+       }
+       
+       
+       function uEditDeleteAddr(id, noAlert) {
+           if (patron.isnew() && orgSettings['ui.patron.registration.require_address']) {
+               if (dojo.query('tr[name=uedit-addr-divider]').length < 2) {
+                   alert(localeStrings.NEED_ADDRESS);
+                   return;
+               }
+           }
+           if(!noAlert) {
+               if(!confirm(dojo.string.substitute(localeStrings.DELETE_ADDRESS, [id]))) return;
+           }
+           var addr = patron.addresses().filter(function(i){return (i.id() == id)})[0];
+           if (addr) { addr.isdeleted(1); }
+           var m_a = patron.mailing_address();
+               if (typeof m_a == 'object' && m_a != null) { m_a = m_a.id(); }
+               if (m_a == id) { patron.mailing_address(null); }
+           var b_a = patron.billing_address();
+               if (typeof b_a == 'object' && b_a != null) { b_a = b_a.id(); }
+               if (b_a == id) { patron.billing_address(null); }
+       
+           var rows = dojo.query('tr[addr='+id+']', tbody);
+           for(var i = 0; i < rows.length; i++)
+               rows[i].parentNode.removeChild(rows[i]);
+           widgetPile = widgetPile.filter(function(w){return (w._addr != id)});
+       }
+       
+       function uEditToggleRequired(level) {
+           openils_Util.removeCSSClass(tbody, 'hide-non-required');
+           openils_Util.removeCSSClass(tbody, 'hide-non-suggested');
+           openils_Util.show('uedit-show-required');
+           openils_Util.show('uedit-show-required-br');
+           openils_Util.show('uedit-show-suggested');
+           openils_Util.show('uedit-show-suggested-br');
+           openils_Util.show('uedit-show-all');
+           switch(level) {
+               case 1:
+                   openils_Util.hide('uedit-show-required');
+                   openils_Util.hide('uedit-show-required-br');
+                   openils_Util.addCSSClass(tbody, 'hide-non-required');
+                   break;
+               case 2:
+                   openils_Util.hide('uedit-show-suggested');
+                   openils_Util.hide('uedit-show-suggested-br');
+                   openils_Util.addCSSClass(tbody, 'hide-non-suggested');
+                   break;
+               default:
+                   openils_Util.hide('uedit-show-all');
+                   break;
+           } 
+       }
+       
+       function printable_output() {
+           var temp; var s = '=-=-=-=\r\n';
+           for (var idx in widgetPile) {
+               var w = widgetPile[idx];
+               var val = uEditWidgetVal(w);
+               var label;
+               if (typeof w.idlField == 'undefined') {
+                   label = w._wtype;
+                   if (w._wtype == 'statcat') {
+                       var stat = statCats.filter(
+                           function(m){
+                               return (m.id() == w._statcat) })[0];
+                       label = stat.name();
+                   } else if (w._wtype == 'survey') {
+                       var survey = surveys.filter(
+                           function(m){
+                               return (m.id() == w._survey) })[0];
+                       var question = survey.questions().filter(
+                           function(m){
+                               return (m.id() == w._question) })[0];
+                       label = survey.name() + ' : ' + question.question();
+                   } else {
+                       label = 'FIXME';
+                   }
+               } else {
+                   label = w.idlField.label;
+               }
+               if (temp != w._wtype) {
+                   temp = w._wtype;
+                   s += '-------\r\n';
+               }
+               s += label + ':\t' + (typeof val == 'object' ? '' : val) + '\r\n';
+           }
+           s += '=-=-=-=\r\n';
+           return s;
+       }
+       
+       openils_Util.addOnLoad(load);
+       
 
-       uEditRefreshXUL(newPatron);
-}
-
-function uEditRefresh() {
-    var usr = cgi.param('usr');
-    var href = location.href.replace(/\?.*/, '');
-    href += ((usr) ? '?usr=' + usr : '');
-    location.href = href;
-}
-
-function uEditRefreshXUL(newuser) {
-       if (window.xulG && typeof window.xulG.on_save == 'function') 
-               window.xulG.on_save(newuser);
-}
-
-
-/**
- * Create a new address and insert it into the DOM
- * @param evt ignored
- * @param id The address id
- * @param mkLinks If true, set the new address as the 
- *  mailing/billing address for the user
- */
-function uEditNewAddr(evt, id, mkLinks) {
-
-    if(id == null) 
-        id = --uEditAddrVirtId; // new address
-
-    var addr =  patron.addresses().filter(
-        function(i) { return (i.id() == id) })[0];
-
-    dojo.forEach(addrTemplateRows, 
-        function(row) {
-
-            row = tbody.insertBefore(row.cloneNode(true), dojo.byId('new-addr-row'));
-            row.setAttribute('type', '');
-            row.setAttribute('addr', id+'');
-
-            if(row.getAttribute('fmclass')) {
-                var widget = fleshFMRow(row, 'aua', {addr:id});
-
-                // make new addresses a default address type
-                if(id < 0 && row.getAttribute('fmfield') == 'address_type') 
-                    widget.widget.attr('value', localeStrings.DEFAULT_ADDRESS_TYPE); 
-
-                // make new addresses valid by default
-                if(id < 0 && row.getAttribute('fmfield') == 'valid') 
-                    widget.widget.attr('value', true); 
-
-                // make new addresses use the org setting for default country 
-                if(id < 0 && row.getAttribute('fmfield') == 'country') 
-                    widget.widget.attr('value',orgSettings['ui.patron.default_country']);
-
-            } else if(row.getAttribute('name') == 'uedit-addr-pending-row') {
-
-                // if it's a pending address, show the 'approve' button
-                if(addr && openils.Util.isTrue(addr.pending())) {
-                    openils.Util.show(row, 'table-row');
-                    dojo.query('[name=approve-button]', row)[0].onclick = 
-                        function() { uEditApproveAddress(addr); };
-
-                    if(addr.replaces()) {
-                        var div = dojo.query('[name=replaced-addr]', row)[0]
-                        var replaced =  patron.addresses().filter(
-                            function(i) { return (i.id() == addr.replaces()) })[0];
-
-                        div.innerHTML = dojo.string.substitute(localeStrings.REPLACED_ADDRESS, [
-                            replaced.address_type() || '',
-                            replaced.street1() || '',
-                            replaced.street2() || '',
-                            replaced.city() || '',
-                            replaced.state() || '',
-                            replaced.post_code() || ''
-                        ]);
-
-                    } else {
-                        openils.Util.hide(dojo.query('[name=replaced-addr-div]', row)[0]);
-                    }
-                }
-
-            } else if(row.getAttribute('name') == 'uedit-addr-owner-row') {
-                // address is owned by someone else.  provide option to load the
-                // user in a different tab
-                
-                if(addr && addr.usr() != patron.id()) {
-                    openils.Util.show(row, 'table-row');
-                    var link = getByName(row, 'addr-owner');
-
-                    // fetch the linked user so we can present their name in the UI
-                    var addrUser;
-                    if(cloneUserObj && cloneUserObj.id() == addr.usr()) {
-                        addrUser = [
-                            cloneUserObj.first_given_name(), 
-                            cloneUserObj.second_given_name(), 
-                            cloneUserObj.family_name()
-                        ];
-                    } else {
-                        addrUser = fieldmapper.standardRequest(
-                            ['open-ils.actor', 'open-ils.actor.user.retrieve.parts'],
-                            {params: [
-                                openils.User.authtoken, 
-                                addr.usr(), 
-                                ['first_given_name', 'second_given_name', 'family_name']
-                            ]}
-                        );
-                    }
-
-                    link.innerHTML = (addrUser.map(function(name) { return (name) ? name+' ' : '' })+'').replace(/,/g,''); // TODO i18n
-                    link.onclick = function() {
-                        if(openils.XUL.isXUL()) { 
-                            window.xulG.spawn_editor({ses:openils.User.authtoken, usr:addr.usr()})
-                        } else {
-                            parent.location.href = location.href.replace(/clone=\d+/, 'usr=' + addr.usr());
-                        }
-                    }
-                }
-
-            } else if(row.getAttribute('name') == 'uedit-addr-divider') {
-                // link up the billing/mailing address and give the inputs IDs so we can access the later
-                
-                // billing address
-                var ba = getByName(row, 'billing_address');
-                ba.id = 'uedit-billing-address-' + id;
-                if(mkLinks || (patron.billing_address() && patron.billing_address().id() == id)) {
-                    ba.checked = true;
-                    prevBillingAddress = id;
-                }
-
-                // mailing address
-                var ma = getByName(row, 'mailing_address');
-                ma.id = 'uedit-mailing-address-' + id;
-                if(mkLinks || (patron.mailing_address() && patron.mailing_address().id() == id)) {
-                    ma.checked = true;
-                    prevMailingAddress = id;
-                }
-
-                ba.onclick = function() { console.log('ba.onchange ' + id); uEditAddressAlertMarshal(id, true) };
-                ma.onclick = function() { uEditAddressAlertMarshal(id, false, true) };
-                
-                var btn = dojo.query('[name=delete-button]', row)[0];
-                if(btn) btn.onclick = function(){ uEditDeleteAddr(id) };
-            }
-        }
-    );
-}
-
-function uEditApproveAddress(addr) {
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.pending_address.approve'],
-        {   async: true,
-            params:  [openils.User.authtoken, addr],
-
-            oncomplete : function(r) {
-                var oldId = openils.Util.readResponse(r);
-                    
-                // remove addrs from UI
-                dojo.forEach(
-                    patron.addresses(), 
-                    function(addr) { uEditDeleteAddr(addr.id(), true); }
-                );
-
-                if(oldId != null) {
-                    
-                    // remove the replaced address 
-                    if(oldId != addr.id()) {
-                               patron.addresses(
-                            patron.addresses().filter(
-                                               function(i) { return (i.id() != oldId); }
-                                       )
-                               );
-                    }
-                    
-                    // fix the the new address
-                    addr.id(oldId);
-                    addr.replaces(null);
-                    addr.pending('f');
-
-                }
-
-                // redraw addrs
-                loadAllAddrs();
-            }
-        }
-    );
-}
-
-
-function uEditDeleteAddr(id, noAlert) {
-    if (patron.isnew() && orgSettings['ui.patron.registration.require_address']) {
-        if (dojo.query('tr[name=uedit-addr-divider]').length < 2) {
-            alert(localeStrings.NEED_ADDRESS);
-            return;
-        }
-    }
-    if(!noAlert) {
-        if(!confirm(dojo.string.substitute(localeStrings.DELETE_ADDRESS, [id]))) return;
-    }
-    var addr = patron.addresses().filter(function(i){return (i.id() == id)})[0];
-    if (addr) { addr.isdeleted(1); }
-    var m_a = patron.mailing_address();
-        if (typeof m_a == 'object' && m_a != null) { m_a = m_a.id(); }
-        if (m_a == id) { patron.mailing_address(null); }
-    var b_a = patron.billing_address();
-        if (typeof b_a == 'object' && b_a != null) { b_a = b_a.id(); }
-        if (b_a == id) { patron.billing_address(null); }
-
-    var rows = dojo.query('tr[addr='+id+']', tbody);
-    for(var i = 0; i < rows.length; i++)
-        rows[i].parentNode.removeChild(rows[i]);
-    widgetPile = widgetPile.filter(function(w){return (w._addr != id)});
-}
-
-function uEditToggleRequired(level) {
-    openils.Util.removeCSSClass(tbody, 'hide-non-required');
-    openils.Util.removeCSSClass(tbody, 'hide-non-suggested');
-    openils.Util.show('uedit-show-required');
-    openils.Util.show('uedit-show-required-br');
-    openils.Util.show('uedit-show-suggested');
-    openils.Util.show('uedit-show-suggested-br');
-    openils.Util.show('uedit-show-all');
-    switch(level) {
-        case 1:
-            openils.Util.hide('uedit-show-required');
-            openils.Util.hide('uedit-show-required-br');
-            openils.Util.addCSSClass(tbody, 'hide-non-required');
-            break;
-        case 2:
-            openils.Util.hide('uedit-show-suggested');
-            openils.Util.hide('uedit-show-suggested-br');
-            openils.Util.addCSSClass(tbody, 'hide-non-suggested');
-            break;
-        default:
-            openils.Util.hide('uedit-show-all');
-            break;
-    } 
-}
-
-function printable_output() {
-    var temp; var s = '=-=-=-=\r\n';
-    for (var idx in widgetPile) {
-        var w = widgetPile[idx];
-        var val = uEditWidgetVal(w);
-        var label;
-        if (typeof w.idlField == 'undefined') {
-            label = w._wtype;
-            if (w._wtype == 'statcat') {
-                var stat = statCats.filter(
-                    function(m){
-                        return (m.id() == w._statcat) })[0];
-                label = stat.name();
-            } else if (w._wtype == 'survey') {
-                var survey = surveys.filter(
-                    function(m){
-                        return (m.id() == w._survey) })[0];
-                var question = survey.questions().filter(
-                    function(m){
-                        return (m.id() == w._question) })[0];
-                label = survey.name() + ' : ' + question.question();
-            } else {
-                label = 'FIXME';
-            }
-        } else {
-            label = w.idlField.label;
-        }
-        if (temp != w._wtype) {
-            temp = w._wtype;
-            s += '-------\r\n';
-        }
-        s += label + ':\t' + (typeof val == 'object' ? '' : val) + '\r\n';
-    }
-    s += '=-=-=-=\r\n';
-    return s;
-}
-
-openils.Util.addOnLoad(load);
+});
\ No newline at end of file
index 1f734df..3785296 100644 (file)
@@ -1,87 +1,96 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('openils.Util');
-dojo.require('openils.User');
-
-// need these to represent the event def name
-dojo.requireLocalization('openils.conify', 'conify');
-var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
-
-var evtCache = {};
-
-function init() {
-    var store = new dojo.data.ItemFileWriteStore({data:acqf.initStoreData()});
-    evtGrid.setStore(store);
-    evtGrid.render();
-
-    function onResponse(r) {
-        var evt = openils.Util.readResponse(r);
-        evtCache[evt.id()] = evt;
-        evtGrid.store.newItem(evt.toStoreItem()); 
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.events.circ'],
-        {   async: true,
-            params: [openils.User.authtoken, patronId],
-            onresponse : onResponse
-        }
-    );
-
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.events.ahr'],
-        {   async: true,
-            params: [openils.User.authtoken, patronId],
-            onresponse : onResponse
-        }
-    );
-}
-
-function getField(rowIdx, item) {
-    if(!item) return '';
-    var evt = evtCache[this.grid.store.getValue(item, 'id')];
-
-    switch(this.field) {
-        case 'event_def':
-            return dojo.string.substitute(
-                localeStrings.EVENT_DEF_LABEL, [
-                    fieldmapper.aou.findOrgUnit(evt.event_def().owner()).shortname(), 
-                    evt.event_def().name()
-                ]);
-        case 'reactor':
-            return evt.event_def().reactor().module();
-        case 'validator':
-            return evt.event_def().validator().module();
-        case 'hook':
-            return evt.event_def().hook();
-        case 'target':
-            switch(evt.target().classname) {
-                case 'circ':
-                    return evt.target().target_copy().barcode();
-                case 'ahr':
-                    if(evt.target().currrent_copy())
-                        return evt.target().currrent_copy().barcode();
-            }
-            
-    }
-
-    return this.grid.store.getValue(item, this.field) || '';
-}
-
-function evtCancelSelected() {
-    var selected = evtGrid.selection.getSelected();
-    if(selected.length == 0) return;
-    var eventIds = selected.map(
-        function(item) { return evtGrid.store.getValue(item, 'id') } );
-    alert(eventIds);
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.event.cancel.batch'],
-        {   async: true,
-            params: [openils.User.authtoken, eventIds],
-            oncomplete : init
-        }
-    );
-}
-
-openils.Util.addOnLoad(init);
+require([
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileWriteStore",
+       "openils/Util",
+       "openils/User"
+       ],
+function(dojox_grid_DataGrid,
+       dojo_data_ItemFileWriteStore,
+       openils_Util,
+       openils_User){
+       
+       // need these to represent the event def name
+       dojo.requireLocalization('openils.conify', 'conify');
+       var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
+       
+       var evtCache = {};
+       
+       function init() {
+           var store = new dojo_data_ItemFileWriteStore({data:acqf.initStoreData()});
+           evtGrid.setStore(store);
+           evtGrid.render();
+       
+           function onResponse(r) {
+               var evt = openils_Util.readResponse(r);
+               evtCache[evt.id()] = evt;
+               evtGrid.store.newItem(evt.toStoreItem()); 
+           }
+       
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.events.circ'],
+               {   async: true,
+                   params: [openils_User.authtoken, patronId],
+                   onresponse : onResponse
+               }
+           );
+       
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.events.ahr'],
+               {   async: true,
+                   params: [openils_User.authtoken, patronId],
+                   onresponse : onResponse
+               }
+           );
+       }
+       
+       function getField(rowIdx, item) {
+           if(!item) return '';
+           var evt = evtCache[this.grid.store.getValue(item, 'id')];
+       
+           switch(this.field) {
+               case 'event_def':
+                   return dojo.string.substitute(
+                       localeStrings.EVENT_DEF_LABEL, [
+                           fieldmapper.aou.findOrgUnit(evt.event_def().owner()).shortname(), 
+                           evt.event_def().name()
+                       ]);
+               case 'reactor':
+                   return evt.event_def().reactor().module();
+               case 'validator':
+                   return evt.event_def().validator().module();
+               case 'hook':
+                   return evt.event_def().hook();
+               case 'target':
+                   switch(evt.target().classname) {
+                       case 'circ':
+                           return evt.target().target_copy().barcode();
+                       case 'ahr':
+                           if(evt.target().currrent_copy())
+                               return evt.target().currrent_copy().barcode();
+                   }
+                   
+           }
+       
+           return this.grid.store.getValue(item, this.field) || '';
+       }
+       
+       function evtCancelSelected() {
+           var selected = evtGrid.selection.getSelected();
+           if(selected.length == 0) return;
+           var eventIds = selected.map(
+               function(item) { return evtGrid.store.getValue(item, 'id') } );
+           alert(eventIds);
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.event.cancel.batch'],
+               {   async: true,
+                   params: [openils_User.authtoken, eventIds],
+                   oncomplete : init
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(init);
+       
+       
 
+});
\ No newline at end of file
index 0e69a2d..fa42164 100644 (file)
-dojo.require("openils.User");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
-dojo.requireLocalization("openils.booking", "capture");
-
-const CAPTURE_FAILURE = 0;
-const CAPTURE_SUCCESS = 1;
-const CAPTURE_UNKNOWN = 2;
-
-var localeStrings = dojo.i18n.getLocalization("openils.booking", "capture");
-
-function CaptureDisplay(control_holder, data_holder) {
-    this.control_holder = control_holder;
-    this.data_holder = data_holder;
-}
-CaptureDisplay.prototype.no_payload = function() {
-    this.data_holder.appendChild(
-        document.createTextNode(localeStrings.NO_PAYLOAD)
-    );
-};
-CaptureDisplay.prototype.dump = function(payload) {
-    var div = document.createElement("div");
-    div.appendChild(document.createTextNode(localeStrings.HERES_WHAT_WE_KNOW));
-    this.data_holder.appendChild(div);
-
-    var ul = document.createElement("ul");
-    for (var k in payload) {
-        var li = document.createElement("li");
-        li.appendChild(document.createTextNode(k + ": " + payload[k]));
-        ul.appendChild(li);
-    }
-    this.data_holder.appendChild(ul);
-};
-CaptureDisplay.prototype._generate_barcode_line = function(payload) {
-    var div = document.createElement("div");
-    div.appendChild(document.createTextNode(
-        localeStrings.BARCODE + ": " + payload.resource.barcode()
-    ));
-    return div;
-};
-CaptureDisplay.prototype._generate_title_line = function(payload) {
-    var div = document.createElement("div");
-    div.appendChild(document.createTextNode(
-        localeStrings.TITLE + ": " +
-        (payload.mvr ? payload.mvr.title() : payload.type.name())
-    ));
-    return div;
-};
-CaptureDisplay.prototype._generate_author_line = function(payload) {
-    var div = document.createElement("div");
-    if (payload.mvr) {
-        div.appendChild(document.createTextNode(
-            localeStrings.AUTHOR + ": " + payload.mvr.author()
-        ));
-    }
-    return div;
-};
-CaptureDisplay.prototype._generate_transit_notice = function(payload) {
-    var div = document.createElement("div");
-    if (payload.transit) {
-        div.setAttribute("class", "transit_notice");
-        div.appendChild(document.createTextNode(localeStrings.TRANSIT));
-    }
-    return div;
-};
-CaptureDisplay.prototype._generate_route_line = function(payload) {
-    var div = document.createElement("div");
-    var strong = document.createElement("strong");
-    strong.appendChild(document.createTextNode(
-        (payload.transit ?
-            fieldmapper.aou.findOrgUnit(payload.transit.dest()).shortname() :
-            localeStrings.RESERVATION_SHELF) + ":"
-    ));
-    div.appendChild(document.createTextNode(
-        localeStrings.NEEDS_ROUTED_TO + " "
-    ));
-    div.appendChild(strong);
-    return div;
-};
-CaptureDisplay.prototype._generate_patron_info = function(payload) {
-    var p = document.createElement("p");
-    p.innerHTML = "<strong>" + localeStrings.RESERVED + "</strong> " +
-        formal_name(payload.reservation.usr()) + "<br />" +
-        localeStrings.BARCODE + ": " +
-        payload.reservation.usr().card().barcode();
-    return p;
-};
-CaptureDisplay.prototype._generate_resv_info = function(payload) {
-    var p = document.createElement("p");
-    p.innerHTML = localeStrings.REQUEST + ": " +
-        humanize_timestamp_string(payload.reservation.request_time()) +
-        "<br />" + 
-        localeStrings.DURATION + ": " +
-        humanize_timestamp_string(payload.reservation.start_time()) +
-        " - " + 
-        humanize_timestamp_string(payload.reservation.end_time());
-    return p;
-};
-CaptureDisplay.prototype._generate_meta_info = function(result) {
-    var p = document.createElement("p");
-    p.innerHTML = localeStrings.SLIP_DATE + ": " + result.servertime +
-        "<br />" + localeStrings.PRINTED_BY + " " +
-        formal_name(openils.User.user) + " " + localeStrings.AT + " " +
-        fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname()
-    return p;
-};
-CaptureDisplay.prototype.display_with_transit_info = function(result) {
-    var div = document.createElement("div");
-    var span = document.createElement("span");
-    span.appendChild(document.createTextNode(localeStrings.CAPTURE_INFO));
-    span.setAttribute("class", "capture_info");
-    this.control_holder.appendChild(span);
-
-    var button = document.createElement("button");
-    button.setAttribute("class", "print_slip");
-    button.setAttribute("type", "button");
-    button.setAttribute("accesskey", localeStrings.PRINT_ACCESSKEY);
-    button.innerHTML = localeStrings.PRINT;
-    button.onclick = function() {
-        try { dojo.byId("printing_iframe").contentWindow.print(); }
-        catch (E) { alert(E); } /* XXX */
-        return false;
-    };
-    this.control_holder.appendChild(button);
-
-    div.appendChild(this._generate_transit_notice(result.payload));
-
-    var p = document.createElement("p");
-    p.appendChild(this._generate_route_line(result.payload));
-    p.appendChild(this._generate_barcode_line(result.payload));
-    p.appendChild(this._generate_title_line(result.payload));
-    p.appendChild(this._generate_author_line(result.payload));
-    div.appendChild(p);
-
-    div.appendChild(this._generate_patron_info(result.payload));
-    div.appendChild(this._generate_resv_info(result.payload));
-    div.appendChild(this._generate_meta_info(result));
-
-    this._create_iframe(div);
-};
-CaptureDisplay.prototype._create_iframe = function(contents) {
-    var iframe = document.createElement("iframe");
-    iframe.setAttribute("name", "printing_iframe");
-    iframe.setAttribute("id", "printing_iframe");
-    iframe.setAttribute("src", "");
-    iframe.setAttribute("width", "100%");
-    iframe.setAttribute("height", "400"); /* hardcode 400px? really? */
-
-    this.data_holder.appendChild(iframe);
-
-    var w = dojo.byId("printing_iframe").contentWindow;
-    w.document.open();
-    w.document.write(
-        "<html><head><link rel='stylesheet' type='text/css' href='" +
-        dojo.byId("booking_stylesheet_link").href +
-        "' /><body></body></html>"
-    );
-    w.document.close();
-    w.document.body.appendChild(contents);
-    /* FIXME if (determine_autoprint_setting_somehow()) w.print(); */
-};
-CaptureDisplay.prototype.clear = function() {
-    this.control_holder.innerHTML = "";
-    this.data_holder.innerHTML = "";
-};
-CaptureDisplay.prototype.load = function(result) {
-    try {
-        this.control_holder.appendChild(document.createElement("hr"));
-        if (!result.payload) {
-            this.no_payload();
-        } else if (!result.payload.fail_cause && result.payload.captured) {
-            this.display_with_transit_info(result);
-        } else {
-            this.dump(result.payload);
-        }
-    } catch (E) {
-        alert(E); /* XXX */
-    }
-};
-
-var capture_display;
-var last_result;
-
-function clear_for_next() {
-    if (last_result == CAPTURE_SUCCESS) {
-        last_result = undefined;
-        document.getElementById("result_display").innerHTML = "";
-        document.getElementById("resource_barcode").value = "";
-    }
-}
-
-function capture() {
-    var barcode = document.getElementById("resource_barcode").value;
-    var result = fieldmapper.standardRequest(
-        [
-            "open-ils.booking",
-            "open-ils.booking.resources.capture_for_reservation"
-        ],
-        [openils.User.authtoken, barcode]
-    );
-
-    if (result && result.ilsevent !== undefined) {
-        if (result.payload && result.payload.captured > 0) {
-            capture_display.load(result);
-            return CAPTURE_SUCCESS;
-        } else {
-            capture_display.load(result);
-            alert(my_ils_error(localeStrings.CAPTURED_NOTHING, result));
-            return CAPTURE_FAILURE;
-        }
-    } else {
-        return CAPTURE_UNKNOWN;
-    }
-}
-
-function attempt_capture() {
-    var rd = document.getElementById("result_display");
-    capture_display.clear();
-    switch(last_result = capture()) {
-        case CAPTURE_FAILURE:
-            rd.setAttribute("class", "capture_failure");
-            rd.innerHTML = localeStrings.FAILURE;
-            break;
-        case CAPTURE_SUCCESS:
-            rd.setAttribute("class", "capture_success");
-            rd.innerHTML = localeStrings.SUCCESS;
-            break;
-        default:
-            alert(localeStrings.UNKNOWN_PROBLEM);
-            break;
-    }
-}
-
-function my_init() {
-    init_auto_l10n(dojo.byId("auto_l10n_start_here"));
-    capture_display = new CaptureDisplay(
-        dojo.byId("capture_info_top"),
-        dojo.byId("capture_info_bottom")
-    );
-}
+require([
+       "openils/User",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(openils_User,
+       openils_widget_OrgUnitFilteringSelect){
+       dojo.requireLocalization("openils.booking", "capture");
+       
+       const CAPTURE_FAILURE = 0;
+       const CAPTURE_SUCCESS = 1;
+       const CAPTURE_UNKNOWN = 2;
+       
+       var localeStrings = dojo.i18n.getLocalization("openils.booking", "capture");
+       
+       function CaptureDisplay(control_holder, data_holder) {
+           this.control_holder = control_holder;
+           this.data_holder = data_holder;
+       }
+       CaptureDisplay.prototype.no_payload = function() {
+           this.data_holder.appendChild(
+               document.createTextNode(localeStrings.NO_PAYLOAD)
+           );
+       };
+       CaptureDisplay.prototype.dump = function(payload) {
+           var div = document.createElement("div");
+           div.appendChild(document.createTextNode(localeStrings.HERES_WHAT_WE_KNOW));
+           this.data_holder.appendChild(div);
+       
+           var ul = document.createElement("ul");
+           for (var k in payload) {
+               var li = document.createElement("li");
+               li.appendChild(document.createTextNode(k + ": " + payload[k]));
+               ul.appendChild(li);
+           }
+           this.data_holder.appendChild(ul);
+       };
+       CaptureDisplay.prototype._generate_barcode_line = function(payload) {
+           var div = document.createElement("div");
+           div.appendChild(document.createTextNode(
+               localeStrings.BARCODE + ": " + payload.resource.barcode()
+           ));
+           return div;
+       };
+       CaptureDisplay.prototype._generate_title_line = function(payload) {
+           var div = document.createElement("div");
+           div.appendChild(document.createTextNode(
+               localeStrings.TITLE + ": " +
+               (payload.mvr ? payload.mvr.title() : payload.type.name())
+           ));
+           return div;
+       };
+       CaptureDisplay.prototype._generate_author_line = function(payload) {
+           var div = document.createElement("div");
+           if (payload.mvr) {
+               div.appendChild(document.createTextNode(
+                   localeStrings.AUTHOR + ": " + payload.mvr.author()
+               ));
+           }
+           return div;
+       };
+       CaptureDisplay.prototype._generate_transit_notice = function(payload) {
+           var div = document.createElement("div");
+           if (payload.transit) {
+               div.setAttribute("class", "transit_notice");
+               div.appendChild(document.createTextNode(localeStrings.TRANSIT));
+           }
+           return div;
+       };
+       CaptureDisplay.prototype._generate_route_line = function(payload) {
+           var div = document.createElement("div");
+           var strong = document.createElement("strong");
+           strong.appendChild(document.createTextNode(
+               (payload.transit ?
+                   fieldmapper.aou.findOrgUnit(payload.transit.dest()).shortname() :
+                   localeStrings.RESERVATION_SHELF) + ":"
+           ));
+           div.appendChild(document.createTextNode(
+               localeStrings.NEEDS_ROUTED_TO + " "
+           ));
+           div.appendChild(strong);
+           return div;
+       };
+       CaptureDisplay.prototype._generate_patron_info = function(payload) {
+           var p = document.createElement("p");
+           p.innerHTML = "<strong>" + localeStrings.RESERVED + "</strong> " +
+               formal_name(payload.reservation.usr()) + "<br />" +
+               localeStrings.BARCODE + ": " +
+               payload.reservation.usr().card().barcode();
+           return p;
+       };
+       CaptureDisplay.prototype._generate_resv_info = function(payload) {
+           var p = document.createElement("p");
+           p.innerHTML = localeStrings.REQUEST + ": " +
+               humanize_timestamp_string(payload.reservation.request_time()) +
+               "<br />" + 
+               localeStrings.DURATION + ": " +
+               humanize_timestamp_string(payload.reservation.start_time()) +
+               " - " + 
+               humanize_timestamp_string(payload.reservation.end_time());
+           return p;
+       };
+       CaptureDisplay.prototype._generate_meta_info = function(result) {
+           var p = document.createElement("p");
+           p.innerHTML = localeStrings.SLIP_DATE + ": " + result.servertime +
+               "<br />" + localeStrings.PRINTED_BY + " " +
+               formal_name(openils_User.user) + " " + localeStrings.AT + " " +
+               fieldmapper.aou.findOrgUnit(openils_User.user.ws_ou()).shortname()
+           return p;
+       };
+       CaptureDisplay.prototype.display_with_transit_info = function(result) {
+           var div = document.createElement("div");
+           var span = document.createElement("span");
+           span.appendChild(document.createTextNode(localeStrings.CAPTURE_INFO));
+           span.setAttribute("class", "capture_info");
+           this.control_holder.appendChild(span);
+       
+           var button = document.createElement("button");
+           button.setAttribute("class", "print_slip");
+           button.setAttribute("type", "button");
+           button.setAttribute("accesskey", localeStrings.PRINT_ACCESSKEY);
+           button.innerHTML = localeStrings.PRINT;
+           button.onclick = function() {
+               try { dojo.byId("printing_iframe").contentWindow.print(); }
+               catch (E) { alert(E); } /* XXX */
+               return false;
+           };
+           this.control_holder.appendChild(button);
+       
+           div.appendChild(this._generate_transit_notice(result.payload));
+       
+           var p = document.createElement("p");
+           p.appendChild(this._generate_route_line(result.payload));
+           p.appendChild(this._generate_barcode_line(result.payload));
+           p.appendChild(this._generate_title_line(result.payload));
+           p.appendChild(this._generate_author_line(result.payload));
+           div.appendChild(p);
+       
+           div.appendChild(this._generate_patron_info(result.payload));
+           div.appendChild(this._generate_resv_info(result.payload));
+           div.appendChild(this._generate_meta_info(result));
+       
+           this._create_iframe(div);
+       };
+       CaptureDisplay.prototype._create_iframe = function(contents) {
+           var iframe = document.createElement("iframe");
+           iframe.setAttribute("name", "printing_iframe");
+           iframe.setAttribute("id", "printing_iframe");
+           iframe.setAttribute("src", "");
+           iframe.setAttribute("width", "100%");
+           iframe.setAttribute("height", "400"); /* hardcode 400px? really? */
+       
+           this.data_holder.appendChild(iframe);
+       
+           var w = dojo.byId("printing_iframe").contentWindow;
+           w.document.open();
+           w.document.write(
+               "<html><head><link rel='stylesheet' type='text/css' href='" +
+               dojo.byId("booking_stylesheet_link").href +
+               "' /><body></body></html>"
+           );
+           w.document.close();
+           w.document.body.appendChild(contents);
+           /* FIXME if (determine_autoprint_setting_somehow()) w.print(); */
+       };
+       CaptureDisplay.prototype.clear = function() {
+           this.control_holder.innerHTML = "";
+           this.data_holder.innerHTML = "";
+       };
+       CaptureDisplay.prototype.load = function(result) {
+           try {
+               this.control_holder.appendChild(document.createElement("hr"));
+               if (!result.payload) {
+                   this.no_payload();
+               } else if (!result.payload.fail_cause && result.payload.captured) {
+                   this.display_with_transit_info(result);
+               } else {
+                   this.dump(result.payload);
+               }
+           } catch (E) {
+               alert(E); /* XXX */
+           }
+       };
+       
+       var capture_display;
+       var last_result;
+       
+       function clear_for_next() {
+           if (last_result == CAPTURE_SUCCESS) {
+               last_result = undefined;
+               document.getElementById("result_display").innerHTML = "";
+               document.getElementById("resource_barcode").value = "";
+           }
+       }
+       
+       function capture() {
+           var barcode = document.getElementById("resource_barcode").value;
+           var result = fieldmapper.standardRequest(
+               [
+                   "open-ils.booking",
+                   "open-ils.booking.resources.capture_for_reservation"
+               ],
+               [openils_User.authtoken, barcode]
+           );
+       
+           if (result && result.ilsevent !== undefined) {
+               if (result.payload && result.payload.captured > 0) {
+                   capture_display.load(result);
+                   return CAPTURE_SUCCESS;
+               } else {
+                   capture_display.load(result);
+                   alert(my_ils_error(localeStrings.CAPTURED_NOTHING, result));
+                   return CAPTURE_FAILURE;
+               }
+           } else {
+               return CAPTURE_UNKNOWN;
+           }
+       }
+       
+       function attempt_capture() {
+           var rd = document.getElementById("result_display");
+           capture_display.clear();
+           switch(last_result = capture()) {
+               case CAPTURE_FAILURE:
+                   rd.setAttribute("class", "capture_failure");
+                   rd.innerHTML = localeStrings.FAILURE;
+                   break;
+               case CAPTURE_SUCCESS:
+                   rd.setAttribute("class", "capture_success");
+                   rd.innerHTML = localeStrings.SUCCESS;
+                   break;
+               default:
+                   alert(localeStrings.UNKNOWN_PROBLEM);
+                   break;
+           }
+       }
+       
+       function my_init() {
+           init_auto_l10n(dojo.byId("auto_l10n_start_here"));
+           capture_display = new CaptureDisplay(
+               dojo.byId("capture_info_top"),
+               dojo.byId("capture_info_bottom")
+           );
+       }
+       
+
+});
\ No newline at end of file
index 3bcbe44..7b71e54 100644 (file)
@@ -1,80 +1,86 @@
-dojo.require("dijit.form.Button");
+require([
+       "dijit/form/Button"
+       ],
+function(dijit_form_Button){
+       
+       /* Quick and dirty way to localize some strings; not recommended for reuse.
+        * I'm sure dojo provides a better mechanism for this, but at the moment
+        * this is faster to implement anew than figuring out the Right way to do
+        * the same thing w/ dojo.
+        */
+       function init_auto_l10n(el) {
+           function do_it(myel, cls) {
+               if (cls) {
+                   var clss = cls.split(" ");
+                   for (var k in clss) {
+                       var parts = clss[k].match(/^AUTO_ATTR_([A-Z]+)_.+$/);
+                       if (parts && localeStrings[clss[k]]) {
+                           myel.setAttribute(
+                               parts[1].toLowerCase(), localeStrings[clss[k]]
+                           );
+                       } else if (clss[k].match(/^AUTO_/) && localeStrings[clss[k]]) {
+                           myel.innerHTML = localeStrings[clss[k]];
+                       }
+                   }
+               }
+           }
+       
+           for (var i in el.attributes) {
+               if (el.attributes[i].nodeName == "class") {
+                   do_it(el, el.attributes[i].value);
+                   break;
+               }
+           }
+           for (var i in el.childNodes) {
+               if (el.childNodes[i].nodeType == 1) { // element node?
+                   init_auto_l10n(el.childNodes[i]); // recurse!
+               }
+           }
+       }
+       
+       function get_keys(L) { var K = []; for (var k in L) K.push(k); return K; }
+       function hide_dom_element(e) { e.style.display = "none"; };
+       function reveal_dom_element(e) { e.style.display = ""; };
+       function formal_name(u) {
+           var name = u.family_name() + ", " + u.first_given_name();
+           if (u.second_given_name())
+               name += (" " + u.second_given_name());
+           return name;
+       }
+       function humanize_timestamp_string(ts) {
+           /* For now, this discards time zones. */
+           var parts = ts.split("T");
+           var timeparts = parts[1].split("-")[0].split(":");
+           return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
+       }
+       function humanize_timestamp_string2(ts) {
+           /* For now, this discards time zones, too. */
+           var parts = ts.split(" ");
+           parts[1] = parts[1].replace(/[\-\+]\d+$/, "");
+           var timeparts = parts[1].split("-")[0].split(":");
+           return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
+       }
+       function is_ils_event(e) { return (e.ilsevent != undefined); }
+       function is_ils_actor_card_error(e) {
+           return (e.textcode == "ACTOR_CARD_NOT_FOUND");
+       }
+       function my_ils_error(leader, e) {
+           var s = leader + "\n";
+           var keys = [
+               "ilsevent", "desc", "textcode", "servertime", "pid", "stacktrace"
+           ];
+           for (var i in keys) {
+               if (e[keys[i]]) s += ("\t" + keys[i] + ": " + e[keys[i]] + "\n");
+           }
+           return s;
+       }
+       function set_datagrid_empty_store(grid, flattener) {
+           grid.setStore(
+               new dojo.data.ItemFileReadStore(
+                   {"data": flattener([])}
+               )
+           );
+       }
+       
 
-/* Quick and dirty way to localize some strings; not recommended for reuse.
- * I'm sure dojo provides a better mechanism for this, but at the moment
- * this is faster to implement anew than figuring out the Right way to do
- * the same thing w/ dojo.
- */
-function init_auto_l10n(el) {
-    function do_it(myel, cls) {
-        if (cls) {
-            var clss = cls.split(" ");
-            for (var k in clss) {
-                var parts = clss[k].match(/^AUTO_ATTR_([A-Z]+)_.+$/);
-                if (parts && localeStrings[clss[k]]) {
-                    myel.setAttribute(
-                        parts[1].toLowerCase(), localeStrings[clss[k]]
-                    );
-                } else if (clss[k].match(/^AUTO_/) && localeStrings[clss[k]]) {
-                    myel.innerHTML = localeStrings[clss[k]];
-                }
-            }
-        }
-    }
-
-    for (var i in el.attributes) {
-        if (el.attributes[i].nodeName == "class") {
-            do_it(el, el.attributes[i].value);
-            break;
-        }
-    }
-    for (var i in el.childNodes) {
-        if (el.childNodes[i].nodeType == 1) { // element node?
-            init_auto_l10n(el.childNodes[i]); // recurse!
-        }
-    }
-}
-
-function get_keys(L) { var K = []; for (var k in L) K.push(k); return K; }
-function hide_dom_element(e) { e.style.display = "none"; };
-function reveal_dom_element(e) { e.style.display = ""; };
-function formal_name(u) {
-    var name = u.family_name() + ", " + u.first_given_name();
-    if (u.second_given_name())
-        name += (" " + u.second_given_name());
-    return name;
-}
-function humanize_timestamp_string(ts) {
-    /* For now, this discards time zones. */
-    var parts = ts.split("T");
-    var timeparts = parts[1].split("-")[0].split(":");
-    return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
-}
-function humanize_timestamp_string2(ts) {
-    /* For now, this discards time zones, too. */
-    var parts = ts.split(" ");
-    parts[1] = parts[1].replace(/[\-\+]\d+$/, "");
-    var timeparts = parts[1].split("-")[0].split(":");
-    return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
-}
-function is_ils_event(e) { return (e.ilsevent != undefined); }
-function is_ils_actor_card_error(e) {
-    return (e.textcode == "ACTOR_CARD_NOT_FOUND");
-}
-function my_ils_error(leader, e) {
-    var s = leader + "\n";
-    var keys = [
-        "ilsevent", "desc", "textcode", "servertime", "pid", "stacktrace"
-    ];
-    for (var i in keys) {
-        if (e[keys[i]]) s += ("\t" + keys[i] + ": " + e[keys[i]] + "\n");
-    }
-    return s;
-}
-function set_datagrid_empty_store(grid, flattener) {
-    grid.setStore(
-        new dojo.data.ItemFileReadStore(
-            {"data": flattener([])}
-        )
-    );
-}
+});
\ No newline at end of file
index 1eeb7cb..61686e4 100644 (file)
-/* This module depends on common.js being loaded, as well as the
- * localization (Dojo/nls) for pickup and return . */
+require([
+       "dojo/data/ItemFileReadStore",
+       "dojo/date/locale",
+       "openils/PermaCrud",
+       "dojo/string"
+       ],
+function(dojo_data_ItemFileReadStore,
+       dojo_date_locale,
+       openils_PermaCrud,
+       dojo_string){
+       /* This module depends on common.js being loaded, as well as the
+        * localization (Dojo/nls) for pickup and return . */
+       
+       
+       function Populator(widgets, primary_input) {
+           this.widgets = widgets;
+       
+           this.all = [];
+           for (var k in widgets) this.all.push(k);
+       
+           if (primary_input) this.primary_input = primary_input;
+       
+           this.prepare_cache();
+           this.prepare_empty_stores();
+           this.reset();
+       }
+       Populator.prototype.prepare_cache = function(data) {
+           this.cache = {};
+           for (var k in this.all) this.cache[this.all[k]] = {};
+       };
+       Populator.prototype.prepare_empty_stores = function(data) {
+           this.empty_stores = {};
+       
+           for (var i in this.all) {
+               var name = this.all[i];
+       
+               if (this.widgets[name] && this["flatten_" + name]) {
+                   this.empty_stores[name] =
+                       new dojo_data_ItemFileReadStore({
+                           "data": this["flatten_" + name]([])
+                       });
+                   this.widgets[name].setStore(this.empty_stores[name]);
+               }
+           }
+       };
+       Populator.prototype.flatten_ready = function(data) {
+           return {
+               "label": "id",
+               "identifier": "id",
+               "items": data.map(function(o) {
+                   return {
+                       "id": o.id(),
+                       "type": o.target_resource_type().name(),
+                       "resource": o.current_resource().barcode(),
+                       "start_time": humanize_timestamp_string(o.start_time()),
+                       "end_time": humanize_timestamp_string(o.end_time())
+                   };
+               })
+           };
+       };
+       Populator.prototype.flatten_out = function(data) {
+           return {
+               "label": "id",
+               "identifier": "id",
+               "items": data.map(function(o) {
+                   return {
+                       "id": o.id(),
+                       "type": o.target_resource_type().name(),
+                       "resource": o.current_resource().barcode(),
+                       "pickup_time": humanize_timestamp_string(o.pickup_time()),
+                       "end_time": humanize_timestamp_string(o.end_time())
+                   };
+               })
+           };
+       };
+       Populator.prototype.flatten_in = function(data) {
+           return {
+               "label": "id",
+               "identifier": "id",
+               "items": data.map(function(o) {
+                   return {
+                       "id": o.id(),
+                       "type": o.target_resource_type().name(),
+                       "resource": o.current_resource().barcode(),
+                       "due_time": humanize_timestamp_string(o.end_time()),
+                       "return_time": humanize_timestamp_string(o.return_time())
+                   };
+               })
+           };
+       };
+       Populator.prototype.reveal_container = function(widget) {
+           var el = document.getElementById("contains_" + widget.id);
+           if (el) reveal_dom_element(el);
+       };
+       Populator.prototype.hide_container = function(widget) {
+           var el = document.getElementById("contains_" + widget.id);
+           if (el) hide_dom_element(el);
+       };
+       Populator.prototype.populate_ready = function(data) {
+           return this._populate_any_resv_grid(data, "ready");
+       };
+       Populator.prototype.populate_out = function(data) {
+           return this._populate_any_resv_grid(data, "out");
+       };
+       Populator.prototype.populate_in = function(data) {
+           return this._populate_any_resv_grid(data, "in");
+       };
+       Populator.prototype._populate_any_resv_grid = function(data, which) {
+           var flattener = this["flatten_" + which];
+           var widget = this.widgets[which];
+           var cache = this.cache[which];
+           var empty_store = this.empty_stores[which];
+       
+           this.reveal_container(widget);
+       
+           if (!data || !data.length) {
+               widget.setStore(empty_store);
+               this.toggle_anyness(false, which);
+           } else {
+               for (var i in data) cache[data[i].id()] = data[i];
+       
+               widget.setStore(
+                   new dojo_data_ItemFileReadStore({"data": flattener(data)})
+               );
+       
+               this.toggle_anyness(true, which);
+       
+               /* Arrrgh! Horrid but necessary: */
+               setTimeout(function() { widget.sort(); }, 100);
+           }
+       };
+       Populator.prototype.populate_patron = function(data) {
+           var h2 = document.createElement("h2");
+           h2.setAttribute("class", "booking");
+           h2.appendChild(document.createTextNode(formal_name(data)));
+       
+           this.widgets.patron.innerHTML = "";
+           this.widgets.patron.appendChild(h2);
+       
+           this.reveal_container(this.widgets.patron);
+           /* Maybe add patron's home OU or something here later... */
+       };
+       Populator.prototype.return_by_resource = function(barcode, override) {
+           /* XXX instead of talking to the server every time we do this, we could
+            * also check the "out" cache, iff we have one.  */
+           var r = fieldmapper.standardRequest(
+               ["open-ils.booking",
+               "open-ils.booking.reservations.by_returnable_resource_barcode"],
+               [openils.User.authtoken, barcode]
+           );
+           if (!r || r.length < 1) {
+               alert(localeStrings.NO_SUCH_RETURNABLE_RESOURCE);
+           } else if (is_ils_event(r)) {
+               alert(my_ils_error(localeStrings.RETURNABLE_RESOURCE_ERROR, r));
+           } else {
+               try {
+                   var new_barcode = r.usr().card().barcode();
+               } catch (E) {
+                   alert(localeStrings.RETURN_ERROR + "\nr: " + js2JSON(r) + "\n" + E);
+                   return;
+               }
+               if (this.patron_barcode && this.patron_barcode != new_barcode) {
+                   /* XXX make this more subtle, i.e. flash something in background */
+                   alert(localeStrings.NOTICE_CHANGE_OF_PATRON);
+               }
+               this.patron_barcode = new_barcode;
+               var ret = this.return(r, override);
+               if (!ret) {
+                   alert(localeStrings.RETURN_NO_RESPONSE);
+               } else if (is_ils_event(ret) && ret.textcode != "SUCCESS") {
+                   if (ret.textcode == "ROUTE_ITEM") {
+                       display_transit_slip(ret);
+                   } else if (ret.textcode == "COPY_ALERT_MESSAGE") {
+                       if (
+                           confirm(
+                               dojo_string.substitute(
+                                   localeStrings.COPY_ALERT, [ret.desc, ret.payload]
+                              )
+                           )
+                       ) {
+                           this.return_by_resource(barcode, true /*override*/);
+                           return;
+                       }
+                   } else {
+                       alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
+                   }
+               } else {
+                   /* XXX speedbump should go, but something has to happen else
+                    * there's no indication to staff that anything happened when
+                    * starting from a fresh (blank) return interface.
+                    */
+                   alert(localeStrings.RETURN_SUCCESS);
+               }
+               this.populate(); /* Won't recurse with no args. All is well. */
+           }
+       };
+       Populator.prototype.populate = function(barcode, which) {
+           if (barcode) {
+               if (barcode.patron) {
+                   this.patron_barcode = barcode.patron;
+               }
+               else if (barcode.resource) { /* resource OR patron, not both */
+                   if (!this.return_by_resource(barcode.resource))
+                       return;
+               }
+           }
+           if (!this.patron_barcode) {
+               alert(localeStrings.NO_PATRON_BARCODE);
+               return;
+           }
+       
+           if (!which) which = this.all;
+       
+           var result = fieldmapper.standardRequest(
+               ["open-ils.booking", "open-ils.booking.reservations.get_captured"],
+               [openils.User.authtoken, this.patron_barcode, which]
+           );
+       
+           if (!result) {
+               this.patron_barcode = undefined;
+               alert(localeStrings.RESERVATIONS_NO_RESPONSE);
+           } else if (is_ils_event(result)) {
+               this.patron_barcode = undefined;
+               alert(my_ils_error(localeStrings.RESERVATIONS_ERROR, result));
+           } else {
+               for (var k in result)
+                   this["populate_" + k](result[k]);
+           }
+       };
+       Populator.prototype.toggle_anyness = function(any, which) {
+           var widget = this.widgets[which].domNode;
+           var empty_alternate = document.getElementById("no_" + widget.id);
+           var controls = document.getElementById("controls_" + widget.id);
+           if (any) {
+               reveal_dom_element(widget);
+               if (empty_alternate) hide_dom_element(empty_alternate);
+               if (controls) reveal_dom_element(controls);
+           } else {
+               hide_dom_element(widget);
+               if (empty_alternate) reveal_dom_element(empty_alternate);
+               if (controls) hide_dom_element(controls);
+           }
+       };
+       Populator.prototype.pickup = function(reservation) {
+           return fieldmapper.standardRequest(
+               ["open-ils.circ", "open-ils.circ.reservation.pickup"],
+               [openils.User.authtoken, {
+                   "patron_barcode": this.patron_barcode,
+                   "reservation": reservation
+               }]
+           );
+       };
+       Populator.prototype.return = function(reservation, override) {
+           var method = "open-ils.circ.reservation.return";
+           if (override) method += ".override";
+           return fieldmapper.standardRequest(
+               ["open-ils.circ", method],
+               [openils.User.authtoken, {
+                   "patron_barcode": this.patron_barcode,
+                   "reservation": reservation.id()
+                   /* yeah just id here ------^; lack of parallelism */
+               }]
+           );
+       };
+       Populator.prototype.act_on_selected = function(how, which) {
+           var widget = this.widgets[which];
+           var cache = this.cache[which];
+           var no_response_msg = localeStrings[how.toUpperCase() + "_NO_RESPONSE"];
+           var error_msg = localeStrings[how.toUpperCase() + "_ERROR"];
+       
+           var selected_id_list =
+               widget.selection.getSelected().map(function(o) { return o.id[0]; });
+       
+           if (!selected_id_list || !selected_id_list.length) {
+               alert(localeStrings.SELECT_SOMETHING);
+               return;
+           }
+       
+           var reservations = selected_id_list.map(function(o) { return cache[o]; });
+       
+           /* Do we have to process these one at a time?  I think so... */
+           var self = this;
+           function looper(reservation, override) {
+               if (looper._done) return;
+               var result = self[how](reservation, override);
+               if (!result) {
+                   alert(no_response_msg);
+               } else if (is_ils_event(result) && result.textcode != "SUCCESS") {
+                   if (result.textcode == "ROUTE_ITEM") {
+                       display_transit_slip(result);
+                   } else if (result.textcode == "COPY_ALERT_MESSAGE") {
+                       if (confirm(
+                           dojo_string.substitute(
+                               localeStrings.COPY_ALERT, [result.desc, result.payload]
+                          )
+                       )) {
+                           looper(reservation, true);
+                       }
+                       return; // continues processing other resvs
+                   } else {
+                       alert(my_ils_error(error_msg, result));
+                   }
+               } else {
+                   return;
+               }
+               looper._done = true;
+           }
+           dojo.forEach(reservations, looper);
+       
+           this.populate();
+       };
+       Populator.prototype.reset = function() {
+           for (var k in this.widgets) {
+               this.hide_container(this.widgets[k]);
+           }
+           this.patron_barcode = undefined;
+       
+           if (typeof(this._extra_resetting) == "function")
+               this._extra_resetting();
+       
+           if (this.primary_input) {
+               this.primary_input.value = "";
+               this.primary_input.focus();
+           }
+       };
+       
+       /* XXX needs to be combined with the code that shows transit slips in the
+        * booking capture interface. */
+       function display_transit_slip(e) {
+           var ou = fieldmapper.aou.findOrgUnit(e.org, /* slim_ok */false);
+           var ma = (new openils_PermaCrud()).retrieve("aoa", ou.mailing_address());
+           var mas = ma ?
+               dojo_string.substitute(
+                   localeStrings.ADDRESS,
+                   [ma.street1(),ma.street2(),ma.city(),ma.state(),ma.post_code()].map(
+                       function(o) { return o ? o : ""; }
+                   )
+               ).replace("\n\n", "\n").replace("\n", "<br />") : "[Unknown address]";
+           /* XXX i18n and/or template */
+           try {
+               var win = window.open(
+                   "","","resizeable,width=600,height=400,scrollbars=1"
+               );
+               win.document.body.innerHTML =
+                   "<h1>Transit Slip</h1>\n" +
+                   //"<img src='/xul/server/skin/media/images/turtle.gif' />\n" +
+                   "<p>Destination: <strong>" + ou.name() + "</strong></p>\n" +
+                   "<p>" + mas + "</p>\n" +
+                   "<p>Barcode: " + e.payload.copy.barcode() + "<br />\n" +
+                   "Title: <span id='title'></span><br />\n" +
+                   "Author: <span id='author'></span><br />\n" +
+                   "Slip Date: " +
+                       dojo_date_locale.format(new Date(), {"formatLength": "short"}) +
+                   "</p>";
+               fieldmapper.standardRequest(
+                   ["open-ils.search", "open-ils.search.biblio.mods_from_copy"], {
+                       "params": [e.payload.copy.id()],
+                       "async": true,
+                       "onresponse": function(r) {
+                           var mvr = openils.Util.readResponse(r);
+                           dojo.byId("title", win.document).innerHTML = mvr.title();
+                           dojo.byId("author", win.document).innerHTML = mvr.author();
+                       },
+                       "oncomplete": function() {
+                           win[confirm("Print transit slip?") ? "print" : "close"]();
+                       }
+                   }
+               );
+           } catch (E) {
+               alert("exception rendering transit slip: " + E); // XXX
+           }
+       }
+       
 
-dojo.require("dojo.data.ItemFileReadStore");
-dojo.require("dojo.date.locale");
-dojo.require("openils.PermaCrud");
-dojo.require("dojo.string");
-
-function Populator(widgets, primary_input) {
-    this.widgets = widgets;
-
-    this.all = [];
-    for (var k in widgets) this.all.push(k);
-
-    if (primary_input) this.primary_input = primary_input;
-
-    this.prepare_cache();
-    this.prepare_empty_stores();
-    this.reset();
-}
-Populator.prototype.prepare_cache = function(data) {
-    this.cache = {};
-    for (var k in this.all) this.cache[this.all[k]] = {};
-};
-Populator.prototype.prepare_empty_stores = function(data) {
-    this.empty_stores = {};
-
-    for (var i in this.all) {
-        var name = this.all[i];
-
-        if (this.widgets[name] && this["flatten_" + name]) {
-            this.empty_stores[name] =
-                new dojo.data.ItemFileReadStore({
-                    "data": this["flatten_" + name]([])
-                });
-            this.widgets[name].setStore(this.empty_stores[name]);
-        }
-    }
-};
-Populator.prototype.flatten_ready = function(data) {
-    return {
-        "label": "id",
-        "identifier": "id",
-        "items": data.map(function(o) {
-            return {
-                "id": o.id(),
-                "type": o.target_resource_type().name(),
-                "resource": o.current_resource().barcode(),
-                "start_time": humanize_timestamp_string(o.start_time()),
-                "end_time": humanize_timestamp_string(o.end_time())
-            };
-        })
-    };
-};
-Populator.prototype.flatten_out = function(data) {
-    return {
-        "label": "id",
-        "identifier": "id",
-        "items": data.map(function(o) {
-            return {
-                "id": o.id(),
-                "type": o.target_resource_type().name(),
-                "resource": o.current_resource().barcode(),
-                "pickup_time": humanize_timestamp_string(o.pickup_time()),
-                "end_time": humanize_timestamp_string(o.end_time())
-            };
-        })
-    };
-};
-Populator.prototype.flatten_in = function(data) {
-    return {
-        "label": "id",
-        "identifier": "id",
-        "items": data.map(function(o) {
-            return {
-                "id": o.id(),
-                "type": o.target_resource_type().name(),
-                "resource": o.current_resource().barcode(),
-                "due_time": humanize_timestamp_string(o.end_time()),
-                "return_time": humanize_timestamp_string(o.return_time())
-            };
-        })
-    };
-};
-Populator.prototype.reveal_container = function(widget) {
-    var el = document.getElementById("contains_" + widget.id);
-    if (el) reveal_dom_element(el);
-};
-Populator.prototype.hide_container = function(widget) {
-    var el = document.getElementById("contains_" + widget.id);
-    if (el) hide_dom_element(el);
-};
-Populator.prototype.populate_ready = function(data) {
-    return this._populate_any_resv_grid(data, "ready");
-};
-Populator.prototype.populate_out = function(data) {
-    return this._populate_any_resv_grid(data, "out");
-};
-Populator.prototype.populate_in = function(data) {
-    return this._populate_any_resv_grid(data, "in");
-};
-Populator.prototype._populate_any_resv_grid = function(data, which) {
-    var flattener = this["flatten_" + which];
-    var widget = this.widgets[which];
-    var cache = this.cache[which];
-    var empty_store = this.empty_stores[which];
-
-    this.reveal_container(widget);
-
-    if (!data || !data.length) {
-        widget.setStore(empty_store);
-        this.toggle_anyness(false, which);
-    } else {
-        for (var i in data) cache[data[i].id()] = data[i];
-
-        widget.setStore(
-            new dojo.data.ItemFileReadStore({"data": flattener(data)})
-        );
-
-        this.toggle_anyness(true, which);
-
-        /* Arrrgh! Horrid but necessary: */
-        setTimeout(function() { widget.sort(); }, 100);
-    }
-};
-Populator.prototype.populate_patron = function(data) {
-    var h2 = document.createElement("h2");
-    h2.setAttribute("class", "booking");
-    h2.appendChild(document.createTextNode(formal_name(data)));
-
-    this.widgets.patron.innerHTML = "";
-    this.widgets.patron.appendChild(h2);
-
-    this.reveal_container(this.widgets.patron);
-    /* Maybe add patron's home OU or something here later... */
-};
-Populator.prototype.return_by_resource = function(barcode, override) {
-    /* XXX instead of talking to the server every time we do this, we could
-     * also check the "out" cache, iff we have one.  */
-    var r = fieldmapper.standardRequest(
-        ["open-ils.booking",
-        "open-ils.booking.reservations.by_returnable_resource_barcode"],
-        [openils.User.authtoken, barcode]
-    );
-    if (!r || r.length < 1) {
-        alert(localeStrings.NO_SUCH_RETURNABLE_RESOURCE);
-    } else if (is_ils_event(r)) {
-        alert(my_ils_error(localeStrings.RETURNABLE_RESOURCE_ERROR, r));
-    } else {
-        try {
-            var new_barcode = r.usr().card().barcode();
-        } catch (E) {
-            alert(localeStrings.RETURN_ERROR + "\nr: " + js2JSON(r) + "\n" + E);
-            return;
-        }
-        if (this.patron_barcode && this.patron_barcode != new_barcode) {
-            /* XXX make this more subtle, i.e. flash something in background */
-            alert(localeStrings.NOTICE_CHANGE_OF_PATRON);
-        }
-        this.patron_barcode = new_barcode;
-        var ret = this.return(r, override);
-        if (!ret) {
-            alert(localeStrings.RETURN_NO_RESPONSE);
-        } else if (is_ils_event(ret) && ret.textcode != "SUCCESS") {
-            if (ret.textcode == "ROUTE_ITEM") {
-                display_transit_slip(ret);
-            } else if (ret.textcode == "COPY_ALERT_MESSAGE") {
-                if (
-                    confirm(
-                        dojo.string.substitute(
-                            localeStrings.COPY_ALERT, [ret.desc, ret.payload]
-                       )
-                    )
-                ) {
-                    this.return_by_resource(barcode, true /*override*/);
-                    return;
-                }
-            } else {
-                alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
-            }
-        } else {
-            /* XXX speedbump should go, but something has to happen else
-             * there's no indication to staff that anything happened when
-             * starting from a fresh (blank) return interface.
-             */
-            alert(localeStrings.RETURN_SUCCESS);
-        }
-        this.populate(); /* Won't recurse with no args. All is well. */
-    }
-};
-Populator.prototype.populate = function(barcode, which) {
-    if (barcode) {
-        if (barcode.patron) {
-            this.patron_barcode = barcode.patron;
-        }
-        else if (barcode.resource) { /* resource OR patron, not both */
-            if (!this.return_by_resource(barcode.resource))
-                return;
-        }
-    }
-    if (!this.patron_barcode) {
-        alert(localeStrings.NO_PATRON_BARCODE);
-        return;
-    }
-
-    if (!which) which = this.all;
-
-    var result = fieldmapper.standardRequest(
-        ["open-ils.booking", "open-ils.booking.reservations.get_captured"],
-        [openils.User.authtoken, this.patron_barcode, which]
-    );
-
-    if (!result) {
-        this.patron_barcode = undefined;
-        alert(localeStrings.RESERVATIONS_NO_RESPONSE);
-    } else if (is_ils_event(result)) {
-        this.patron_barcode = undefined;
-        alert(my_ils_error(localeStrings.RESERVATIONS_ERROR, result));
-    } else {
-        for (var k in result)
-            this["populate_" + k](result[k]);
-    }
-};
-Populator.prototype.toggle_anyness = function(any, which) {
-    var widget = this.widgets[which].domNode;
-    var empty_alternate = document.getElementById("no_" + widget.id);
-    var controls = document.getElementById("controls_" + widget.id);
-    if (any) {
-        reveal_dom_element(widget);
-        if (empty_alternate) hide_dom_element(empty_alternate);
-        if (controls) reveal_dom_element(controls);
-    } else {
-        hide_dom_element(widget);
-        if (empty_alternate) reveal_dom_element(empty_alternate);
-        if (controls) hide_dom_element(controls);
-    }
-};
-Populator.prototype.pickup = function(reservation) {
-    return fieldmapper.standardRequest(
-        ["open-ils.circ", "open-ils.circ.reservation.pickup"],
-        [openils.User.authtoken, {
-            "patron_barcode": this.patron_barcode,
-            "reservation": reservation
-        }]
-    );
-};
-Populator.prototype.return = function(reservation, override) {
-    var method = "open-ils.circ.reservation.return";
-    if (override) method += ".override";
-    return fieldmapper.standardRequest(
-        ["open-ils.circ", method],
-        [openils.User.authtoken, {
-            "patron_barcode": this.patron_barcode,
-            "reservation": reservation.id()
-            /* yeah just id here ------^; lack of parallelism */
-        }]
-    );
-};
-Populator.prototype.act_on_selected = function(how, which) {
-    var widget = this.widgets[which];
-    var cache = this.cache[which];
-    var no_response_msg = localeStrings[how.toUpperCase() + "_NO_RESPONSE"];
-    var error_msg = localeStrings[how.toUpperCase() + "_ERROR"];
-
-    var selected_id_list =
-        widget.selection.getSelected().map(function(o) { return o.id[0]; });
-
-    if (!selected_id_list || !selected_id_list.length) {
-        alert(localeStrings.SELECT_SOMETHING);
-        return;
-    }
-
-    var reservations = selected_id_list.map(function(o) { return cache[o]; });
-
-    /* Do we have to process these one at a time?  I think so... */
-    var self = this;
-    function looper(reservation, override) {
-        if (looper._done) return;
-        var result = self[how](reservation, override);
-        if (!result) {
-            alert(no_response_msg);
-        } else if (is_ils_event(result) && result.textcode != "SUCCESS") {
-            if (result.textcode == "ROUTE_ITEM") {
-                display_transit_slip(result);
-            } else if (result.textcode == "COPY_ALERT_MESSAGE") {
-                if (confirm(
-                    dojo.string.substitute(
-                        localeStrings.COPY_ALERT, [result.desc, result.payload]
-                   )
-                )) {
-                    looper(reservation, true);
-                }
-                return; // continues processing other resvs
-            } else {
-                alert(my_ils_error(error_msg, result));
-            }
-        } else {
-            return;
-        }
-        looper._done = true;
-    }
-    dojo.forEach(reservations, looper);
-
-    this.populate();
-};
-Populator.prototype.reset = function() {
-    for (var k in this.widgets) {
-        this.hide_container(this.widgets[k]);
-    }
-    this.patron_barcode = undefined;
-
-    if (typeof(this._extra_resetting) == "function")
-        this._extra_resetting();
-
-    if (this.primary_input) {
-        this.primary_input.value = "";
-        this.primary_input.focus();
-    }
-};
-
-/* XXX needs to be combined with the code that shows transit slips in the
- * booking capture interface. */
-function display_transit_slip(e) {
-    var ou = fieldmapper.aou.findOrgUnit(e.org, /* slim_ok */false);
-    var ma = (new openils.PermaCrud()).retrieve("aoa", ou.mailing_address());
-    var mas = ma ?
-        dojo.string.substitute(
-            localeStrings.ADDRESS,
-            [ma.street1(),ma.street2(),ma.city(),ma.state(),ma.post_code()].map(
-                function(o) { return o ? o : ""; }
-            )
-        ).replace("\n\n", "\n").replace("\n", "<br />") : "[Unknown address]";
-    /* XXX i18n and/or template */
-    try {
-        var win = window.open(
-            "","","resizeable,width=600,height=400,scrollbars=1"
-        );
-        win.document.body.innerHTML =
-            "<h1>Transit Slip</h1>\n" +
-            //"<img src='/xul/server/skin/media/images/turtle.gif' />\n" +
-            "<p>Destination: <strong>" + ou.name() + "</strong></p>\n" +
-            "<p>" + mas + "</p>\n" +
-            "<p>Barcode: " + e.payload.copy.barcode() + "<br />\n" +
-            "Title: <span id='title'></span><br />\n" +
-            "Author: <span id='author'></span><br />\n" +
-            "Slip Date: " +
-                dojo.date.locale.format(new Date(), {"formatLength": "short"}) +
-            "</p>";
-        fieldmapper.standardRequest(
-            ["open-ils.search", "open-ils.search.biblio.mods_from_copy"], {
-                "params": [e.payload.copy.id()],
-                "async": true,
-                "onresponse": function(r) {
-                    var mvr = openils.Util.readResponse(r);
-                    dojo.byId("title", win.document).innerHTML = mvr.title();
-                    dojo.byId("author", win.document).innerHTML = mvr.author();
-                },
-                "oncomplete": function() {
-                    win[confirm("Print transit slip?") ? "print" : "close"]();
-                }
-            }
-        );
-    } catch (E) {
-        alert("exception rendering transit slip: " + E); // XXX
-    }
-}
+});
\ No newline at end of file
index 831a9de..978b7be 100644 (file)
-dojo.require("openils.User");
-dojo.require("openils.PermaCrud");
-dojo.require("fieldmapper.OrgUtils");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
-dojo.requireLocalization("openils.booking", "pull_list");
-
-var localeStrings = dojo.i18n.getLocalization("openils.booking", "pull_list");
-var pcrud = new openils.PermaCrud();
-
-var owning_lib_selected;
-var acp_cache = {};
-
-function init_owning_lib_selector() {
-    var User = new openils.User();
-    User.buildPermOrgSelector(
-        "RETRIEVE_RESERVATION_PULL_LIST", owning_lib_selector, null,
-        function() {
-            owning_lib_selected = owning_lib_selector.getValue();
-            dojo.connect(owning_lib_selector, "onChange",
-                function() { owning_lib_selected = this.getValue(); }
-            )
-        }
-    );
-}
-
-function retrieve_pull_list(ivl_in_days) {
-    var secs = Number(ivl_in_days) * 86400;
-
-    if (isNaN(secs) || secs < 1)
-        throw new Error("Invalid interval");
-
-    return fieldmapper.standardRequest(
-        ["open-ils.booking", "open-ils.booking.reservations.get_pull_list"],
-        [openils.User.authtoken, null, secs, owning_lib_selected]
-    );
-}
-
-function dom_table_rowid(resource_id) {
-    return "pull_list_resource_" + resource_id;
-}
-
-function generate_result_row(one) {
-    function cell(id, content) {
-        var td = document.createElement("td");
-        if (id != undefined) td.setAttribute("id", id);
-        td.appendChild(document.createTextNode(content));
-        return td;
-    }
-
-    function render_pickup_lib(pickup_lib) {
-        var span = document.createElement("span");
-        if (pickup_lib != owning_lib_selected)
-            span.setAttribute("class", "pull_list_will_transit");
-        span.innerHTML = localeStrings.AT + " " +
-            fieldmapper.aou.findOrgUnit(pickup_lib).shortname();
-        return span;
-    }
-
-    function reservation_info_cell(one) {
-        var td = document.createElement("td");
-        for (var i in one.reservations) {
-            var one_resv = one.reservations[i];
-            var div = document.createElement("div");
-            div.setAttribute("class", "pull_list_resv_detail");
-            var content = [
-                document.createTextNode(
-                    humanize_timestamp_string(one_resv.start_time()) +
-                    " - " + humanize_timestamp_string(one_resv.end_time())
-                ),
-                document.createElement("br"),
-                render_pickup_lib(one_resv.pickup_lib()),
-                document.createTextNode(
-                    " " + localeStrings.FOR + " " + formal_name(one_resv.usr())
-                )
-            ];
-            for (var k in content) { div.appendChild(content[k]); }
-            td.appendChild(div);
-        }
-        return td;
-    }
-
-    var baseid = dom_table_rowid(one.current_resource.id());
-
-    var cells = [];
-    cells.push(cell(undefined, one.target_resource_type.name()));
-    cells.push(cell(undefined, one.current_resource.barcode()));
-    cells.push(cell(baseid + "_call_number", "-"));
-    cells.push(cell(baseid + "_copy_location", "-"));
-    cells.push(reservation_info_cell(one));
-
-    var row = document.createElement("tr");
-    row.setAttribute("id", baseid);
-
-    for (var i in cells) row.appendChild(cells[i]);
-    return row;
-}
-
-function render_pull_list_fundamentals(list) {
-    var rows = [];
-
-    for (var i in list)
-        rows.push(generate_result_row(list[i]));
-
-    document.getElementById("the_table_body").innerHTML = "";
-
-    for (var i in rows)
-        document.getElementById("the_table_body").appendChild(rows[i]);
-}
-
-function get_all_relevant_acp(list) {
-    var barcodes = [];
-    for (var i in list) {
-        if (list[i].target_resource_type.catalog_item()) {
-            /* There shouldn't be any duplicates. No need to worry bout that */
-            barcodes.push(list[i].current_resource.barcode());
-        }
-    }
-    if (barcodes.length > 0) {
-        var results = fieldmapper.standardRequest(
-            [
-                "open-ils.booking",
-                "open-ils.booking.asset.get_copy_fleshed_just_right"
-            ],
-            [openils.User.authtoken, barcodes]
-        );
-
-        if (!results) {
-            alert(localeStrings.COPY_LOOKUP_NO_RESPONSE);
-            return null;
-        } else if (is_ils_event(results)) {
-            alert(my_ils_error(localeStrings.COPY_LOOKUP_ERROR, results));
-            return null;
-        } else {
-            return results;
-        }
-    }
-    return null;
-}
-
-function fill_in_pull_list_details(list, acp_cache) {
-    for (var i in list) {
-        var one = list[i];
-        if (one.target_resource_type.catalog_item() == "t") {
-            /* FIXME: This block could stand to be a lot more elegant. */
-            var call_number_el = document.getElementById(
-                dom_table_rowid(one.current_resource.id()) + "_call_number"
-            );
-            var copy_location_el = document.getElementById(
-                dom_table_rowid(one.current_resource.id()) + "_copy_location"
-            );
-            var bc = one.current_resource.barcode();
-
-            if (acp_cache[bc]) {
-                if (call_number_el && acp_cache[bc].call_number()) {
-                    var value = acp_cache[bc].call_number().label();
-                    if (value) call_number_el.innerHTML = value;
-                }
-                if (copy_location_el && acp_cache[bc].location()) {
-                    var value = acp_cache[bc].location().name();
-                    if (value) copy_location_el.innerHTML = value;
-                }
-            } else {
-                alert(localeStrings.COPY_MISSING + bc);
-            }
-        }
-    }
-}
-
-function populate_pull_list(form) {
-    /* Step 1: get the pull list from the server. */
-    try {
-        var results = retrieve_pull_list(form.interval_in_days.value);
-    } catch (E) {
-        alert(localeStrings.PULL_LIST_ERROR + E);
-        return;
-    }
-    if (results == null) {
-        alert(localeStrings.PULL_LIST_NO_RESPONSE);
-        return;
-    } else if (is_ils_event(results)) {
-        alert(my_ils_error(localeStrings.PULL_LIST_ERROR, results));
-        return;
-    }
-
-    if (results.length) {
-        reveal_dom_element(document.getElementById("table_goes_here"));
-        hide_dom_element(document.getElementById("no_results"));
-
-        /* Step 2: render the table with the pull list */
-        render_pull_list_fundamentals(results);
-
-        /* Step 3: asynchronously fill in the copy details we're missing */
-        setTimeout(function() {
-            var acp_cache = {};
-            if ((acp_cache = get_all_relevant_acp(results)))
-                fill_in_pull_list_details(results, acp_cache);
-        }, 0);
-    } else {
-        hide_dom_element(document.getElementById("table_goes_here"));
-        reveal_dom_element(document.getElementById("no_results"));
-    }
-
-}
-
-function my_init() {
-    hide_dom_element(document.getElementById("table_goes_here"));
-    hide_dom_element(document.getElementById("no_results"));
-    init_owning_lib_selector();
-    init_auto_l10n(document.getElementById("auto_l10n_start_here"));
-}
+require([
+       "openils/User",
+       "openils/PermaCrud",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(openils_User,
+       openils_PermaCrud,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect){
+       dojo.requireLocalization("openils.booking", "pull_list");
+       
+       var localeStrings = dojo.i18n.getLocalization("openils.booking", "pull_list");
+       var pcrud = new openils_PermaCrud();
+       
+       var owning_lib_selected;
+       var acp_cache = {};
+       
+       function init_owning_lib_selector() {
+           var User = new openils_User();
+           User.buildPermOrgSelector(
+               "RETRIEVE_RESERVATION_PULL_LIST", owning_lib_selector, null,
+               function() {
+                   owning_lib_selected = owning_lib_selector.getValue();
+                   dojo.connect(owning_lib_selector, "onChange",
+                       function() { owning_lib_selected = this.getValue(); }
+                   )
+               }
+           );
+       }
+       
+       function retrieve_pull_list(ivl_in_days) {
+           var secs = Number(ivl_in_days) * 86400;
+       
+           if (isNaN(secs) || secs < 1)
+               throw new Error("Invalid interval");
+       
+           return fieldmapper.standardRequest(
+               ["open-ils.booking", "open-ils.booking.reservations.get_pull_list"],
+               [openils_User.authtoken, null, secs, owning_lib_selected]
+           );
+       }
+       
+       function dom_table_rowid(resource_id) {
+           return "pull_list_resource_" + resource_id;
+       }
+       
+       function generate_result_row(one) {
+           function cell(id, content) {
+               var td = document.createElement("td");
+               if (id != undefined) td.setAttribute("id", id);
+               td.appendChild(document.createTextNode(content));
+               return td;
+           }
+       
+           function render_pickup_lib(pickup_lib) {
+               var span = document.createElement("span");
+               if (pickup_lib != owning_lib_selected)
+                   span.setAttribute("class", "pull_list_will_transit");
+               span.innerHTML = localeStrings.AT + " " +
+                   fieldmapper.aou.findOrgUnit(pickup_lib).shortname();
+               return span;
+           }
+       
+           function reservation_info_cell(one) {
+               var td = document.createElement("td");
+               for (var i in one.reservations) {
+                   var one_resv = one.reservations[i];
+                   var div = document.createElement("div");
+                   div.setAttribute("class", "pull_list_resv_detail");
+                   var content = [
+                       document.createTextNode(
+                           humanize_timestamp_string(one_resv.start_time()) +
+                           " - " + humanize_timestamp_string(one_resv.end_time())
+                       ),
+                       document.createElement("br"),
+                       render_pickup_lib(one_resv.pickup_lib()),
+                       document.createTextNode(
+                           " " + localeStrings.FOR + " " + formal_name(one_resv.usr())
+                       )
+                   ];
+                   for (var k in content) { div.appendChild(content[k]); }
+                   td.appendChild(div);
+               }
+               return td;
+           }
+       
+           var baseid = dom_table_rowid(one.current_resource.id());
+       
+           var cells = [];
+           cells.push(cell(undefined, one.target_resource_type.name()));
+           cells.push(cell(undefined, one.current_resource.barcode()));
+           cells.push(cell(baseid + "_call_number", "-"));
+           cells.push(cell(baseid + "_copy_location", "-"));
+           cells.push(reservation_info_cell(one));
+       
+           var row = document.createElement("tr");
+           row.setAttribute("id", baseid);
+       
+           for (var i in cells) row.appendChild(cells[i]);
+           return row;
+       }
+       
+       function render_pull_list_fundamentals(list) {
+           var rows = [];
+       
+           for (var i in list)
+               rows.push(generate_result_row(list[i]));
+       
+           document.getElementById("the_table_body").innerHTML = "";
+       
+           for (var i in rows)
+               document.getElementById("the_table_body").appendChild(rows[i]);
+       }
+       
+       function get_all_relevant_acp(list) {
+           var barcodes = [];
+           for (var i in list) {
+               if (list[i].target_resource_type.catalog_item()) {
+                   /* There shouldn't be any duplicates. No need to worry bout that */
+                   barcodes.push(list[i].current_resource.barcode());
+               }
+           }
+           if (barcodes.length > 0) {
+               var results = fieldmapper.standardRequest(
+                   [
+                       "open-ils.booking",
+                       "open-ils.booking.asset.get_copy_fleshed_just_right"
+                   ],
+                   [openils_User.authtoken, barcodes]
+               );
+       
+               if (!results) {
+                   alert(localeStrings.COPY_LOOKUP_NO_RESPONSE);
+                   return null;
+               } else if (is_ils_event(results)) {
+                   alert(my_ils_error(localeStrings.COPY_LOOKUP_ERROR, results));
+                   return null;
+               } else {
+                   return results;
+               }
+           }
+           return null;
+       }
+       
+       function fill_in_pull_list_details(list, acp_cache) {
+           for (var i in list) {
+               var one = list[i];
+               if (one.target_resource_type.catalog_item() == "t") {
+                   /* FIXME: This block could stand to be a lot more elegant. */
+                   var call_number_el = document.getElementById(
+                       dom_table_rowid(one.current_resource.id()) + "_call_number"
+                   );
+                   var copy_location_el = document.getElementById(
+                       dom_table_rowid(one.current_resource.id()) + "_copy_location"
+                   );
+                   var bc = one.current_resource.barcode();
+       
+                   if (acp_cache[bc]) {
+                       if (call_number_el && acp_cache[bc].call_number()) {
+                           var value = acp_cache[bc].call_number().label();
+                           if (value) call_number_el.innerHTML = value;
+                       }
+                       if (copy_location_el && acp_cache[bc].location()) {
+                           var value = acp_cache[bc].location().name();
+                           if (value) copy_location_el.innerHTML = value;
+                       }
+                   } else {
+                       alert(localeStrings.COPY_MISSING + bc);
+                   }
+               }
+           }
+       }
+       
+       function populate_pull_list(form) {
+           /* Step 1: get the pull list from the server. */
+           try {
+               var results = retrieve_pull_list(form.interval_in_days.value);
+           } catch (E) {
+               alert(localeStrings.PULL_LIST_ERROR + E);
+               return;
+           }
+           if (results == null) {
+               alert(localeStrings.PULL_LIST_NO_RESPONSE);
+               return;
+           } else if (is_ils_event(results)) {
+               alert(my_ils_error(localeStrings.PULL_LIST_ERROR, results));
+               return;
+           }
+       
+           if (results.length) {
+               reveal_dom_element(document.getElementById("table_goes_here"));
+               hide_dom_element(document.getElementById("no_results"));
+       
+               /* Step 2: render the table with the pull list */
+               render_pull_list_fundamentals(results);
+       
+               /* Step 3: asynchronously fill in the copy details we're missing */
+               setTimeout(function() {
+                   var acp_cache = {};
+                   if ((acp_cache = get_all_relevant_acp(results)))
+                       fill_in_pull_list_details(results, acp_cache);
+               }, 0);
+           } else {
+               hide_dom_element(document.getElementById("table_goes_here"));
+               reveal_dom_element(document.getElementById("no_results"));
+           }
+       
+       }
+       
+       function my_init() {
+           hide_dom_element(document.getElementById("table_goes_here"));
+           hide_dom_element(document.getElementById("no_results"));
+           init_owning_lib_selector();
+           init_auto_l10n(document.getElementById("auto_l10n_start_here"));
+       }
+       
+
+});
\ No newline at end of file
index fcaf572..67abbc2 100644 (file)
-/*
- * Details, details...
- */
-dojo.require("fieldmapper.OrgUtils");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.User");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
-dojo.require("dojo.data.ItemFileReadStore");
-dojo.require("dijit.form.DateTextBox");
-dojo.require("dijit.form.TimeTextBox");
-dojo.require("dojo.date.stamp");
-dojo.requireLocalization("openils.booking", "reservation");
-
-/*
- * Globals; prototypes and their instances
- */
-var localeStrings = dojo.i18n.getLocalization("openils.booking", "reservation");
-var pcrud = new openils.PermaCrud();
-var opts;
-var our_brt;
-var pickup_lib_selected;
-var brt_list = [];
-var brsrc_index = {};
-var bresv_index = {};
-var just_reserved_now = {};
-var aous_cache = {};
-
-function AttrValueTable() { this.t = {}; }
-AttrValueTable.prototype.set = function(attr, value) { this.t[attr] = value; };
-AttrValueTable.prototype.update_from_selector = function(selector) {
-    var attr  = selector.name.match(/_(\d+)$/)[1];
-    var value = selector.options[selector.selectedIndex].value;
-    if (attr)
-        attr_value_table.set(attr, value);
-};
-AttrValueTable.prototype.get_all_values = function() {
-    var values = [];
-    for (var k in this.t) {
-        if (this.t[k] != undefined && this.t[k] != "")
-            values.push(this.t[k]);
-    }
-    return values;
-};
-var attr_value_table =  new AttrValueTable();
-
-function TimestampRange() {
-    this.start = new Date();
-    this.end = new Date();
-
-    this.validity = {"start": false, "end": false};
-    this.nodes = {
-        "start": {"date": undefined, "time": undefined},
-        "end": {"date": undefined, "time": undefined}
-    };
-    this.saved_style_properties = {};
-    this.invalid_style_properties = {
-        "backgroundColor": "#ffcccc",
-        "color": "#990000",
-        "borderColor": "#990000",
-        "fontWeight": "bold"
-    };
-}
-TimestampRange.prototype.get_timestamp = function(when) {
-    return dojo.date.stamp.toISOString(this[when]).
-        replace("T", " ").substr(0, 19);
-};
-TimestampRange.prototype.get_range = function() {
-    return this.is_backwards() ?
-        [this.get_timestamp("end"), this.get_timestamp("start")] :
-        [this.get_timestamp("start"), this.get_timestamp("end")];
-};
-TimestampRange.prototype.update_from_widget = function(widget) {
-    var when = widget.name.match(/(start|end)/)[1];
-    var which = widget.name.match(/(date|time)/)[1];
-
-    if (this.nodes[when][which] == undefined)
-        this.nodes[when][which] = widget.domNode; /* We'll need this later */
-
-    if (when && which) {
-        this.update_timestamp(when, which, widget.attr("value"));
-    }
-
-    this.compute_validity();
-    this.paint_validity();
-};
-TimestampRange.prototype.compute_validity = function() {
-    if (Math.abs(this.start - this.end) < 1000) {
-        this.validity.end = false;
-    } else {
-        if (this.start < this.current_minimum())
-            this.validity.start = false;
-        else
-            this.validity.start = true;
-
-        if (this.end < this.current_minimum())
-            this.validity.end = false;
-        else
-            this.validity.end = true;
-    }
-};
-/* This method provides the minimum timestamp that is considered valid. For
- * now it's arbitrarily "now + 15 minutes", meaning that all reservations
- * must be made at least 15 minutes in the future.
- *
- * For reasons of keeping the middle layer happy, this should always return
- * a time that is at least somewhat in the future. The ML isn't able to target
- * any resources for a reservation with a start date that isn't in the future.
- */
-TimestampRange.prototype.current_minimum = function() {
-    /* XXX This is going to be a problem with local clocks that are off. */
-    var n = new Date();
-    n.setTime(n.getTime() + 1000 * 900); /* XXX 15 minutes; stop hardcoding! */
-    return n;
-};
-TimestampRange.prototype.update_timestamp = function(when, which, value) {
-    if (which == "date") {
-        this[when].setFullYear(value.getFullYear());
-        /* month and date MUST be done together */
-        this[when].setMonth(value.getMonth(), value.getDate());
-    } else {    /* "time" */
-        this[when].setHours(value.getHours());
-        this[when].setMinutes(value.getMinutes());
-        this[when].setSeconds(0);
-    }
-};
-TimestampRange.prototype.is_backwards = function() {
-    return (this.start > this.end);
-};
-TimestampRange.prototype.paint_validity = function()  {
-    for (var when in this.validity) {
-        if (this.validity[when]) {
-            this.paint_valid_node(this.nodes[when].date);
-            this.paint_valid_node(this.nodes[when].time);
-        } else {
-            this.paint_invalid_node(this.nodes[when].date);
-            this.paint_invalid_node(this.nodes[when].time);
-        }
-    }
-};
-TimestampRange.prototype.paint_invalid_node = function(node) {
-    if (node) {
-        /* Just toggling the class of something would be better than
-         * manually setting style here, but I haven't been able to get that
-         * to play nicely with dojo's styling of the date/time textboxen.
-         */
-        if (this.saved_style_properties.backgroundColor == undefined) {
-            for (var k in this.invalid_style_properties) {
-                this.saved_style_properties[k] = node.style[k];
-            }
-        }
-        for (var k in this.invalid_style_properties) {
-            node.style[k] = this.invalid_style_properties[k];
-        }
-    }
-};
-TimestampRange.prototype.paint_valid_node = function(node) {
-    if (node) {
-        for (var k in this.saved_style_properties) {
-            node.style[k] = this.saved_style_properties[k];
-        }
-    }
-};
-TimestampRange.prototype.is_valid = function() {
-    return (this.validity.start && this.validity.end);
-};
-var reserve_timestamp_range = new TimestampRange();
-
-function SelectorMemory(selector) {
-    this.selector = selector;
-    this.memory = {};
-}
-SelectorMemory.prototype.save = function() {
-    for (var i = 0; i < this.selector.options.length; i++) {
-        if (this.selector.options[i].selected) {
-            this.memory[this.selector.options[i].value] = true;
-        }
-    }
-};
-SelectorMemory.prototype.restore = function() {
-    for (var i = 0; i < this.selector.options.length; i++) {
-        if (this.memory[this.selector.options[i].value]) {
-            if (!this.selector.options[i].disabled)
-                this.selector.options[i].selected = true;
-        }
-    }
-};
-
-/*
- * These functions communicate with the middle layer.
- */
-function get_all_noncat_brt() {
-    return pcrud.search("brt",
-        {"id": {"!=": null}, "catalog_item": "f"},
-        {"order_by": {"brt":"name"}}
-    );
-}
-
-function get_brt_by_id(id) {
-    return pcrud.retrieve("brt", id);
-}
-
-function get_brsrc_id_list() {
-    var options = {"type": our_brt.id(), "pickup_lib": pickup_lib_selected};
-
-    /* This mechanism for avoiding the passing of an empty 'attribute_values'
-     * option is essential because if you pass such an option to the
-     * middle layer API at all, it won't return any IDs for brsrcs that
-     * don't have at least one attribute of some kind.
-     */
-    var attribute_values = attr_value_table.get_all_values();
-    if (attribute_values.length > 0)
-        options.attribute_values = attribute_values;
-
-    options.available = reserve_timestamp_range.get_range();
-
-    return fieldmapper.standardRequest(
-        ["open-ils.booking", "open-ils.booking.resources.filtered_id_list"],
-        [openils.User.authtoken, options]
-    );
-}
-
-/* FIXME: We need failure checking after pcrud.retrieve() */
-function add_brsrc_to_index_if_needed(list, further) {
-    for (var i in list) {
-        if (!brsrc_index[list[i]]) {
-            brsrc_index[list[i]] = pcrud.retrieve("brsrc", list[i]);
-        }
-        if (further)
-            further(brsrc_index[list[i]]);
-    }
-}
-
-function sync_brsrc_index_from_ids(available_list, additional_list) {
-    /* Default states for everything in the index. Read the further comments. */
-    for (var i in brsrc_index) {
-        brsrc_index[i].isdeleted(true);
-        brsrc_index[i].ischanged(false);
-    }
-
-    /* Populate the cache with anything that's missing and tag everything
-     * in the "available" list as *not* deleted, and tag everything in the
-     * additional list as "changed." See below. */
-    add_brsrc_to_index_if_needed(
-        available_list, function(o) { o.isdeleted(false); }
-    );
-    add_brsrc_to_index_if_needed(
-        additional_list,
-        function(o) {
-            if (!(o.id() in just_reserved_now)) o.ischanged(true);
-        }
-    );
-    /* NOTE: We lightly abuse the isdeleted() and ischanged() magic fieldmapper
-     * attributes of the brsrcs in our cache.  Because we're not going to
-     * pass back any brsrcs to the middle layer, it doesn't really matter
-     * what we set this attribute to. What we're using it for is to indicate
-     * in our little brsrc cache how a given brsrc should be displayed in this
-     * UI's current state (based on whether the brsrc matches timestamp range
-     * availability (isdeleted(false)) and whether the brsrc has been forced
-     * into the list because it was selected in a previous interface (like
-     * the catalog) (ischanged(true))).
-     */
-}
-
-function check_bresv_targeting(results) {
-    var missing = 0;
-    var due_dates = [];
-    for (var i in results) {
-        var targ = results[i].targeting;
-        if (!(targ && targ.current_resource)) {
-            missing++;
-            if (targ) {
-                if (targ.error == "NO_COPIES" && targ.conflicts) {
-                    for (var k in targ.conflicts) {
-                        /* Could potentially get more circ information from
-                         * targ.conflicts for display in the future. */
-                        due_dates.push(humanize_timestamp_string2(targ.conflicts[k].due_date()));
-                    }
-                }
-            }
-        } else {
-            just_reserved_now[results[i].targeting.current_resource] = true;
-        }
-    }
-    return {"missing": missing, "due_dates": due_dates};
-}
-
-function create_bresv(resource_list) {
-    var barcode = document.getElementById("patron_barcode").value;
-    if (barcode == "") {
-        alert(localeStrings.WHERES_THE_BARCODE);
-        return;
-    } else if (!reserve_timestamp_range.is_valid()) {
-        alert(localeStrings.INVALID_TS_RANGE);
-        return;
-    }
-    var email_notify = document.getElementById("email_notify").checked ? true : false;
-    var results;
-    try {
-        results = fieldmapper.standardRequest(
-            ["open-ils.booking", "open-ils.booking.reservations.create"],
-            [
-                openils.User.authtoken,
-                barcode,
-                reserve_timestamp_range.get_range(),
-                pickup_lib_selected,
-                our_brt.id(),
-                resource_list,
-                attr_value_table.get_all_values(),
-                email_notify
-            ]
-        );
-    } catch (E) {
-        alert(localeStrings.CREATE_BRESV_LOCAL_ERROR + E);
-    }
-    if (results) {
-        if (is_ils_event(results)) {
-            if (is_ils_actor_card_error(results)) {
-                alert(localeStrings.ACTOR_CARD_NOT_FOUND);
-            } else {
-                alert(my_ils_error(
-                    localeStrings.CREATE_BRESV_SERVER_ERROR, results
-                ));
-            }
-        } else {
-            var targeting = check_bresv_targeting(results);
-            if (targeting.missing) {
-                if (aous_cache["booking.require_successful_targeting"]) {
-                    alert(
-                        dojo.string.substitute(
-                            localeStrings.CREATE_BRESV_OK_MISSING_TARGET,
-                                [results.length, targeting.missing]
-                        ) + "\n\n" +
-                        dojo.string.substitute(
-                            localeStrings.CREATE_BRESV_OK_MISSING_TARGET_BLOCKED_BY_CIRC,
-                                [targeting.due_dates]
-                        ) + "\n\n" +
-                        localeStrings.CREATE_BRESV_OK_MISSING_TARGET_WILL_CANCEL
-                    );
-                    cancel_reservations(
-                        results.map(
-                            function(o) { return o.bresv; },
-                            true /* skip_update */
-                        )
-                    );
-                } else {
-                    alert(
-                        dojo.string.substitute(
-                            localeStrings.CREATE_BRESV_OK_MISSING_TARGET,
-                                [results.length, targeting.missing]
-                        ) + "\n\n" +
-                        dojo.string.substitute(
-                            localeStrings.CREATE_BRESV_OK_MISSING_TARGET_BLOCKED_BY_CIRC,
-                                [targeting.due_dates]
-                        )
-                    );
-                }
-            } else {
-                alert(
-                    dojo.string.substitute(
-                        localeStrings.CREATE_BRESV_OK, [results.length]
-                    )
-                );
-            }
-            update_brsrc_list();
-            update_bresv_grid();
-        }
-    } else {
-        alert(localeStrings.CREATE_BRESV_SERVER_NO_RESPONSE);
-    }
-}
-
-function flatten_to_dojo_data(obj_list) {
-    return {
-        "label": "id",
-        "identifier": "id",
-        "items": obj_list.map(function(o) {
-            var new_obj = {
-                "id": o.id(),
-                "type": o.target_resource_type().name(),
-                "start_time": humanize_timestamp_string(o.start_time()),
-                "end_time": humanize_timestamp_string(o.end_time())
-            };
-
-            if (o.current_resource())
-                new_obj["resource"] = o.current_resource().barcode();
-            else if (o.target_resource())
-                new_obj["resource"] = "* " + o.target_resource().barcode();
-            else
-                new_obj["resource"] = "* " + localeStrings.UNTARGETED + " *";
-            return new_obj;
-        })
-    };
-}
-
-function create_bresv_on_brsrc() {
-    var selector = document.getElementById("brsrc_list");
-    var selected_values = [];
-    for (var i in selector.options) {
-        if (selector.options[i] && selector.options[i].selected)
-            selected_values.push(selector.options[i].value);
-    }
-    if (selected_values.length > 0)
-        create_bresv(selected_values);
-    else
-        alert(localeStrings.SELECT_A_BRSRC_THEN);
-}
-
-function create_bresv_on_brt() {
-    if (any_usable_brsrc())
-        create_bresv();
-    else
-        alert(localeStrings.NO_USABLE_BRSRC);
-}
-
-function get_actor_by_barcode(barcode) {
-    var usr = fieldmapper.standardRequest(
-        ["open-ils.actor", "open-ils.actor.user.fleshed.retrieve_by_barcode"],
-        [openils.User.authtoken, barcode]
-    );
-    if (usr == null) {
-        alert(localeStrings.GET_PATRON_NO_RESULT);
-    } else if (is_ils_event(usr)) {
-        return null; /* XXX inelegant: this function is quiet about errors
-                        here because to report them would be redundant with
-                        another function that gets called right after this one.
-                      */
-    } else {
-        return usr;
-    }
-}
-
-function init_bresv_grid(barcode) {
-    var result = fieldmapper.standardRequest(
-        ["open-ils.booking",
-            "open-ils.booking.reservations.filtered_id_list"
-        ],
-        [openils.User.authtoken, {
-            "user_barcode": barcode,
-            "fields": {
-                "pickup_time": null,
-                "cancel_time": null,
-                "return_time": null
-            }
-        }, /* whole_obj */ true]
-    );
-    if (result == null) {
-        set_datagrid_empty_store(bresvGrid, flatten_to_dojo_data);
-        alert(localeStrings.GET_BRESV_LIST_NO_RESULT);
-    } else if (is_ils_event(result)) {
-        set_datagrid_empty_store(bresvGrid, flatten_to_dojo_data);
-        if (is_ils_actor_card_error(result)) {
-            alert(localeStrings.ACTOR_CARD_NOT_FOUND);
-        } else {
-            alert(my_ils_error(localeStrings.GET_BRESV_LIST_ERR, result));
-        }
-    } else {
-        if (result.length < 1) {
-            document.getElementById("bresv_grid_alt_explanation").innerHTML =
-                localeStrings.NO_EXISTING_BRESV;
-            hide_dom_element(document.getElementById("bresv_grid"));
-            reveal_dom_element(document.getElementById("reserve_under"));
-        } else {
-            document.getElementById("bresv_grid_alt_explanation").innerHTML =
-                "";
-            reveal_dom_element(document.getElementById("bresv_grid"));
-            reveal_dom_element(document.getElementById("reserve_under"));
-        }
-        /* May as well do the following in either case... */
-        bresvGrid.setStore(
-            new dojo.data.ItemFileReadStore(
-                {"data": flatten_to_dojo_data(result)}
-            )
-        );
-        bresv_index = {};
-        for (var i in result) {
-            bresv_index[result[i].id()] = result[i];
-        }
-    }
-}
-
-function cancel_reservations(bresv_id_list, skip_update) {
-    try {
-        var result = fieldmapper.standardRequest(
-            ["open-ils.booking", "open-ils.booking.reservations.cancel"],
-            [openils.User.authtoken, bresv_id_list]
-        );
-    } catch (E) {
-        alert(localeStrings.CXL_BRESV_FAILURE2 + E);
-        return;
-    }
-    if (!skip_update) setTimeout(update_bresv_grid, 0);
-    if (!result) {
-        alert(localeStrings.CXL_BRESV_FAILURE);
-    } else if (is_ils_event(result)) {
-        alert(my_ils_error(localeStrings.CXL_BRESV_FAILURE2, result));
-    } else {
-        alert(
-            dojo.string.substitute(
-                localeStrings.CXL_BRESV_SUCCESS, [result.length]
-            )
-        );
-    }
-}
-
-function munge_specific_resource(barcode) {
-    try {
-        var copy_list = pcrud.search(
-            "acp", {"barcode": barcode, "deleted": "f"}
-        );
-        if (copy_list && copy_list.length > 0) {
-            var r = fieldmapper.standardRequest(
-                ["open-ils.booking",
-                    "open-ils.booking.resources.create_from_copies"],
-                [openils.User.authtoken,
-                    copy_list.map(function(o) { return o.id(); })]
-            );
-
-            if (!r) {
-                alert(localeStrings.ON_FLY_NO_RESPONSE);
-            } else if (is_ils_event(r)) {
-                alert(my_ils_error(localeStrings.ON_FLY_ERROR, r));
-            } else {
-                if (!(our_brt = get_brt_by_id(r.brt[0][0]))) {
-                    alert(localeStrings.COULD_NOT_RETRIEVE_BRT_PASSED_IN);
-                } else {
-                    opts.booking_results = r;
-                    init_reservation_interface();
-                }
-            }
-        } else {
-            alert(localeStrings.BRSRC_NOT_FOUND);
-        }
-    } catch (E) {
-        alert(localeStrings.BRSRC_RETRIEVE_ERROR + E);
-    }
-}
-
-/*
- * These functions deal with interface tricks (populating widgets,
- * changing the page, etc.).
- */
-function init_pickup_lib_selector() {
-    var User = new openils.User();
-    User.buildPermOrgSelector(
-        "ADMIN_BOOKING_RESERVATION", pickup_lib_selector, null,
-        function() {
-            pickup_lib_selected = pickup_lib_selector.getValue();
-            dojo.connect(pickup_lib_selector, "onChange",
-                function() {
-                    pickup_lib_selected = this.getValue();
-                    update_brsrc_list();
-                }
-            )
-        }
-    );
-}
-
-function provide_brt_selector(targ_div) {
-    if (!targ_div) {
-        alert(localeStrings.NO_TARG_DIV);
-    } else {
-        brt_list = get_all_noncat_brt();
-        if (!brt_list || brt_list.length < 1) {
-            document.getElementById("select_noncat_brt_block").
-                style.display = "none";
-        } else {
-            var selector = document.createElement("select");
-            selector.setAttribute("id", "brt_selector");
-            selector.setAttribute("name", "brt_selector");
-            /* I'm reluctantly hardcoding this "size" attribute as 8
-             * because you can't accomplish this with CSS anyway.
-             */
-            selector.setAttribute("size", 8);
-            for (var i in brt_list) {
-                var option = document.createElement("option");
-                option.setAttribute("value", brt_list[i].id());
-                option.appendChild(document.createTextNode(brt_list[i].name()));
-                selector.appendChild(option);
-            }
-            targ_div.innerHTML = "";
-            targ_div.appendChild(selector);
-        }
-    }
-}
-
-function init_resv_iface_arb() {
-    init_reservation_interface(document.getElementById("arbitrary_resource"));
-}
-
-function init_resv_iface_sel() {
-    init_reservation_interface(document.getElementById("brt_selector"));
-}
-
-function init_reservation_interface(widget) {
-    /* Show or hide the email notification checkbox depending on org unit setting. */
-    if (!aous_cache["booking.allow_email_notify"]) {
-        hide_dom_element(document.getElementById("contain_email_notify"));
-    }
-    /* Save a global reference to the brt we're going to reserve */
-    if (widget && (widget.selectedIndex != undefined)) {
-        our_brt = brt_list[widget.selectedIndex];
-    } else if (widget != undefined) {
-        if (!munge_specific_resource(widget.value))
-            return;
-    }
-
-    /* Hide and reveal relevant divs. */
-    var search_block = document.getElementById("brt_search_block");
-    var reserve_block = document.getElementById("brt_reserve_block");
-    hide_dom_element(search_block);
-    reveal_dom_element(reserve_block);
-
-    /* Get a list of attributes that can apply to that brt. */
-    var bra_list = pcrud.search("bra", {"resource_type": our_brt.id()});
-    if (!bra_list) {
-        alert(localeString.NO_BRA_LIST);
-        return;
-    }
-
-    /* Get a table of values that can apply to the above attributes. */
-    var brav_by_bra = {};
-    bra_list.map(function(o) {
-        brav_by_bra[o.id()] = pcrud.search("brav", {"attr": o.id()});
-    });
-
-    /* Hide the label over the attributes widgets if we have nothing to show. */
-    var domf = (bra_list.length < 1) ? hide_dom_element : reveal_dom_element;
-    domf(document.getElementById("bra_and_brav_header"));
-
-    /* Create DOM widgets to represent each attribute/values set. */
-    for (var i in bra_list) {
-        var bra_div = document.createElement("div");
-        bra_div.setAttribute("class", "nice_vertical_padding");
-
-        var bra_select = document.createElement("select");
-        bra_select.setAttribute("name", "bra_" + bra_list[i].id());
-        bra_select.setAttribute(
-            "onchange",
-            "attr_value_table.update_from_selector(this); update_brsrc_list();"
-        );
-
-        var bra_opt_any = document.createElement("option");
-        bra_opt_any.appendChild(document.createTextNode(localeStrings.ANY));
-        bra_opt_any.setAttribute("value", "");
-
-        bra_select.appendChild(bra_opt_any);
-
-        var bra_label = document.createElement("label");
-        bra_label.setAttribute("class", "bra");
-        bra_label.appendChild(document.createTextNode(bra_list[i].name()));
-
-        var j = bra_list[i].id();
-        for (var k in brav_by_bra[j]) {
-            var bra_opt = document.createElement("option");
-            bra_opt.setAttribute("value", brav_by_bra[j][k].id());
-            bra_opt.appendChild(
-                document.createTextNode(brav_by_bra[j][k].valid_value())
-            );
-            bra_select.appendChild(bra_opt);
-        }
-
-        bra_div.appendChild(bra_label);
-        bra_div.appendChild(bra_select);
-        document.getElementById("bra_and_brav").appendChild(bra_div);
-    }
-    /* Add a prominent label reminding the user what resource type they're
-     * asking about. */
-    document.getElementById("brsrc_list_header").innerHTML = our_brt.name();
-    init_pickup_lib_selector();
-    update_brsrc_list();
-}
-
-function update_brsrc_list() {
-    var brsrc_id_list = get_brsrc_id_list();
-    var force_list = (opts.booking_results && opts.booking_results.brsrc) ?
-        opts.booking_results.brsrc.map(function(o) { return o[0]; }) : [];
-
-    sync_brsrc_index_from_ids(brsrc_id_list, force_list);
-
-    var target_selector = document.getElementById("brsrc_list");
-    var selector_memory = new SelectorMemory(target_selector);
-    selector_memory.save();
-    target_selector.innerHTML = "";
-
-    for (var i in brsrc_index) {
-        if (brsrc_index[i].isdeleted() && (!brsrc_index[i].ischanged()))
-            continue;
-
-        var opt = document.createElement("option");
-        opt.setAttribute("value", brsrc_index[i].id());
-        opt.appendChild(document.createTextNode(brsrc_index[i].barcode()));
-
-        if (brsrc_index[i].isdeleted() && (brsrc_index[i].ischanged())) {
-            opt.setAttribute("class", "forced_unavailable");
-            opt.setAttribute("disabled", "disabled");
-        }
-
-        target_selector.appendChild(opt);
-    }
-
-    selector_memory.restore();
-}
-
-function any_usable_brsrc() {
-    for (var i in brsrc_index) {
-        if (!brsrc_index[i].isdeleted())
-            return true;
-    }
-    return false;
-}
-
-function update_bresv_grid() {
-    var widg = document.getElementById("patron_barcode");
-    if (widg.value != "") {
-        setTimeout(function() {
-            var target = document.getElementById(
-                "existing_reservation_patron_line"
-            );
-            var patron = get_actor_by_barcode(widg.value);
-            if (patron) {
-                target.innerHTML = (
-                    localeStrings.HERE_ARE_EXISTING_BRESV + " " +
-                    formal_name(patron) + ": "
-                );
-            } else {
-                target.innerHTML = "";
-            }
-        }, 0);
-        setTimeout(function() { init_bresv_grid(widg.value); }, 0);
-    }
-}
-
-function init_timestamp_widgets() {
-    var when = ["start", "end"];
-    for (var i in when) {
-        reserve_timestamp_range.update_from_widget(
-            new dijit.form.TimeTextBox({
-                name: "reserve_time_" + when[i],
-                value: new Date(),
-                constraints: {
-                    timePattern: "HH:mm",
-                    clickableIncrement: "T00:15:00",
-                    visibleIncrement: "T00:15:00",
-                    visibleRange: "T01:30:00"
-                },
-                onChange: function() {
-                    reserve_timestamp_range.update_from_widget(this);
-                    update_brsrc_list();
-                }
-            }, "reserve_time_" + when[i])
-        );
-        reserve_timestamp_range.update_from_widget(
-            new dijit.form.DateTextBox({
-                name: "reserve_date_" + when[i],
-                value: new Date(),
-                onChange: function() {
-                    reserve_timestamp_range.update_from_widget(this);
-                    update_brsrc_list();
-                }
-            }, "reserve_date_" + when[i])
-        );
-    }
-}
-
-function cancel_selected_bresv(bresv_dojo_items) {
-    if (bresv_dojo_items && bresv_dojo_items.length > 0 &&
-        (bresv_dojo_items[0].length == undefined ||
-            bresv_dojo_items[0].length > 0)) {
-        cancel_reservations(
-            bresv_dojo_items.map(function(o) { return o.id[0]; })
-        );
-        /* After some delay to allow the cancellations a chance to get
-         * committed, refresh the brsrc list as it might reflect newly
-         * available resources now. */
-        if (our_brt) setTimeout(update_brsrc_list, 2000);
-    } else {
-        alert(localeStrings.CXL_BRESV_SELECT_SOMETHING);
-    }
-}
-
-/* The following function should return true if the reservation interface
- * should start normally (show a list of brt to choose from) or false if
- * it should not (because we've "started" it some other way by setting up
- * and displaying other widgets).
- */
-function early_action_passthru() {
-    if (opts.booking_results) {
-        if (opts.booking_results.brt.length != 1) {
-            alert(localeStrings.NEED_EXACTLY_ONE_BRT_PASSED_IN);
-            return true;
-        } else if (!(our_brt = get_brt_by_id(opts.booking_results.brt[0][0]))) {
-            alert(localeStrings.COULD_NOT_RETRIEVE_BRT_PASSED_IN);
-            return true;
-        }
-
-        init_reservation_interface();
-        return false;
-    }
-
-    if (opts.patron_barcode) {
-        document.getElementById("contain_patron_barcode").style.display="none";
-        document.getElementById("patron_barcode").value = opts.patron_barcode;
-        update_bresv_grid();
-    }
-
-    return true;
-}
-
-function init_aous_cache() {
-    /* The following method call could be given a longer
-     * list of OU settings to fetch in the future if needed. */
-    var results = fieldmapper.aou.fetchOrgSettingBatch(
-        openils.User.user.ws_ou(), ["booking.require_successful_targeting", "booking.allow_email_notify"]
-    );
-    if (results && !is_ils_event(results)) {
-        for (var k in results) {
-            if (results[k] != undefined)
-                aous_cache[k] = results[k].value;
-        }
-    } else if (results) {
-        alert(my_ils_error(localeStrings.ERROR_FETCHING_AOUS, results));
-    } else {
-        alert(localeStrings.ERROR_FETCHING_AOUS);
-    }
-}
-
-/*
- * my_init
- */
-function my_init() {
-    hide_dom_element(document.getElementById("brt_reserve_block"));
-    reveal_dom_element(document.getElementById("brt_search_block"));
-    hide_dom_element(document.getElementById("reserve_under"));
-    init_auto_l10n(document.getElementById("auto_l10n_start_here"));
-    init_aous_cache();
-    init_timestamp_widgets();
-
-    if (!(opts = xulG.bresv_interface_opts)) opts = {};
-    if (early_action_passthru())
-        provide_brt_selector(document.getElementById("brt_selector_here"));
-}
+require([
+       "fieldmapper/OrgUtils",
+       "openils/PermaCrud",
+       "openils/User",
+       "openils/widget/OrgUnitFilteringSelect",
+       "dojo/data/ItemFileReadStore",
+       "dijit/form/DateTextBox",
+       "dijit/form/TimeTextBox",
+       "dojo/date/stamp"
+       ],
+function(fieldmapper_OrgUtils,
+       openils_PermaCrud,
+       openils_User,
+       openils_widget_OrgUnitFilteringSelect,
+       dojo_data_ItemFileReadStore,
+       dijit_form_DateTextBox,
+       dijit_form_TimeTextBox,
+       dojo_date_stamp){
+       /*
+        * Details, details...
+        */
+       dojo.requireLocalization("openils.booking", "reservation");
+       
+       /*
+        * Globals; prototypes and their instances
+        */
+       var localeStrings = dojo.i18n.getLocalization("openils.booking", "reservation");
+       var pcrud = new openils_PermaCrud();
+       var opts;
+       var our_brt;
+       var pickup_lib_selected;
+       var brt_list = [];
+       var brsrc_index = {};
+       var bresv_index = {};
+       var just_reserved_now = {};
+       var aous_cache = {};
+       
+       function AttrValueTable() { this.t = {}; }
+       AttrValueTable.prototype.set = function(attr, value) { this.t[attr] = value; };
+       AttrValueTable.prototype.update_from_selector = function(selector) {
+           var attr  = selector.name.match(/_(\d+)$/)[1];
+           var value = selector.options[selector.selectedIndex].value;
+           if (attr)
+               attr_value_table.set(attr, value);
+       };
+       AttrValueTable.prototype.get_all_values = function() {
+           var values = [];
+           for (var k in this.t) {
+               if (this.t[k] != undefined && this.t[k] != "")
+                   values.push(this.t[k]);
+           }
+           return values;
+       };
+       var attr_value_table =  new AttrValueTable();
+       
+       function TimestampRange() {
+           this.start = new Date();
+           this.end = new Date();
+       
+           this.validity = {"start": false, "end": false};
+           this.nodes = {
+               "start": {"date": undefined, "time": undefined},
+               "end": {"date": undefined, "time": undefined}
+           };
+           this.saved_style_properties = {};
+           this.invalid_style_properties = {
+               "backgroundColor": "#ffcccc",
+               "color": "#990000",
+               "borderColor": "#990000",
+               "fontWeight": "bold"
+           };
+       }
+       TimestampRange.prototype.get_timestamp = function(when) {
+           return dojo_date_stamp.toISOString(this[when]).
+               replace("T", " ").substr(0, 19);
+       };
+       TimestampRange.prototype.get_range = function() {
+           return this.is_backwards() ?
+               [this.get_timestamp("end"), this.get_timestamp("start")] :
+               [this.get_timestamp("start"), this.get_timestamp("end")];
+       };
+       TimestampRange.prototype.update_from_widget = function(widget) {
+           var when = widget.name.match(/(start|end)/)[1];
+           var which = widget.name.match(/(date|time)/)[1];
+       
+           if (this.nodes[when][which] == undefined)
+               this.nodes[when][which] = widget.domNode; /* We'll need this later */
+       
+           if (when && which) {
+               this.update_timestamp(when, which, widget.attr("value"));
+           }
+       
+           this.compute_validity();
+           this.paint_validity();
+       };
+       TimestampRange.prototype.compute_validity = function() {
+           if (Math.abs(this.start - this.end) < 1000) {
+               this.validity.end = false;
+           } else {
+               if (this.start < this.current_minimum())
+                   this.validity.start = false;
+               else
+                   this.validity.start = true;
+       
+               if (this.end < this.current_minimum())
+                   this.validity.end = false;
+               else
+                   this.validity.end = true;
+           }
+       };
+       /* This method provides the minimum timestamp that is considered valid. For
+        * now it's arbitrarily "now + 15 minutes", meaning that all reservations
+        * must be made at least 15 minutes in the future.
+        *
+        * For reasons of keeping the middle layer happy, this should always return
+        * a time that is at least somewhat in the future. The ML isn't able to target
+        * any resources for a reservation with a start date that isn't in the future.
+        */
+       TimestampRange.prototype.current_minimum = function() {
+           /* XXX This is going to be a problem with local clocks that are off. */
+           var n = new Date();
+           n.setTime(n.getTime() + 1000 * 900); /* XXX 15 minutes; stop hardcoding! */
+           return n;
+       };
+       TimestampRange.prototype.update_timestamp = function(when, which, value) {
+           if (which == "date") {
+               this[when].setFullYear(value.getFullYear());
+               /* month and date MUST be done together */
+               this[when].setMonth(value.getMonth(), value.getDate());
+           } else {    /* "time" */
+               this[when].setHours(value.getHours());
+               this[when].setMinutes(value.getMinutes());
+               this[when].setSeconds(0);
+           }
+       };
+       TimestampRange.prototype.is_backwards = function() {
+           return (this.start > this.end);
+       };
+       TimestampRange.prototype.paint_validity = function()  {
+           for (var when in this.validity) {
+               if (this.validity[when]) {
+                   this.paint_valid_node(this.nodes[when].date);
+                   this.paint_valid_node(this.nodes[when].time);
+               } else {
+                   this.paint_invalid_node(this.nodes[when].date);
+                   this.paint_invalid_node(this.nodes[when].time);
+               }
+           }
+       };
+       TimestampRange.prototype.paint_invalid_node = function(node) {
+           if (node) {
+               /* Just toggling the class of something would be better than
+                * manually setting style here, but I haven't been able to get that
+                * to play nicely with dojo's styling of the date/time textboxen.
+                */
+               if (this.saved_style_properties.backgroundColor == undefined) {
+                   for (var k in this.invalid_style_properties) {
+                       this.saved_style_properties[k] = node.style[k];
+                   }
+               }
+               for (var k in this.invalid_style_properties) {
+                   node.style[k] = this.invalid_style_properties[k];
+               }
+           }
+       };
+       TimestampRange.prototype.paint_valid_node = function(node) {
+           if (node) {
+               for (var k in this.saved_style_properties) {
+                   node.style[k] = this.saved_style_properties[k];
+               }
+           }
+       };
+       TimestampRange.prototype.is_valid = function() {
+           return (this.validity.start && this.validity.end);
+       };
+       var reserve_timestamp_range = new TimestampRange();
+       
+       function SelectorMemory(selector) {
+           this.selector = selector;
+           this.memory = {};
+       }
+       SelectorMemory.prototype.save = function() {
+           for (var i = 0; i < this.selector.options.length; i++) {
+               if (this.selector.options[i].selected) {
+                   this.memory[this.selector.options[i].value] = true;
+               }
+           }
+       };
+       SelectorMemory.prototype.restore = function() {
+           for (var i = 0; i < this.selector.options.length; i++) {
+               if (this.memory[this.selector.options[i].value]) {
+                   if (!this.selector.options[i].disabled)
+                       this.selector.options[i].selected = true;
+               }
+           }
+       };
+       
+       /*
+        * These functions communicate with the middle layer.
+        */
+       function get_all_noncat_brt() {
+           return pcrud.search("brt",
+               {"id": {"!=": null}, "catalog_item": "f"},
+               {"order_by": {"brt":"name"}}
+           );
+       }
+       
+       function get_brt_by_id(id) {
+           return pcrud.retrieve("brt", id);
+       }
+       
+       function get_brsrc_id_list() {
+           var options = {"type": our_brt.id(), "pickup_lib": pickup_lib_selected};
+       
+           /* This mechanism for avoiding the passing of an empty 'attribute_values'
+            * option is essential because if you pass such an option to the
+            * middle layer API at all, it won't return any IDs for brsrcs that
+            * don't have at least one attribute of some kind.
+            */
+           var attribute_values = attr_value_table.get_all_values();
+           if (attribute_values.length > 0)
+               options.attribute_values = attribute_values;
+       
+           options.available = reserve_timestamp_range.get_range();
+       
+           return fieldmapper.standardRequest(
+               ["open-ils.booking", "open-ils.booking.resources.filtered_id_list"],
+               [openils_User.authtoken, options]
+           );
+       }
+       
+       /* FIXME: We need failure checking after pcrud.retrieve() */
+       function add_brsrc_to_index_if_needed(list, further) {
+           for (var i in list) {
+               if (!brsrc_index[list[i]]) {
+                   brsrc_index[list[i]] = pcrud.retrieve("brsrc", list[i]);
+               }
+               if (further)
+                   further(brsrc_index[list[i]]);
+           }
+       }
+       
+       function sync_brsrc_index_from_ids(available_list, additional_list) {
+           /* Default states for everything in the index. Read the further comments. */
+           for (var i in brsrc_index) {
+               brsrc_index[i].isdeleted(true);
+               brsrc_index[i].ischanged(false);
+           }
+       
+           /* Populate the cache with anything that's missing and tag everything
+            * in the "available" list as *not* deleted, and tag everything in the
+            * additional list as "changed." See below. */
+           add_brsrc_to_index_if_needed(
+               available_list, function(o) { o.isdeleted(false); }
+           );
+           add_brsrc_to_index_if_needed(
+               additional_list,
+               function(o) {
+                   if (!(o.id() in just_reserved_now)) o.ischanged(true);
+               }
+           );
+           /* NOTE: We lightly abuse the isdeleted() and ischanged() magic fieldmapper
+            * attributes of the brsrcs in our cache.  Because we're not going to
+            * pass back any brsrcs to the middle layer, it doesn't really matter
+            * what we set this attribute to. What we're using it for is to indicate
+            * in our little brsrc cache how a given brsrc should be displayed in this
+            * UI's current state (based on whether the brsrc matches timestamp range
+            * availability (isdeleted(false)) and whether the brsrc has been forced
+            * into the list because it was selected in a previous interface (like
+            * the catalog) (ischanged(true))).
+            */
+       }
+       
+       function check_bresv_targeting(results) {
+           var missing = 0;
+           var due_dates = [];
+           for (var i in results) {
+               var targ = results[i].targeting;
+               if (!(targ && targ.current_resource)) {
+                   missing++;
+                   if (targ) {
+                       if (targ.error == "NO_COPIES" && targ.conflicts) {
+                           for (var k in targ.conflicts) {
+                               /* Could potentially get more circ information from
+                                * targ.conflicts for display in the future. */
+                               due_dates.push(humanize_timestamp_string2(targ.conflicts[k].due_date()));
+                           }
+                       }
+                   }
+               } else {
+                   just_reserved_now[results[i].targeting.current_resource] = true;
+               }
+           }
+           return {"missing": missing, "due_dates": due_dates};
+       }
+       
+       function create_bresv(resource_list) {
+           var barcode = document.getElementById("patron_barcode").value;
+           if (barcode == "") {
+               alert(localeStrings.WHERES_THE_BARCODE);
+               return;
+           } else if (!reserve_timestamp_range.is_valid()) {
+               alert(localeStrings.INVALID_TS_RANGE);
+               return;
+           }
+           var email_notify = document.getElementById("email_notify").checked ? true : false;
+           var results;
+           try {
+               results = fieldmapper.standardRequest(
+                   ["open-ils.booking", "open-ils.booking.reservations.create"],
+                   [
+                       openils_User.authtoken,
+                       barcode,
+                       reserve_timestamp_range.get_range(),
+                       pickup_lib_selected,
+                       our_brt.id(),
+                       resource_list,
+                       attr_value_table.get_all_values(),
+                       email_notify
+                   ]
+               );
+           } catch (E) {
+               alert(localeStrings.CREATE_BRESV_LOCAL_ERROR + E);
+           }
+           if (results) {
+               if (is_ils_event(results)) {
+                   if (is_ils_actor_card_error(results)) {
+                       alert(localeStrings.ACTOR_CARD_NOT_FOUND);
+                   } else {
+                       alert(my_ils_error(
+                           localeStrings.CREATE_BRESV_SERVER_ERROR, results
+                       ));
+                   }
+               } else {
+                   var targeting = check_bresv_targeting(results);
+                   if (targeting.missing) {
+                       if (aous_cache["booking.require_successful_targeting"]) {
+                           alert(
+                               dojo.string.substitute(
+                                   localeStrings.CREATE_BRESV_OK_MISSING_TARGET,
+                                       [results.length, targeting.missing]
+                               ) + "\n\n" +
+                               dojo.string.substitute(
+                                   localeStrings.CREATE_BRESV_OK_MISSING_TARGET_BLOCKED_BY_CIRC,
+                                       [targeting.due_dates]
+                               ) + "\n\n" +
+                               localeStrings.CREATE_BRESV_OK_MISSING_TARGET_WILL_CANCEL
+                           );
+                           cancel_reservations(
+                               results.map(
+                                   function(o) { return o.bresv; },
+                                   true /* skip_update */
+                               )
+                           );
+                       } else {
+                           alert(
+                               dojo.string.substitute(
+                                   localeStrings.CREATE_BRESV_OK_MISSING_TARGET,
+                                       [results.length, targeting.missing]
+                               ) + "\n\n" +
+                               dojo.string.substitute(
+                                   localeStrings.CREATE_BRESV_OK_MISSING_TARGET_BLOCKED_BY_CIRC,
+                                       [targeting.due_dates]
+                               )
+                           );
+                       }
+                   } else {
+                       alert(
+                           dojo.string.substitute(
+                               localeStrings.CREATE_BRESV_OK, [results.length]
+                           )
+                       );
+                   }
+                   update_brsrc_list();
+                   update_bresv_grid();
+               }
+           } else {
+               alert(localeStrings.CREATE_BRESV_SERVER_NO_RESPONSE);
+           }
+       }
+       
+       function flatten_to_dojo_data(obj_list) {
+           return {
+               "label": "id",
+               "identifier": "id",
+               "items": obj_list.map(function(o) {
+                   var new_obj = {
+                       "id": o.id(),
+                       "type": o.target_resource_type().name(),
+                       "start_time": humanize_timestamp_string(o.start_time()),
+                       "end_time": humanize_timestamp_string(o.end_time())
+                   };
+       
+                   if (o.current_resource())
+                       new_obj["resource"] = o.current_resource().barcode();
+                   else if (o.target_resource())
+                       new_obj["resource"] = "* " + o.target_resource().barcode();
+                   else
+                       new_obj["resource"] = "* " + localeStrings.UNTARGETED + " *";
+                   return new_obj;
+               })
+           };
+       }
+       
+       function create_bresv_on_brsrc() {
+           var selector = document.getElementById("brsrc_list");
+           var selected_values = [];
+           for (var i in selector.options) {
+               if (selector.options[i] && selector.options[i].selected)
+                   selected_values.push(selector.options[i].value);
+           }
+           if (selected_values.length > 0)
+               create_bresv(selected_values);
+           else
+               alert(localeStrings.SELECT_A_BRSRC_THEN);
+       }
+       
+       function create_bresv_on_brt() {
+           if (any_usable_brsrc())
+               create_bresv();
+           else
+               alert(localeStrings.NO_USABLE_BRSRC);
+       }
+       
+       function get_actor_by_barcode(barcode) {
+           var usr = fieldmapper.standardRequest(
+               ["open-ils.actor", "open-ils.actor.user.fleshed.retrieve_by_barcode"],
+               [openils_User.authtoken, barcode]
+           );
+           if (usr == null) {
+               alert(localeStrings.GET_PATRON_NO_RESULT);
+           } else if (is_ils_event(usr)) {
+               return null; /* XXX inelegant: this function is quiet about errors
+                               here because to report them would be redundant with
+                               another function that gets called right after this one.
+                             */
+           } else {
+               return usr;
+           }
+       }
+       
+       function init_bresv_grid(barcode) {
+           var result = fieldmapper.standardRequest(
+               ["open-ils.booking",
+                   "open-ils.booking.reservations.filtered_id_list"
+               ],
+               [openils_User.authtoken, {
+                   "user_barcode": barcode,
+                   "fields": {
+                       "pickup_time": null,
+                       "cancel_time": null,
+                       "return_time": null
+                   }
+               }, /* whole_obj */ true]
+           );
+           if (result == null) {
+               set_datagrid_empty_store(bresvGrid, flatten_to_dojo_data);
+               alert(localeStrings.GET_BRESV_LIST_NO_RESULT);
+           } else if (is_ils_event(result)) {
+               set_datagrid_empty_store(bresvGrid, flatten_to_dojo_data);
+               if (is_ils_actor_card_error(result)) {
+                   alert(localeStrings.ACTOR_CARD_NOT_FOUND);
+               } else {
+                   alert(my_ils_error(localeStrings.GET_BRESV_LIST_ERR, result));
+               }
+           } else {
+               if (result.length < 1) {
+                   document.getElementById("bresv_grid_alt_explanation").innerHTML =
+                       localeStrings.NO_EXISTING_BRESV;
+                   hide_dom_element(document.getElementById("bresv_grid"));
+                   reveal_dom_element(document.getElementById("reserve_under"));
+               } else {
+                   document.getElementById("bresv_grid_alt_explanation").innerHTML =
+                       "";
+                   reveal_dom_element(document.getElementById("bresv_grid"));
+                   reveal_dom_element(document.getElementById("reserve_under"));
+               }
+               /* May as well do the following in either case... */
+               bresvGrid.setStore(
+                   new dojo_data_ItemFileReadStore(
+                       {"data": flatten_to_dojo_data(result)}
+                   )
+               );
+               bresv_index = {};
+               for (var i in result) {
+                   bresv_index[result[i].id()] = result[i];
+               }
+           }
+       }
+       
+       function cancel_reservations(bresv_id_list, skip_update) {
+           try {
+               var result = fieldmapper.standardRequest(
+                   ["open-ils.booking", "open-ils.booking.reservations.cancel"],
+                   [openils_User.authtoken, bresv_id_list]
+               );
+           } catch (E) {
+               alert(localeStrings.CXL_BRESV_FAILURE2 + E);
+               return;
+           }
+           if (!skip_update) setTimeout(update_bresv_grid, 0);
+           if (!result) {
+               alert(localeStrings.CXL_BRESV_FAILURE);
+           } else if (is_ils_event(result)) {
+               alert(my_ils_error(localeStrings.CXL_BRESV_FAILURE2, result));
+           } else {
+               alert(
+                   dojo.string.substitute(
+                       localeStrings.CXL_BRESV_SUCCESS, [result.length]
+                   )
+               );
+           }
+       }
+       
+       function munge_specific_resource(barcode) {
+           try {
+               var copy_list = pcrud.search(
+                   "acp", {"barcode": barcode, "deleted": "f"}
+               );
+               if (copy_list && copy_list.length > 0) {
+                   var r = fieldmapper.standardRequest(
+                       ["open-ils.booking",
+                           "open-ils.booking.resources.create_from_copies"],
+                       [openils_User.authtoken,
+                           copy_list.map(function(o) { return o.id(); })]
+                   );
+       
+                   if (!r) {
+                       alert(localeStrings.ON_FLY_NO_RESPONSE);
+                   } else if (is_ils_event(r)) {
+                       alert(my_ils_error(localeStrings.ON_FLY_ERROR, r));
+                   } else {
+                       if (!(our_brt = get_brt_by_id(r.brt[0][0]))) {
+                           alert(localeStrings.COULD_NOT_RETRIEVE_BRT_PASSED_IN);
+                       } else {
+                           opts.booking_results = r;
+                           init_reservation_interface();
+                       }
+                   }
+               } else {
+                   alert(localeStrings.BRSRC_NOT_FOUND);
+               }
+           } catch (E) {
+               alert(localeStrings.BRSRC_RETRIEVE_ERROR + E);
+           }
+       }
+       
+       /*
+        * These functions deal with interface tricks (populating widgets,
+        * changing the page, etc.).
+        */
+       function init_pickup_lib_selector() {
+           var User = new openils_User();
+           User.buildPermOrgSelector(
+               "ADMIN_BOOKING_RESERVATION", pickup_lib_selector, null,
+               function() {
+                   pickup_lib_selected = pickup_lib_selector.getValue();
+                   dojo.connect(pickup_lib_selector, "onChange",
+                       function() {
+                           pickup_lib_selected = this.getValue();
+                           update_brsrc_list();
+                       }
+                   )
+               }
+           );
+       }
+       
+       function provide_brt_selector(targ_div) {
+           if (!targ_div) {
+               alert(localeStrings.NO_TARG_DIV);
+           } else {
+               brt_list = get_all_noncat_brt();
+               if (!brt_list || brt_list.length < 1) {
+                   document.getElementById("select_noncat_brt_block").
+                       style.display = "none";
+               } else {
+                   var selector = document.createElement("select");
+                   selector.setAttribute("id", "brt_selector");
+                   selector.setAttribute("name", "brt_selector");
+                   /* I'm reluctantly hardcoding this "size" attribute as 8
+                    * because you can't accomplish this with CSS anyway.
+                    */
+                   selector.setAttribute("size", 8);
+                   for (var i in brt_list) {
+                       var option = document.createElement("option");
+                       option.setAttribute("value", brt_list[i].id());
+                       option.appendChild(document.createTextNode(brt_list[i].name()));
+                       selector.appendChild(option);
+                   }
+                   targ_div.innerHTML = "";
+                   targ_div.appendChild(selector);
+               }
+           }
+       }
+       
+       function init_resv_iface_arb() {
+           init_reservation_interface(document.getElementById("arbitrary_resource"));
+       }
+       
+       function init_resv_iface_sel() {
+           init_reservation_interface(document.getElementById("brt_selector"));
+       }
+       
+       function init_reservation_interface(widget) {
+           /* Show or hide the email notification checkbox depending on org unit setting. */
+           if (!aous_cache["booking.allow_email_notify"]) {
+               hide_dom_element(document.getElementById("contain_email_notify"));
+           }
+           /* Save a global reference to the brt we're going to reserve */
+           if (widget && (widget.selectedIndex != undefined)) {
+               our_brt = brt_list[widget.selectedIndex];
+           } else if (widget != undefined) {
+               if (!munge_specific_resource(widget.value))
+                   return;
+           }
+       
+           /* Hide and reveal relevant divs. */
+           var search_block = document.getElementById("brt_search_block");
+           var reserve_block = document.getElementById("brt_reserve_block");
+           hide_dom_element(search_block);
+           reveal_dom_element(reserve_block);
+       
+           /* Get a list of attributes that can apply to that brt. */
+           var bra_list = pcrud.search("bra", {"resource_type": our_brt.id()});
+           if (!bra_list) {
+               alert(localeString.NO_BRA_LIST);
+               return;
+           }
+       
+           /* Get a table of values that can apply to the above attributes. */
+           var brav_by_bra = {};
+           bra_list.map(function(o) {
+               brav_by_bra[o.id()] = pcrud.search("brav", {"attr": o.id()});
+           });
+       
+           /* Hide the label over the attributes widgets if we have nothing to show. */
+           var domf = (bra_list.length < 1) ? hide_dom_element : reveal_dom_element;
+           domf(document.getElementById("bra_and_brav_header"));
+       
+           /* Create DOM widgets to represent each attribute/values set. */
+           for (var i in bra_list) {
+               var bra_div = document.createElement("div");
+               bra_div.setAttribute("class", "nice_vertical_padding");
+       
+               var bra_select = document.createElement("select");
+               bra_select.setAttribute("name", "bra_" + bra_list[i].id());
+               bra_select.setAttribute(
+                   "onchange",
+                   "attr_value_table.update_from_selector(this); update_brsrc_list();"
+               );
+       
+               var bra_opt_any = document.createElement("option");
+               bra_opt_any.appendChild(document.createTextNode(localeStrings.ANY));
+               bra_opt_any.setAttribute("value", "");
+       
+               bra_select.appendChild(bra_opt_any);
+       
+               var bra_label = document.createElement("label");
+               bra_label.setAttribute("class", "bra");
+               bra_label.appendChild(document.createTextNode(bra_list[i].name()));
+       
+               var j = bra_list[i].id();
+               for (var k in brav_by_bra[j]) {
+                   var bra_opt = document.createElement("option");
+                   bra_opt.setAttribute("value", brav_by_bra[j][k].id());
+                   bra_opt.appendChild(
+                       document.createTextNode(brav_by_bra[j][k].valid_value())
+                   );
+                   bra_select.appendChild(bra_opt);
+               }
+       
+               bra_div.appendChild(bra_label);
+               bra_div.appendChild(bra_select);
+               document.getElementById("bra_and_brav").appendChild(bra_div);
+           }
+           /* Add a prominent label reminding the user what resource type they're
+            * asking about. */
+           document.getElementById("brsrc_list_header").innerHTML = our_brt.name();
+           init_pickup_lib_selector();
+           update_brsrc_list();
+       }
+       
+       function update_brsrc_list() {
+           var brsrc_id_list = get_brsrc_id_list();
+           var force_list = (opts.booking_results && opts.booking_results.brsrc) ?
+               opts.booking_results.brsrc.map(function(o) { return o[0]; }) : [];
+       
+           sync_brsrc_index_from_ids(brsrc_id_list, force_list);
+       
+           var target_selector = document.getElementById("brsrc_list");
+           var selector_memory = new SelectorMemory(target_selector);
+           selector_memory.save();
+           target_selector.innerHTML = "";
+       
+           for (var i in brsrc_index) {
+               if (brsrc_index[i].isdeleted() && (!brsrc_index[i].ischanged()))
+                   continue;
+       
+               var opt = document.createElement("option");
+               opt.setAttribute("value", brsrc_index[i].id());
+               opt.appendChild(document.createTextNode(brsrc_index[i].barcode()));
+       
+               if (brsrc_index[i].isdeleted() && (brsrc_index[i].ischanged())) {
+                   opt.setAttribute("class", "forced_unavailable");
+                   opt.setAttribute("disabled", "disabled");
+               }
+       
+               target_selector.appendChild(opt);
+           }
+       
+           selector_memory.restore();
+       }
+       
+       function any_usable_brsrc() {
+           for (var i in brsrc_index) {
+               if (!brsrc_index[i].isdeleted())
+                   return true;
+           }
+           return false;
+       }
+       
+       function update_bresv_grid() {
+           var widg = document.getElementById("patron_barcode");
+           if (widg.value != "") {
+               setTimeout(function() {
+                   var target = document.getElementById(
+                       "existing_reservation_patron_line"
+                   );
+                   var patron = get_actor_by_barcode(widg.value);
+                   if (patron) {
+                       target.innerHTML = (
+                           localeStrings.HERE_ARE_EXISTING_BRESV + " " +
+                           formal_name(patron) + ": "
+                       );
+                   } else {
+                       target.innerHTML = "";
+                   }
+               }, 0);
+               setTimeout(function() { init_bresv_grid(widg.value); }, 0);
+           }
+       }
+       
+       function init_timestamp_widgets() {
+           var when = ["start", "end"];
+           for (var i in when) {
+               reserve_timestamp_range.update_from_widget(
+                   new dijit_form_TimeTextBox({
+                       name: "reserve_time_" + when[i],
+                       value: new Date(),
+                       constraints: {
+                           timePattern: "HH:mm",
+                           clickableIncrement: "T00:15:00",
+                           visibleIncrement: "T00:15:00",
+                           visibleRange: "T01:30:00"
+                       },
+                       onChange: function() {
+                           reserve_timestamp_range.update_from_widget(this);
+                           update_brsrc_list();
+                       }
+                   }, "reserve_time_" + when[i])
+               );
+               reserve_timestamp_range.update_from_widget(
+                   new dijit_form_DateTextBox({
+                       name: "reserve_date_" + when[i],
+                       value: new Date(),
+                       onChange: function() {
+                           reserve_timestamp_range.update_from_widget(this);
+                           update_brsrc_list();
+                       }
+                   }, "reserve_date_" + when[i])
+               );
+           }
+       }
+       
+       function cancel_selected_bresv(bresv_dojo_items) {
+           if (bresv_dojo_items && bresv_dojo_items.length > 0 &&
+               (bresv_dojo_items[0].length == undefined ||
+                   bresv_dojo_items[0].length > 0)) {
+               cancel_reservations(
+                   bresv_dojo_items.map(function(o) { return o.id[0]; })
+               );
+               /* After some delay to allow the cancellations a chance to get
+                * committed, refresh the brsrc list as it might reflect newly
+                * available resources now. */
+               if (our_brt) setTimeout(update_brsrc_list, 2000);
+           } else {
+               alert(localeStrings.CXL_BRESV_SELECT_SOMETHING);
+           }
+       }
+       
+       /* The following function should return true if the reservation interface
+        * should start normally (show a list of brt to choose from) or false if
+        * it should not (because we've "started" it some other way by setting up
+        * and displaying other widgets).
+        */
+       function early_action_passthru() {
+           if (opts.booking_results) {
+               if (opts.booking_results.brt.length != 1) {
+                   alert(localeStrings.NEED_EXACTLY_ONE_BRT_PASSED_IN);
+                   return true;
+               } else if (!(our_brt = get_brt_by_id(opts.booking_results.brt[0][0]))) {
+                   alert(localeStrings.COULD_NOT_RETRIEVE_BRT_PASSED_IN);
+                   return true;
+               }
+       
+               init_reservation_interface();
+               return false;
+           }
+       
+           if (opts.patron_barcode) {
+               document.getElementById("contain_patron_barcode").style.display="none";
+               document.getElementById("patron_barcode").value = opts.patron_barcode;
+               update_bresv_grid();
+           }
+       
+           return true;
+       }
+       
+       function init_aous_cache() {
+           /* The following method call could be given a longer
+            * list of OU settings to fetch in the future if needed. */
+           var results = fieldmapper.aou.fetchOrgSettingBatch(
+               openils_User.user.ws_ou(), ["booking.require_successful_targeting", "booking.allow_email_notify"]
+           );
+           if (results && !is_ils_event(results)) {
+               for (var k in results) {
+                   if (results[k] != undefined)
+                       aous_cache[k] = results[k].value;
+               }
+           } else if (results) {
+               alert(my_ils_error(localeStrings.ERROR_FETCHING_AOUS, results));
+           } else {
+               alert(localeStrings.ERROR_FETCHING_AOUS);
+           }
+       }
+       
+       /*
+        * my_init
+        */
+       function my_init() {
+           hide_dom_element(document.getElementById("brt_reserve_block"));
+           reveal_dom_element(document.getElementById("brt_search_block"));
+           hide_dom_element(document.getElementById("reserve_under"));
+           init_auto_l10n(document.getElementById("auto_l10n_start_here"));
+           init_aous_cache();
+           init_timestamp_widgets();
+       
+           if (!(opts = xulG.bresv_interface_opts)) opts = {};
+           if (early_action_passthru())
+               provide_brt_selector(document.getElementById("brt_selector_here"));
+       }
+       
+
+});
\ No newline at end of file
index 591bfb8..e66bbe6 100644 (file)
-dojo.require('dijit.Dialog');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.DropDownButton');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.Form');
-dojo.require('dijit.form.NumberSpinner');
-dojo.require('dijit.form.TextBox');
-dojo.require("dijit.Menu");
-dojo.require("dijit.MenuItem");
-dojo.require('dojox.xml.parser');
-dojo.require('DojoSRF');
-dojo.require("fieldmapper.Fieldmapper");
-dojo.require('openils.CGI');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.XUL');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require("openils.widget.PCrudAutocompleteBox");
-dojo.requireLocalization("openils.authority", "authority");
-var auth_strings = dojo.i18n.getLocalization("openils.authority", "authority");
-
-var cgi = new openils.CGI();
-
-/*
-// OrgUnits do not currently affect the retrieval of authority records,
-// but this is how to display them if they become OrgUnit-aware
-function authOUListInit() {
-    new openils.User().buildPermOrgSelector(
-        "STAFF_LOGIN", // anywhere you can log in
-        dijit.byId("authOU"),
-        null, // pre-selected org
-        null
-    );
-}
-dojo.addOnLoad(authOUListInit);
-*/
-function displayAuthorities(data) { 
-
-    var idArr = [];
-    // Grab each record from the returned authority records
-    dojo.query("record", data).forEach(function(node) {
-        var auth = {};
-        auth.text = '';
-        auth.id = 0;
-
-        // Grab each authority record field from the authority record
-        dojo.query("datafield[tag^='1']", node).forEach(function(dfNode) {
-            auth.text += dojox.xml.parser.textContent(dfNode); 
-            auth.name = dojo.attr(dfNode, 'tag');
-            auth.ind1 = dojo.attr(dfNode, 'ind1');
-            auth.ind2 = dojo.attr(dfNode, 'ind2');
-        });
-
-        
-        // Grab the ID of the authority record
-        dojo.query("datafield[tag='901'] subfield[code='c']", node).forEach(function(dfNode) {
-            auth.id = dojox.xml.parser.textContent(dfNode); 
-        });
-
-        idArr.push(parseInt(auth.id));
-
-        // Create the authority record listing entry
-        dojo.place('<div class="authEntry" id="auth' + auth.id + '"><span class="text" id="authLabel' + auth.id + '">' + auth.text + '</span></div>', "authlist-div", "last");
-
-        // Add the menu of new/edit/delete/mark-for-merge options
-        var auth_menu = new dijit.Menu({});
-
-        // "Edit" menu item
-        new dijit.MenuItem({"id": "edit_" + auth.id, "onClick": function(){
-            var pcrud = new openils.PermaCrud();
-            var auth_rec = pcrud.retrieve("are", auth.id);
-            if (auth_rec) {
-                loadMarcEditor(pcrud, auth_rec);
-            }
-        }, "label":auth_strings.MENU_EDIT}).placeAt(auth_menu, "first");
-
-        // "Merge" menu item
-        new dijit.MenuItem({"id": "merge_" + auth.id, "onClick":function(){
-            auth.text = '';
-            dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
-                auth.text += dojox.xml.parser.textContent(node); 
-            });
-
-            // If there is a toMerge item already, this is a target record
-            var mergeRole = '<td style="border: 1px solid black; padding-left: 0.5em; padding-right: 1em;">';
-            var isTarget = dojo.query('.toMerge').length;
-            if (isTarget) {
-                mergeRole += auth_strings.TARGET_RECORD + '</td>';
-            } else {
-                mergeRole += auth_strings.MASTER_RECORD + '</td>';
-            }
-
-            dojo.place('<tr class="toMerge" id="toMerge_' + auth.id + '"><td>' + mergeRole + '</td><td  style="border: 1px solid black;" id="mergeMeta_' + auth.id + '"></td><td style="border: 1px solid black; padding-left: 1em; padding-right: 1em;" >' + auth.text + '</td></tr>', 'mergebox-tbody', 'last');
-            dojo.place('<span class="authmeta" style="font-family: monospace;">' + auth.name + ' ' + auth.ind1 + auth.ind2 + '</span>', 'mergeMeta_' + auth.id, 'last');
-            dojo.removeClass('mergebox-div', 'hidden');
-        }, "label":auth_strings.MENU_MERGE}).placeAt(auth_menu, "last");
-
-        // "Delete" menu item
-        new dijit.MenuItem({
-            "id": "delete_" + auth.id,
-            "onClick":function(){
-                auth.text = '';
-
-                var pcrud = new openils.PermaCrud();
-                var auth_rec = pcrud.retrieve("are", auth.id);
-
-                // Bit of a hack to get the linked bib count until an explicit ID
-                var linkedBibs = dojox.xml.parser.textContent(
-                    dojo.query("#authLabel" + auth.id)[0].previousSibling
-                );
-
-                var delDlg = dijit.byId("delDialog_" + auth.id);
-
-                dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
-                    auth.text += dojo.trim(dojox.xml.parser.textContent(node)); 
-                });
-
-                if (!delDlg) {
-                    var content = '<div>' + dojo.string.substitute(auth_strings.CONFIRM_DELETE_TITLE, [auth.text]) + '</div>';
-                    if (parseInt(linkedBibs) > 0) {
-                        content = "<div id='delAuthSum_" + auth.id + "'>"
-                            + dojo.string.substitute(auth_strings.LINKED_BIBS, [linkedBibs])
-                            + "</div>";
-                    }
-                    content += "<div id='authMARC" + auth.id + "' style='width: 100%; display:none;'>";
-                    content += "<hr style='width: 100%;' />";
-                    content += marcToHTML(auth_rec.marc());
-                    content += "</div><hr style='width: 100%;' /><div>";
-                    content += "<input type='button' dojoType='dijit.form.Button' label='" + auth_strings.CANCEL + "' onClick='cancelDelete(" + auth.id + ")'/>";
-                    content += "<input type='button' dojoType='dijit.form.Button' label='" + auth_strings.DELETE + "' onClick='confirmDelete(" + auth.id + ")'/>";
-                    content += "<input id='viewMARC" + auth.id + "' type='button' "
-                        + "style='float:right;' dojoType='dijit.form.Button' "
-                        + "label='" + auth_strings.VIEW_MARC + "' onClick='viewMARC(" + auth.id + ")'/>";
-                    content += "<input id='hideMARC" + auth.id + "' type='button' "
-                        + "style='display: none; float:right;' dojoType='dijit.form.Button' "
-                        + "label='" + auth_strings.HIDE_MARC + "' onClick='hideMARC(" + auth.id + ")'/>";
-                    content += "</div>";
-                    delDlg = new dijit.Dialog({
-                        "id":"delDialog_" + auth.id,
-                        "title": dojo.string.substitute(auth_strings.CONFIRM_DELETE_PROMPT, [auth.id]),
-                        "content": content
-                    });
-                }
-                delDlg.show();
-
-        }, "label":auth_strings.DELETE}).placeAt(auth_menu, "last");
-
-        auth_mb = new dijit.form.DropDownButton({dropDown: auth_menu, label: auth_strings.ACTIONS, id:"menu" + auth.id});
-        auth_mb.placeAt("auth" + auth.id, "first");
-        auth_menu.startup();
-    });
-
-    showBibCount(idArr);
-}
-
-function viewMARC(recId) {
-    dojo.style(dojo.byId("authMARC" + recId), 'display', 'block');
-    dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'none');
-    dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'block');
-}
-
-function hideMARC(recId) {
-    dojo.style(dojo.byId("authMARC" + recId), 'display', 'none');
-    dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'none');
-    dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'block');
-}
-
-function marcToHTML(marc) {
-    var html = '<table><tbody>';
-    marc = dojox.xml.parser.parse(marc);
-    dojo.query('leader', marc).forEach(function(node) {
-        html += '<tr><td>LDR</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox.xml.parser.textContent(node) + '</td></tr>';
-    });
-    dojo.query('controlfield', marc).forEach(function(node) {
-        html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox.xml.parser.textContent(node) + '</td></tr>';
-    });
-    dojo.query('datafield', marc).forEach(function(node) {
-        var cnt = 0;
-        html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>' + dojo.attr(node, "ind1") + '</td><td>' + dojo.attr(node, "ind2") + '</td>';
-        dojo.query('subfield', node).forEach(function(sf) {
-            if (cnt == 0) {
-                html += '<td>$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '</td></tr>';
-                cnt = 1;
-            } else {
-                html += '<tr><td colspan="3"></td><td>$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '</td></tr>';
-            }
-        });
-    });
-    html += '</tbody></table>';
-    return html;
-}
-
-function cancelDelete(recId) {
-    dijit.byId("delDialog_" + recId).hide();
-}
-
-function confirmDelete(recId) {
-    var pcrud = new openils.PermaCrud();
-    var auth_rec = pcrud.retrieve("are", recId);
-    if (auth_rec) {
-        pcrud.eliminate(auth_rec);
-        dijit.byId("delDialog_" + recId).attr("content", dojo.string.substitute(auth_strings.CONFIRM_DELETE_RESULT, [recId]));
-        setTimeout(function() {
-            dijit.byId("delDialog_" + recId).hide();
-        }, 3000);
-    }
-}
-
-function showBibCount(authIds) {
-    /* Decorate the list with # of bibs linked to each authority record */
-    var ses = new OpenSRF.ClientSession('open-ils.cat');
-    var req = ses.request('open-ils.cat.authority.records.count_linked_bibs', authIds);
-    var linkedIds = [];
-    req.oncomplete = function(r) {
-        var msg = r.recv().content();
-        dojo.forEach(msg, function(auth) {
-                linkedIds.push(auth.authority);
-                dojo.place('<span class="bibcount">' + auth.bibs + '</span>', 'authLabel' + auth.authority, 'before');
-            }
-        );
-
-        /* Assign counts of 0 for every non-linked authority */
-        dojo.forEach(authIds, function (id) {
-            var found = false;
-            dojo.forEach(linkedIds, function (lid) {
-                if (id == lid) {
-                    found = true;
-                }
-            });
-            if (!found) {
-                dojo.place('<span class="bibcount">0</span>', 'authLabel' + id, 'before');
-            }
-        });
-    }
-    req.send();
-}
-
-function loadMarcEditor(pcrud, rec) {
-    /*
-       To run in Firefox directly, must set signed.applets.codebase_principal_support
-       to true in about:config
-     */
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-    win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
-
-    win.xulG = {
-        "record": {"marc": rec.marc(), "rtype": "are"},
-        "save": {
-            "label": auth_strings.SAVE,
-            "func": function(xmlString) {
-                rec.marc(xmlString);
-                rec.edit_date('now');
-                rec.ischanged(true);
-                pcrud.update(rec);
-                alert(auth_strings.SAVE_RESULT_SUCCESS);
-                win.close();
-            }
-        },
-        'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
-        'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
-    };
-}
-
-function authListInit() {
-    var term = cgi.param('authTerm') || '';
-    var page = cgi.param('authPage') || 0;
-    var axis = cgi.param('authAxis') || 'authority.author';
-    if (axis) {
-        dijit.byId('authAxis').attr('value', axis);
-    }
-    if (page) {
-        dijit.byId('authPage').attr('value', page);
-    }
-    if (term) {
-        dijit.byId('authTerm').attr('value', term);
-        displayRecords();
-    }
-
-    dojo.connect(dijit.byId('authAxis'), 'onKeyPress', function(evt) {
-        if (evt.keyCode == dojo.keys.ENTER) {
-            dijit.byId('authPage').attr('value', 0);
-            displayRecords();
-        }
-    }); 
-
-    dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) {
-        if (evt.keyCode == dojo.keys.ENTER) {
-            dijit.byId('authPage').attr('value', 0);
-            displayRecords();
-        }
-    });
-
-    dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) {
-        if (evt.keyCode == dojo.keys.ENTER) {
-            dijit.byId('authPage').attr('value', 0);
-            displayRecords();
-        }
-    });
-
-    dijit.byId('authTerm').focus();
-
-}
-dojo.addOnLoad(authListInit);
-
-function displayRecords(parms) {
-
-    if (parms && parms.page) {
-        if (parms.page == 'next') {
-            page = dijit.byId('authPage').attr('value');
-            dijit.byId('authPage').attr('value', page + 1);
-        } else if (parms.page == 'prev') {
-            page = dijit.byId('authPage').attr('value');
-            dijit.byId('authPage').attr('value', page - 1);
-        } else {
-            dijit.byId('authPage').attr('value', parms.page);
-        }
-    }
-
-    /* Protect against null input */
-    if (!dijit.byId('authTerm').attr('value')) {
-        return;
-    }
-
-    /* Clear out the current contents of the page */
-    var widgets = dijit.findWidgets(dojo.byId('authlist-div'));
-    dojo.forEach(widgets, function(w) { w.destroyRecursive(true); });
-
-    dojo.query("#authlist-div div").orphan();
-
-    var url = '/opac/extras/browse/marcxml/authority.'
-        + dijit.byId('authAxis').attr('value')
-        // + '/' + dijit.byId('authOU').attr('value')
-        + '/1' // replace with preceding line if OUs gain some meaning
-        + '/' + dijit.byId('authTerm').attr('value')
-        + '/' + dijit.byId('authPage').attr('value')
-        + '/' + '20' // 20 results per page
-    ;
-    dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities });
-}
-
-function clearMergeRecords() {
-    var records = dojo.query('.toMerge').orphan();
-    dojo.addClass('mergebox-div', 'hidden');
-}
-
-function mergeRecords() {
-    var records = dojo.query('.toMerge').attr('id');
-    dojo.forEach(records, function(item, idx) {
-        records[idx] = parseInt(item.slice(item.lastIndexOf('_') + 1));
-    });
-
-    /* Take the first record in the list and use that as the master */
-    fieldmapper.standardRequest(
-        ['open-ils.cat', 'open-ils.cat.authority.records.merge'],
-        {   async: false,
-            params: [openils.User.authtoken, records.shift(), records],
-            oncomplete : function(r) {
-                alert(auth_strings.MERGE_RESULT_SUCCESS);
-                clearMergeRecords();
-                displayRecords();
-            }
-        }
-    );
-}
+require([
+       "dijit/Dialog",
+       "dijit/form/Button",
+       "dijit/form/DropDownButton",
+       "dijit/form/FilteringSelect",
+       "dijit/form/Form",
+       "dijit/form/NumberSpinner",
+       "dijit/form/TextBox",
+       "dijit/Menu",
+       "dijit/MenuItem",
+       "dojox/xml/parser",
+       "DojoSRF",
+       "fieldmapper/Fieldmapper",
+       "openils/CGI",
+       "openils/PermaCrud",
+       "openils/XUL",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/PCrudAutocompleteBox"
+       ],
+function(dijit_Dialog,
+       dijit_form_Button,
+       dijit_form_DropDownButton,
+       dijit_form_FilteringSelect,
+       dijit_form_Form,
+       dijit_form_NumberSpinner,
+       dijit_form_TextBox,
+       dijit_Menu,
+       dijit_MenuItem,
+       dojox_xml_parser,
+       DojoSRF,
+       fieldmapper_Fieldmapper,
+       openils_CGI,
+       openils_PermaCrud,
+       openils_XUL,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_PCrudAutocompleteBox){
+       dojo.requireLocalization("openils.authority", "authority");
+       var auth_strings = dojo.i18n.getLocalization("openils.authority", "authority");
+       
+       var cgi = new openils_CGI();
+       
+       /*
+       // OrgUnits do not currently affect the retrieval of authority records,
+       // but this is how to display them if they become OrgUnit-aware
+       function authOUListInit() {
+           new openils.User().buildPermOrgSelector(
+               "STAFF_LOGIN", // anywhere you can log in
+               dijit.byId("authOU"),
+               null, // pre-selected org
+               null
+           );
+       }
+       dojo.addOnLoad(authOUListInit);
+       */
+       function displayAuthorities(data) { 
+       
+           var idArr = [];
+           // Grab each record from the returned authority records
+           dojo.query("record", data).forEach(function(node) {
+               var auth = {};
+               auth.text = '';
+               auth.id = 0;
+       
+               // Grab each authority record field from the authority record
+               dojo.query("datafield[tag^='1']", node).forEach(function(dfNode) {
+                   auth.text += dojox_xml_parser.textContent(dfNode); 
+                   auth.name = dojo.attr(dfNode, 'tag');
+                   auth.ind1 = dojo.attr(dfNode, 'ind1');
+                   auth.ind2 = dojo.attr(dfNode, 'ind2');
+               });
+       
+               
+               // Grab the ID of the authority record
+               dojo.query("datafield[tag='901'] subfield[code='c']", node).forEach(function(dfNode) {
+                   auth.id = dojox_xml_parser.textContent(dfNode); 
+               });
+       
+               idArr.push(parseInt(auth.id));
+       
+               // Create the authority record listing entry
+               dojo.place('<div class="authEntry" id="auth' + auth.id + '"><span class="text" id="authLabel' + auth.id + '">' + auth.text + '</span></div>', "authlist-div", "last");
+       
+               // Add the menu of new/edit/delete/mark-for-merge options
+               var auth_menu = new dijit_Menu({});
+       
+               // "Edit" menu item
+               new dijit_MenuItem({"id": "edit_" + auth.id, "onClick": function(){
+                   var pcrud = new openils_PermaCrud();
+                   var auth_rec = pcrud.retrieve("are", auth.id);
+                   if (auth_rec) {
+                       loadMarcEditor(pcrud, auth_rec);
+                   }
+               }, "label":auth_strings.MENU_EDIT}).placeAt(auth_menu, "first");
+       
+               // "Merge" menu item
+               new dijit_MenuItem({"id": "merge_" + auth.id, "onClick":function(){
+                   auth.text = '';
+                   dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
+                       auth.text += dojox_xml_parser.textContent(node); 
+                   });
+       
+                   // If there is a toMerge item already, this is a target record
+                   var mergeRole = '<td style="border: 1px solid black; padding-left: 0.5em; padding-right: 1em;">';
+                   var isTarget = dojo.query('.toMerge').length;
+                   if (isTarget) {
+                       mergeRole += auth_strings.TARGET_RECORD + '</td>';
+                   } else {
+                       mergeRole += auth_strings.MASTER_RECORD + '</td>';
+                   }
+       
+                   dojo.place('<tr class="toMerge" id="toMerge_' + auth.id + '"><td>' + mergeRole + '</td><td  style="border: 1px solid black;" id="mergeMeta_' + auth.id + '"></td><td style="border: 1px solid black; padding-left: 1em; padding-right: 1em;" >' + auth.text + '</td></tr>', 'mergebox-tbody', 'last');
+                   dojo.place('<span class="authmeta" style="font-family: monospace;">' + auth.name + ' ' + auth.ind1 + auth.ind2 + '</span>', 'mergeMeta_' + auth.id, 'last');
+                   dojo.removeClass('mergebox-div', 'hidden');
+               }, "label":auth_strings.MENU_MERGE}).placeAt(auth_menu, "last");
+       
+               // "Delete" menu item
+               new dijit_MenuItem({
+                   "id": "delete_" + auth.id,
+                   "onClick":function(){
+                       auth.text = '';
+       
+                       var pcrud = new openils_PermaCrud();
+                       var auth_rec = pcrud.retrieve("are", auth.id);
+       
+                       // Bit of a hack to get the linked bib count until an explicit ID
+                       var linkedBibs = dojox_xml_parser.textContent(
+                           dojo.query("#authLabel" + auth.id)[0].previousSibling
+                       );
+       
+                       var delDlg = dijit.byId("delDialog_" + auth.id);
+       
+                       dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
+                           auth.text += dojo.trim(dojox_xml_parser.textContent(node)); 
+                       });
+       
+                       if (!delDlg) {
+                           var content = '<div>' + dojo.string.substitute(auth_strings.CONFIRM_DELETE_TITLE, [auth.text]) + '</div>';
+                           if (parseInt(linkedBibs) > 0) {
+                               content = "<div id='delAuthSum_" + auth.id + "'>"
+                                   + dojo.string.substitute(auth_strings.LINKED_BIBS, [linkedBibs])
+                                   + "</div>";
+                           }
+                           content += "<div id='authMARC" + auth.id + "' style='width: 100%; display:none;'>";
+                           content += "<hr style='width: 100%;' />";
+                           content += marcToHTML(auth_rec.marc());
+                           content += "</div><hr style='width: 100%;' /><div>";
+                           content += "<input type='button' dojoType='dijit_form_Button' label='" + auth_strings.CANCEL + "' onClick='cancelDelete(" + auth.id + ")'/>";
+                           content += "<input type='button' dojoType='dijit_form_Button' label='" + auth_strings.DELETE + "' onClick='confirmDelete(" + auth.id + ")'/>";
+                           content += "<input id='viewMARC" + auth.id + "' type='button' "
+                               + "style='float:right;' dojoType='dijit_form_Button' "
+                               + "label='" + auth_strings.VIEW_MARC + "' onClick='viewMARC(" + auth.id + ")'/>";
+                           content += "<input id='hideMARC" + auth.id + "' type='button' "
+                               + "style='display: none; float:right;' dojoType='dijit_form_Button' "
+                               + "label='" + auth_strings.HIDE_MARC + "' onClick='hideMARC(" + auth.id + ")'/>";
+                           content += "</div>";
+                           delDlg = new dijit_Dialog({
+                               "id":"delDialog_" + auth.id,
+                               "title": dojo.string.substitute(auth_strings.CONFIRM_DELETE_PROMPT, [auth.id]),
+                               "content": content
+                           });
+                       }
+                       delDlg.show();
+       
+               }, "label":auth_strings.DELETE}).placeAt(auth_menu, "last");
+       
+               auth_mb = new dijit_form_DropDownButton({dropDown: auth_menu, label: auth_strings.ACTIONS, id:"menu" + auth.id});
+               auth_mb.placeAt("auth" + auth.id, "first");
+               auth_menu.startup();
+           });
+       
+           showBibCount(idArr);
+       }
+       
+       function viewMARC(recId) {
+           dojo.style(dojo.byId("authMARC" + recId), 'display', 'block');
+           dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'none');
+           dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'block');
+       }
+       
+       function hideMARC(recId) {
+           dojo.style(dojo.byId("authMARC" + recId), 'display', 'none');
+           dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'none');
+           dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'block');
+       }
+       
+       function marcToHTML(marc) {
+           var html = '<table><tbody>';
+           marc = dojox_xml_parser.parse(marc);
+           dojo.query('leader', marc).forEach(function(node) {
+               html += '<tr><td>LDR</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox_xml_parser.textContent(node) + '</td></tr>';
+           });
+           dojo.query('controlfield', marc).forEach(function(node) {
+               html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox_xml_parser.textContent(node) + '</td></tr>';
+           });
+           dojo.query('datafield', marc).forEach(function(node) {
+               var cnt = 0;
+               html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>' + dojo.attr(node, "ind1") + '</td><td>' + dojo.attr(node, "ind2") + '</td>';
+               dojo.query('subfield', node).forEach(function(sf) {
+                   if (cnt == 0) {
+                       html += '<td>$' + dojo.attr(sf, "code") + ' ' + dojox_xml_parser.textContent(sf) + '</td></tr>';
+                       cnt = 1;
+                   } else {
+                       html += '<tr><td colspan="3"></td><td>$' + dojo.attr(sf, "code") + ' ' + dojox_xml_parser.textContent(sf) + '</td></tr>';
+                   }
+               });
+           });
+           html += '</tbody></table>';
+           return html;
+       }
+       
+       function cancelDelete(recId) {
+           dijit.byId("delDialog_" + recId).hide();
+       }
+       
+       function confirmDelete(recId) {
+           var pcrud = new openils_PermaCrud();
+           var auth_rec = pcrud.retrieve("are", recId);
+           if (auth_rec) {
+               pcrud.eliminate(auth_rec);
+               dijit.byId("delDialog_" + recId).attr("content", dojo.string.substitute(auth_strings.CONFIRM_DELETE_RESULT, [recId]));
+               setTimeout(function() {
+                   dijit.byId("delDialog_" + recId).hide();
+               }, 3000);
+           }
+       }
+       
+       function showBibCount(authIds) {
+           /* Decorate the list with # of bibs linked to each authority record */
+           var ses = new OpenSRF.ClientSession('open-ils.cat');
+           var req = ses.request('open-ils.cat.authority.records.count_linked_bibs', authIds);
+           var linkedIds = [];
+           req.oncomplete = function(r) {
+               var msg = r.recv().content();
+               dojo.forEach(msg, function(auth) {
+                       linkedIds.push(auth.authority);
+                       dojo.place('<span class="bibcount">' + auth.bibs + '</span>', 'authLabel' + auth.authority, 'before');
+                   }
+               );
+       
+               /* Assign counts of 0 for every non-linked authority */
+               dojo.forEach(authIds, function (id) {
+                   var found = false;
+                   dojo.forEach(linkedIds, function (lid) {
+                       if (id == lid) {
+                           found = true;
+                       }
+                   });
+                   if (!found) {
+                       dojo.place('<span class="bibcount">0</span>', 'authLabel' + id, 'before');
+                   }
+               });
+           }
+           req.send();
+       }
+       
+       function loadMarcEditor(pcrud, rec) {
+           /*
+              To run in Firefox directly, must set signed.applets.codebase_principal_support
+              to true in about:config
+            */
+           netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+           win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
+       
+           win.xulG = {
+               "record": {"marc": rec.marc(), "rtype": "are"},
+               "save": {
+                   "label": auth_strings.SAVE,
+                   "func": function(xmlString) {
+                       rec.marc(xmlString);
+                       rec.edit_date('now');
+                       rec.ischanged(true);
+                       pcrud.update(rec);
+                       alert(auth_strings.SAVE_RESULT_SUCCESS);
+                       win.close();
+                   }
+               },
+               'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
+               'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
+           };
+       }
+       
+       function authListInit() {
+           var term = cgi.param('authTerm') || '';
+           var page = cgi.param('authPage') || 0;
+           var axis = cgi.param('authAxis') || 'authority.author';
+           if (axis) {
+               dijit.byId('authAxis').attr('value', axis);
+           }
+           if (page) {
+               dijit.byId('authPage').attr('value', page);
+           }
+           if (term) {
+               dijit.byId('authTerm').attr('value', term);
+               displayRecords();
+           }
+       
+           dojo.connect(dijit.byId('authAxis'), 'onKeyPress', function(evt) {
+               if (evt.keyCode == dojo.keys.ENTER) {
+                   dijit.byId('authPage').attr('value', 0);
+                   displayRecords();
+               }
+           }); 
+       
+           dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) {
+               if (evt.keyCode == dojo.keys.ENTER) {
+                   dijit.byId('authPage').attr('value', 0);
+                   displayRecords();
+               }
+           });
+       
+           dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) {
+               if (evt.keyCode == dojo.keys.ENTER) {
+                   dijit.byId('authPage').attr('value', 0);
+                   displayRecords();
+               }
+           });
+       
+           dijit.byId('authTerm').focus();
+       
+       }
+       dojo.addOnLoad(authListInit);
+       
+       function displayRecords(parms) {
+       
+           if (parms && parms.page) {
+               if (parms.page == 'next') {
+                   page = dijit.byId('authPage').attr('value');
+                   dijit.byId('authPage').attr('value', page + 1);
+               } else if (parms.page == 'prev') {
+                   page = dijit.byId('authPage').attr('value');
+                   dijit.byId('authPage').attr('value', page - 1);
+               } else {
+                   dijit.byId('authPage').attr('value', parms.page);
+               }
+           }
+       
+           /* Protect against null input */
+           if (!dijit.byId('authTerm').attr('value')) {
+               return;
+           }
+       
+           /* Clear out the current contents of the page */
+           var widgets = dijit.findWidgets(dojo.byId('authlist-div'));
+           dojo.forEach(widgets, function(w) { w.destroyRecursive(true); });
+       
+           dojo.query("#authlist-div div").orphan();
+       
+           var url = '/opac/extras/browse/marcxml/authority.'
+               + dijit.byId('authAxis').attr('value')
+               // + '/' + dijit.byId('authOU').attr('value')
+               + '/1' // replace with preceding line if OUs gain some meaning
+               + '/' + dijit.byId('authTerm').attr('value')
+               + '/' + dijit.byId('authPage').attr('value')
+               + '/' + '20' // 20 results per page
+           ;
+           dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities });
+       }
+       
+       function clearMergeRecords() {
+           var records = dojo.query('.toMerge').orphan();
+           dojo.addClass('mergebox-div', 'hidden');
+       }
+       
+       function mergeRecords() {
+           var records = dojo.query('.toMerge').attr('id');
+           dojo.forEach(records, function(item, idx) {
+               records[idx] = parseInt(item.slice(item.lastIndexOf('_') + 1));
+           });
+       
+           /* Take the first record in the list and use that as the master */
+           fieldmapper.standardRequest(
+               ['open-ils.cat', 'open-ils.cat.authority.records.merge'],
+               {   async: false,
+                   params: [openils.User.authtoken, records.shift(), records],
+                   oncomplete : function(r) {
+                       alert(auth_strings.MERGE_RESULT_SUCCESS);
+                       clearMergeRecords();
+                       displayRecords();
+                   }
+               }
+           );
+       }
+       
+
+});
\ No newline at end of file
index 886c8c3..e433774 100644 (file)
-dojo.require('dojo.date.locale');
-dojo.require('dojo.cookie');
-dojo.require('dojo.date.stamp');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.form.NumberSpinner');
-dojo.require('openils.CGI');
-dojo.require('openils.Util');
-dojo.require('openils.User');
-dojo.require('openils.Event');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-
-dojo.requireLocalization('openils.circ', 'selfcheck');
-var localeStrings = dojo.i18n.getLocalization('openils.circ', 'selfcheck');
-
-
-const SET_BARCODE_REGEX = 'opac.barcode_regex';
-const SET_PATRON_TIMEOUT = 'circ.selfcheck.patron_login_timeout';
-const SET_AUTO_OVERRIDE_EVENTS = 'circ.selfcheck.auto_override_checkout_events';
-const SET_PATRON_PASSWORD_REQUIRED = 'circ.selfcheck.patron_password_required';
-const SET_AUTO_RENEW_INTERVAL = 'circ.checkout_auto_renew_age';
-const SET_WORKSTATION_REQUIRED = 'circ.selfcheck.workstation_required';
-const SET_ALERT_POPUP = 'circ.selfcheck.alert.popup';
-const SET_ALERT_SOUND = 'circ.selfcheck.alert.sound';
-const SET_CC_PAYMENT_ALLOWED = 'credit.payments.allow';
-// This setting only comes into play if COPY_NOT_AVAILABLE is in the SET_AUTO_OVERRIDE_EVENTS list
-const SET_BLOCK_CHECKOUT_ON_COPY_STATUS = 'circ.selfcheck.block_checkout_on_copy_status';
-
-// set before the login dialog is rendered
-openils.User.default_login_agent = 'selfcheck';
-
-function SelfCheckManager() {
-
-    this.cgi = new openils.CGI();
-    this.staff = null; 
-    this.workstation = null;
-    this.authtoken = null;
-
-    this.patron = null; 
-    this.patronBarcodeRegex = null;
-
-    this.checkouts = [];
-    this.itemsOut = [];
-
-    // During renewals, keep track of the ID of the previous circulation. 
-    // Previous circ is used for tracking failed renewals (for receipts).
-    this.prevCirc = null;
-
-    // current item barcode
-    this.itemBarcode = null; 
-
-    // are we currently performing a renewal?
-    this.isRenewal = false; 
-
-    // dict of org unit settings for "here"
-    this.orgSettings = {};
-
-    // Construct a mock checkout for debugging purposes
-    if(this.mockCheckouts = this.cgi.param('mock-circ')) {
-
-        this.mockCheckout = {
-            payload : {
-                record : new fieldmapper.mvr(),
-                copy : new fieldmapper.acp(),
-                circ : new fieldmapper.circ()
-            }
-        };
-
-        this.mockCheckout.payload.record.title('Jazz improvisation for guitar');
-        this.mockCheckout.payload.record.author('Wise, Les');
-        this.mockCheckout.payload.record.isbn('0634033565');
-        this.mockCheckout.payload.copy.barcode('123456789');
-        this.mockCheckout.payload.circ.renewal_remaining(1);
-        this.mockCheckout.payload.circ.parent_circ(1);
-        this.mockCheckout.payload.circ.due_date('2012-12-21');
-    }
-
-    this.initPrinter();
-}
-
-SelfCheckManager.prototype.setupStaffLogin = function(verify) {
-
-    if(verify) oilsSetupUser(); 
-    this.staff = openils.User.user;
-    this.workstation = openils.User.workstation;
-    this.authtoken = openils.User.authtoken;
-}
-
-
-
-/**
- * Fetch the org-unit settings, initialize the display, etc.
- */
-SelfCheckManager.prototype.init = function() {
-
-    this.setupStaffLogin();
-    this.loadOrgSettings();
-
-    this.circTbody = dojo.byId('oils-selfck-circ-tbody');
-    this.itemsOutTbody = dojo.byId('oils-selfck-circ-out-tbody');
-
-    // workstation is required but none provided
-    if(this.orgSettings[SET_WORKSTATION_REQUIRED] && !this.workstation) {
-        if(confirm(dojo.string.substitute(localeStrings.WORKSTATION_REQUIRED))) {
-            this.registerWorkstation();
-        }
-        return;
-    }
-    
-    var self = this;
-    // connect onclick handlers to the various navigation links
-    var linkHandlers = {
-        'oils-selfck-hold-details-link' : function() { self.drawHoldsPage(); },
-        'oils-selfck-view-fines-link' : function() { self.drawFinesPage(); },
-        'oils-selfck-pay-fines-link' : function() {
-            self.goToTab("payment");
-            self.drawPayFinesPage(
-                self.patron,
-                self.getSelectedFinesTotal(),
-                self.getSelectedFineTransactions(),
-                function(resp) {
-                    var evt = openils.Event.parse(resp);
-                    if(evt) {
-                        var message = evt + '';
-                        if(evt.textcode == 'CREDIT_PROCESSOR_DECLINED_TRANSACTION' && evt.payload)
-                            message += '\n' + evt.payload.error_message;
-                        if(evt.textcode == 'INVALID_USER_XACT_ID')
-                            message += '\n' + localeStrings.PAYMENT_INVALID_USER_XACT_ID;
-                        self.handleAlert(message, true, 'payment-failure');
-                        return;
-                    }
-
-                    self.patron.last_xact_id(resp.last_xact_id); // update to match latest from server
-                    self.printPaymentReceipt(
-                        resp,
-                        function() {
-                            self.updateFinesSummary();
-                            self.drawFinesPage();
-                        }
-                    );
-                }
-            );
-        },
-        'oils-selfck-nav-home' : function() { self.drawCircPage(); },
-        'oils-selfck-nav-logout' : function() { self.logoutPatron(); },
-        'oils-selfck-nav-logout-print' : function() { self.logoutPatron(true); },
-        'oils-selfck-items-out-details-link' : function() { self.drawItemsOutPage(); },
-        'oils-selfck-print-list-link' : function() { self.printList(); }
-    }
-
-    for(var id in linkHandlers) 
-        dojo.connect(dojo.byId(id), 'onclick', linkHandlers[id]);
-
-
-    if(this.cgi.param('patron')) {
-        
-        // Patron barcode via cgi param.  Mainly used for debugging and
-        // only works if password is not required by policy
-        this.loginPatron(this.cgi.param('patron'));
-
-    } else {
-        this.drawLoginPage();
-    }
-
-    /**
-     * To test printing, pass a URL param of 'testprint'.  The value for the param
-     * should be a JSON string like so:  [{circ:<circ_id>}, ...]
-     */
-    var testPrint = this.cgi.param('testprint');
-    if(testPrint) {
-        this.checkouts = JSON2js(testPrint);
-        this.printSessionReceipt();
-        this.checkouts = [];
-    }
-}
-
-
-SelfCheckManager.prototype.getSelectedFinesTotal = function() {
-    var total = 0;
-    dojo.forEach(
-        dojo.query("[name=selector]", this.finesTbody),
-        function(input) {
-            if(input.checked)
-                total += Number(input.getAttribute("balance_owed"));
-        }
-    );
-    return total.toFixed(2);
-};
-
-SelfCheckManager.prototype.getSelectedFineTransactions = function() {
-    return dojo.query("[name=selector]", this.finesTbody).
-        filter(function (o) { return o.checked }).
-        map(
-            function (o) {
-                return [
-                    o.getAttribute("xact"),
-                    Number(o.getAttribute("balance_owed")).toFixed(2)
-                ];
-            }
-        );
-};
-
-/**
- * Registers a new workstion
- */
-SelfCheckManager.prototype.registerWorkstation = function() {
-    
-    oilsSelfckWsDialog.show();
-
-    new openils.User().buildPermOrgSelector(
-        'REGISTER_WORKSTATION', 
-        oilsSelfckWsLocSelector, 
-        this.staff.home_ou()
-    );
-
-
-    var self = this;
-    dojo.connect(oilsSelfckWsSubmit, 'onClick', 
-
-        function() {
-            oilsSelfckWsDialog.hide();
-            var name = oilsSelfckWsLocSelector.attr('displayedValue') + '-' + oilsSelfckWsName.attr('value');
-
-            var res = fieldmapper.standardRequest(
-                ['open-ils.actor', 'open-ils.actor.workstation.register'],
-                { params : [
-                        self.authtoken, name, oilsSelfckWsLocSelector.attr('value')
-                    ]
-                }
-            );
-
-            if(evt = openils.Event.parse(res)) {
-                if(evt.textcode == 'WORKSTATION_NAME_EXISTS') {
-                    if(confirm(localeStrings.WORKSTATION_EXISTS)) {
-                        location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;
-                    } else {
-                        self.registerWorkstation();
-                    }
-                    return;
-                } else {
-                    alert(evt);
-                }
-            } else {
-                location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;
-            }
-        }
-    );
-}
-
-/**
- * Loads the org unit settings
- */
-SelfCheckManager.prototype.loadOrgSettings = function() {
-
-    var settings = fieldmapper.aou.fetchOrgSettingBatch(
-        this.staff.ws_ou(), [
-            SET_BARCODE_REGEX,
-            SET_PATRON_TIMEOUT,
-            SET_ALERT_POPUP,
-            SET_ALERT_SOUND,
-            SET_AUTO_OVERRIDE_EVENTS,
-            SET_BLOCK_CHECKOUT_ON_COPY_STATUS,
-            SET_PATRON_PASSWORD_REQUIRED,
-            SET_AUTO_RENEW_INTERVAL,
-            SET_WORKSTATION_REQUIRED,
-            SET_CC_PAYMENT_ALLOWED
-        ]
-    );
-
-    for(k in settings) {
-        if(settings[k])
-            this.orgSettings[k] = settings[k].value;
-    }
-
-    if(settings[SET_BARCODE_REGEX]) 
-        this.patronBarcodeRegex = new RegExp(settings[SET_BARCODE_REGEX].value);
-}
-
-SelfCheckManager.prototype.drawLoginPage = function() {
-    var self = this;
-
-    var bcHandler = function(barcode_or_usrname) {
-        // handle patron barcode/usrname entry
-
-        if(self.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
-            
-            // password is required.  wire up the scan box to read it
-            self.updateScanBox({
-                msg : 'Please enter your password', // TODO i18n 
-                handler : function(pw) { self.loginPatron(barcode_or_usrname, pw); },
-                password : true
-            });
-
-        } else {
-            // password is not required, go ahead and login
-            self.loginPatron(barcode_or_usrname);
-        }
-    };
-
-    this.updateScanBox({
-        msg : 'Please log in with your username or library barcode.', // TODO
-        handler : bcHandler
-    });
-}
-
-/**
- * Login the patron.  
- */
-SelfCheckManager.prototype.loginPatron = function(barcode_or_usrname, passwd) {
-
-    this.setupStaffLogin(true); // verify still valid
-
-    var barcode = null;
-    var usrname = null;
-    console.log('testing ' + barcode_or_usrname);
-    if (barcode_or_usrname.match(this.patronBarcodeRegex)) {
-        console.log('barcode');
-        barcode = barcode_or_usrname;
-    } else {
-        console.log('usrname');
-        usrname = barcode_or_usrname;
-    }
-
-    if(this.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
-        
-        if(!passwd) {
-            // would only happen in dev/debug mode when using the patron= param
-            alert('password required by org setting.  remove patron= from URL'); 
-            return;
-        }
-
-        // patron password is required.  Verify it.
-
-        var self = this;
-        new openils.User().auth_verify(
-            {   username : usrname, barcode : barcode, 
-                type : 'opac', passwd : passwd, agent : 'selfcheck' },
-            function(OK) {
-                if (OK) {
-                    self.fetchPatron(barcode, usrname);
-
-                } else {
-                    // auth verify failed
-                    self.handleAlert(
-                        dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode_or_usrname]),
-                        false, 'login-failure'
-                    );
-                    self.drawLoginPage();
-                }
-            }
-        );
-
-    } else {
-        this.fetchPatron(barcode, usrname);
-    }
-};
-
-SelfCheckManager.prototype.fetchPatron = function(barcode, usrname) {
-
-    var patron_id = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.retrieve_id_by_barcode_or_username'],
-        {params : [this.authtoken, barcode, usrname]}
-    );
-
-    // retrieve the fleshed user by id
-    this.patron = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
-        {params : [this.authtoken, patron_id]}
-    );
-
-    var evt = openils.Event.parse(this.patron);
-    
-    // verify validity of the card used to log in
-    var inactiveCard = false;
-    if(!evt) {
-        var card;
-        if (barcode) {
-            card = this.patron.cards().filter(
-                function(c) { return (c.barcode() == barcode); })[0];
-        } else {
-            card = this.patron.card();
-        }
-        inactiveCard = !openils.Util.isTrue(card.active());
-    }
-
-    if(evt || inactiveCard) {
-        this.handleAlert(
-            dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode || usrname]),
-            false, 'login-failure'
-        );
-        this.drawLoginPage();
-
-    } else {
-
-        this.handleAlert('', false, 'login-success');
-        dojo.byId('oils-selfck-user-banner').innerHTML = 
-            dojo.string.substitute(localeStrings.WELCOME_BANNER, [this.patron.first_given_name()]);
-        this.drawCircPage();
-    }
-}
-
-
-SelfCheckManager.prototype.handleAlert = function(message, shouldPopup, sound) {
-
-    console.log("Handling alert " + message);
-
-    dojo.byId('oils-selfck-status-div').innerHTML = message;
-
-    if(shouldPopup)
-        openils.Util.addCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );
-    else
-        openils.Util.removeCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );
-
-    if(shouldPopup && this.orgSettings[SET_ALERT_POPUP]) 
-        alert(message);
-
-    if(sound && this.orgSettings[SET_ALERT_SOUND])
-        openils.Util.playAudioUrl(SelfCheckManager.audioConfig[sound]);
-}
-
-
-/**
- * Manages the main input box
- * @param msg The context message to display with the box
- * @param clearOnly Don't update the context message, just clear the value and re-focus
- * @param handler Optional "on-enter" handler.  
- */
-SelfCheckManager.prototype.updateScanBox = function(args) {
-    args = args || {};
-
-    if(args.select) {
-        selfckScanBox.domNode.select();
-    } else {
-        selfckScanBox.attr('value', '');
-    }
-
-    if(args.password) {
-        selfckScanBox.domNode.setAttribute('type', 'password');
-    } else {
-        selfckScanBox.domNode.setAttribute('type', '');
-    }
-
-    if(args.value)
-        selfckScanBox.attr('value', args.value);
-
-    if(args.msg) 
-        dojo.byId('oils-selfck-scan-text').innerHTML = args.msg;
-
-    if(selfckScanBox._lastHandler && (args.handler || args.clearHandler)) {
-        dojo.disconnect(selfckScanBox._lastHandler);
-    }
-
-    if(args.handler) {
-        selfckScanBox._lastHandler = dojo.connect(
-            selfckScanBox, 
-            'onKeyDown', 
-            function(e) {
-                if(e.keyCode != dojo.keys.ENTER) 
-                    return;
-                args.handler(selfckScanBox.attr('value'));
-            }
-        );
-    }
-
-    selfckScanBox.focus();
-}
-
-/**
- *  Sets up the checkout/renewal interface
- */
-SelfCheckManager.prototype.drawCircPage = function() {
-
-    openils.Util.show('oils-selfck-circ-tbody', 'table-row-group');
-    this.goToTab('checkout');
-
-    while(this.itemsOutTbody.childNodes[0])
-        this.itemsOutTbody.removeChild(this.itemsOutTbody.childNodes[0]);
-
-    var self = this;
-    this.updateScanBox({
-        msg : 'Please enter an item barcode', // TODO i18n
-        handler : function(barcode) { self.checkout(barcode); }
-    });
-
-    if(!this.circTemplate)
-        this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row'));
-
-    // fines summary
-    this.updateFinesSummary();
-
-    // holds summary
-    this.updateHoldsSummary();
-
-    // items out summary
-    this.updateCircSummary();
-
-    // render mock checkouts for debugging?
-    if(this.mockCheckouts) {
-        for(var i in [1,2,3]) 
-            this.displayCheckout(this.mockCheckout, 'checkout');
-    }
-}
-
-
-SelfCheckManager.prototype.updateFinesSummary = function() {
-    var self = this; 
-
-    // fines summary
-    fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.fines.summary'],
-        {   async : true,
-            params : [this.authtoken, this.patron.id()],
-            oncomplete : function(r) {
-
-                var summary = openils.Util.readResponse(r);
-
-                dojo.byId('oils-selfck-fines-total').innerHTML = 
-                    dojo.string.substitute(
-                        localeStrings.TOTAL_FINES_ACCOUNT, 
-                        [summary.balance_owed()]
-                    );
-
-                self.creditPayableBalance = summary.balance_owed();
-            }
-        }
-    );
-}
-
-
-SelfCheckManager.prototype.drawItemsOutPage = function() {
-    openils.Util.hide('oils-selfck-circ-tbody');
-
-    this.goToTab('items_out');
-
-    while(this.itemsOutTbody.childNodes[0])
-        this.itemsOutTbody.removeChild(this.itemsOutTbody.childNodes[0]);
-
-    progressDialog.show(true);
-    
-    var self = this;
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.actor.user.checked_out.atomic'],
-        {
-            async : true,
-            params : [this.authtoken, this.patron.id()],
-            oncomplete : function(r) {
-
-                var resp = openils.Util.readResponse(r);
-
-                var circs = resp.sort(
-                    function(a, b) {
-                        if(a.circ.due_date() > b.circ.due_date())
-                            return -1;
-                        return 1;
-                    }
-                );
-
-                progressDialog.hide();
-
-                self.itemsOut = [];
-                dojo.forEach(circs,
-                    function(circ) {
-                        self.itemsOut.push(circ.circ.id());
-                        self.displayCheckout(
-                            {payload : circ}, 
-                            (circ.circ.parent_circ()) ? 'renew' : 'checkout',
-                            true
-                        );
-                    }
-                );
-            }
-        }
-    );
-}
-
-
-SelfCheckManager.prototype.goToTab = function(name) {
-    this.tabName = name;
-
-    openils.Util.hide('oils-selfck-fines-page');
-    openils.Util.hide('oils-selfck-payment-page');
-    openils.Util.hide('oils-selfck-holds-page');
-    openils.Util.hide('oils-selfck-circ-page');
-    openils.Util.hide('oils-selfck-pay-fines-link');
-    
-    switch(name) {
-        case 'checkout':
-            openils.Util.show('oils-selfck-circ-page');
-            break;
-        case 'items_out':
-            openils.Util.show('oils-selfck-circ-page');
-            break;
-        case 'holds':
-            openils.Util.show('oils-selfck-holds-page');
-            break;
-        case 'fines':
-            openils.Util.show('oils-selfck-fines-page');
-            break;
-        case 'payment':
-            openils.Util.show('oils-selfck-payment-page');
-            break;
-    }
-}
-
-
-SelfCheckManager.prototype.printList = function() {
-    switch(this.tabName) {
-        case 'checkout':
-            this.printSessionReceipt();
-            break;
-        case 'items_out':
-            this.printItemsOutReceipt();
-            break;
-        case 'holds':
-            this.printHoldsReceipt();
-            break;
-        case 'fines':
-            this.printFinesReceipt();
-            break;
-    }
-}
-
-SelfCheckManager.prototype.updateHoldsSummary = function() {
-
-    if(!this.holdsSummary) {
-        var summary = fieldmapper.standardRequest(
-            ['open-ils.circ', 'open-ils.circ.holds.user_summary'],
-            {params : [this.authtoken, this.patron.id()]}
-        );
-
-        this.holdsSummary = {};
-        this.holdsSummary.ready = Number(summary['4']);
-        this.holdsSummary.total = 0;
-
-        for(var i in summary) 
-            this.holdsSummary.total += Number(summary[i]);
-    }
-
-    dojo.byId('oils-selfck-holds-total').innerHTML = 
-        dojo.string.substitute(
-            localeStrings.TOTAL_HOLDS, 
-            [this.holdsSummary.total]
-        );
-
-    dojo.byId('oils-selfck-holds-ready').innerHTML = 
-        dojo.string.substitute(
-            localeStrings.HOLDS_READY_FOR_PICKUP, 
-            [this.holdsSummary.ready]
-        );
-}
-
-
-SelfCheckManager.prototype.updateCircSummary = function(increment) {
-
-    if(!this.circSummary) {
-
-        var summary = fieldmapper.standardRequest(
-            ['open-ils.actor', 'open-ils.actor.user.checked_out.count'],
-            {params : [this.authtoken, this.patron.id()]}
-        );
-
-        this.circSummary = {
-            total : Number(summary.out) + Number(summary.overdue),
-            overdue : Number(summary.overdue),
-            session : 0
-        };
-    }
-
-    if(increment) {
-        // local checkout occurred.  Add to the total and the session.
-        this.circSummary.total += 1;
-        this.circSummary.session += 1;
-    }
-
-    dojo.byId('oils-selfck-circ-account-total').innerHTML = 
-        dojo.string.substitute(
-            localeStrings.TOTAL_ITEMS_ACCOUNT, 
-            [this.circSummary.total]
-        );
-
-    dojo.byId('oils-selfck-circ-session-total').innerHTML = 
-        dojo.string.substitute(
-            localeStrings.TOTAL_ITEMS_SESSION, 
-            [this.circSummary.session]
-        );
-}
-
-
-SelfCheckManager.prototype.drawHoldsPage = function() {
-
-    // TODO add option to hid scanBox
-    // this.updateScanBox(...)
-
-    this.goToTab('holds');
-
-    this.holdTbody = dojo.byId('oils-selfck-hold-tbody');
-    if(!this.holdTemplate)
-        this.holdTemplate = this.holdTbody.removeChild(dojo.byId('oils-selfck-hold-row'));
-    while(this.holdTbody.childNodes[0])
-        this.holdTbody.removeChild(this.holdTbody.childNodes[0]);
-
-    progressDialog.show(true);
-
-    var self = this;
-    fieldmapper.standardRequest( // fetch the hold IDs
-
-        ['open-ils.circ', 'open-ils.circ.holds.id_list.retrieve'],
-        {   async : true,
-            params : [this.authtoken, this.patron.id()],
-
-            oncomplete : function(r) { 
-                var ids = openils.Util.readResponse(r);
-                if(!ids || ids.length == 0) {
-                    progressDialog.hide();
-                    return;
-                }
-
-                fieldmapper.standardRequest( // fetch the hold objects with fleshed details
-                    ['open-ils.circ', 'open-ils.circ.hold.details.batch.retrieve'],
-                    {   async : true,
-                        params : [self.authtoken, ids],
-                        onresponse : function(rr) {
-                            progressDialog.hide();
-                            self.insertHold(openils.Util.readResponse(rr));
-                        }
-                    }
-                );
-            }
-        }
-    );
-}
-
-SelfCheckManager.prototype.insertHold = function(data) {
-    var row = this.holdTemplate.cloneNode(true);
-
-    if(data.mvr.isbn()) {
-        this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + data.mvr.isbn());
-    }
-
-    this.byName(row, 'title').innerHTML = data.mvr.title();
-    this.byName(row, 'author').innerHTML = data.mvr.author();
-
-    if(data.status == 4) {
-
-        // hold is ready for pickup
-        this.byName(row, 'status').innerHTML = localeStrings.HOLD_STATUS_READY;
-
-    } else {
-
-        // hold is still pending
-        this.byName(row, 'status').innerHTML = 
-            dojo.string.substitute(
-                localeStrings.HOLD_STATUS_WAITING,
-                [data.queue_position, data.potential_copies]
-            );
-    }
-
-    // find the correct place the table to slot in the hold based on queue position
-
-    var position = (data.status == 4) ? 0 : data.queue_position;
-    row.setAttribute('position', position);
-
-    for(var i = 0; i < this.holdTbody.childNodes.length; i++) {
-        var node = this.holdTbody.childNodes[i];
-        if(Number(node.getAttribute('position')) >= position) {
-            this.holdTbody.insertBefore(row, node);
-            return;
-        }
-    }
-
-    this.holdTbody.appendChild(row);
-}
-
-
-SelfCheckManager.prototype.drawFinesPage = function() {
-
-    // TODO add option to hid scanBox
-    // this.updateScanBox(...)
-
-    this.goToTab('fines');
-    progressDialog.show(true);
-
-    if(this.creditPayableBalance > 0 && this.orgSettings[SET_CC_PAYMENT_ALLOWED]) {
-        openils.Util.show('oils-selfck-pay-fines-link', 'inline');
-    }
-
-    this.finesTbody = dojo.byId('oils-selfck-fines-tbody');
-    if(!this.finesTemplate)
-        this.finesTemplate = this.finesTbody.removeChild(dojo.byId('oils-selfck-fines-row'));
-    while(this.finesTbody.childNodes[0])
-        this.finesTbody.removeChild(this.finesTbody.childNodes[0]);
-
-    // when user clicks on a selector checkbox, update the total owed
-    var updateSelected = function() {
-        var total = 0;
-        dojo.forEach(
-            dojo.query('[name=selector]', this.finesTbody),
-            function(input) {
-                if(input.checked)
-                    total += Number(input.getAttribute('balance_owed'));
-            }
-        );
-
-        total = total.toFixed(2);
-        dojo.byId('oils-selfck-selected-total').innerHTML = 
-            dojo.string.substitute(localeStrings.TOTAL_FINES_SELECTED, [total]);
-    }
-
-    // wire up the batch on/off selector
-    var sel = dojo.byId('oils-selfck-fines-selector');
-    sel.onchange = function() {
-        dojo.forEach(
-            dojo.query('[name=selector]', this.finesTbody),
-            function(input) {
-                input.checked = sel.checked;
-            }
-        );
-    };
-
-    var self = this;
-    var handler = function(dataList) {
-
-        self.finesCount = dataList.length;
-        self.finesData = dataList;
-
-        for(var i in dataList) {
-
-            var data = dataList[i];
-            var row = self.finesTemplate.cloneNode(true);
-            var type = data.transaction.xact_type();
-
-            if(type == 'circulation') {
-                self.byName(row, 'type').innerHTML = type;
-                self.byName(row, 'details').innerHTML = data.record.title();
-
-            } else if(type == 'grocery') {
-                self.byName(row, 'type').innerHTML = 'Miscellaneous'; // Go ahead and head off any confusion around "grocery".  TODO i18n
-                self.byName(row, 'details').innerHTML = data.transaction.last_billing_type();
-            }
-
-            self.byName(row, 'total_owed').innerHTML = data.transaction.total_owed();
-            self.byName(row, 'total_paid').innerHTML = data.transaction.total_paid();
-            self.byName(row, 'balance').innerHTML = data.transaction.balance_owed();
-
-            // row selector
-            var selector = self.byName(row, 'selector')
-            selector.onchange = updateSelected;
-            selector.setAttribute('xact', data.transaction.id());
-            selector.setAttribute('balance_owed', data.transaction.balance_owed());
-            selector.checked = true;
-
-            self.finesTbody.appendChild(row);
-        }
-
-        updateSelected();
-    }
-
-
-    fieldmapper.standardRequest( 
-        ['open-ils.actor', 'open-ils.actor.user.transactions.have_balance.fleshed'],
-        {   async : true,
-            params : [this.authtoken, this.patron.id()],
-            oncomplete : function(r) { 
-                progressDialog.hide();
-                handler(openils.Util.readResponse(r));
-            }
-        }
-    );
-}
-
-SelfCheckManager.prototype.checkin = function(barcode, abortTransit) {
-
-    var resp = fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.transit.abort'],
-        {params : [this.authtoken, {barcode : barcode}]}
-    );
-
-    // resp == 1 on success
-    if(openils.Event.parse(resp))
-        return false;
-
-    var resp = fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.checkin.override'],
-        {params : [
-            this.authtoken, {
-                patron_id : this.patron.id(),
-                copy_barcode : barcode,
-                noop : true
-            }
-        ]}
-    );
-
-    if(!resp.length) resp = [resp];
-    for(var i = 0; i < resp.length; i++) {
-        var tc = openils.Event.parse(resp[i]).textcode;
-        if(tc == 'SUCCESS' || tc == 'NO_CHANGE') {
-            continue;
-        } else {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-/**
- * Check out a single item.  If the item is already checked 
- * out to the patron, redirect to renew()
- */
-SelfCheckManager.prototype.checkout = function(barcode, override) {
-
-    this.prevCirc = null;
-
-    if(!barcode) {
-        this.updateScanbox(null, true);
-        return;
-    }
-
-    if(this.mockCheckouts) {
-        // if we're in mock-checkout mode, just insert another
-        // fake circ into the table and get out of here.
-        this.displayCheckout(this.mockCheckout, 'checkout');
-        return;
-    }
-
-    // TODO see if it's a patron barcode
-    // TODO see if this item has already been checked out in this session
-
-    var method = 'open-ils.circ.checkout.full';
-    if(override) method += '.override';
-
-    console.log("Checkout out item " + barcode + " with method " + method);
-
-    var result = fieldmapper.standardRequest(
-        ['open-ils.circ', method],
-        {params: [
-            this.authtoken, {
-                patron_id : this.patron.id(),
-                copy_barcode : barcode
-            }
-        ]}
-    );
-
-    var stat = this.handleXactResult('checkout', barcode, result);
-
-    if(stat.override) {
-        this.checkout(barcode, true);
-    } else if(stat.doOver) {
-        this.checkout(barcode);
-    } else if(stat.renew) {
-        this.renew(barcode);
-    }
-}
-
-SelfCheckManager.prototype.failPartMessage = function(result) {
-    if (result.payload && result.payload.fail_part) {
-        var stringKey = "FAIL_PART_" +
-            result.payload.fail_part.replace(/\./g, "_");
-        return localeStrings[stringKey];
-    } else {
-        return null;
-    }
-}
-
-SelfCheckManager.prototype.handleXactResult = function(action, item, result) {
-
-    var displayText = '';
-
-    // If true, the display message is important enough to pop up.  Whether or not
-    // an alert() actually occurs, depends on org unit settings
-    var popup = false;  
-    var sound = ''; // sound file reference
-    var payload = result.payload || {};
-    var overrideEvents = this.orgSettings[SET_AUTO_OVERRIDE_EVENTS];
-    var blockStatuses = this.orgSettings[SET_BLOCK_CHECKOUT_ON_COPY_STATUS];
-        
-    if(result.textcode == 'NO_SESSION') {
-
-        return this.logoutStaff();
-
-    } else if(result.textcode == 'SUCCESS') {
-
-        if(action == 'checkout') {
-
-            displayText = dojo.string.substitute(localeStrings.CHECKOUT_SUCCESS, [item]);
-            this.displayCheckout(result, 'checkout');
-
-            if(payload.holds_fulfilled && payload.holds_fulfilled.length) {
-                // A hold was fulfilled, update the hold numbers in the circ summary
-                console.log("fulfilled hold " + payload.holds_fulfilled + " during checkout");
-                this.holdsSummary = null;
-                this.updateHoldsSummary();
-            }
-
-            this.updateCircSummary(true);
-
-        } else if(action == 'renew') {
-
-            displayText = dojo.string.substitute(localeStrings.RENEW_SUCCESS, [item]);
-            this.displayCheckout(result, 'renew');
-        }
-
-        this.checkouts.push({circ : result.payload.circ.id()});
-        sound = 'checkout-success';
-        this.updateScanBox();
-
-    } else if(result.textcode == 'OPEN_CIRCULATION_EXISTS' && action == 'checkout') {
-
-        // Server says the item is already checked out.  If it's checked out to the
-        // current user, we may need to renew it.  
-
-        if(payload.old_circ) { 
-
-            /*
-            old_circ refers to the previous checkout IFF it's for the same user. 
-            If no auto-renew interval is not defined, assume we should renew it
-            If an auto-renew interval is defined and the payload comes back with
-            auto_renew set to true, do the renewal.  Otherwise, let the patron know
-            the item is already checked out to them.  */
-
-            if( !this.orgSettings[SET_AUTO_RENEW_INTERVAL] ||
-                (this.orgSettings[SET_AUTO_RENEW_INTERVAL] && payload.auto_renew) ) {
-                this.prevCirc = payload.old_circ.id();
-                return { renew : true };
-            }
-
-            popup = true;
-            sound = 'checkout-failure';
-            displayText = dojo.string.substitute(localeStrings.ALREADY_OUT, [item]);
-
-        } else {
-
-            if( // copy is marked lost.  if configured to do so, check it in and try again.
-                result.payload.copy && 
-                result.payload.copy.status() == /* LOST */ 3 &&
-                overrideEvents && overrideEvents.length &&
-                overrideEvents.indexOf('COPY_STATUS_LOST') != -1) {
-
-                    if(this.checkin(item)) {
-                        return { doOver : true };
-                    }
-            }
-
-            
-            // item is checked out to some other user
-            popup = true;
-            sound = 'checkout-failure';
-            displayText = dojo.string.substitute(localeStrings.OPEN_CIRCULATION_EXISTS, [item]);
-        }
-
-        this.updateScanBox({select:true});
-
-    } else {
-
-    
-        if(overrideEvents && overrideEvents.length) {
-            
-            // see if the events we received are all in the list of
-            // events to override
-    
-            if(!result.length) result = [result];
-    
-            var override = true;
-            for(var i = 0; i < result.length; i++) {
-
-                var match = overrideEvents.filter(function(e) { return (e == result[i].textcode); })[0];
-
-                if(!match) {
-                    override = false;
-                    break;
-                }
-
-                if(result[i].textcode == 'COPY_NOT_AVAILABLE' && blockStatuses && blockStatuses.length) {
-
-                    var stat = result[i].payload.status(); // copy status
-                    if(typeof stat == 'object') stat = stat.id();
-
-                    var match2 = blockStatuses.filter(function(e) { return (e == stat); })[0];
-
-                    if(match2) { // copy is in a blocked status
-                        override = false;
-                        break;
-                    }
-                }
-
-                if(result[i].textcode == 'COPY_IN_TRANSIT') {
-                    // to override a transit, we have to abort the transit and check it in first
-                    if(this.checkin(item, true)) {
-                        return { doOver : true };
-                    } else {
-                        override = false;
-                    }
-                }
-            }
-
-            if(override) 
-                return { override : true };
-        }
-    
-        this.updateScanBox({select : true});
-        popup = true;
-        sound = 'checkout-failure';
-
-        if(action == 'renew')
-            this.checkouts.push({circ : this.prevCirc, renewal_failure : true});
-
-        if(result.length) 
-            result = result[0];
-
-        switch(result.textcode) {
-
-            // TODO custom handler for blocking penalties
-
-            case 'MAX_RENEWALS_REACHED' :
-                displayText = dojo.string.substitute(
-                    localeStrings.MAX_RENEWALS, [item]);
-                break;
-
-            case 'ITEM_NOT_CATALOGED' :
-                displayText = dojo.string.substitute(
-                    localeStrings.ITEM_NOT_CATALOGED, [item]);
-                break;
-
-            case 'OPEN_CIRCULATION_EXISTS' :
-                displayText = dojo.string.substitute(
-                    localeStrings.OPEN_CIRCULATION_EXISTS, [item]);
-
-                break;
-
-            default:
-                console.error('Unhandled event ' + result.textcode);
-
-                if (!(displayText = this.failPartMessage(result))) {
-                    if (action == 'checkout' || action == 'renew') {
-                        displayText = dojo.string.substitute(
-                            localeStrings.GENERIC_CIRC_FAILURE, [item]);
-                    } else {
-                        displayText = dojo.string.substitute(
-                            localeStrings.UNKNOWN_ERROR, [result.textcode]);
-                    }
-                }
-        }
-    }
-
-    this.handleAlert(displayText, popup, sound);
-    return {};
-}
-
-
-/**
- * Renew an item
- */
-SelfCheckManager.prototype.renew = function(barcode, override) {
-
-    var method = 'open-ils.circ.renew';
-    if(override) method += '.override';
-
-    console.log("Renewing item " + barcode + " with method " + method);
-
-    var result = fieldmapper.standardRequest(
-        ['open-ils.circ', method],
-        {params: [
-            this.authtoken, {
-                patron_id : this.patron.id(),
-                copy_barcode : barcode
-            }
-        ]}
-    );
-
-    console.log(js2JSON(result));
-
-    var stat = this.handleXactResult('renew', barcode, result);
-
-    if(stat.override)
-        this.renew(barcode, true);
-}
-
-/**
- * Display the result of a checkout or renewal in the items out table
- */
-SelfCheckManager.prototype.displayCheckout = function(evt, type, itemsOut) {
-
-    var copy = evt.payload.copy;
-    var record = evt.payload.record;
-    var circ = evt.payload.circ;
-    var row = this.circTemplate.cloneNode(true);
-
-    if(record.isbn()) {
-        this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + record.isbn());
-    }
-
-    this.byName(row, 'barcode').innerHTML = copy.barcode();
-    this.byName(row, 'title').innerHTML = record.title();
-    this.byName(row, 'author').innerHTML = record.author();
-    this.byName(row, 'remaining').innerHTML = circ.renewal_remaining();
-    openils.Util.show(this.byName(row, type));
-
-    var date = dojo.date.stamp.fromISOString(circ.due_date());
-    this.byName(row, 'due_date').innerHTML = 
-        dojo.date.locale.format(date, {selector : 'date'});
-
-    // put new circs at the top of the list
-    var tbody = this.circTbody;
-    if(itemsOut) tbody = this.itemsOutTbody;
-    tbody.insertBefore(row, tbody.getElementsByTagName('tr')[0]);
-}
-
-
-SelfCheckManager.prototype.byName = function(node, name) {
-    return dojo.query('[name=' + name+']', node)[0];
-}
-
-
-SelfCheckManager.prototype.initPrinter = function() {
-    try { // Mozilla only
-               netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-        netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-        netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesRead');
-        netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesWrite');
-        var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
-        if (pref)
-            pref.setBoolPref('print.always_print_silent', true);
-    } catch(E) {
-        console.log("Unable to initialize auto-printing"); 
-    }
-}
-
-/**
- * Print a receipt for this session's checkouts
- */
-SelfCheckManager.prototype.printSessionReceipt = function(callback) {
-
-    var circIds = [];
-    var circCtx = []; // circ context data.  in this case, renewal_failure info
-
-    // collect the circs and failure info
-    dojo.forEach(
-        this.checkouts, 
-        function(blob) {
-            circIds.push(blob.circ);
-            circCtx.push({renewal_failure:blob.renewal_failure});
-        }
-    );
-
-    var params = [
-        this.authtoken, 
-        this.staff.ws_ou(),
-        null,
-        'format.selfcheck.checkout',
-        'print-on-demand',
-        circIds,
-        circCtx
-    ];
-
-    var self = this;
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],
-        {   
-            async : true,
-            params : params,
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                var output = resp.template_output();
-                if(output) {
-                    self.printData(output.data(), self.checkouts.length, callback); 
-                } else {
-                    var error = resp.error_output();
-                    if(error) {
-                        throw new Error("Error creating receipt: " + error.data());
-                    } else {
-                        throw new Error("No receipt data returned from server");
-                    }
-                }
-            }
-        }
-    );
-}
-
-SelfCheckManager.prototype.printData = function(data, numItems, callback) {
-
-    var win = window.open('', '', 'resizable,width=700,height=500,scrollbars=1'); 
-    win.document.body.innerHTML = data;
-    win.print();
-
-    /*
-     * There is no way to know when the browser is done printing.
-     * Make a best guess at when to close the print window by basing
-     * the setTimeout wait on the number of items to be printed plus
-     * a small buffer
-     */
-    var sleepTime = 1000;
-    if(numItems > 0) 
-        sleepTime += (numItems / 2) * 1000;
-
-    setTimeout(
-        function() { 
-            win.close(); // close the print window
-            if(callback)
-                callback(); // fire optional post-print callback
-        },
-        sleepTime 
-    );
-}
-
-
-/**
- * Print a receipt for this user's items out
- */
-SelfCheckManager.prototype.printItemsOutReceipt = function(callback) {
-
-    if(!this.itemsOut.length) return;
-
-    progressDialog.show(true);
-
-    var params = [
-        this.authtoken, 
-        this.staff.ws_ou(),
-        null,
-        'format.selfcheck.items_out',
-        'print-on-demand',
-        this.itemsOut
-    ];
-
-    var self = this;
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],
-        {   
-            async : true,
-            params : params,
-            oncomplete : function(r) {
-                progressDialog.hide();
-                var resp = openils.Util.readResponse(r);
-                var output = resp.template_output();
-                if(output) {
-                    self.printData(output.data(), self.itemsOut.length, callback); 
-                } else {
-                    var error = resp.error_output();
-                    if(error) {
-                        throw new Error("Error creating receipt: " + error.data());
-                    } else {
-                        throw new Error("No receipt data returned from server");
-                    }
-                }
-            }
-        }
-    );
-}
-
-/**
- * Print a receipt for this user's items out
- */
-SelfCheckManager.prototype.printHoldsReceipt = function(callback) {
-
-    if(!this.holds.length) return;
-
-    progressDialog.show(true);
-
-    var holdIds = [];
-    var holdData = [];
-
-    dojo.forEach(this.holds,
-        function(data) {
-            holdIds.push(data.hold.id());
-            if(data.status == 4) {
-                holdData.push({ready : true});
-            } else {
-                holdData.push({
-                    queue_position : data.queue_position, 
-                    potential_copies : data.potential_copies
-                });
-            }
-        }
-    );
-
-    var params = [
-        this.authtoken, 
-        this.staff.ws_ou(),
-        null,
-        'format.selfcheck.holds',
-        'print-on-demand',
-        holdIds,
-        holdData
-    ];
-
-    var self = this;
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.fire_hold_trigger_events'],
-        {   
-            async : true,
-            params : params,
-            oncomplete : function(r) {
-                progressDialog.hide();
-                var resp = openils.Util.readResponse(r);
-                var output = resp.template_output();
-                if(output) {
-                    self.printData(output.data(), self.holds.length, callback); 
-                } else {
-                    var error = resp.error_output();
-                    if(error) {
-                        throw new Error("Error creating receipt: " + error.data());
-                    } else {
-                        throw new Error("No receipt data returned from server");
-                    }
-                }
-            }
-        }
-    );
-}
-
-
-SelfCheckManager.prototype.printPaymentReceipt = function(response, callback) {
-    
-    var self = this;
-    progressDialog.show(true);
-
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.money.payment_receipt.print'],
-        {
-            async : true,
-            params : [this.authtoken, response.payments],
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                var output = resp.template_output();
-                progressDialog.hide();
-                if(output) {
-                    self.printData(output.data(), 1, callback); 
-                } else {
-                    var error = resp.error_output();
-                    if(error) {
-                        throw new Error("Error creating receipt: " + error.data());
-                    } else {
-                        throw new Error("No receipt data returned from server");
-                    }
-                }
-            }
-        }
-    );
-}
-
-/**
- * Print a receipt for this user's items out
- */
-SelfCheckManager.prototype.printFinesReceipt = function(callback) {
-
-    progressDialog.show(true);
-
-    var params = [
-        this.authtoken, 
-        this.staff.ws_ou(),
-        null,
-        'format.selfcheck.fines',
-        'print-on-demand',
-        [this.patron.id()]
-    ];
-
-    var self = this;
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.fire_user_trigger_events'],
-        {   
-            async : true,
-            params : params,
-            oncomplete : function(r) {
-                progressDialog.hide();
-                var resp = openils.Util.readResponse(r);
-                var output = resp.template_output();
-                if(output) {
-                    self.printData(output.data(), self.finesCount, callback); 
-                } else {
-                    var error = resp.error_output();
-                    if(error) {
-                        throw new Error("Error creating receipt: " + error.data());
-                    } else {
-                        throw new Error("No receipt data returned from server");
-                    }
-                }
-            }
-        }
-    );
-}
-
-
-
-
-/**
- * Logout the patron and return to the login page
- */
-SelfCheckManager.prototype.logoutPatron = function(print) {
-    progressDialog.show(true); // prevent patron from clicking logout link twice
-    if(print && this.checkouts.length) {
-        this.printSessionReceipt(
-            function() {
-                location.href = location.href;
-            }
-        );
-    } else {
-        location.href = location.href;
-    }
-}
-
-
-/**
- * Fire up the manager on page load
- */
-openils.Util.addOnLoad(
-    function() {
-        new SelfCheckManager().init();
-    }
-);
+require([
+       "dojo/date/locale",
+       "dojo/cookie",
+       "dojo/date/stamp",
+       "dijit/form/CheckBox",
+       "dijit/form/NumberSpinner",
+       "openils/CGI",
+       "openils/Util",
+       "openils/User",
+       "openils/Event",
+       "openils/widget/ProgressDialog",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojo_date_locale,
+       dojo_cookie,
+       dojo_date_stamp,
+       dijit_form_CheckBox,
+       dijit_form_NumberSpinner,
+       openils_CGI,
+       openils_Util,
+       openils_User,
+       openils_Event,
+       openils_widget_ProgressDialog,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       
+       dojo.requireLocalization('openils.circ', 'selfcheck');
+       var localeStrings = dojo.i18n.getLocalization('openils.circ', 'selfcheck');
+       
+       
+       const SET_BARCODE_REGEX = 'opac.barcode_regex';
+       const SET_PATRON_TIMEOUT = 'circ.selfcheck.patron_login_timeout';
+       const SET_AUTO_OVERRIDE_EVENTS = 'circ.selfcheck.auto_override_checkout_events';
+       const SET_PATRON_PASSWORD_REQUIRED = 'circ.selfcheck.patron_password_required';
+       const SET_AUTO_RENEW_INTERVAL = 'circ.checkout_auto_renew_age';
+       const SET_WORKSTATION_REQUIRED = 'circ.selfcheck.workstation_required';
+       const SET_ALERT_POPUP = 'circ.selfcheck.alert.popup';
+       const SET_ALERT_SOUND = 'circ.selfcheck.alert.sound';
+       const SET_CC_PAYMENT_ALLOWED = 'credit.payments.allow';
+       // This setting only comes into play if COPY_NOT_AVAILABLE is in the SET_AUTO_OVERRIDE_EVENTS list
+       const SET_BLOCK_CHECKOUT_ON_COPY_STATUS = 'circ.selfcheck.block_checkout_on_copy_status';
+       
+       // set before the login dialog is rendered
+       openils_User.default_login_agent = 'selfcheck';
+       
+       function SelfCheckManager() {
+       
+           this.cgi = new openils_CGI();
+           this.staff = null; 
+           this.workstation = null;
+           this.authtoken = null;
+       
+           this.patron = null; 
+           this.patronBarcodeRegex = null;
+       
+           this.checkouts = [];
+           this.itemsOut = [];
+       
+           // During renewals, keep track of the ID of the previous circulation. 
+           // Previous circ is used for tracking failed renewals (for receipts).
+           this.prevCirc = null;
+       
+           // current item barcode
+           this.itemBarcode = null; 
+       
+           // are we currently performing a renewal?
+           this.isRenewal = false; 
+       
+           // dict of org unit settings for "here"
+           this.orgSettings = {};
+       
+           // Construct a mock checkout for debugging purposes
+           if(this.mockCheckouts = this.cgi.param('mock-circ')) {
+       
+               this.mockCheckout = {
+                   payload : {
+                       record : new fieldmapper.mvr(),
+                       copy : new fieldmapper.acp(),
+                       circ : new fieldmapper.circ()
+                   }
+               };
+       
+               this.mockCheckout.payload.record.title('Jazz improvisation for guitar');
+               this.mockCheckout.payload.record.author('Wise, Les');
+               this.mockCheckout.payload.record.isbn('0634033565');
+               this.mockCheckout.payload.copy.barcode('123456789');
+               this.mockCheckout.payload.circ.renewal_remaining(1);
+               this.mockCheckout.payload.circ.parent_circ(1);
+               this.mockCheckout.payload.circ.due_date('2012-12-21');
+           }
+       
+           this.initPrinter();
+       }
+       
+       SelfCheckManager.prototype.setupStaffLogin = function(verify) {
+       
+           if(verify) oilsSetupUser(); 
+           this.staff = openils_User.user;
+           this.workstation = openils_User.workstation;
+           this.authtoken = openils_User.authtoken;
+       }
+       
+       
+       
+       /**
+        * Fetch the org-unit settings, initialize the display, etc.
+        */
+       SelfCheckManager.prototype.init = function() {
+       
+           this.setupStaffLogin();
+           this.loadOrgSettings();
+       
+           this.circTbody = dojo.byId('oils-selfck-circ-tbody');
+           this.itemsOutTbody = dojo.byId('oils-selfck-circ-out-tbody');
+       
+           // workstation is required but none provided
+           if(this.orgSettings[SET_WORKSTATION_REQUIRED] && !this.workstation) {
+               if(confirm(dojo.string.substitute(localeStrings.WORKSTATION_REQUIRED))) {
+                   this.registerWorkstation();
+               }
+               return;
+           }
+           
+           var self = this;
+           // connect onclick handlers to the various navigation links
+           var linkHandlers = {
+               'oils-selfck-hold-details-link' : function() { self.drawHoldsPage(); },
+               'oils-selfck-view-fines-link' : function() { self.drawFinesPage(); },
+               'oils-selfck-pay-fines-link' : function() {
+                   self.goToTab("payment");
+                   self.drawPayFinesPage(
+                       self.patron,
+                       self.getSelectedFinesTotal(),
+                       self.getSelectedFineTransactions(),
+                       function(resp) {
+                           var evt = openils_Event.parse(resp);
+                           if(evt) {
+                               var message = evt + '';
+                               if(evt.textcode == 'CREDIT_PROCESSOR_DECLINED_TRANSACTION' && evt.payload)
+                                   message += '\n' + evt.payload.error_message;
+                               if(evt.textcode == 'INVALID_USER_XACT_ID')
+                                   message += '\n' + localeStrings.PAYMENT_INVALID_USER_XACT_ID;
+                               self.handleAlert(message, true, 'payment-failure');
+                               return;
+                           }
+       
+                           self.patron.last_xact_id(resp.last_xact_id); // update to match latest from server
+                           self.printPaymentReceipt(
+                               resp,
+                               function() {
+                                   self.updateFinesSummary();
+                                   self.drawFinesPage();
+                               }
+                           );
+                       }
+                   );
+               },
+               'oils-selfck-nav-home' : function() { self.drawCircPage(); },
+               'oils-selfck-nav-logout' : function() { self.logoutPatron(); },
+               'oils-selfck-nav-logout-print' : function() { self.logoutPatron(true); },
+               'oils-selfck-items-out-details-link' : function() { self.drawItemsOutPage(); },
+               'oils-selfck-print-list-link' : function() { self.printList(); }
+           }
+       
+           for(var id in linkHandlers) 
+               dojo.connect(dojo.byId(id), 'onclick', linkHandlers[id]);
+       
+       
+           if(this.cgi.param('patron')) {
+               
+               // Patron barcode via cgi param.  Mainly used for debugging and
+               // only works if password is not required by policy
+               this.loginPatron(this.cgi.param('patron'));
+       
+           } else {
+               this.drawLoginPage();
+           }
+       
+           /**
+            * To test printing, pass a URL param of 'testprint'.  The value for the param
+            * should be a JSON string like so:  [{circ:<circ_id>}, ...]
+            */
+           var testPrint = this.cgi.param('testprint');
+           if(testPrint) {
+               this.checkouts = JSON2js(testPrint);
+               this.printSessionReceipt();
+               this.checkouts = [];
+           }
+       }
+       
+       
+       SelfCheckManager.prototype.getSelectedFinesTotal = function() {
+           var total = 0;
+           dojo.forEach(
+               dojo.query("[name=selector]", this.finesTbody),
+               function(input) {
+                   if(input.checked)
+                       total += Number(input.getAttribute("balance_owed"));
+               }
+           );
+           return total.toFixed(2);
+       };
+       
+       SelfCheckManager.prototype.getSelectedFineTransactions = function() {
+           return dojo.query("[name=selector]", this.finesTbody).
+               filter(function (o) { return o.checked }).
+               map(
+                   function (o) {
+                       return [
+                           o.getAttribute("xact"),
+                           Number(o.getAttribute("balance_owed")).toFixed(2)
+                       ];
+                   }
+               );
+       };
+       
+       /**
+        * Registers a new workstion
+        */
+       SelfCheckManager.prototype.registerWorkstation = function() {
+           
+           oilsSelfckWsDialog.show();
+       
+           new openils_User().buildPermOrgSelector(
+               'REGISTER_WORKSTATION', 
+               oilsSelfckWsLocSelector, 
+               this.staff.home_ou()
+           );
+       
+       
+           var self = this;
+           dojo.connect(oilsSelfckWsSubmit, 'onClick', 
+       
+               function() {
+                   oilsSelfckWsDialog.hide();
+                   var name = oilsSelfckWsLocSelector.attr('displayedValue') + '-' + oilsSelfckWsName.attr('value');
+       
+                   var res = fieldmapper.standardRequest(
+                       ['open-ils.actor', 'open-ils.actor.workstation.register'],
+                       { params : [
+                               self.authtoken, name, oilsSelfckWsLocSelector.attr('value')
+                           ]
+                       }
+                   );
+       
+                   if(evt = openils_Event.parse(res)) {
+                       if(evt.textcode == 'WORKSTATION_NAME_EXISTS') {
+                           if(confirm(localeStrings.WORKSTATION_EXISTS)) {
+                               location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;
+                           } else {
+                               self.registerWorkstation();
+                           }
+                           return;
+                       } else {
+                           alert(evt);
+                       }
+                   } else {
+                       location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;
+                   }
+               }
+           );
+       }
+       
+       /**
+        * Loads the org unit settings
+        */
+       SelfCheckManager.prototype.loadOrgSettings = function() {
+       
+           var settings = fieldmapper.aou.fetchOrgSettingBatch(
+               this.staff.ws_ou(), [
+                   SET_BARCODE_REGEX,
+                   SET_PATRON_TIMEOUT,
+                   SET_ALERT_POPUP,
+                   SET_ALERT_SOUND,
+                   SET_AUTO_OVERRIDE_EVENTS,
+                   SET_BLOCK_CHECKOUT_ON_COPY_STATUS,
+                   SET_PATRON_PASSWORD_REQUIRED,
+                   SET_AUTO_RENEW_INTERVAL,
+                   SET_WORKSTATION_REQUIRED,
+                   SET_CC_PAYMENT_ALLOWED
+               ]
+           );
+       
+           for(k in settings) {
+               if(settings[k])
+                   this.orgSettings[k] = settings[k].value;
+           }
+       
+           if(settings[SET_BARCODE_REGEX]) 
+               this.patronBarcodeRegex = new RegExp(settings[SET_BARCODE_REGEX].value);
+       }
+       
+       SelfCheckManager.prototype.drawLoginPage = function() {
+           var self = this;
+       
+           var bcHandler = function(barcode_or_usrname) {
+               // handle patron barcode/usrname entry
+       
+               if(self.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
+                   
+                   // password is required.  wire up the scan box to read it
+                   self.updateScanBox({
+                       msg : 'Please enter your password', // TODO i18n 
+                       handler : function(pw) { self.loginPatron(barcode_or_usrname, pw); },
+                       password : true
+                   });
+       
+               } else {
+                   // password is not required, go ahead and login
+                   self.loginPatron(barcode_or_usrname);
+               }
+           };
+       
+           this.updateScanBox({
+               msg : 'Please log in with your username or library barcode.', // TODO
+               handler : bcHandler
+           });
+       }
+       
+       /**
+        * Login the patron.  
+        */
+       SelfCheckManager.prototype.loginPatron = function(barcode_or_usrname, passwd) {
+       
+           this.setupStaffLogin(true); // verify still valid
+       
+           var barcode = null;
+           var usrname = null;
+           console.log('testing ' + barcode_or_usrname);
+           if (barcode_or_usrname.match(this.patronBarcodeRegex)) {
+               console.log('barcode');
+               barcode = barcode_or_usrname;
+           } else {
+               console.log('usrname');
+               usrname = barcode_or_usrname;
+           }
+       
+           if(this.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
+               
+               if(!passwd) {
+                   // would only happen in dev/debug mode when using the patron= param
+                   alert('password required by org setting.  remove patron= from URL'); 
+                   return;
+               }
+       
+               // patron password is required.  Verify it.
+       
+               var self = this;
+               new openils_User().auth_verify(
+                   {   username : usrname, barcode : barcode, 
+                       type : 'opac', passwd : passwd, agent : 'selfcheck' },
+                   function(OK) {
+                       if (OK) {
+                           self.fetchPatron(barcode, usrname);
+       
+                       } else {
+                           // auth verify failed
+                           self.handleAlert(
+                               dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode_or_usrname]),
+                               false, 'login-failure'
+                           );
+                           self.drawLoginPage();
+                       }
+                   }
+               );
+       
+           } else {
+               this.fetchPatron(barcode, usrname);
+           }
+       };
+       
+       SelfCheckManager.prototype.fetchPatron = function(barcode, usrname) {
+       
+           var patron_id = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.retrieve_id_by_barcode_or_username'],
+               {params : [this.authtoken, barcode, usrname]}
+           );
+       
+           // retrieve the fleshed user by id
+           this.patron = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
+               {params : [this.authtoken, patron_id]}
+           );
+       
+           var evt = openils_Event.parse(this.patron);
+           
+           // verify validity of the card used to log in
+           var inactiveCard = false;
+           if(!evt) {
+               var card;
+               if (barcode) {
+                   card = this.patron.cards().filter(
+                       function(c) { return (c.barcode() == barcode); })[0];
+               } else {
+                   card = this.patron.card();
+               }
+               inactiveCard = !openils_Util.isTrue(card.active());
+           }
+       
+           if(evt || inactiveCard) {
+               this.handleAlert(
+                   dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode || usrname]),
+                   false, 'login-failure'
+               );
+               this.drawLoginPage();
+       
+           } else {
+       
+               this.handleAlert('', false, 'login-success');
+               dojo.byId('oils-selfck-user-banner').innerHTML = 
+                   dojo.string.substitute(localeStrings.WELCOME_BANNER, [this.patron.first_given_name()]);
+               this.drawCircPage();
+           }
+       }
+       
+       
+       SelfCheckManager.prototype.handleAlert = function(message, shouldPopup, sound) {
+       
+           console.log("Handling alert " + message);
+       
+           dojo.byId('oils-selfck-status-div').innerHTML = message;
+       
+           if(shouldPopup)
+               openils_Util.addCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );
+           else
+               openils_Util.removeCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );
+       
+           if(shouldPopup && this.orgSettings[SET_ALERT_POPUP]) 
+               alert(message);
+       
+           if(sound && this.orgSettings[SET_ALERT_SOUND])
+               openils_Util.playAudioUrl(SelfCheckManager.audioConfig[sound]);
+       }
+       
+       
+       /**
+        * Manages the main input box
+        * @param msg The context message to display with the box
+        * @param clearOnly Don't update the context message, just clear the value and re-focus
+        * @param handler Optional "on-enter" handler.  
+        */
+       SelfCheckManager.prototype.updateScanBox = function(args) {
+           args = args || {};
+       
+           if(args.select) {
+               selfckScanBox.domNode.select();
+           } else {
+               selfckScanBox.attr('value', '');
+           }
+       
+           if(args.password) {
+               selfckScanBox.domNode.setAttribute('type', 'password');
+           } else {
+               selfckScanBox.domNode.setAttribute('type', '');
+           }
+       
+           if(args.value)
+               selfckScanBox.attr('value', args.value);
+       
+           if(args.msg) 
+               dojo.byId('oils-selfck-scan-text').innerHTML = args.msg;
+       
+           if(selfckScanBox._lastHandler && (args.handler || args.clearHandler)) {
+               dojo.disconnect(selfckScanBox._lastHandler);
+           }
+       
+           if(args.handler) {
+               selfckScanBox._lastHandler = dojo.connect(
+                   selfckScanBox, 
+                   'onKeyDown', 
+                   function(e) {
+                       if(e.keyCode != dojo.keys.ENTER) 
+                           return;
+                       args.handler(selfckScanBox.attr('value'));
+                   }
+               );
+           }
+       
+           selfckScanBox.focus();
+       }
+       
+       /**
+        *  Sets up the checkout/renewal interface
+        */
+       SelfCheckManager.prototype.drawCircPage = function() {
+       
+           openils_Util.show('oils-selfck-circ-tbody', 'table-row-group');
+           this.goToTab('checkout');
+       
+           while(this.itemsOutTbody.childNodes[0])
+               this.itemsOutTbody.removeChild(this.itemsOutTbody.childNodes[0]);
+       
+           var self = this;
+           this.updateScanBox({
+               msg : 'Please enter an item barcode', // TODO i18n
+               handler : function(barcode) { self.checkout(barcode); }
+           });
+       
+           if(!this.circTemplate)
+               this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row'));
+       
+           // fines summary
+           this.updateFinesSummary();
+       
+           // holds summary
+           this.updateHoldsSummary();
+       
+           // items out summary
+           this.updateCircSummary();
+       
+           // render mock checkouts for debugging?
+           if(this.mockCheckouts) {
+               for(var i in [1,2,3]) 
+                   this.displayCheckout(this.mockCheckout, 'checkout');
+           }
+       }
+       
+       
+       SelfCheckManager.prototype.updateFinesSummary = function() {
+           var self = this; 
+       
+           // fines summary
+           fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.fines.summary'],
+               {   async : true,
+                   params : [this.authtoken, this.patron.id()],
+                   oncomplete : function(r) {
+       
+                       var summary = openils_Util.readResponse(r);
+       
+                       dojo.byId('oils-selfck-fines-total').innerHTML = 
+                           dojo.string.substitute(
+                               localeStrings.TOTAL_FINES_ACCOUNT, 
+                               [summary.balance_owed()]
+                           );
+       
+                       self.creditPayableBalance = summary.balance_owed();
+                   }
+               }
+           );
+       }
+       
+       
+       SelfCheckManager.prototype.drawItemsOutPage = function() {
+           openils_Util.hide('oils-selfck-circ-tbody');
+       
+           this.goToTab('items_out');
+       
+           while(this.itemsOutTbody.childNodes[0])
+               this.itemsOutTbody.removeChild(this.itemsOutTbody.childNodes[0]);
+       
+           progressDialog.show(true);
+           
+           var self = this;
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.actor.user.checked_out.atomic'],
+               {
+                   async : true,
+                   params : [this.authtoken, this.patron.id()],
+                   oncomplete : function(r) {
+       
+                       var resp = openils_Util.readResponse(r);
+       
+                       var circs = resp.sort(
+                           function(a, b) {
+                               if(a.circ.due_date() > b.circ.due_date())
+                                   return -1;
+                               return 1;
+                           }
+                       );
+       
+                       progressDialog.hide();
+       
+                       self.itemsOut = [];
+                       dojo.forEach(circs,
+                           function(circ) {
+                               self.itemsOut.push(circ.circ.id());
+                               self.displayCheckout(
+                                   {payload : circ}, 
+                                   (circ.circ.parent_circ()) ? 'renew' : 'checkout',
+                                   true
+                               );
+                           }
+                       );
+                   }
+               }
+           );
+       }
+       
+       
+       SelfCheckManager.prototype.goToTab = function(name) {
+           this.tabName = name;
+       
+           openils_Util.hide('oils-selfck-fines-page');
+           openils_Util.hide('oils-selfck-payment-page');
+           openils_Util.hide('oils-selfck-holds-page');
+           openils_Util.hide('oils-selfck-circ-page');
+           openils_Util.hide('oils-selfck-pay-fines-link');
+           
+           switch(name) {
+               case 'checkout':
+                   openils_Util.show('oils-selfck-circ-page');
+                   break;
+               case 'items_out':
+                   openils_Util.show('oils-selfck-circ-page');
+                   break;
+               case 'holds':
+                   openils_Util.show('oils-selfck-holds-page');
+                   break;
+               case 'fines':
+                   openils_Util.show('oils-selfck-fines-page');
+                   break;
+               case 'payment':
+                   openils_Util.show('oils-selfck-payment-page');
+                   break;
+           }
+       }
+       
+       
+       SelfCheckManager.prototype.printList = function() {
+           switch(this.tabName) {
+               case 'checkout':
+                   this.printSessionReceipt();
+                   break;
+               case 'items_out':
+                   this.printItemsOutReceipt();
+                   break;
+               case 'holds':
+                   this.printHoldsReceipt();
+                   break;
+               case 'fines':
+                   this.printFinesReceipt();
+                   break;
+           }
+       }
+       
+       SelfCheckManager.prototype.updateHoldsSummary = function() {
+       
+           if(!this.holdsSummary) {
+               var summary = fieldmapper.standardRequest(
+                   ['open-ils.circ', 'open-ils.circ.holds.user_summary'],
+                   {params : [this.authtoken, this.patron.id()]}
+               );
+       
+               this.holdsSummary = {};
+               this.holdsSummary.ready = Number(summary['4']);
+               this.holdsSummary.total = 0;
+       
+               for(var i in summary) 
+                   this.holdsSummary.total += Number(summary[i]);
+           }
+       
+           dojo.byId('oils-selfck-holds-total').innerHTML = 
+               dojo.string.substitute(
+                   localeStrings.TOTAL_HOLDS, 
+                   [this.holdsSummary.total]
+               );
+       
+           dojo.byId('oils-selfck-holds-ready').innerHTML = 
+               dojo.string.substitute(
+                   localeStrings.HOLDS_READY_FOR_PICKUP, 
+                   [this.holdsSummary.ready]
+               );
+       }
+       
+       
+       SelfCheckManager.prototype.updateCircSummary = function(increment) {
+       
+           if(!this.circSummary) {
+       
+               var summary = fieldmapper.standardRequest(
+                   ['open-ils.actor', 'open-ils.actor.user.checked_out.count'],
+                   {params : [this.authtoken, this.patron.id()]}
+               );
+       
+               this.circSummary = {
+                   total : Number(summary.out) + Number(summary.overdue),
+                   overdue : Number(summary.overdue),
+                   session : 0
+               };
+           }
+       
+           if(increment) {
+               // local checkout occurred.  Add to the total and the session.
+               this.circSummary.total += 1;
+               this.circSummary.session += 1;
+           }
+       
+           dojo.byId('oils-selfck-circ-account-total').innerHTML = 
+               dojo.string.substitute(
+                   localeStrings.TOTAL_ITEMS_ACCOUNT, 
+                   [this.circSummary.total]
+               );
+       
+           dojo.byId('oils-selfck-circ-session-total').innerHTML = 
+               dojo.string.substitute(
+                   localeStrings.TOTAL_ITEMS_SESSION, 
+                   [this.circSummary.session]
+               );
+       }
+       
+       
+       SelfCheckManager.prototype.drawHoldsPage = function() {
+       
+           // TODO add option to hid scanBox
+           // this.updateScanBox(...)
+       
+           this.goToTab('holds');
+       
+           this.holdTbody = dojo.byId('oils-selfck-hold-tbody');
+           if(!this.holdTemplate)
+               this.holdTemplate = this.holdTbody.removeChild(dojo.byId('oils-selfck-hold-row'));
+           while(this.holdTbody.childNodes[0])
+               this.holdTbody.removeChild(this.holdTbody.childNodes[0]);
+       
+           progressDialog.show(true);
+       
+           var self = this;
+           fieldmapper.standardRequest( // fetch the hold IDs
+       
+               ['open-ils.circ', 'open-ils.circ.holds.id_list.retrieve'],
+               {   async : true,
+                   params : [this.authtoken, this.patron.id()],
+       
+                   oncomplete : function(r) { 
+                       var ids = openils_Util.readResponse(r);
+                       if(!ids || ids.length == 0) {
+                           progressDialog.hide();
+                           return;
+                       }
+       
+                       fieldmapper.standardRequest( // fetch the hold objects with fleshed details
+                           ['open-ils.circ', 'open-ils.circ.hold.details.batch.retrieve'],
+                           {   async : true,
+                               params : [self.authtoken, ids],
+                               onresponse : function(rr) {
+                                   progressDialog.hide();
+                                   self.insertHold(openils_Util.readResponse(rr));
+                               }
+                           }
+                       );
+                   }
+               }
+           );
+       }
+       
+       SelfCheckManager.prototype.insertHold = function(data) {
+           var row = this.holdTemplate.cloneNode(true);
+       
+           if(data.mvr.isbn()) {
+               this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + data.mvr.isbn());
+           }
+       
+           this.byName(row, 'title').innerHTML = data.mvr.title();
+           this.byName(row, 'author').innerHTML = data.mvr.author();
+       
+           if(data.status == 4) {
+       
+               // hold is ready for pickup
+               this.byName(row, 'status').innerHTML = localeStrings.HOLD_STATUS_READY;
+       
+           } else {
+       
+               // hold is still pending
+               this.byName(row, 'status').innerHTML = 
+                   dojo.string.substitute(
+                       localeStrings.HOLD_STATUS_WAITING,
+                       [data.queue_position, data.potential_copies]
+                   );
+           }
+       
+           // find the correct place the table to slot in the hold based on queue position
+       
+           var position = (data.status == 4) ? 0 : data.queue_position;
+           row.setAttribute('position', position);
+       
+           for(var i = 0; i < this.holdTbody.childNodes.length; i++) {
+               var node = this.holdTbody.childNodes[i];
+               if(Number(node.getAttribute('position')) >= position) {
+                   this.holdTbody.insertBefore(row, node);
+                   return;
+               }
+           }
+       
+           this.holdTbody.appendChild(row);
+       }
+       
+       
+       SelfCheckManager.prototype.drawFinesPage = function() {
+       
+           // TODO add option to hid scanBox
+           // this.updateScanBox(...)
+       
+           this.goToTab('fines');
+           progressDialog.show(true);
+       
+           if(this.creditPayableBalance > 0 && this.orgSettings[SET_CC_PAYMENT_ALLOWED]) {
+               openils_Util.show('oils-selfck-pay-fines-link', 'inline');
+           }
+       
+           this.finesTbody = dojo.byId('oils-selfck-fines-tbody');
+           if(!this.finesTemplate)
+               this.finesTemplate = this.finesTbody.removeChild(dojo.byId('oils-selfck-fines-row'));
+           while(this.finesTbody.childNodes[0])
+               this.finesTbody.removeChild(this.finesTbody.childNodes[0]);
+       
+           // when user clicks on a selector checkbox, update the total owed
+           var updateSelected = function() {
+               var total = 0;
+               dojo.forEach(
+                   dojo.query('[name=selector]', this.finesTbody),
+                   function(input) {
+                       if(input.checked)
+                           total += Number(input.getAttribute('balance_owed'));
+                   }
+               );
+       
+               total = total.toFixed(2);
+               dojo.byId('oils-selfck-selected-total').innerHTML = 
+                   dojo.string.substitute(localeStrings.TOTAL_FINES_SELECTED, [total]);
+           }
+       
+           // wire up the batch on/off selector
+           var sel = dojo.byId('oils-selfck-fines-selector');
+           sel.onchange = function() {
+               dojo.forEach(
+                   dojo.query('[name=selector]', this.finesTbody),
+                   function(input) {
+                       input.checked = sel.checked;
+                   }
+               );
+           };
+       
+           var self = this;
+           var handler = function(dataList) {
+       
+               self.finesCount = dataList.length;
+               self.finesData = dataList;
+       
+               for(var i in dataList) {
+       
+                   var data = dataList[i];
+                   var row = self.finesTemplate.cloneNode(true);
+                   var type = data.transaction.xact_type();
+       
+                   if(type == 'circulation') {
+                       self.byName(row, 'type').innerHTML = type;
+                       self.byName(row, 'details').innerHTML = data.record.title();
+       
+                   } else if(type == 'grocery') {
+                       self.byName(row, 'type').innerHTML = 'Miscellaneous'; // Go ahead and head off any confusion around "grocery".  TODO i18n
+                       self.byName(row, 'details').innerHTML = data.transaction.last_billing_type();
+                   }
+       
+                   self.byName(row, 'total_owed').innerHTML = data.transaction.total_owed();
+                   self.byName(row, 'total_paid').innerHTML = data.transaction.total_paid();
+                   self.byName(row, 'balance').innerHTML = data.transaction.balance_owed();
+       
+                   // row selector
+                   var selector = self.byName(row, 'selector')
+                   selector.onchange = updateSelected;
+                   selector.setAttribute('xact', data.transaction.id());
+                   selector.setAttribute('balance_owed', data.transaction.balance_owed());
+                   selector.checked = true;
+       
+                   self.finesTbody.appendChild(row);
+               }
+       
+               updateSelected();
+           }
+       
+       
+           fieldmapper.standardRequest( 
+               ['open-ils.actor', 'open-ils.actor.user.transactions.have_balance.fleshed'],
+               {   async : true,
+                   params : [this.authtoken, this.patron.id()],
+                   oncomplete : function(r) { 
+                       progressDialog.hide();
+                       handler(openils_Util.readResponse(r));
+                   }
+               }
+           );
+       }
+       
+       SelfCheckManager.prototype.checkin = function(barcode, abortTransit) {
+       
+           var resp = fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.transit.abort'],
+               {params : [this.authtoken, {barcode : barcode}]}
+           );
+       
+           // resp == 1 on success
+           if(openils_Event.parse(resp))
+               return false;
+       
+           var resp = fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.checkin.override'],
+               {params : [
+                   this.authtoken, {
+                       patron_id : this.patron.id(),
+                       copy_barcode : barcode,
+                       noop : true
+                   }
+               ]}
+           );
+       
+           if(!resp.length) resp = [resp];
+           for(var i = 0; i < resp.length; i++) {
+               var tc = openils_Event.parse(resp[i]).textcode;
+               if(tc == 'SUCCESS' || tc == 'NO_CHANGE') {
+                   continue;
+               } else {
+                   return false;
+               }
+           }
+       
+           return true;
+       }
+       
+       /**
+        * Check out a single item.  If the item is already checked 
+        * out to the patron, redirect to renew()
+        */
+       SelfCheckManager.prototype.checkout = function(barcode, override) {
+       
+           this.prevCirc = null;
+       
+           if(!barcode) {
+               this.updateScanbox(null, true);
+               return;
+           }
+       
+           if(this.mockCheckouts) {
+               // if we're in mock-checkout mode, just insert another
+               // fake circ into the table and get out of here.
+               this.displayCheckout(this.mockCheckout, 'checkout');
+               return;
+           }
+       
+           // TODO see if it's a patron barcode
+           // TODO see if this item has already been checked out in this session
+       
+           var method = 'open-ils.circ.checkout.full';
+           if(override) method += '.override';
+       
+           console.log("Checkout out item " + barcode + " with method " + method);
+       
+           var result = fieldmapper.standardRequest(
+               ['open-ils.circ', method],
+               {params: [
+                   this.authtoken, {
+                       patron_id : this.patron.id(),
+                       copy_barcode : barcode
+                   }
+               ]}
+           );
+       
+           var stat = this.handleXactResult('checkout', barcode, result);
+       
+           if(stat.override) {
+               this.checkout(barcode, true);
+           } else if(stat.doOver) {
+               this.checkout(barcode);
+           } else if(stat.renew) {
+               this.renew(barcode);
+           }
+       }
+       
+       SelfCheckManager.prototype.failPartMessage = function(result) {
+           if (result.payload && result.payload.fail_part) {
+               var stringKey = "FAIL_PART_" +
+                   result.payload.fail_part.replace(/\./g, "_");
+               return localeStrings[stringKey];
+           } else {
+               return null;
+           }
+       }
+       
+       SelfCheckManager.prototype.handleXactResult = function(action, item, result) {
+       
+           var displayText = '';
+       
+           // If true, the display message is important enough to pop up.  Whether or not
+           // an alert() actually occurs, depends on org unit settings
+           var popup = false;  
+           var sound = ''; // sound file reference
+           var payload = result.payload || {};
+           var overrideEvents = this.orgSettings[SET_AUTO_OVERRIDE_EVENTS];
+           var blockStatuses = this.orgSettings[SET_BLOCK_CHECKOUT_ON_COPY_STATUS];
+               
+           if(result.textcode == 'NO_SESSION') {
+       
+               return this.logoutStaff();
+       
+           } else if(result.textcode == 'SUCCESS') {
+       
+               if(action == 'checkout') {
+       
+                   displayText = dojo.string.substitute(localeStrings.CHECKOUT_SUCCESS, [item]);
+                   this.displayCheckout(result, 'checkout');
+       
+                   if(payload.holds_fulfilled && payload.holds_fulfilled.length) {
+                       // A hold was fulfilled, update the hold numbers in the circ summary
+                       console.log("fulfilled hold " + payload.holds_fulfilled + " during checkout");
+                       this.holdsSummary = null;
+                       this.updateHoldsSummary();
+                   }
+       
+                   this.updateCircSummary(true);
+       
+               } else if(action == 'renew') {
+       
+                   displayText = dojo.string.substitute(localeStrings.RENEW_SUCCESS, [item]);
+                   this.displayCheckout(result, 'renew');
+               }
+       
+               this.checkouts.push({circ : result.payload.circ.id()});
+               sound = 'checkout-success';
+               this.updateScanBox();
+       
+           } else if(result.textcode == 'OPEN_CIRCULATION_EXISTS' && action == 'checkout') {
+       
+               // Server says the item is already checked out.  If it's checked out to the
+               // current user, we may need to renew it.  
+       
+               if(payload.old_circ) { 
+       
+                   /*
+                   old_circ refers to the previous checkout IFF it's for the same user. 
+                   If no auto-renew interval is not defined, assume we should renew it
+                   If an auto-renew interval is defined and the payload comes back with
+                   auto_renew set to true, do the renewal.  Otherwise, let the patron know
+                   the item is already checked out to them.  */
+       
+                   if( !this.orgSettings[SET_AUTO_RENEW_INTERVAL] ||
+                       (this.orgSettings[SET_AUTO_RENEW_INTERVAL] && payload.auto_renew) ) {
+                       this.prevCirc = payload.old_circ.id();
+                       return { renew : true };
+                   }
+       
+                   popup = true;
+                   sound = 'checkout-failure';
+                   displayText = dojo.string.substitute(localeStrings.ALREADY_OUT, [item]);
+       
+               } else {
+       
+                   if( // copy is marked lost.  if configured to do so, check it in and try again.
+                       result.payload.copy && 
+                       result.payload.copy.status() == /* LOST */ 3 &&
+                       overrideEvents && overrideEvents.length &&
+                       overrideEvents.indexOf('COPY_STATUS_LOST') != -1) {
+       
+                           if(this.checkin(item)) {
+                               return { doOver : true };
+                           }
+                   }
+       
+                   
+                   // item is checked out to some other user
+                   popup = true;
+                   sound = 'checkout-failure';
+                   displayText = dojo.string.substitute(localeStrings.OPEN_CIRCULATION_EXISTS, [item]);
+               }
+       
+               this.updateScanBox({select:true});
+       
+           } else {
+       
+           
+               if(overrideEvents && overrideEvents.length) {
+                   
+                   // see if the events we received are all in the list of
+                   // events to override
+           
+                   if(!result.length) result = [result];
+           
+                   var override = true;
+                   for(var i = 0; i < result.length; i++) {
+       
+                       var match = overrideEvents.filter(function(e) { return (e == result[i].textcode); })[0];
+       
+                       if(!match) {
+                           override = false;
+                           break;
+                       }
+       
+                       if(result[i].textcode == 'COPY_NOT_AVAILABLE' && blockStatuses && blockStatuses.length) {
+       
+                           var stat = result[i].payload.status(); // copy status
+                           if(typeof stat == 'object') stat = stat.id();
+       
+                           var match2 = blockStatuses.filter(function(e) { return (e == stat); })[0];
+       
+                           if(match2) { // copy is in a blocked status
+                               override = false;
+                               break;
+                           }
+                       }
+       
+                       if(result[i].textcode == 'COPY_IN_TRANSIT') {
+                           // to override a transit, we have to abort the transit and check it in first
+                           if(this.checkin(item, true)) {
+                               return { doOver : true };
+                           } else {
+                               override = false;
+                           }
+                       }
+                   }
+       
+                   if(override) 
+                       return { override : true };
+               }
+           
+               this.updateScanBox({select : true});
+               popup = true;
+               sound = 'checkout-failure';
+       
+               if(action == 'renew')
+                   this.checkouts.push({circ : this.prevCirc, renewal_failure : true});
+       
+               if(result.length) 
+                   result = result[0];
+       
+               switch(result.textcode) {
+       
+                   // TODO custom handler for blocking penalties
+       
+                   case 'MAX_RENEWALS_REACHED' :
+                       displayText = dojo.string.substitute(
+                           localeStrings.MAX_RENEWALS, [item]);
+                       break;
+       
+                   case 'ITEM_NOT_CATALOGED' :
+                       displayText = dojo.string.substitute(
+                           localeStrings.ITEM_NOT_CATALOGED, [item]);
+                       break;
+       
+                   case 'OPEN_CIRCULATION_EXISTS' :
+                       displayText = dojo.string.substitute(
+                           localeStrings.OPEN_CIRCULATION_EXISTS, [item]);
+       
+                       break;
+       
+                   default:
+                       console.error('Unhandled event ' + result.textcode);
+       
+                       if (!(displayText = this.failPartMessage(result))) {
+                           if (action == 'checkout' || action == 'renew') {
+                               displayText = dojo.string.substitute(
+                                   localeStrings.GENERIC_CIRC_FAILURE, [item]);
+                           } else {
+                               displayText = dojo.string.substitute(
+                                   localeStrings.UNKNOWN_ERROR, [result.textcode]);
+                           }
+                       }
+               }
+           }
+       
+           this.handleAlert(displayText, popup, sound);
+           return {};
+       }
+       
+       
+       /**
+        * Renew an item
+        */
+       SelfCheckManager.prototype.renew = function(barcode, override) {
+       
+           var method = 'open-ils.circ.renew';
+           if(override) method += '.override';
+       
+           console.log("Renewing item " + barcode + " with method " + method);
+       
+           var result = fieldmapper.standardRequest(
+               ['open-ils.circ', method],
+               {params: [
+                   this.authtoken, {
+                       patron_id : this.patron.id(),
+                       copy_barcode : barcode
+                   }
+               ]}
+           );
+       
+           console.log(js2JSON(result));
+       
+           var stat = this.handleXactResult('renew', barcode, result);
+       
+           if(stat.override)
+               this.renew(barcode, true);
+       }
+       
+       /**
+        * Display the result of a checkout or renewal in the items out table
+        */
+       SelfCheckManager.prototype.displayCheckout = function(evt, type, itemsOut) {
+       
+           var copy = evt.payload.copy;
+           var record = evt.payload.record;
+           var circ = evt.payload.circ;
+           var row = this.circTemplate.cloneNode(true);
+       
+           if(record.isbn()) {
+               this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + record.isbn());
+           }
+       
+           this.byName(row, 'barcode').innerHTML = copy.barcode();
+           this.byName(row, 'title').innerHTML = record.title();
+           this.byName(row, 'author').innerHTML = record.author();
+           this.byName(row, 'remaining').innerHTML = circ.renewal_remaining();
+           openils_Util.show(this.byName(row, type));
+       
+           var date = dojo_date_stamp.fromISOString(circ.due_date());
+           this.byName(row, 'due_date').innerHTML = 
+               dojo_date_locale.format(date, {selector : 'date'});
+       
+           // put new circs at the top of the list
+           var tbody = this.circTbody;
+           if(itemsOut) tbody = this.itemsOutTbody;
+           tbody.insertBefore(row, tbody.getElementsByTagName('tr')[0]);
+       }
+       
+       
+       SelfCheckManager.prototype.byName = function(node, name) {
+           return dojo.query('[name=' + name+']', node)[0];
+       }
+       
+       
+       SelfCheckManager.prototype.initPrinter = function() {
+           try { // Mozilla only
+                       netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
+               netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+               netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesRead');
+               netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesWrite');
+               var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+               if (pref)
+                   pref.setBoolPref('print.always_print_silent', true);
+           } catch(E) {
+               console.log("Unable to initialize auto-printing"); 
+           }
+       }
+       
+       /**
+        * Print a receipt for this session's checkouts
+        */
+       SelfCheckManager.prototype.printSessionReceipt = function(callback) {
+       
+           var circIds = [];
+           var circCtx = []; // circ context data.  in this case, renewal_failure info
+       
+           // collect the circs and failure info
+           dojo.forEach(
+               this.checkouts, 
+               function(blob) {
+                   circIds.push(blob.circ);
+                   circCtx.push({renewal_failure:blob.renewal_failure});
+               }
+           );
+       
+           var params = [
+               this.authtoken, 
+               this.staff.ws_ou(),
+               null,
+               'format.selfcheck.checkout',
+               'print-on-demand',
+               circIds,
+               circCtx
+           ];
+       
+           var self = this;
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],
+               {   
+                   async : true,
+                   params : params,
+                   oncomplete : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       var output = resp.template_output();
+                       if(output) {
+                           self.printData(output.data(), self.checkouts.length, callback); 
+                       } else {
+                           var error = resp.error_output();
+                           if(error) {
+                               throw new Error("Error creating receipt: " + error.data());
+                           } else {
+                               throw new Error("No receipt data returned from server");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       SelfCheckManager.prototype.printData = function(data, numItems, callback) {
+       
+           var win = window.open('', '', 'resizable,width=700,height=500,scrollbars=1'); 
+           win.document.body.innerHTML = data;
+           win.print();
+       
+           /*
+            * There is no way to know when the browser is done printing.
+            * Make a best guess at when to close the print window by basing
+            * the setTimeout wait on the number of items to be printed plus
+            * a small buffer
+            */
+           var sleepTime = 1000;
+           if(numItems > 0) 
+               sleepTime += (numItems / 2) * 1000;
+       
+           setTimeout(
+               function() { 
+                   win.close(); // close the print window
+                   if(callback)
+                       callback(); // fire optional post-print callback
+               },
+               sleepTime 
+           );
+       }
+       
+       
+       /**
+        * Print a receipt for this user's items out
+        */
+       SelfCheckManager.prototype.printItemsOutReceipt = function(callback) {
+       
+           if(!this.itemsOut.length) return;
+       
+           progressDialog.show(true);
+       
+           var params = [
+               this.authtoken, 
+               this.staff.ws_ou(),
+               null,
+               'format.selfcheck.items_out',
+               'print-on-demand',
+               this.itemsOut
+           ];
+       
+           var self = this;
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],
+               {   
+                   async : true,
+                   params : params,
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       var resp = openils_Util.readResponse(r);
+                       var output = resp.template_output();
+                       if(output) {
+                           self.printData(output.data(), self.itemsOut.length, callback); 
+                       } else {
+                           var error = resp.error_output();
+                           if(error) {
+                               throw new Error("Error creating receipt: " + error.data());
+                           } else {
+                               throw new Error("No receipt data returned from server");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       /**
+        * Print a receipt for this user's items out
+        */
+       SelfCheckManager.prototype.printHoldsReceipt = function(callback) {
+       
+           if(!this.holds.length) return;
+       
+           progressDialog.show(true);
+       
+           var holdIds = [];
+           var holdData = [];
+       
+           dojo.forEach(this.holds,
+               function(data) {
+                   holdIds.push(data.hold.id());
+                   if(data.status == 4) {
+                       holdData.push({ready : true});
+                   } else {
+                       holdData.push({
+                           queue_position : data.queue_position, 
+                           potential_copies : data.potential_copies
+                       });
+                   }
+               }
+           );
+       
+           var params = [
+               this.authtoken, 
+               this.staff.ws_ou(),
+               null,
+               'format.selfcheck.holds',
+               'print-on-demand',
+               holdIds,
+               holdData
+           ];
+       
+           var self = this;
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.fire_hold_trigger_events'],
+               {   
+                   async : true,
+                   params : params,
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       var resp = openils_Util.readResponse(r);
+                       var output = resp.template_output();
+                       if(output) {
+                           self.printData(output.data(), self.holds.length, callback); 
+                       } else {
+                           var error = resp.error_output();
+                           if(error) {
+                               throw new Error("Error creating receipt: " + error.data());
+                           } else {
+                               throw new Error("No receipt data returned from server");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       
+       SelfCheckManager.prototype.printPaymentReceipt = function(response, callback) {
+           
+           var self = this;
+           progressDialog.show(true);
+       
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.money.payment_receipt.print'],
+               {
+                   async : true,
+                   params : [this.authtoken, response.payments],
+                   oncomplete : function(r) {
+                       var resp = openils_Util.readResponse(r);
+                       var output = resp.template_output();
+                       progressDialog.hide();
+                       if(output) {
+                           self.printData(output.data(), 1, callback); 
+                       } else {
+                           var error = resp.error_output();
+                           if(error) {
+                               throw new Error("Error creating receipt: " + error.data());
+                           } else {
+                               throw new Error("No receipt data returned from server");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       /**
+        * Print a receipt for this user's items out
+        */
+       SelfCheckManager.prototype.printFinesReceipt = function(callback) {
+       
+           progressDialog.show(true);
+       
+           var params = [
+               this.authtoken, 
+               this.staff.ws_ou(),
+               null,
+               'format.selfcheck.fines',
+               'print-on-demand',
+               [this.patron.id()]
+           ];
+       
+           var self = this;
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.fire_user_trigger_events'],
+               {   
+                   async : true,
+                   params : params,
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       var resp = openils_Util.readResponse(r);
+                       var output = resp.template_output();
+                       if(output) {
+                           self.printData(output.data(), self.finesCount, callback); 
+                       } else {
+                           var error = resp.error_output();
+                           if(error) {
+                               throw new Error("Error creating receipt: " + error.data());
+                           } else {
+                               throw new Error("No receipt data returned from server");
+                           }
+                       }
+                   }
+               }
+           );
+       }
+       
+       
+       
+       
+       /**
+        * Logout the patron and return to the login page
+        */
+       SelfCheckManager.prototype.logoutPatron = function(print) {
+           progressDialog.show(true); // prevent patron from clicking logout link twice
+           if(print && this.checkouts.length) {
+               this.printSessionReceipt(
+                   function() {
+                       location.href = location.href;
+                   }
+               );
+           } else {
+               location.href = location.href;
+           }
+       }
+       
+       
+       /**
+        * Fire up the manager on page load
+        */
+       openils_Util.addOnLoad(
+           function() {
+               new SelfCheckManager().init();
+           }
+       );
+       
+
+});
\ No newline at end of file
index 6cd329f..38d7dc3 100644 (file)
@@ -1,39 +1,49 @@
-dojo.require('openils.Util');
-dojo.require('openils.User');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var contextOrg;
-
-function setup() {
-    buildGrid();
-
-    var connect = function() {
-        dojo.connect(contextOrgSelector, 'onChange',
-            function() {
-                contextOrg = this.attr('value');
-                crGrid.resetStore();
-                buildGrid();
-            }
-        );
-    };
-
-    new openils.User().buildPermOrgSelector(
-        'ADMIN_ACQ_CANCEL_CAUSE', contextOrgSelector, null, connect);
-}
-
-function buildGrid() {
-
-    if(contextOrg == null)
-        contextOrg = openils.User.user.ws_ou();
-
-    crGrid.loadAll( 
-        {order_by : {acqcr : 'label'}}, 
-        {org_unit : fieldmapper.aou.fullPath(contextOrg, true)}
-    );
-}
-
-openils.Util.addOnLoad(setup);
-
-
+require([
+       "openils/Util",
+       "openils/User",
+       "openils/widget/AutoGrid",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(openils_Util,
+       openils_User,
+       openils_widget_AutoGrid,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var contextOrg;
+       
+       function setup() {
+           buildGrid();
+       
+           var connect = function() {
+               dojo.connect(contextOrgSelector, 'onChange',
+                   function() {
+                       contextOrg = this.attr('value');
+                       crGrid.resetStore();
+                       buildGrid();
+                   }
+               );
+           };
+       
+           new openils_User().buildPermOrgSelector(
+               'ADMIN_ACQ_CANCEL_CAUSE', contextOrgSelector, null, connect);
+       }
+       
+       function buildGrid() {
+       
+           if(contextOrg == null)
+               contextOrg = openils_User.user.ws_ou();
+       
+           crGrid.loadAll( 
+               {order_by : {acqcr : 'label'}}, 
+               {org_unit : fieldmapper.aou.fullPath(contextOrg, true)}
+           );
+       }
+       
+       openils_Util.addOnLoad(setup);
+       
+       
+       
+
+});
\ No newline at end of file
index 2557717..a2bf46b 100644 (file)
@@ -1,35 +1,42 @@
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
+require([
+       "openils/widget/AutoGrid",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(openils_widget_AutoGrid,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var owner;
+       
+       function prepareOwnerSelector(perm) {
+           new openils.User().buildPermOrgSelector(
+               perm,
+               ownerSelect,
+               null,
+               function() {
+                   dojo.connect(
+                       ownerSelect,
+                       "onChange",
+                       function() {
+                           owner = fieldmapper.aou.findOrgUnit(this.attr("value"));
+                           grid.resetStore();
+                           populateGrid();
+                       }
+                   );
+               }
+           );
+       }
+       
+       function populateGrid(id) {
+           var search = typeof(ownerSelect) == "undefined" ? {"id": {"!=": null}} : {
+               "org_unit": fieldmapper.aou.orgNodeTrail(
+                   owner || fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()),
+                   true /* asId */
+               )
+           };
+           if (id) search.id = id;
+       
+           grid.loadAll(null, search);
+       }
+       
 
-var owner;
-
-function prepareOwnerSelector(perm) {
-    new openils.User().buildPermOrgSelector(
-        perm,
-        ownerSelect,
-        null,
-        function() {
-            dojo.connect(
-                ownerSelect,
-                "onChange",
-                function() {
-                    owner = fieldmapper.aou.findOrgUnit(this.attr("value"));
-                    grid.resetStore();
-                    populateGrid();
-                }
-            );
-        }
-    );
-}
-
-function populateGrid(id) {
-    var search = typeof(ownerSelect) == "undefined" ? {"id": {"!=": null}} : {
-        "org_unit": fieldmapper.aou.orgNodeTrail(
-            owner || fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()),
-            true /* asId */
-        )
-    };
-    if (id) search.id = id;
-
-    grid.loadAll(null, search);
-}
+});
\ No newline at end of file
index 69e3932..e478c19 100644 (file)
-dojo.require("dojo.dnd.Container");
-dojo.require("dojo.dnd.Source");
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.requireLocalization('openils.conify', 'conify');
-var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
-
-
-var formCache = {};
-var formula, entryTbody, entryTemplate, dndSource;
-var virtualId = -1;
-var pcrud;
-
-
-function gridDataLoader() {
-    fListGrid.resetStore();
-    fListGrid.showLoadProgressIndicator();
-    fieldmapper.standardRequest(
-        ["open-ils.acq", "open-ils.acq.distribution_formula.ranged.retrieve"], {
-            "async": true,
-            "params": [
-                openils.User.authtoken,
-                fListGrid.displayOffset,
-                fListGrid.displayLimit
-            ],
-            "onresponse": function(r) {
-                var form = openils.Util.readResponse(r);
-                formCache[form.id()] = form;
-                fListGrid.store.newItem(form.toStoreItem());
-            },
-            "oncomplete": function() {
-                fListGrid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-
-function draw() {
-
-    pcrud = new openils.PermaCrud();
-
-    if(formulaId) {
-        openils.Util.hide('formula-list-div');
-        drawFormulaSummary();
-    } else {
-
-        openils.Util.hide('formula-entry-div');
-        fListGrid.onPostCreate = function(fmObject) {
-            location.href = location.href + '/' + fmObject.id();
-        }
-
-        fListGrid.dataLoader = gridDataLoader;
-        gridDataLoader();
-    }
-}
-
-function cloneSelectedFormula() {
-    var item = fListGrid.getSelectedItems()[0];
-    if(!item) return;
-    var formula = new fieldmapper.acqf().fromStoreItem(item);
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.distribution_formula.clone'],
-        {
-            asnyc : true,
-            params : [
-                openils.User.authtoken, 
-                formula.id(), 
-                dojo.string.substitute(localeStrings.ACQ_DISTRIB_FORMULA_NAME_CLONE, [formula.name()])
-            ],
-            oncomplete : function(r) {
-                if(r = openils.Util.readResponse(r)) {
-                    location.href = oilsBasePath + '/conify/global/acq/distribution_formula/' + r;
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(draw);
-
-function getItemCount(rowIndex, item) {
-    if(!item) return '';
-    var form = formCache[this.grid.store.getValue(item, "id")];
-    if(!form) return 0;
-    var count = 0;
-    dojo.forEach(form.entries(), function(e) { count = count + e.item_count(); });
-    return count;
-}
-
-function byName(node, name) {
-    return dojo.query('[name='+name+']', node)[0];
-}
-
-function drawFormulaSummary() {
-    openils.Util.show('formula-entry-div');
-
-    var entries = pcrud.search('acqdfe', {formula: formulaId}, {order_by:{acqdfe : 'position'}});
-    formula = pcrud.retrieve('acqdf', formulaId);
-    formula.entries(entries);
-
-    dojo.byId('formula_head').innerHTML = formula.name();
-    dojo.byId('formula_head').onclick = function() {
-        var name = prompt(localeStrings.ACQ_DISTRIB_FORMULA_NAME_PROMPT, formula.name());
-        if(name && name != formula.name()) {
-            formula.name(name);
-            pcrud = new openils.PermaCrud();
-            pcrud.update(formula);
-            dojo.byId('formula_head').innerHTML = name;
-        }
-    }
-
-    dojo.forEach(entries, function(entry) { addEntry(entry); } );
-}
-
-function addEntry(entry) {
-
-    if(!entryTbody) {
-        entryTbody = dojo.byId('formula-entry-tbody');
-        entryTemplate = entryTbody.removeChild(dojo.byId('formula-entry-tempate'));
-        dndSource = new dojo.dnd.Source(entryTbody);
-        dndSource.selectAll(); 
-        dndSource.deleteSelectedNodes();
-        dndSource.clearItems();
-    }
-
-    if(!entry) {
-        entry = new fieldmapper.acqdfe();
-        entry.formula(formulaId);
-        entry.item_count(1);
-        entry.owning_lib(openils.User.user.ws_ou());
-        entry.id(virtualId--);
-        entry.isnew(true);
-        formula.entries().push(entry);
-    }
-
-    var row = entryTbody.appendChild(entryTemplate.cloneNode(true));
-    row.setAttribute('entry', entry.id());
-    dndSource.insertNodes(false, [row]);
-    byName(row, 'delete').onclick = function() {
-        entry.isdeleted(true);
-        entryTbody.removeChild(row);
-        dndSource.sync();
-    };
-
-    dojo.forEach(
-        ['owning_lib', 'location', 'item_count'],
-        function(field) {
-            new openils.widget.AutoFieldWidget({
-                forceSync : true,
-                fmField : field, 
-                fmObject : entry,
-                fmClass : 'acqdfe',
-                parentNode : byName(row, field),
-                orgDefaultsToWs : true,
-                orgLimitPerms : ['ADMIN_ACQ_DISTRIB_FORMULA'],
-                widgetClass : (field == 'item_count') ? 'dijit.form.NumberSpinner' : null,
-                dijitArgs : (field == 'item_count') ? {min:1, places:0} : null
-            }).build(
-                function(w, ww) {
-                    dojo.connect(w, 'onChange', 
-                        function(newVal) {
-                            entry[field]( newVal );
-                            entry.ischanged(true);
-                        }
-                    )
-                }
-            );
-        }
-    );
-}
-
-function saveFormula() {
-    var pos = 1;
-    var updatedEntries = [];
-    var deletedEntries = [];
-
-    // remove deleted entries from consideration for collision protection
-    for(var i = 0; i < formula.entries().length; i++) {
-        if(formula.entries()[i].isdeleted())
-            deletedEntries.push(formula.entries().splice(i--, 1)[0])
-    }
-
-    // update entry positions and create temporary collision avoidance entries
-    dojo.forEach(
-        dndSource.getAllNodes(),
-        function(node) {
-
-            var entryId = node.getAttribute('entry');
-            var entry = formula.entries().filter(function(e) {return (e.id() == entryId)})[0];
-
-            if(entry.position() != pos) {
-
-                // update the position
-                var changedEntry = entry.clone();
-                changedEntry.position(pos);
-                changedEntry.ischanged(true);
-                updatedEntries.push(changedEntry);
-
-                // clear the virtual ID
-                if(changedEntry.isnew())
-                    changedEntry.id(null); 
-
-                var oldEntry = formula.entries().filter(function(e) {return (e.position() == pos)})[0];
-
-                if(oldEntry) {
-                    // move the entry currently in that spot temporarily into negative territory
-                    var moveMe = oldEntry.clone();
-                    moveMe.ischanged(true);
-                    moveMe.position(moveMe.position() * -1); 
-                    updatedEntries.unshift(moveMe);
-                }
-            }
-            pos++;
-        }
-    );
-
-    // finally, for every entry that changed w/o changing position
-    // throw it on the list for update
-    dojo.forEach(
-        formula.entries(),
-        function(entry) {
-            if(entry.ischanged() && !entry.isdeleted() && !entry.isnew()) {
-                if(updatedEntries.filter(function(e) { return (e.id() == entry.id()) }).length == 0)
-                    updatedEntries.push(entry);
-            }
-        }
-    );
-
-    updatedEntries = deletedEntries.concat(updatedEntries);
-    if(updatedEntries.length) {
-        pcrud = new openils.PermaCrud();
-        try { 
-            pcrud.apply(updatedEntries);
-        } catch(E) {
-            alert('error updating: ' + E);
-            return;
-        }
-        location.href = location.href;
-    }
-}
-
-
+require([
+       "dojo/dnd/Container",
+       "dojo/dnd/Source",
+       "openils/widget/AutoGrid",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/AutoFieldWidget"
+       ],
+function(dojo_dnd_Container,
+       dojo_dnd_Source,
+       openils_widget_AutoGrid,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_widget_AutoFieldWidget){
+       dojo.requireLocalization('openils.conify', 'conify');
+       var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
+       
+       
+       var formCache = {};
+       var formula, entryTbody, entryTemplate, dndSource;
+       var virtualId = -1;
+       var pcrud;
+       
+       
+       function gridDataLoader() {
+           fListGrid.resetStore();
+           fListGrid.showLoadProgressIndicator();
+           fieldmapper.standardRequest(
+               ["open-ils.acq", "open-ils.acq.distribution_formula.ranged.retrieve"], {
+                   "async": true,
+                   "params": [
+                       openils.User.authtoken,
+                       fListGrid.displayOffset,
+                       fListGrid.displayLimit
+                   ],
+                   "onresponse": function(r) {
+                       var form = openils.Util.readResponse(r);
+                       formCache[form.id()] = form;
+                       fListGrid.store.newItem(form.toStoreItem());
+                   },
+                   "oncomplete": function() {
+                       fListGrid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       
+       function draw() {
+       
+           pcrud = new openils_PermaCrud();
+       
+           if(formulaId) {
+               openils.Util.hide('formula-list-div');
+               drawFormulaSummary();
+           } else {
+       
+               openils.Util.hide('formula-entry-div');
+               fListGrid.onPostCreate = function(fmObject) {
+                   location.href = location.href + '/' + fmObject.id();
+               }
+       
+               fListGrid.dataLoader = gridDataLoader;
+               gridDataLoader();
+           }
+       }
+       
+       function cloneSelectedFormula() {
+           var item = fListGrid.getSelectedItems()[0];
+           if(!item) return;
+           var formula = new fieldmapper.acqf().fromStoreItem(item);
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.distribution_formula.clone'],
+               {
+                   asnyc : true,
+                   params : [
+                       openils.User.authtoken, 
+                       formula.id(), 
+                       dojo.string.substitute(localeStrings.ACQ_DISTRIB_FORMULA_NAME_CLONE, [formula.name()])
+                   ],
+                   oncomplete : function(r) {
+                       if(r = openils.Util.readResponse(r)) {
+                           location.href = oilsBasePath + '/conify/global/acq/distribution_formula/' + r;
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(draw);
+       
+       function getItemCount(rowIndex, item) {
+           if(!item) return '';
+           var form = formCache[this.grid.store.getValue(item, "id")];
+           if(!form) return 0;
+           var count = 0;
+           dojo.forEach(form.entries(), function(e) { count = count + e.item_count(); });
+           return count;
+       }
+       
+       function byName(node, name) {
+           return dojo.query('[name='+name+']', node)[0];
+       }
+       
+       function drawFormulaSummary() {
+           openils.Util.show('formula-entry-div');
+       
+           var entries = pcrud.search('acqdfe', {formula: formulaId}, {order_by:{acqdfe : 'position'}});
+           formula = pcrud.retrieve('acqdf', formulaId);
+           formula.entries(entries);
+       
+           dojo.byId('formula_head').innerHTML = formula.name();
+           dojo.byId('formula_head').onclick = function() {
+               var name = prompt(localeStrings.ACQ_DISTRIB_FORMULA_NAME_PROMPT, formula.name());
+               if(name && name != formula.name()) {
+                   formula.name(name);
+                   pcrud = new openils_PermaCrud();
+                   pcrud.update(formula);
+                   dojo.byId('formula_head').innerHTML = name;
+               }
+           }
+       
+           dojo.forEach(entries, function(entry) { addEntry(entry); } );
+       }
+       
+       function addEntry(entry) {
+       
+           if(!entryTbody) {
+               entryTbody = dojo.byId('formula-entry-tbody');
+               entryTemplate = entryTbody.removeChild(dojo.byId('formula-entry-tempate'));
+               dndSource = new dojo_dnd_Source(entryTbody);
+               dndSource.selectAll(); 
+               dndSource.deleteSelectedNodes();
+               dndSource.clearItems();
+           }
+       
+           if(!entry) {
+               entry = new fieldmapper.acqdfe();
+               entry.formula(formulaId);
+               entry.item_count(1);
+               entry.owning_lib(openils.User.user.ws_ou());
+               entry.id(virtualId--);
+               entry.isnew(true);
+               formula.entries().push(entry);
+           }
+       
+           var row = entryTbody.appendChild(entryTemplate.cloneNode(true));
+           row.setAttribute('entry', entry.id());
+           dndSource.insertNodes(false, [row]);
+           byName(row, 'delete').onclick = function() {
+               entry.isdeleted(true);
+               entryTbody.removeChild(row);
+               dndSource.sync();
+           };
+       
+           dojo.forEach(
+               ['owning_lib', 'location', 'item_count'],
+               function(field) {
+                   new openils_widget_AutoFieldWidget({
+                       forceSync : true,
+                       fmField : field, 
+                       fmObject : entry,
+                       fmClass : 'acqdfe',
+                       parentNode : byName(row, field),
+                       orgDefaultsToWs : true,
+                       orgLimitPerms : ['ADMIN_ACQ_DISTRIB_FORMULA'],
+                       widgetClass : (field == 'item_count') ? 'dijit.form.NumberSpinner' : null,
+                       dijitArgs : (field == 'item_count') ? {min:1, places:0} : null
+                   }).build(
+                       function(w, ww) {
+                           dojo.connect(w, 'onChange', 
+                               function(newVal) {
+                                   entry[field]( newVal );
+                                   entry.ischanged(true);
+                               }
+                           )
+                       }
+                   );
+               }
+           );
+       }
+       
+       function saveFormula() {
+           var pos = 1;
+           var updatedEntries = [];
+           var deletedEntries = [];
+       
+           // remove deleted entries from consideration for collision protection
+           for(var i = 0; i < formula.entries().length; i++) {
+               if(formula.entries()[i].isdeleted())
+                   deletedEntries.push(formula.entries().splice(i--, 1)[0])
+           }
+       
+           // update entry positions and create temporary collision avoidance entries
+           dojo.forEach(
+               dndSource.getAllNodes(),
+               function(node) {
+       
+                   var entryId = node.getAttribute('entry');
+                   var entry = formula.entries().filter(function(e) {return (e.id() == entryId)})[0];
+       
+                   if(entry.position() != pos) {
+       
+                       // update the position
+                       var changedEntry = entry.clone();
+                       changedEntry.position(pos);
+                       changedEntry.ischanged(true);
+                       updatedEntries.push(changedEntry);
+       
+                       // clear the virtual ID
+                       if(changedEntry.isnew())
+                           changedEntry.id(null); 
+       
+                       var oldEntry = formula.entries().filter(function(e) {return (e.position() == pos)})[0];
+       
+                       if(oldEntry) {
+                           // move the entry currently in that spot temporarily into negative territory
+                           var moveMe = oldEntry.clone();
+                           moveMe.ischanged(true);
+                           moveMe.position(moveMe.position() * -1); 
+                           updatedEntries.unshift(moveMe);
+                       }
+                   }
+                   pos++;
+               }
+           );
+       
+           // finally, for every entry that changed w/o changing position
+           // throw it on the list for update
+           dojo.forEach(
+               formula.entries(),
+               function(entry) {
+                   if(entry.ischanged() && !entry.isdeleted() && !entry.isnew()) {
+                       if(updatedEntries.filter(function(e) { return (e.id() == entry.id()) }).length == 0)
+                           updatedEntries.push(entry);
+                   }
+               }
+           );
+       
+           updatedEntries = deletedEntries.concat(updatedEntries);
+           if(updatedEntries.length) {
+               pcrud = new openils_PermaCrud();
+               try { 
+                   pcrud.apply(updatedEntries);
+               } catch(E) {
+                   alert('error updating: ' + E);
+                   return;
+               }
+               location.href = location.href;
+           }
+       }
+       
+       
+       
+
+});
\ No newline at end of file
index 87ebecd..0fb2a6c 100644 (file)
@@ -1,25 +1,33 @@
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-
-function draw() {
-    if (! targetId) {
-        pListGrid.loadAll({order_by:{acqedi : "id"}});       
-        // The following code does no good ...
-//        pListGrid.onPostCreate = function(fmObject) {
-//            location.href = location.href + '/' + fmObject.id();
-//        };
-        return;
-    }
-   
-    var pcrud = new openils.PermaCrud();
-    pcrud.retrieve('acqedi', targetId, {
-        // ... because this code here does nothing yet
-        oncomplete : function(r) {
-            console.log('edi_account is' + js2JSON(openils.Util.readResponse(r)));
-        }
-    });
-}
-
-openils.Util.addOnLoad(draw);
+require([
+       "openils/widget/AutoGrid",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud"
+       ],
+function(openils_widget_AutoGrid,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud){
+       
+       function draw() {
+           if (! targetId) {
+               pListGrid.loadAll({order_by:{acqedi : "id"}});       
+               // The following code does no good ...
+       //        pListGrid.onPostCreate = function(fmObject) {
+       //            location.href = location.href + '/' + fmObject.id();
+       //        };
+               return;
+           }
+          
+           var pcrud = new openils_PermaCrud();
+           pcrud.retrieve('acqedi', targetId, {
+               // ... because this code here does nothing yet
+               oncomplete : function(r) {
+                   console.log('edi_account is' + js2JSON(openils.Util.readResponse(r)));
+               }
+           });
+       }
+       
+       openils.Util.addOnLoad(draw);
+       
+       
 
+});
\ No newline at end of file
index d23be78..bd158bb 100644 (file)
@@ -1,63 +1,77 @@
-dojo.require("dojo.data.ItemFileWriteStore");
-dojo.require("dojox.grid.DataGrid");
-dojo.require("dojox.grid.cells.dijit");
-dojo.require("dojox.widget.PlaceholderMenuItem");
-dojo.require("dijit.form.CurrencyTextBox");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
+require([
+       "dojo/data/ItemFileWriteStore",
+       "dojox/grid/DataGrid",
+       "dojox/grid/cells/dijit",
+       "dojox/widget/PlaceholderMenuItem",
+       "dijit/form/CurrencyTextBox",
+       "dijit/form/FilteringSelect",
+       "openils/widget/AutoGrid",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojo_data_ItemFileWriteStore,
+       dojox_grid_DataGrid,
+       dojox_grid_cells_dijit,
+       dojox_widget_PlaceholderMenuItem,
+       dijit_form_CurrencyTextBox,
+       dijit_form_FilteringSelect,
+       openils_widget_AutoGrid,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var pcrud;
+       var ftOwner;
+       var ftList;
+       
+       function ftInit() {
+           pcrud = new openils_PermaCrud();
+       
+           new openils.User().buildPermOrgSelector(
+               "ADMIN_ACQ_FUND_TAG",
+               ftOwnerSelect,
+               null,
+               function() {
+                   dojo.connect(
+                       ftOwnerSelect,
+                       "onChange",
+                       function() {
+                           ftOwner = fieldmapper.aou.findOrgUnit(this.attr("value"));
+                           ftGrid.resetStore();
+                           buildFtGrid();
+                       }
+                   );
+                   buildFtGrid();
+               }
+           );
+       }
+       
+       function buildFtGrid() {
+           if (!ftOwner)
+               ftOwner = fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou());
+       
+           pcrud.search(
+               "acqft",
+               {"owner": fieldmapper.aou.orgNodeTrail(ftOwner, true /* asId */)},
+               {
+                   "async": true,
+                   "onresponse": function(r) {
+                       if ((ftList = openils.Util.readResponse(r))) {
+                           ftList = openils.Util.objectSort(ftList);
+                           ftList.forEach(
+                               function(o) {
+                                   ftGrid.store.newItem(acqft.toStoreItem(o));
+                               }
+                           );
+                       }
+                   },
+                   "oncomplete": function() {
+                       ftGrid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(ftInit);
+       
 
-var pcrud;
-var ftOwner;
-var ftList;
-
-function ftInit() {
-    pcrud = new openils.PermaCrud();
-
-    new openils.User().buildPermOrgSelector(
-        "ADMIN_ACQ_FUND_TAG",
-        ftOwnerSelect,
-        null,
-        function() {
-            dojo.connect(
-                ftOwnerSelect,
-                "onChange",
-                function() {
-                    ftOwner = fieldmapper.aou.findOrgUnit(this.attr("value"));
-                    ftGrid.resetStore();
-                    buildFtGrid();
-                }
-            );
-            buildFtGrid();
-        }
-    );
-}
-
-function buildFtGrid() {
-    if (!ftOwner)
-        ftOwner = fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou());
-
-    pcrud.search(
-        "acqft",
-        {"owner": fieldmapper.aou.orgNodeTrail(ftOwner, true /* asId */)},
-        {
-            "async": true,
-            "onresponse": function(r) {
-                if ((ftList = openils.Util.readResponse(r))) {
-                    ftList = openils.Util.objectSort(ftList);
-                    ftList.forEach(
-                        function(o) {
-                            ftGrid.store.newItem(acqft.toStoreItem(o));
-                        }
-                    );
-                }
-            },
-            "oncomplete": function() {
-                ftGrid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(ftInit);
+});
\ No newline at end of file
index 94fa959..9a1a255 100644 (file)
@@ -1,54 +1,70 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var alertContextOrg;
-var alertList;
-
-function alertInit() {
-
-    buildAlertGrid();
-    var connect = function() {
-        dojo.connect(alertContextOrgSelect, 'onChange',
-                     function() {
-                         alertContextOrg = this.getValue();
-                         alertGrid.resetStore();
-                         buildAlertGrid();
-                     }
-                    );
-    };
-    new openils.User().buildPermOrgSelector('ADMIN_ACQ_LINEITEM_ALERT_TEXT', alertContextOrgSelect, null, connect);
-}
-
-function buildAlertGrid() {
-    if(alertContextOrg == null)
-        alertContextOrg = openils.User.user.ws_ou();
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.line_item_alert_text.ranged.retrieve.all'],
-        {   async: true,
-            params: [openils.User.authtoken, alertContextOrg, fieldmapper.aou.findOrgDepth(alertContextOrg)],
-            oncomplete: function(r) {
-                if(alertList = openils.Util.readResponse(r)) {
-                    alertList = openils.Util.objectSort(alertList);
-                    dojo.forEach(alertList,
-                                 function(e) {
-                                     alertGrid.store.newItem(acqliat.toStoreItem(e));
-                                 }
-                                );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(alertInit);
-
+require([
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/CurrencyTextBox",
+       "dijit/Dialog",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_form_CurrencyTextBox,
+       dijit_Dialog,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var alertContextOrg;
+       var alertList;
+       
+       function alertInit() {
+       
+           buildAlertGrid();
+           var connect = function() {
+               dojo.connect(alertContextOrgSelect, 'onChange',
+                            function() {
+                                alertContextOrg = this.getValue();
+                                alertGrid.resetStore();
+                                buildAlertGrid();
+                            }
+                           );
+           };
+           new openils.User().buildPermOrgSelector('ADMIN_ACQ_LINEITEM_ALERT_TEXT', alertContextOrgSelect, null, connect);
+       }
+       
+       function buildAlertGrid() {
+           if(alertContextOrg == null)
+               alertContextOrg = openils.User.user.ws_ou();
+           fieldmapper.standardRequest(
+               ['open-ils.acq', 'open-ils.acq.line_item_alert_text.ranged.retrieve.all'],
+               {   async: true,
+                   params: [openils.User.authtoken, alertContextOrg, fieldmapper.aou.findOrgDepth(alertContextOrg)],
+                   oncomplete: function(r) {
+                       if(alertList = openils.Util.readResponse(r)) {
+                           alertList = openils.Util.objectSort(alertList);
+                           dojo.forEach(alertList,
+                                        function(e) {
+                                            alertGrid.store.newItem(acqliat.toStoreItem(e));
+                                        }
+                                       );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(alertInit);
+       
+       
+       
 
+});
\ No newline at end of file
index b91db71..32709ae 100644 (file)
@@ -1,21 +1,31 @@
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.Util');
-dojo.require('openils.User');
-dojo.require('openils.MarcXPathParser');
+require([
+       "openils/widget/AutoGrid",
+       "openils/PermaCrud",
+       "openils/Util",
+       "openils/User",
+       "openils/MarcXPathParser"
+       ],
+function(openils_widget_AutoGrid,
+       openils_PermaCrud,
+       openils_Util,
+       openils_User,
+       openils_MarcXPathParser){
+       
+       var xpathParser = new openils_MarcXPathParser();
+       
+       function init() {
+           attrGrid.loadAll({order_by : {acqlimad : 'code'}});
+       }
+       
+       function attrGridGetTag(rowIdx, item) {
+           return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).tags;
+       }
+       
+       function attrGridGetSubfield(rowIdx, item) {
+           return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).subfields;
+       }
+       
+       openils_Util.addOnLoad(init);
+       
 
-var xpathParser = new openils.MarcXPathParser();
-
-function init() {
-    attrGrid.loadAll({order_by : {acqlimad : 'code'}});
-}
-
-function attrGridGetTag(rowIdx, item) {
-    return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).tags;
-}
-
-function attrGridGetSubfield(rowIdx, item) {
-    return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).subfields;
-}
-
-openils.Util.addOnLoad(init);
+});
\ No newline at end of file
index 44eeb77..5a03f58 100644 (file)
-dojo.require('dijit.layout.TabContainer');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.MarcXPathParser');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-
-var provider;
-var xpathParser = new openils.MarcXPathParser();
-var subFields= [];
-var adminPermOrgs = [];
-var viewPermOrgs = [];
-var user;
-var viewPerms = [
-    'ADMIN_PROVIDER', 
-    'MANAGE_PROVIDER', 
-    'VIEW_PROVIDER'
-]; 
-    
-
-function draw() {
-
-    user = new openils.User();
-
-    if(providerId) {
-        drawOneProvider();
-        return;
-    }
-
-    openils.Util.hide('provider-details-div');
-
-    // after a provider is created, load the provider page
-    pListGrid.onPostCreate = function(fmObject) {
-        location.href = location.href + '/' + fmObject.id();
-    }
-
-    user.buildPermOrgSelector(
-        viewPerms,
-        contextOrgSelector, null,
-
-        function() {
-            if (!contextOrgSelector.attr('value')) return
-
-            dojo.connect(contextOrgSelector, 'onChange', drawProviderGrid);
-
-            // fetch the admin org units
-            user.getPermOrgList(
-                'ADMIN_PROVIDER',
-
-                function(list) {
-                    adminPermOrgs = list;
-
-                    // fetch the view org units
-                    user.getPermOrgList(
-                        viewPerms,
-                        function(list2) {
-                            viewPermOrgs = list2
-                            drawProviderGrid();
-                        },
-                        true, true
-                    );
-                },
-                true, true
-            );
-        }
-    );
-}
-
-
-function drawOneProvider() {
-    openils.Util.hide('provider-list-div');
-   
-    var pcrud = new openils.PermaCrud();
-    pcrud.retrieve('acqpro', providerId, {
-        oncomplete : function(r) {
-            provider = openils.Util.readResponse(r);
-            console.log('provider is' + js2JSON(provider));
-            var pane = new openils.widget.EditPane({fmObject:provider, paneStackCount:2}, dojo.byId('provider-summary-pane'));
-            pane.startup();
-            console.log("pane started");
-            dojo.connect(providerTabs, 'selectChild', drawProviderSummary);                        
-        }
-    });
-  
-    drawProviderSummary();
-}
-
-
-function drawProviderGrid() {
-    pListGrid.resetStore();
-
-    // view providers for here plus children
-    var list = fieldmapper.aou.descendantNodeList(
-        contextOrgSelector.attr('value'), true, true);
-
-    pListGrid.loadAll(
-        {order_by : [ // sort providers I can edit to the front
-            {   'class' : 'acqpro',
-                field : 'owner',
-                compare : {'in' : adminPermOrgs},
-                direction : 'desc'
-            },
-            {   'class' : 'acqpro',
-                field : 'name'
-            }
-        ]}, 
-        {'owner' : list}
-    );
-}
-
-function drawProviderSummary(child) {
-    var loadedTabs = {'provider-address' : true};
-    if(child){   
-        if(loadedTabs[child.id]) return;
-        loadedTabs[child.id] = true;
-        switch(child.id) {
-        case 'tab-pro-contact': 
-            pcListGrid.overrideEditWidgets.provider = new
-                dijit.form.TextBox({disabled: 'true', value: providerId});
-            pcListGrid.resetStore();
-            pcListGrid.loadAll({
-                oncomplete:function(r) {
-                    var count = 0; 
-                    pcListGrid.store.fetch( {
-                        onComplete:function(list) { 
-                            count =  list.length
-                            if (count>=1) {
-                                var contactIds = [];                                                    
-                                dojo.forEach(list, function(item) {
-                                        contactIds.push(pcListGrid.store.getValue(item, 'id')); 
-                                });
-                            
-                                pcaListGrid.overrideEditWidgets.contact = new
-                                dijit.form.FilteringSelect({store: pcListGrid.store});
-                                pcaListGrid.resetStore();
-                                pcaListGrid.loadAll({order_by:{acqpca : 'contact'}}, {contact: contactIds});
-
-                            } else { 
-                                return;
-                            }            
-                        }
-                    });
-                }
-            }, {provider : providerId});
-            
-            break;
-
-        case 'tab-attr': 
-            padListGrid.overrideEditWidgets.provider = new
-                dijit.form.TextBox({disabled: 'true', value: providerId});
-            padListGrid.resetStore();
-            padListGrid.loadAll({order_by:{acqlipad : 'code'}}, {provider : providerId});
-            break;
-
-        case 'tab-hold': 
-            phsListGrid.overrideEditWidgets.provider = new
-                dijit.form.TextBox({disabled: 'true', value: providerId});
-            phsListGrid.resetStore();
-            phsListGrid.loadAll({order_by:{acqphsm : 'name'}}, {provider : providerId});
-            break;
-
-        case "tab-invoice":
-            invListGrid.resetStore();
-            invListGrid.loadAll(
-                {"order_by": {"acqinv": "recv_date DESC"}},
-                {"provider": providerId}
-            );
-            break;
-
-        default:
-            paListGrid.overrideEditWidgets.provider = new
-                dijit.form.TextBox({disabled: 'true', value: providerId});
-            paListGrid.resetStore();
-            paListGrid.loadAll({order_by:{acqpa:'provider'}}, {provider: providerId}); 
-        }
-        
-    } else {
-        paListGrid.overrideEditWidgets.provider = new
-            dijit.form.TextBox({disabled: 'true', value: providerId});
-        paListGrid.resetStore();
-        paListGrid.loadAll({order_by:{acqpa:'provider'}}, {provider: providerId}); 
-    }
-}
-
-
-function getParsedTag(rowIndex, item) {
-    return item && xpathParser.parse(padListGrid.store.getValue(item, 'xpath')).tags;
-}
-
-
-function getParsedSubf(rowIndex, item) {
-    if(item) {
-        var subfields = xpathParser.parse(padListGrid.store.getValue(item, 'xpath')).subfields;
-        return subfields.join(',');
-    }
-    return'';
-}
-
-
-openils.Util.addOnLoad(draw);
+require([
+       "dijit/layout/TabContainer",
+       "openils/widget/AutoGrid",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/MarcXPathParser",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dijit_layout_TabContainer,
+       openils_widget_AutoGrid,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_MarcXPathParser,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       
+       var provider;
+       var xpathParser = new openils_MarcXPathParser();
+       var subFields= [];
+       var adminPermOrgs = [];
+       var viewPermOrgs = [];
+       var user;
+       var viewPerms = [
+           'ADMIN_PROVIDER', 
+           'MANAGE_PROVIDER', 
+           'VIEW_PROVIDER'
+       ]; 
+           
+       
+       function draw() {
+       
+           user = new openils.User();
+       
+           if(providerId) {
+               drawOneProvider();
+               return;
+           }
+       
+           openils.Util.hide('provider-details-div');
+       
+           // after a provider is created, load the provider page
+           pListGrid.onPostCreate = function(fmObject) {
+               location.href = location.href + '/' + fmObject.id();
+           }
+       
+           user.buildPermOrgSelector(
+               viewPerms,
+               contextOrgSelector, null,
+       
+               function() {
+                   if (!contextOrgSelector.attr('value')) return
+       
+                   dojo.connect(contextOrgSelector, 'onChange', drawProviderGrid);
+       
+                   // fetch the admin org units
+                   user.getPermOrgList(
+                       'ADMIN_PROVIDER',
+       
+                       function(list) {
+                           adminPermOrgs = list;
+       
+                           // fetch the view org units
+                           user.getPermOrgList(
+                               viewPerms,
+                               function(list2) {
+                                   viewPermOrgs = list2
+                                   drawProviderGrid();
+                               },
+                               true, true
+                           );
+                       },
+                       true, true
+                   );
+               }
+           );
+       }
+       
+       
+       function drawOneProvider() {
+           openils.Util.hide('provider-list-div');
+          
+           var pcrud = new openils_PermaCrud();
+           pcrud.retrieve('acqpro', providerId, {
+               oncomplete : function(r) {
+                   provider = openils.Util.readResponse(r);
+                   console.log('provider is' + js2JSON(provider));
+                   var pane = new openils.widget.EditPane({fmObject:provider, paneStackCount:2}, dojo.byId('provider-summary-pane'));
+                   pane.startup();
+                   console.log("pane started");
+                   dojo.connect(providerTabs, 'selectChild', drawProviderSummary);                        
+               }
+           });
+         
+           drawProviderSummary();
+       }
+       
+       
+       function drawProviderGrid() {
+           pListGrid.resetStore();
+       
+           // view providers for here plus children
+           var list = fieldmapper.aou.descendantNodeList(
+               contextOrgSelector.attr('value'), true, true);
+       
+           pListGrid.loadAll(
+               {order_by : [ // sort providers I can edit to the front
+                   {   'class' : 'acqpro',
+                       field : 'owner',
+                       compare : {'in' : adminPermOrgs},
+                       direction : 'desc'
+                   },
+                   {   'class' : 'acqpro',
+                       field : 'name'
+                   }
+               ]}, 
+               {'owner' : list}
+           );
+       }
+       
+       function drawProviderSummary(child) {
+           var loadedTabs = {'provider-address' : true};
+           if(child){   
+               if(loadedTabs[child.id]) return;
+               loadedTabs[child.id] = true;
+               switch(child.id) {
+               case 'tab-pro-contact': 
+                   pcListGrid.overrideEditWidgets.provider = new
+                       dijit.form.TextBox({disabled: 'true', value: providerId});
+                   pcListGrid.resetStore();
+                   pcListGrid.loadAll({
+                       oncomplete:function(r) {
+                           var count = 0; 
+                           pcListGrid.store.fetch( {
+                               onComplete:function(list) { 
+                                   count =  list.length
+                                   if (count>=1) {
+                                       var contactIds = [];                                                    
+                                       dojo.forEach(list, function(item) {
+                                               contactIds.push(pcListGrid.store.getValue(item, 'id')); 
+                                       });
+                                   
+                                       pcaListGrid.overrideEditWidgets.contact = new
+                                       dijit_form_FilteringSelect({store: pcListGrid.store});
+                                       pcaListGrid.resetStore();
+                                       pcaListGrid.loadAll({order_by:{acqpca : 'contact'}}, {contact: contactIds});
+       
+                                   } else { 
+                                       return;
+                                   }            
+                               }
+                           });
+                       }
+                   }, {provider : providerId});
+                   
+                   break;
+       
+               case 'tab-attr': 
+                   padListGrid.overrideEditWidgets.provider = new
+                       dijit.form.TextBox({disabled: 'true', value: providerId});
+                   padListGrid.resetStore();
+                   padListGrid.loadAll({order_by:{acqlipad : 'code'}}, {provider : providerId});
+                   break;
+       
+               case 'tab-hold': 
+                   phsListGrid.overrideEditWidgets.provider = new
+                       dijit.form.TextBox({disabled: 'true', value: providerId});
+                   phsListGrid.resetStore();
+                   phsListGrid.loadAll({order_by:{acqphsm : 'name'}}, {provider : providerId});
+                   break;
+       
+               case "tab-invoice":
+                   invListGrid.resetStore();
+                   invListGrid.loadAll(
+                       {"order_by": {"acqinv": "recv_date DESC"}},
+                       {"provider": providerId}
+                   );
+                   break;
+       
+               default:
+                   paListGrid.overrideEditWidgets.provider = new
+                       dijit.form.TextBox({disabled: 'true', value: providerId});
+                   paListGrid.resetStore();
+                   paListGrid.loadAll({order_by:{acqpa:'provider'}}, {provider: providerId}); 
+               }
+               
+           } else {
+               paListGrid.overrideEditWidgets.provider = new
+                   dijit.form.TextBox({disabled: 'true', value: providerId});
+               paListGrid.resetStore();
+               paListGrid.loadAll({order_by:{acqpa:'provider'}}, {provider: providerId}); 
+           }
+       }
+       
+       
+       function getParsedTag(rowIndex, item) {
+           return item && xpathParser.parse(padListGrid.store.getValue(item, 'xpath')).tags;
+       }
+       
+       
+       function getParsedSubf(rowIndex, item) {
+           if(item) {
+               var subfields = xpathParser.parse(padListGrid.store.getValue(item, 'xpath')).subfields;
+               return subfields.join(',');
+           }
+           return'';
+       }
+       
+       
+       openils.Util.addOnLoad(draw);
+       
+
+});
\ No newline at end of file
index 4e2b25c..b21da06 100644 (file)
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.Dialog');
-dojo.require('openils.DojoPatch');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.PermaCrud');
-
-var svCache = {};
-var surveyMap;
-var svId;
-var questionId;
-
-
-/** really need to put this in a shared location... */
-function getOrgInfo(rowIndex, item) {
-    if(!item) return '';
-    var orgId = svGrid.store.getValue(item, this.field);
-    return fieldmapper.aou.findOrgUnit(orgId).shortname();
-}
-
-function getDateTimeField(rowIndex, item) {
-    if(!item) return '';
-    var data = svGrid.store.getValue(item, this.field);
-    var date = dojo.date.stamp.fromISOString(data);
-    return dojo.date.locale.format(date, {formatLength:'short'});
-}
-
-function formatBool(inDatum) {
-    switch (inDatum) {
-        case 't':
-            return "<span style='color:green;'>&#x2713;</span>";
-        case 'f':
-            return "<span style='color:red;'>&#x2717;</span>";
-    default:
-        return'';
-    }
-}
-
-function endSurvey() {
-    _endSurvey(svGrid.selection.getSelected(), 0);
-}   
-
-function _endSurvey(list, idx) {
-    if(idx >= list.length) // we've made it through the list
-        return;
-   
-    var item = list[idx];
-    var svId = svGrid.store.getValue(item, 'id');
-    var pcrud = new openils.PermaCrud();
-    var survey = pcrud.retrieve('asv', svId);
-    var today = new Date();
-    var date = dojo.date.stamp.toISOString(today);
-    survey.end_date(date);
-    survey.ischanged(true);
-    pcrud.update(survey);
-    _endSurvey(list, ++idx);               
-
-}
-
-function buildSVGrid() {
-    var store = new dojo.data.ItemFileWriteStore({data:asv.initStoreData('id', {identifier:'id'})});
-    svGrid.setStore(store);
-    svGrid.render();
-    var user = new openils.User();
-    var pcrud = new openils.PermaCrud();
-    var retrieveSurveys = function(orgList) {
-              pcrud.search('asv',
-                     {owner : orgList},
-                     {
-                         async : true,
-                         streaming : true,
-                         onresponse : function(r) {
-                             var survey = openils.Util.readResponse(r);
-                             if(!survey) return'';
-                             svCache[survey.id()] = survey;
-                             store.newItem(survey.toStoreItem());
-                         }
-                     }
-                    );
-    }
-    user.getPermOrgList('ADMIN_SURVEY', retrieveSurveys, true, true);
-
-}
-
-function svPage() {
-    var pcrud = new openils.PermaCrud();
-    var survey = pcrud.retrieve('asv', surveyId);
-    dojo.byId("name").innerHTML = survey.name();
-    dojo.byId("description").innerHTML = survey.description();
-    dojo.byId("start_date").innerHTML = survey.start_date();
-    dojo.byId("end_date").innerHTML = survey.end_date();
-    dojo.byId("opac").innerHTML = survey.opac();
-    dojo.byId("poll").innerHTML = survey.poll();
-    dojo.byId("required").innerHTML = survey.required();
-    dojo.byId("usr_summary").innerHTML = survey.usr_summary();
-    dojo.byId("svQuestion").innerHTML = survey.question();
-    dojo.byId("svAnswer").innerHTML = survey.answer();
-    
-}
-
-function svNewSurvey() {
-    new openils.User().buildPermOrgSelector('ADMIN_SURVEY', asvOwningOrg);
-    svSurveyDialog.show();
-
-}
-
-function svCreate(args) {
-  
-    var sv = new asv();
-    sv.name(args.svName);
-    sv.owner(args.svOwner);
-    sv.description(args.svDescription);
-    sv.start_date(args.svStart_date);
-    sv.end_date(args.svEnd_date);
-    if(args.svPoll == 'on')
-        sv.poll('t')
-        else
-            sv.poll('f');
-
-    if(args.svPoll == 'on')
-        sv.poll('t')
-        else
-            sv.poll('f');
-
-    if(args.svOpac == 'on')
-        sv.opac('t')
-        else
-            sv.opac('f');
-
-    if(args.svRequired == 'on')
-        sv.required('t')
-        else
-            sv.required('f');
-
-    if(args.svUsr_summary == 'on')
-        sv.usr_summary('t')
-        else
-            sv.usr_summary('f');
-   
-    var pcrud = new openils.PermaCrud();
-    pcrud.create(sv,
-                 {           
-                     oncomplete: function(r, objs) {
-                         var obj = objs[0];
-                         if(!obj) return '';
-                         svGrid.store.newItem(asv.toStoreItem(obj));
-                         svSurveyDialog.hide();
-                         svId = obj.id();
-                         document.location.href = oilsBasePath + "/conify/global/action/survey/edit/"+svId;
-                     }
-                 }
-                 );
-}
-
-function redirect(svId) {
-
-}
-    
-
-function deleteFromGrid() {
-    _deleteFromGrid(svGrid.selection.getSelected(), 0);
-}   
-
-function _deleteFromGrid(list, idx) {
-    if(idx >= list.length) // we've made it through the list
-        return;
-
-    var item = list[idx];
-    var code = svGrid.store.getValue(item, 'id');
-  
-    fieldmapper.standardRequest(
-       ['open-ils.circ', 'open-ils.circ.survey.delete.cascade'],
-       {   async: true,
-               streaming: true,
-               params: [openils.User.authtoken, code],
-               onresponse: function(r) {
-               if(stat = openils.Util.readResponse(r)) {
-                   console.log(stat);
-                   svGrid.store.deleteItem(item); 
-               }
-               _deleteFromGrid(list, ++idx);               
-               
-           }
-       }
-    );
-}
-openils.Util.addOnLoad(buildSVGrid);
-
-
+require([
+       "dojox/grid/DataGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/TextBox",
+       "dijit/form/CurrencyTextBox",
+       "dijit/Dialog",
+       "openils/DojoPatch",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/PermaCrud"
+       ],
+function(dojox_grid_DataGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_form_TextBox,
+       dijit_form_CurrencyTextBox,
+       dijit_Dialog,
+       openils_DojoPatch,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_PermaCrud){
+       
+       var svCache = {};
+       var surveyMap;
+       var svId;
+       var questionId;
+       
+       
+       /** really need to put this in a shared location... */
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return '';
+           var orgId = svGrid.store.getValue(item, this.field);
+           return fieldmapper.aou.findOrgUnit(orgId).shortname();
+       }
+       
+       function getDateTimeField(rowIndex, item) {
+           if(!item) return '';
+           var data = svGrid.store.getValue(item, this.field);
+           var date = dojo.date.stamp.fromISOString(data);
+           return dojo.date.locale.format(date, {formatLength:'short'});
+       }
+       
+       function formatBool(inDatum) {
+           switch (inDatum) {
+               case 't':
+                   return "<span style='color:green;'>&#x2713;</span>";
+               case 'f':
+                   return "<span style='color:red;'>&#x2717;</span>";
+           default:
+               return'';
+           }
+       }
+       
+       function endSurvey() {
+           _endSurvey(svGrid.selection.getSelected(), 0);
+       }   
+       
+       function _endSurvey(list, idx) {
+           if(idx >= list.length) // we've made it through the list
+               return;
+          
+           var item = list[idx];
+           var svId = svGrid.store.getValue(item, 'id');
+           var pcrud = new openils_PermaCrud();
+           var survey = pcrud.retrieve('asv', svId);
+           var today = new Date();
+           var date = dojo.date.stamp.toISOString(today);
+           survey.end_date(date);
+           survey.ischanged(true);
+           pcrud.update(survey);
+           _endSurvey(list, ++idx);               
+       
+       }
+       
+       function buildSVGrid() {
+           var store = new dojo_data_ItemFileWriteStore({data:asv.initStoreData('id', {identifier:'id'})});
+           svGrid.setStore(store);
+           svGrid.render();
+           var user = new openils.User();
+           var pcrud = new openils_PermaCrud();
+           var retrieveSurveys = function(orgList) {
+                     pcrud.search('asv',
+                            {owner : orgList},
+                            {
+                                async : true,
+                                streaming : true,
+                                onresponse : function(r) {
+                                    var survey = openils.Util.readResponse(r);
+                                    if(!survey) return'';
+                                    svCache[survey.id()] = survey;
+                                    store.newItem(survey.toStoreItem());
+                                }
+                            }
+                           );
+           }
+           user.getPermOrgList('ADMIN_SURVEY', retrieveSurveys, true, true);
+       
+       }
+       
+       function svPage() {
+           var pcrud = new openils_PermaCrud();
+           var survey = pcrud.retrieve('asv', surveyId);
+           dojo.byId("name").innerHTML = survey.name();
+           dojo.byId("description").innerHTML = survey.description();
+           dojo.byId("start_date").innerHTML = survey.start_date();
+           dojo.byId("end_date").innerHTML = survey.end_date();
+           dojo.byId("opac").innerHTML = survey.opac();
+           dojo.byId("poll").innerHTML = survey.poll();
+           dojo.byId("required").innerHTML = survey.required();
+           dojo.byId("usr_summary").innerHTML = survey.usr_summary();
+           dojo.byId("svQuestion").innerHTML = survey.question();
+           dojo.byId("svAnswer").innerHTML = survey.answer();
+           
+       }
+       
+       function svNewSurvey() {
+           new openils.User().buildPermOrgSelector('ADMIN_SURVEY', asvOwningOrg);
+           svSurveyDialog.show();
+       
+       }
+       
+       function svCreate(args) {
+         
+           var sv = new asv();
+           sv.name(args.svName);
+           sv.owner(args.svOwner);
+           sv.description(args.svDescription);
+           sv.start_date(args.svStart_date);
+           sv.end_date(args.svEnd_date);
+           if(args.svPoll == 'on')
+               sv.poll('t')
+               else
+                   sv.poll('f');
+       
+           if(args.svPoll == 'on')
+               sv.poll('t')
+               else
+                   sv.poll('f');
+       
+           if(args.svOpac == 'on')
+               sv.opac('t')
+               else
+                   sv.opac('f');
+       
+           if(args.svRequired == 'on')
+               sv.required('t')
+               else
+                   sv.required('f');
+       
+           if(args.svUsr_summary == 'on')
+               sv.usr_summary('t')
+               else
+                   sv.usr_summary('f');
+          
+           var pcrud = new openils_PermaCrud();
+           pcrud.create(sv,
+                        {           
+                            oncomplete: function(r, objs) {
+                                var obj = objs[0];
+                                if(!obj) return '';
+                                svGrid.store.newItem(asv.toStoreItem(obj));
+                                svSurveyDialog.hide();
+                                svId = obj.id();
+                                document.location.href = oilsBasePath + "/conify/global/action/survey/edit/"+svId;
+                            }
+                        }
+                        );
+       }
+       
+       function redirect(svId) {
+       
+       }
+           
+       
+       function deleteFromGrid() {
+           _deleteFromGrid(svGrid.selection.getSelected(), 0);
+       }   
+       
+       function _deleteFromGrid(list, idx) {
+           if(idx >= list.length) // we've made it through the list
+               return;
+       
+           var item = list[idx];
+           var code = svGrid.store.getValue(item, 'id');
+         
+           fieldmapper.standardRequest(
+              ['open-ils.circ', 'open-ils.circ.survey.delete.cascade'],
+              {   async: true,
+                      streaming: true,
+                      params: [openils.User.authtoken, code],
+                      onresponse: function(r) {
+                      if(stat = openils.Util.readResponse(r)) {
+                          console.log(stat);
+                          svGrid.store.deleteItem(item); 
+                      }
+                      _deleteFromGrid(list, ++idx);               
+                      
+                  }
+              }
+           );
+       }
+       openils.Util.addOnLoad(buildSVGrid);
+       
+       
+       
+
+});
\ No newline at end of file
index 5dc659d..a099af5 100644 (file)
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dojo.date.stamp');
-dojo.require('dijit.form.TextBox');
-dojo.require('dijit.form.Button');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.GridColumnPicker');
-dojo.require('openils.widget.EditPane');
-dojo.requireLocalization('openils.conify', 'conify');
-
-var surveyId;
-var startDate;
-var endDate;
-var today;
-var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
-
-function drawSurvey(svyId) {
-    today = new Date();    
-    var surveyTable = dojo.byId('edit-pane');
-    var surveyHead = dojo.create('thead', {id: "survey_head"},  surveyTable);
-    var headRow = dojo.create('tr', null,  surveyHead);
-    var headCell = dojo.create('td', {id: "head_cell", innerHTML: "<h2>" +dojo.string.substitute(localeStrings.SURVEY_ID, [svyId])+"</h2>" }, headRow);
-    var pcrud = new openils.PermaCrud();
-    var survey = pcrud.retrieve('asv', svyId);
-    startDate = dojo.date.stamp.fromISOString(survey.start_date());
-    endDate = dojo.date.stamp.fromISOString(survey.end_date());
-    var pane = new openils.widget.EditPane({fmObject : survey, hideActionButtons:false}, dojo.byId('edit-pane'));
-
-    if ( endDate > today) {
-        var buttonBody = dojo.create( 'td', null, surveyHead);
-        var endButton = new dijit.form.Button({label: localeStrings.END_SURVEY, onClick:function() {endSurvey(survey.id())} }, buttonBody);
-    }   
-
-    pane.fieldOrder = ['id', 'name', 'description', 'owner', 'start_date', 'end_date'];
-    pane.onCancel = cancelEdit;
-    pane.startup();
-
-    var surveyFoot = dojo.create('tfoot', { id: "survey_foot"}, surveyTable);
-    var footRow = dojo.create('tr', {id: "foot_row"}, surveyFoot);  
-    var footLabel = dojo.create('td', {id: "foot_label", innerHTML: "<h3>"+localeStrings.SURVEY_FOOT_LABEL+"</h3>"}, footRow);
-    var footCell = dojo.create('td', {innerHTML: "<hr>", id: "foot_cell"}, footRow);
-    getQuestions(svyId, survey);
-
-}
-
-function cancelEdit(){
-    document.location.href = oilsBasePath + "/conify/global/action/survey";
-}
-
-function endSurvey(svyId) {
-    var pcrud = new openils.PermaCrud();
-    var survey = pcrud.retrieve('asv', svyId);
-    var today = new Date();
-    var date = dojo.date.stamp.toISOString(today);
-    survey.end_date(date);
-    survey.ischanged(true);
-    return pcrud.update(survey);
-
-}
-
-// all functions for question manipulation
-
-function getQuestions(svyId, survey) {
-  
-    surveyId = svyId;
-      
-    var pcrud = new openils.PermaCrud();
-    var questions = pcrud.search('asvq', {survey:svyId});
-    
-    for(var i in questions) {
-        questionId = questions[i].id(); 
-        var answers = pcrud.search('asva', {question:questionId});
-        if (answers)
-            drawQuestionBody(questions[i], answers, survey);
-    }
-    if ( startDate > today) newQuestionBody(surveyId);
-}
-function newQuestionBody(svyId) {
-    var surveyTable = dojo.byId("survey_table");
-    var surveyBody = dojo.create('tbody', {style: "background-color: #d9e8f9"}, surveyTable);
-    var questionRow = dojo.create('tr', null, surveyBody);
-    var questionLabel = dojo.create('td',{ innerHTML: localeStrings.SURVEY_QUESTION}, questionRow, "first");
-    var questionTextbox = dojo.create('td', null, questionRow, "second");
-    var qInput = new dijit.form.TextBox(null, questionTextbox);
-    var questionButton = dojo.create('td', null , questionRow);
-    var qButton = new dijit.form.Button({ label: localeStrings.SURVEY_SAVE_ADD, onClick:function() {newQuestion(svyId, qInput.getValue(), questionRow)} }, questionButton);
-    
-}
-
-function drawQuestionBody(question, answers, survey){
-
-    var surveyTable = dojo.byId('survey_table');
-    var surveyBody = dojo.create( 'tbody', {quid:question.id(), id:("q" + question.id()), style: "background-color: #d9e8f9"}, surveyTable);
-    var questionRow = dojo.create('tr', {quid:question.id()}, surveyBody);
-    var questionLabel = dojo.create('td', {quid:question.id(), innerHTML: localeStrings.SURVEY_QUESTION}, questionRow, "first");
-    var questionTextbox = dojo.create('td', {quid: question.id() }, questionRow, "second");
-    var qInput = new dijit.form.TextBox(null, questionTextbox);
-    qInput.attr('value', question.question());
-    if (startDate > today){
-        var questionButton = dojo.create('td', {quid: question.id()}, questionRow);
-        var qButton = new dijit.form.Button({label: localeStrings.SURVEY_DELETE_QUESTION, onClick:function() {deleteQuestion(question.id(), surveyBody) }}, questionButton);
-        var qChangesButton = dojo.create('td', {quid: question.id()}, questionRow);
-        var qcButton = new dijit.form.Button({label: localeStrings.SURVEY_SAVE_CHANGES, onClick:function() {changeQuestion(question.id(), qInput.attr('value')) }}, qChangesButton);
-       
-    }
-    for (var i in answers) {
-        if(!answers) return'';
-        drawAnswer(answers[i], question.id(), surveyBody, survey);
-    }
-    drawNewAnswerRow(question.id(), surveyBody);  
-}
-
-function newQuestion(svyId, questionText, questionRow) {
-    var pcrud = new openils.PermaCrud();
-    var question = new asvq();
-    question.survey(svyId);
-    question.question(questionText);
-    question.isnew(true);
-    pcrud.create(question, 
-        {oncomplete: function(r, qs) 
-             { var q = qs[0];
-                 questionRow.parentNode.removeChild(questionRow);
-                 drawQuestionBody(q, null);
-                 newQuestionBody(svyId);
-             } 
-        }
-    ); 
-}
-
-function changeQuestion(quesId, questionText) {
-    var pcrud = new openils.PermaCrud();
-    var question = pcrud.retrieve('asvq', quesId);
-    question.question(questionText);
-    question.ischanged(true);
-    return pcrud.update(question);
-}
-
-function deleteQuestion(quesId, surveyBody) {
-    var pcrud = new openils.PermaCrud();
-    var delQuestion = new asvq();
-    var answers = pcrud.search('asva', {question:quesId});
-    for(var i in answers){
-        var ansId = answers[i].id();
-        deleteAnswer(ansId);
-    }
-    delQuestion.id(quesId);
-    delQuestion.isdeleted(true);
-    surveyBody.parentNode.removeChild(surveyBody);
-    return pcrud.eliminate(delQuestion);
-
-}
-
-// all functions for answer manipulation
-
-function drawAnswer(answer, qid, surveyBody, survey) {
-    var surveyBody = dojo.byId(("q" + qid)); 
-    var answerRow = dojo.create('tr', {anid: answer.id(), style: "background-color: #FFF"}, surveyBody);
-    var answerSpacer =  dojo.create('td', {anid: answer.id()}, answerRow, "first");
-    var answerLabel =  dojo.create('td', {anid: answer.id(), style: "float: right", innerHTML: localeStrings.SURVEY_ANSWER }, answerRow, "second");
-    var answerTextbox = dojo.create('td', {anid: answer.id() }, answerRow, "third");
-    var input = new dijit.form.TextBox(null, answerTextbox);
-    input.attr('value', answer.answer());
-    if (startDate > today){
-        var answerSpacer =  dojo.create('td', {anid: answer.id()}, answerRow);
-        var delanswerButton = dojo.create('td', {anid: answer.id()}, answerRow);
-        var aid = answer.id();
-        var aButton = new dijit.form.Button({label: localeStrings.SURVEY_DELETE_ANSWER, onClick:function(){deleteAnswer(aid);answerRow.parentNode.removeChild(answerRow)} }, delanswerButton);
-        var aChangesButton = dojo.create('td', {anid: qid}, answerRow);
-        var acButton = new dijit.form.Button({label: localeStrings.SURVEY_SAVE_CHANGES, onClick:function() {changeAnswer(answer.id(), input.attr('value')) }}, aChangesButton);
-    }
-}
-
-function drawNewAnswerRow(qid, surveyBody) {
-    var answerRow = dojo.create('tr', {quid: qid, style: "background-color: #FFF"}, surveyBody);
-    var answerSpacer =  dojo.create('td', {quid: qid}, answerRow, "first");
-    var answerLabel =  dojo.create('td', {quid: qid, innerHTML: localeStrings.SURVEY_ANSWER, style: "float:right" }, answerRow, "second");
-    var answerTextbox = dojo.create('td', {quid: qid }, answerRow, "third");
-    var input = new dijit.form.TextBox(null, answerTextbox);
-    var answerButton = dojo.create('td', {anid: qid}, answerRow);
-    var aButton = new dijit.form.Button({label: localeStrings.SURVEY_ADD_ANSWER, onClick:function() {newAnswer(qid, input.attr('value'), answerRow, surveyBody)} }, answerButton);
-
-}
-
-
-function deleteAnswer(ansId) {
-    var pcrud = new openils.PermaCrud();
-    var delAnswer = new asva();
-    delAnswer.id(ansId);
-    delAnswer.isdeleted(true);
-    return pcrud.eliminate(delAnswer);
-  
-}
-function newAnswer(quesId, answerText, answerRow, surveyBody) {
-    var pcrud = new openils.PermaCrud();
-    var answer = new asva();
-    answer.question(quesId);
-    answer.answer(answerText);
-    answer.isnew(true);
-    answerRow.parentNode.removeChild(answerRow);
-    drawAnswer(answer, answer.question());
-    drawNewAnswerRow(quesId, surveyBody);
-    return pcrud.create(answer);
-}
-
-
-function changeAnswer(ansId, answerText) {
-    var pcrud = new openils.PermaCrud();
-    var answer = pcrud.retrieve('asva', ansId);
-    answer.answer(answerText);
-    answer.ischanged(true);
-    return pcrud.update(answer);
-}
-
+require([
+       "dojox/grid/DataGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dojo/date/stamp",
+       "dijit/form/TextBox",
+       "dijit/form/Button",
+       "dijit/Dialog",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/GridColumnPicker",
+       "openils/widget/EditPane"
+       ],
+function(dojox_grid_DataGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dojo_date_stamp,
+       dijit_form_TextBox,
+       dijit_form_Button,
+       dijit_Dialog,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_PermaCrud,
+       openils_widget_GridColumnPicker,
+       openils_widget_EditPane){
+       dojo.requireLocalization('openils.conify', 'conify');
+       
+       var surveyId;
+       var startDate;
+       var endDate;
+       var today;
+       var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
+       
+       function drawSurvey(svyId) {
+           today = new Date();    
+           var surveyTable = dojo.byId('edit-pane');
+           var surveyHead = dojo.create('thead', {id: "survey_head"},  surveyTable);
+           var headRow = dojo.create('tr', null,  surveyHead);
+           var headCell = dojo.create('td', {id: "head_cell", innerHTML: "<h2>" +dojo.string.substitute(localeStrings.SURVEY_ID, [svyId])+"</h2>" }, headRow);
+           var pcrud = new openils_PermaCrud();
+           var survey = pcrud.retrieve('asv', svyId);
+           startDate = dojo_date_stamp.fromISOString(survey.start_date());
+           endDate = dojo_date_stamp.fromISOString(survey.end_date());
+           var pane = new openils_widget_EditPane({fmObject : survey, hideActionButtons:false}, dojo.byId('edit-pane'));
+       
+           if ( endDate > today) {
+               var buttonBody = dojo.create( 'td', null, surveyHead);
+               var endButton = new dijit_form_Button({label: localeStrings.END_SURVEY, onClick:function() {endSurvey(survey.id())} }, buttonBody);
+           }   
+       
+           pane.fieldOrder = ['id', 'name', 'description', 'owner', 'start_date', 'end_date'];
+           pane.onCancel = cancelEdit;
+           pane.startup();
+       
+           var surveyFoot = dojo.create('tfoot', { id: "survey_foot"}, surveyTable);
+           var footRow = dojo.create('tr', {id: "foot_row"}, surveyFoot);  
+           var footLabel = dojo.create('td', {id: "foot_label", innerHTML: "<h3>"+localeStrings.SURVEY_FOOT_LABEL+"</h3>"}, footRow);
+           var footCell = dojo.create('td', {innerHTML: "<hr>", id: "foot_cell"}, footRow);
+           getQuestions(svyId, survey);
+       
+       }
+       
+       function cancelEdit(){
+           document.location.href = oilsBasePath + "/conify/global/action/survey";
+       }
+       
+       function endSurvey(svyId) {
+           var pcrud = new openils_PermaCrud();
+           var survey = pcrud.retrieve('asv', svyId);
+           var today = new Date();
+           var date = dojo_date_stamp.toISOString(today);
+           survey.end_date(date);
+           survey.ischanged(true);
+           return pcrud.update(survey);
+       
+       }
+       
+       // all functions for question manipulation
+       
+       function getQuestions(svyId, survey) {
+         
+           surveyId = svyId;
+             
+           var pcrud = new openils_PermaCrud();
+           var questions = pcrud.search('asvq', {survey:svyId});
+           
+           for(var i in questions) {
+               questionId = questions[i].id(); 
+               var answers = pcrud.search('asva', {question:questionId});
+               if (answers)
+                   drawQuestionBody(questions[i], answers, survey);
+           }
+           if ( startDate > today) newQuestionBody(surveyId);
+       }
+        
+       function newQuestionBody(svyId) {
+           var surveyTable = dojo.byId("survey_table");
+           var surveyBody = dojo.create('tbody', {style: "background-color: #d9e8f9"}, surveyTable);
+           var questionRow = dojo.create('tr', null, surveyBody);
+           var questionLabel = dojo.create('td',{ innerHTML: localeStrings.SURVEY_QUESTION}, questionRow, "first");
+           var questionTextbox = dojo.create('td', null, questionRow, "second");
+           var qInput = new dijit_form_TextBox(null, questionTextbox);
+           var questionButton = dojo.create('td', null , questionRow);
+           var qButton = new dijit_form_Button({ label: localeStrings.SURVEY_SAVE_ADD, onClick:function() {newQuestion(svyId, qInput.getValue(), questionRow)} }, questionButton);
+           
+       }
+       
+       function drawQuestionBody(question, answers, survey){
+       
+           var surveyTable = dojo.byId('survey_table');
+           var surveyBody = dojo.create( 'tbody', {quid:question.id(), id:("q" + question.id()), style: "background-color: #d9e8f9"}, surveyTable);
+           var questionRow = dojo.create('tr', {quid:question.id()}, surveyBody);
+           var questionLabel = dojo.create('td', {quid:question.id(), innerHTML: localeStrings.SURVEY_QUESTION}, questionRow, "first");
+           var questionTextbox = dojo.create('td', {quid: question.id() }, questionRow, "second");
+           var qInput = new dijit_form_TextBox(null, questionTextbox);
+           qInput.attr('value', question.question());
+           if (startDate > today){
+               var questionButton = dojo.create('td', {quid: question.id()}, questionRow);
+               var qButton = new dijit_form_Button({label: localeStrings.SURVEY_DELETE_QUESTION, onClick:function() {deleteQuestion(question.id(), surveyBody) }}, questionButton);
+               var qChangesButton = dojo.create('td', {quid: question.id()}, questionRow);
+               var qcButton = new dijit_form_Button({label: localeStrings.SURVEY_SAVE_CHANGES, onClick:function() {changeQuestion(question.id(), qInput.attr('value')) }}, qChangesButton);
+              
+           }
+           for (var i in answers) {
+               if(!answers) return'';
+               drawAnswer(answers[i], question.id(), surveyBody, survey);
+           }
+           drawNewAnswerRow(question.id(), surveyBody);  
+       }
+       
+       function newQuestion(svyId, questionText, questionRow) {
+           var pcrud = new openils_PermaCrud();
+           var question = new asvq();
+           question.survey(svyId);
+           question.question(questionText);
+           question.isnew(true);
+           pcrud.create(question, 
+               {oncomplete: function(r, qs) 
+                    { var q = qs[0];
+                        questionRow.parentNode.removeChild(questionRow);
+                        drawQuestionBody(q, null);
+                        newQuestionBody(svyId);
+                    } 
+               }
+           ); 
+       }
+       
+       function changeQuestion(quesId, questionText) {
+           var pcrud = new openils_PermaCrud();
+           var question = pcrud.retrieve('asvq', quesId);
+           question.question(questionText);
+           question.ischanged(true);
+           return pcrud.update(question);
+       }
+       
+       function deleteQuestion(quesId, surveyBody) {
+           var pcrud = new openils_PermaCrud();
+           var delQuestion = new asvq();
+           var answers = pcrud.search('asva', {question:quesId});
+           for(var i in answers){
+               var ansId = answers[i].id();
+               deleteAnswer(ansId);
+           }
+           delQuestion.id(quesId);
+           delQuestion.isdeleted(true);
+           surveyBody.parentNode.removeChild(surveyBody);
+           return pcrud.eliminate(delQuestion);
+       
+       }
+       
+       // all functions for answer manipulation
+       
+       function drawAnswer(answer, qid, surveyBody, survey) {
+           var surveyBody = dojo.byId(("q" + qid)); 
+           var answerRow = dojo.create('tr', {anid: answer.id(), style: "background-color: #FFF"}, surveyBody);
+           var answerSpacer =  dojo.create('td', {anid: answer.id()}, answerRow, "first");
+           var answerLabel =  dojo.create('td', {anid: answer.id(), style: "float: right", innerHTML: localeStrings.SURVEY_ANSWER }, answerRow, "second");
+           var answerTextbox = dojo.create('td', {anid: answer.id() }, answerRow, "third");
+           var input = new dijit_form_TextBox(null, answerTextbox);
+           input.attr('value', answer.answer());
+           if (startDate > today){
+               var answerSpacer =  dojo.create('td', {anid: answer.id()}, answerRow);
+               var delanswerButton = dojo.create('td', {anid: answer.id()}, answerRow);
+               var aid = answer.id();
+               var aButton = new dijit_form_Button({label: localeStrings.SURVEY_DELETE_ANSWER, onClick:function(){deleteAnswer(aid);answerRow.parentNode.removeChild(answerRow)} }, delanswerButton);
+               var aChangesButton = dojo.create('td', {anid: qid}, answerRow);
+               var acButton = new dijit_form_Button({label: localeStrings.SURVEY_SAVE_CHANGES, onClick:function() {changeAnswer(answer.id(), input.attr('value')) }}, aChangesButton);
+           }
+       }
+       
+       function drawNewAnswerRow(qid, surveyBody) {
+           var answerRow = dojo.create('tr', {quid: qid, style: "background-color: #FFF"}, surveyBody);
+           var answerSpacer =  dojo.create('td', {quid: qid}, answerRow, "first");
+           var answerLabel =  dojo.create('td', {quid: qid, innerHTML: localeStrings.SURVEY_ANSWER, style: "float:right" }, answerRow, "second");
+           var answerTextbox = dojo.create('td', {quid: qid }, answerRow, "third");
+           var input = new dijit_form_TextBox(null, answerTextbox);
+           var answerButton = dojo.create('td', {anid: qid}, answerRow);
+           var aButton = new dijit_form_Button({label: localeStrings.SURVEY_ADD_ANSWER, onClick:function() {newAnswer(qid, input.attr('value'), answerRow, surveyBody)} }, answerButton);
+       
+       }
+       
+       
+       function deleteAnswer(ansId) {
+           var pcrud = new openils_PermaCrud();
+           var delAnswer = new asva();
+           delAnswer.id(ansId);
+           delAnswer.isdeleted(true);
+           return pcrud.eliminate(delAnswer);
+         
+       }
+       function newAnswer(quesId, answerText, answerRow, surveyBody) {
+           var pcrud = new openils_PermaCrud();
+           var answer = new asva();
+           answer.question(quesId);
+           answer.answer(answerText);
+           answer.isnew(true);
+           answerRow.parentNode.removeChild(answerRow);
+           drawAnswer(answer, answer.question());
+           drawNewAnswerRow(quesId, surveyBody);
+           return pcrud.create(answer);
+       }
+       
+       
+       function changeAnswer(ansId, answerText) {
+           var pcrud = new openils_PermaCrud();
+           var answer = pcrud.retrieve('asva', ansId);
+           answer.answer(answerText);
+           answer.ischanged(true);
+           return pcrud.update(answer);
+       }
+       
+       
+
+});
\ No newline at end of file
index 2867bb1..cd4f8a5 100644 (file)
-dojo.require('dijit.layout.TabContainer');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dijit.form.TextBox');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.Util');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.Textarea');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('dojox.string.sprintf');
-dojo.requireLocalization('openils.conify', 'conify');
-
-var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
-var eventDef = null;
-
-function loadEventDef() { 
-    eventDefGranularity.attr('value', null);
-    edGrid.overrideEditWidgets.granularity = eventDefGranularity;
-    edGrid.overrideEditWidgets.granularity.shove = {"create": ""};
-    edGrid.loadAll({order_by:{atevdef : 'name, owner, hook, reactor, delay'}});
-    openils.widget.Textarea.width = '600px';
-    openils.widget.Textarea.height = '600px';
-    edGrid.overrideEditWidgetClass.template = 'openils.widget.Textarea';
-    dojo.connect(eventDefTabs,'selectChild', tabLoader);
-}
-
-/**
- * After an event def is cloned, see if the user wants to also clone the event def environment
- * @param {Object} oldItem Grid store item that was cloned
- * @param {Object} newObject Newly created fieldmapper object
- */
-function cloneEventEnv(oldItem, newObject) {
-    if(!confirm('Clone event definition environment as well?')) return; // TODO i18n
-    progressDialog.show(true);
-    var pcrud = new openils.PermaCrud();
-
-    // fetch the env list for the cloned object
-    var env_list = pcrud.search('atenv', {event_def : edGrid.store.getValue(oldItem, 'id')});
-
-    if(env_list && env_list.length) {
-        
-        // clone the environment 
-        env_list = env_list.map(
-            function(item) { 
-                item.id(null);
-                item.event_def(newObject.id()); 
-                return item; 
-            }
-        );
-    
-        // create the cloned environment list
-        pcrud.create(env_list);
-    }
-
-    progressDialog.hide();
-}
-
-function loadEventDefData() { 
-    var pcrud = new openils.PermaCrud();
-    eventDef = pcrud.retrieve('atevdef', eventDefId);
-    var hook = pcrud.retrieve('ath', eventDef.hook());
-
-    if(hook.core_type() == 'circ') {
-        openils.Util.hide('at-test-none');
-        openils.Util.show('at-test-circ');
-    }
-
-    dojo.byId('at-event-def-name').innerHTML = eventDef.name();
-    teeGrid.loadAll({order_by:{atenv : 'path'}}, {event_def : eventDefId}); 
-    dojo.connect(eventDefTabs,'selectChild', tabLoader);
-
-    teeGrid.overrideEditWidgets.event_def = new dijit.form.TextBox({value: eventDefId, disabled : true});
-    tepGrid.overrideEditWidgets.event_def = new dijit.form.TextBox({value: eventDefId, disabled : true});
-}
-
-var loadedTabs = {'tab-atevdef' : true};
-function tabLoader(child) {
-    if(loadedTabs[child.id]) return;
-    loadedTabs[child.id] = true;
-
-    switch(child.id) {
-        case 'tab-atevparam': 
-            tepGrid.loadAll({order_by:{atevparam : 'param'}}, {event_def : eventDefId}); 
-            break;
-        case 'tab-ath': 
-            thGrid.loadAll({order_by:{ath : 'key'}}); 
-            break;
-        case 'tab-atreact': 
-            trGrid.loadAll({order_by:{atreact : 'module'}}); 
-            break;
-        case 'tab-atval': 
-            tvGrid.loadAll({order_by:{atval : 'module'}}); 
-            break;
-        /*
-        case 'tab-test': 
-            loadTestTab();
-            break;
-        */
-    }
-}
-
-function getEventDefNameLink(rowIdx, item) {
-    if(!item) return
-    return this.grid.store.getValue(item, 'id') + ':' + this.grid.store.getValue(item, 'name');
-}
-
-function formatEventDefNameLink(data) {
-    if(!data) return;
-    var parts = data.split(/:/);
-    return dojox.string.sprintf(
-        '<a href="%s/conify/global/action_trigger/event_definition_data/%s">%s</a>',
-        oilsBasePath, parts[0], parts[1]);
-}
-
-
-function evtTestCirc() {
-    var barcode = circTestBarcode.attr('value');
-    if(!barcode) return;
-
-    progressDialog.show();
-
-    function handleResponse(r) {
-        var evt = openils.Util.readResponse(r);
-        progressDialog.hide();
-        if(evt && evt != '0') {
-            var output = evt.template_output();
-            if(!output) output = evt.error_output();
-            var pre = document.createElement('pre');
-            pre.innerHTML = output.data();
-            openils.Util.appendClear('test-event-output', pre);
-            openils.Util.show('test-event-output');
-        }
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.trigger_event_by_def_and_barcode.fire'],
-        {   async: true,
-            params: [openils.User.authtoken, eventDefId, barcode],
-            oncomplete: handleResponse
-        }
-    );
-}
-
+require([
+       "dijit/layout/TabContainer",
+       "dijit/form/FilteringSelect",
+       "dijit/form/TextBox",
+       "dojo/data/ItemFileReadStore",
+       "openils/widget/AutoGrid",
+       "openils/Util",
+       "openils/PermaCrud",
+       "openils/widget/Textarea",
+       "openils/widget/ProgressDialog",
+       "dojox/string/sprintf"
+       ],
+function(dijit_layout_TabContainer,
+       dijit_form_FilteringSelect,
+       dijit_form_TextBox,
+       dojo_data_ItemFileReadStore,
+       openils_widget_AutoGrid,
+       openils_Util,
+       openils_PermaCrud,
+       openils_widget_Textarea,
+       openils_widget_ProgressDialog,
+       dojox_string_sprintf){
+       dojo.requireLocalization('openils.conify', 'conify');
+       
+       var localeStrings = dojo.i18n.getLocalization('openils.conify', 'conify');
+       var eventDef = null;
+       
+       function loadEventDef() { 
+           eventDefGranularity.attr('value', null);
+           edGrid.overrideEditWidgets.granularity = eventDefGranularity;
+           edGrid.overrideEditWidgets.granularity.shove = {"create": ""};
+           edGrid.loadAll({order_by:{atevdef : 'name, owner, hook, reactor, delay'}});
+           openils_widget_Textarea.width = '600px';
+           openils_widget_Textarea.height = '600px';
+           edGrid.overrideEditWidgetClass.template = 'openils_widget_Textarea';
+           dojo.connect(eventDefTabs,'selectChild', tabLoader);
+       }
+       
+       /**
+        * After an event def is cloned, see if the user wants to also clone the event def environment
+        * @param {Object} oldItem Grid store item that was cloned
+        * @param {Object} newObject Newly created fieldmapper object
+        */
+       function cloneEventEnv(oldItem, newObject) {
+           if(!confirm('Clone event definition environment as well?')) return; // TODO i18n
+           progressDialog.show(true);
+           var pcrud = new openils_PermaCrud();
+       
+           // fetch the env list for the cloned object
+           var env_list = pcrud.search('atenv', {event_def : edGrid.store.getValue(oldItem, 'id')});
+       
+           if(env_list && env_list.length) {
+               
+               // clone the environment 
+               env_list = env_list.map(
+                   function(item) { 
+                       item.id(null);
+                       item.event_def(newObject.id()); 
+                       return item; 
+                   }
+               );
+           
+               // create the cloned environment list
+               pcrud.create(env_list);
+           }
+       
+           progressDialog.hide();
+       }
+       
+       function loadEventDefData() { 
+           var pcrud = new openils_PermaCrud();
+           eventDef = pcrud.retrieve('atevdef', eventDefId);
+           var hook = pcrud.retrieve('ath', eventDef.hook());
+       
+           if(hook.core_type() == 'circ') {
+               openils_Util.hide('at-test-none');
+               openils_Util.show('at-test-circ');
+           }
+       
+           dojo.byId('at-event-def-name').innerHTML = eventDef.name();
+           teeGrid.loadAll({order_by:{atenv : 'path'}}, {event_def : eventDefId}); 
+           dojo.connect(eventDefTabs,'selectChild', tabLoader);
+       
+           teeGrid.overrideEditWidgets.event_def = new dijit_form_TextBox({value: eventDefId, disabled : true});
+           tepGrid.overrideEditWidgets.event_def = new dijit_form_TextBox({value: eventDefId, disabled : true});
+       }
+       
+       var loadedTabs = {'tab-atevdef' : true};
+       function tabLoader(child) {
+           if(loadedTabs[child.id]) return;
+           loadedTabs[child.id] = true;
+       
+           switch(child.id) {
+               case 'tab-atevparam': 
+                   tepGrid.loadAll({order_by:{atevparam : 'param'}}, {event_def : eventDefId}); 
+                   break;
+               case 'tab-ath': 
+                   thGrid.loadAll({order_by:{ath : 'key'}}); 
+                   break;
+               case 'tab-atreact': 
+                   trGrid.loadAll({order_by:{atreact : 'module'}}); 
+                   break;
+               case 'tab-atval': 
+                   tvGrid.loadAll({order_by:{atval : 'module'}}); 
+                   break;
+               /*
+               case 'tab-test': 
+                   loadTestTab();
+                   break;
+               */
+           }
+       }
+       
+       function getEventDefNameLink(rowIdx, item) {
+           if(!item) return
+           return this.grid.store.getValue(item, 'id') + ':' + this.grid.store.getValue(item, 'name');
+       }
+       
+       function formatEventDefNameLink(data) {
+           if(!data) return;
+           var parts = data.split(/:/);
+           return dojox_string_sprintf(
+               '<a href="%s/conify/global/action_trigger/event_definition_data/%s">%s</a>',
+               oilsBasePath, parts[0], parts[1]);
+       }
+       
+       
+       function evtTestCirc() {
+           var barcode = circTestBarcode.attr('value');
+           if(!barcode) return;
+       
+           progressDialog.show();
+       
+           function handleResponse(r) {
+               var evt = openils_Util.readResponse(r);
+               progressDialog.hide();
+               if(evt && evt != '0') {
+                   var output = evt.template_output();
+                   if(!output) output = evt.error_output();
+                   var pre = document.createElement('pre');
+                   pre.innerHTML = output.data();
+                   openils_Util.appendClear('test-event-output', pre);
+                   openils_Util.show('test-event-output');
+               }
+           }
+       
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.trigger_event_by_def_and_barcode.fire'],
+               {   async: true,
+                   params: [openils.User.authtoken, eventDefId, barcode],
+                   oncomplete: handleResponse
+               }
+           );
+       }
+       
+       
+
+});
\ No newline at end of file
index 9a9770b..61bfdbc 100644 (file)
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dijit.layout.BorderContainer');
-dojo.require("dojo.dnd.Container");
-dojo.require("dojo.dnd.Source");
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require('openils.Event');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.ProgressDialog');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.widget.EditDialog');
-
-var user;
-var groups;
-var locations;
-var source;
-var locTbody;
-var locRowTemplate;
-var locMapTbody;
-var locMapRowTemplate;
-var currentGroupId;
-var currentGroupMaps;
-var currentOrg;
-
-function init() {
-
-    user = new openils.User();
-   
-    // init the DnD environment
-    source = new dojo.dnd.Source('acplg-list');
-    dojo.connect(source, 'onDndDrop', updateGroupOrder);
-
-    // context org selector
-    user.buildPermOrgSelector(
-        'ADMIN_COPY_LOCATION_GROUP', 
-        contextOrgSelector, 
-        null, 
-        function() {
-            dojo.connect(contextOrgSelector, 'onChange', drawPage);
-        }
-    );
-
-    fetchCopyLocations();
-}
-
-function fetchCopyLocations() {
-    // the full set of copy locations can be very large.  
-    // Only retrieve the set of locations owned by orgs this user 
-    // can use for building location groups.
-    user.getPermOrgList(
-        ['ADMIN_COPY_LOCATION_GROUP'], 
-        function(list) {
-
-            var ownerOrgList = [];
-            dojo.forEach(list,
-                function(org) {
-                    // include parent orgs
-                    ownerOrgList = ownerOrgList.concat(org).concat(
-                        fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true));
-                }
-            );
-
-            var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-            pcrud.search('acpl', // this can take some time...
-                {owning_lib : ownerOrgList},
-                {   
-                    async : true,
-                    join : 'aou',
-                    oncomplete : function(r) {
-                        locations = openils.Util.readResponse(r);
-                        sortCopyLocations();
-                        drawPage(user.user.ws_ou());
-                    }
-                }
-            );
-        },
-        true,
-        true
-    );
-}
-
-// sort the list of copy locations according the shape of 
-// the org unit tree.  apply a secondary sort on name.
-function sortCopyLocations() {
-    var newlist = [];
-
-    function addNode(node) {
-        // find locs for this org
-        var locs = locations.filter(function(loc) { return loc.owning_lib() == node.id() });
-        // sort on name and append to the new list
-        newlist = newlist.concat(locs.sort(function(a, b) { return a.name() < b.name() ? -1 : 1 }));
-        // repeat for org child nodes
-        dojo.forEach(node.children(), addNode);
-    }
-
-    addNode(fieldmapper.aou.globalOrgTree);
-    locations = newlist;
-}
-
-
-function drawPage(org) {
-    currentOrg = org;
-    currentGroupId = null;
-    currentGroupMaps = [];
-    //drawLocations();
-    drawGroupList();
-}
-
-function drawGroupList(selectedGrp) {
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    groups = pcrud.search('acplg', {owner : currentOrg}, {order_by : {acplg : 'pos'}});
-
-
-    source.selectAll();
-    source.deleteSelectedNodes();
-    source.clearItems();
-
-    dojo.forEach(groups,
-        function(group) {
-            if(!group) return;
-
-            var drag = dojo.byId('dnd-drag-actions').cloneNode(true);
-            drag.id = '';
-            var vis = openils.Util.isTrue(group.opac_visible());
-            openils.Util.hide(dojo.query('[name=' + (vis ? 'invisible' : 'visible') + ']', drag)[0]);
-
-
-            var node = source.insertNodes(false, [{ 
-                data : drag.innerHTML.replace(/GRPID/g, group.id()).replace(/GRPNAME/g, group.name()),
-                type : [group.id()+''] // use the type field to store the ID
-            }]);
-        }
-    );
-
-    if (groups.length == 0) {
-        selectedGrp = null
-    } else if (selectedGrp == null) {
-        selectedGrp = groups[0].id();
-    }
-
-    drawGroupEntries(selectedGrp);
-}
-
-function drawLocations() {
-
-    if (!locTbody) {
-        locTbody = dojo.byId('acplg-loc-tbody');
-        locRowTemplate = locTbody.removeChild(dojo.byId('acplg-loc-row'));
-    } else {
-        // clear out the previous table
-        while (node = locTbody.childNodes[0])
-            locTbody.removeChild(node);
-    }
-
-    var allMyOrgs = fieldmapper.aou.fullPath(currentOrg, true);
-
-    dojo.forEach(locations,
-        function(loc) {
-            if (allMyOrgs.indexOf(loc.owning_lib()) == -1) return;
-
-            // don't show locations contained in the current group
-            if (currentGroupMaps.length) {
-                var existing = currentGroupMaps.filter(
-                    function(map) { return (map.location() == loc.id()) });
-                if (existing.length > 0) return;
-            }
-
-            var row = locRowTemplate.cloneNode(true);
-            row.setAttribute('location', loc.id());
-            dojo.query('[name=name]', row)[0].innerHTML = loc.name();
-            dojo.query('[name=owning_lib]', row)[0].innerHTML = fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname();
-            locTbody.appendChild(row);
-        }
-    );
-}
-
-function updateGroupOrder() {
-    var pos = 0;
-    var toUpdate = [];
-
-    // find any groups that have changed position and send them off for update
-    dojo.forEach(
-        source.getAllNodes(),
-        function(node) {
-            var item = source.getItem(node.id);
-            var grpId = item.type[0];
-            var grp = groups.filter(function(g) { return g.id() == grpId })[0];
-            if (grp.pos() != pos) {
-                grp.pos(pos);
-                toUpdate.push(grp);
-            }
-            pos++;
-        }
-    );
-
-    if (toUpdate.length == 0) return;
-
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    pcrud.update(toUpdate); // run sync to prevent UI changes mid-update 
-}
-
-function newGroup() {
-
-    var dialog = new openils.widget.EditDialog({
-        fmClass : 'acplg',
-        mode : 'create',
-        parentNode : dojo.byId('acplg-edit-dialog'),
-        suppressFields : ['id'],
-        // note: when 'pos' is suppressed, the value is not propagated.
-        overrideWidgetArgs : {
-            pos : {widgetValue : groups.length, dijitArgs : {disabled : true}},
-            owner : {widgetValue : currentOrg, dijitArgs : {disabled : true}}
-        },
-        onPostSubmit : function(req, cudResults) {
-            if (cudResults && cudResults.length) {
-                // refresh the group display
-                drawGroupList(cudResults[0].id());
-            }
-        }
-    });
-
-    dialog.startup();
-    dialog.show();
-}
-
-function editGroup(grpId) {
-    var grp = groups.filter(function(g) { return g.id() == grpId })[0];
-
-    var dialog = new openils.widget.EditDialog({
-        fmObject : grp,
-        mode : 'update',
-        parentNode : dojo.byId('acplg-edit-dialog'),
-        suppressFields : ['id', 'pos', 'owner'],
-        onPostSubmit : function(req, cudResults) {
-            if (cudResults && cudResults.length) {
-                // refresh the group display
-                // pcrud.update returns ID only
-                drawGroupList(cudResults[0]);
-            }
-        }
-    });
-
-    dialog.startup();
-    dialog.show();
-}
-
-function deleteGroup(grpId) {
-    // confirm and delete
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    var grp = groups.filter(function(g) { return g.id() == grpId })[0];
-    pcrud.eliminate(grp, {oncomplete : function() { drawGroupList() }});
-}
-
-function drawGroupEntries(grpId) {
-    currentGroupId = grpId;
-
-    // init/reset the table of mapped copy locations
-    if (!locMapTbody) {
-        locMapTbody = dojo.byId('acplg-loc-map-tbody');
-        locMapRowTemplate = locMapTbody.removeChild(dojo.byId('acplg-loc-map-row'));
-    } else {
-        // clear out the previous table
-        while (node = locMapTbody.childNodes[0])
-            locMapTbody.removeChild(node);
-    }
-    
-    // update the 'selected' status
-    dojo.query('[group]').forEach(
-        function(node) {
-            if (node.getAttribute('group') == grpId) {
-                openils.Util.addCSSClass(node, 'acplg-group-selected');
-            } else {
-                openils.Util.removeCSSClass(node, 'acplg-group-selected');
-            }
-        }
-    );
-
-    currentGroupMaps = [];
-
-    // fetch the group
-    if (grpId) {
-        var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-        currentGroupMaps = pcrud.search('acplgm', {lgroup : grpId});
-    } 
-
-    // update the location selector to remove the already-selected orgs
-    drawLocations();
-
-    // draw the mapped copy locations
-    // remove any mapped locations from the location selector
-    dojo.forEach(currentGroupMaps,
-        function(map) {
-            var row = locMapRowTemplate.cloneNode(true);
-            row.setAttribute('map', map.id());
-            var loc = locations.filter(
-                function(loc) { return (loc.id() == map.location()) })[0];
-            dojo.query('[name=name]', row)[0].innerHTML = loc.name();
-            dojo.query('[name=owning_lib]', row)[0].innerHTML = 
-                fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname();
-            locMapTbody.appendChild(row);
-
-            // if the location is in the group, remove it from the location selection list
-            //removeLocationRow(loc.id());
-        }
-    );
-}
-
-function editLocations(action) {
-    var maps = [];
-    var tbody = (action == 'create') ? locTbody : locMapTbody;
-    dojo.forEach(tbody.getElementsByTagName('tr'),
-        function(row) {
-            var selector = dojo.query('[name=selector]', row)[0];
-            if (selector.checked) {
-                var map = new fieldmapper.acplgm();
-                map.lgroup(currentGroupId);
-                if (action == 'create') {
-                    map.location(row.getAttribute('location'));
-                } else {
-                    map.id(row.getAttribute('map'));
-                }
-                maps.push(map);
-            }
-        }
-    );
-
-    if (maps.length == 0) return;
-
-    // check for dupes
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    pcrud[action](maps, {
-        oncomplete : function() { 
-            drawGroupEntries(currentGroupId) 
-            /*
-            if (action != 'create') {
-                drawLocations();
-            }
-            */
-        }
-    });
-}
-
-function deSelectAll(node) {
-    dojo.query('[name=selector]', node).forEach(
-        function(selector) {
-            selector.checked = false;
-        }
-    );
-}
-
-/*
-function removeLocationRow(locId) {
-    var row = dojo.query('[location=' + locId + ']', locTbody)[0];
-    if (row) locTbody.removeChild(row);
-}
-*/
-
-openils.Util.addOnLoad(init);
+require([
+       "dijit/layout/ContentPane",
+       "dijit/layout/BorderContainer",
+       "dojo/dnd/Container",
+       "dojo/dnd/Source",
+       "fieldmapper/OrgUtils",
+       "openils/User",
+       "openils/Util",
+       "openils/Event",
+       "openils/PermaCrud",
+       "openils/widget/AutoGrid",
+       "openils/widget/ProgressDialog",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/EditDialog"
+       ],
+function(dijit_layout_ContentPane,
+       dijit_layout_BorderContainer,
+       dojo_dnd_Container,
+       dojo_dnd_Source,
+       fieldmapper_OrgUtils,
+       openils_User,
+       openils_Util,
+       openils_Event,
+       openils_PermaCrud,
+       openils_widget_AutoGrid,
+       openils_widget_ProgressDialog,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_EditDialog){
+       
+       var user;
+       var groups;
+       var locations;
+       var source;
+       var locTbody;
+       var locRowTemplate;
+       var locMapTbody;
+       var locMapRowTemplate;
+       var currentGroupId;
+       var currentGroupMaps;
+       var currentOrg;
+       
+       function init() {
+       
+           user = new openils_User();
+          
+           // init the DnD environment
+           source = new dojo_dnd_Source('acplg-list');
+           dojo.connect(source, 'onDndDrop', updateGroupOrder);
+       
+           // context org selector
+           user.buildPermOrgSelector(
+               'ADMIN_COPY_LOCATION_GROUP', 
+               contextOrgSelector, 
+               null, 
+               function() {
+                   dojo.connect(contextOrgSelector, 'onChange', drawPage);
+               }
+           );
+       
+           fetchCopyLocations();
+       }
+       
+       function fetchCopyLocations() {
+           // the full set of copy locations can be very large.  
+           // Only retrieve the set of locations owned by orgs this user 
+           // can use for building location groups.
+           user.getPermOrgList(
+               ['ADMIN_COPY_LOCATION_GROUP'], 
+               function(list) {
+       
+                   var ownerOrgList = [];
+                   dojo.forEach(list,
+                       function(org) {
+                           // include parent orgs
+                           ownerOrgList = ownerOrgList.concat(org).concat(
+                               fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true));
+                       }
+                   );
+       
+                   var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+                   pcrud.search('acpl', // this can take some time...
+                       {owning_lib : ownerOrgList},
+                       {   
+                           async : true,
+                           join : 'aou',
+                           oncomplete : function(r) {
+                               locations = openils_Util.readResponse(r);
+                               sortCopyLocations();
+                               drawPage(user.user.ws_ou());
+                           }
+                       }
+                   );
+               },
+               true,
+               true
+           );
+       }
+       
+       // sort the list of copy locations according the shape of 
+       // the org unit tree.  apply a secondary sort on name.
+       function sortCopyLocations() {
+           var newlist = [];
+       
+           function addNode(node) {
+               // find locs for this org
+               var locs = locations.filter(function(loc) { return loc.owning_lib() == node.id() });
+               // sort on name and append to the new list
+               newlist = newlist.concat(locs.sort(function(a, b) { return a.name() < b.name() ? -1 : 1 }));
+               // repeat for org child nodes
+               dojo.forEach(node.children(), addNode);
+           }
+       
+           addNode(fieldmapper.aou.globalOrgTree);
+           locations = newlist;
+       }
+       
+       
+       function drawPage(org) {
+           currentOrg = org;
+           currentGroupId = null;
+           currentGroupMaps = [];
+           //drawLocations();
+           drawGroupList();
+       }
+       
+       function drawGroupList(selectedGrp) {
+           var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+           groups = pcrud.search('acplg', {owner : currentOrg}, {order_by : {acplg : 'pos'}});
+       
+       
+           source.selectAll();
+           source.deleteSelectedNodes();
+           source.clearItems();
+       
+           dojo.forEach(groups,
+               function(group) {
+                   if(!group) return;
+       
+                   var drag = dojo.byId('dnd-drag-actions').cloneNode(true);
+                   drag.id = '';
+                   var vis = openils_Util.isTrue(group.opac_visible());
+                   openils_Util.hide(dojo.query('[name=' + (vis ? 'invisible' : 'visible') + ']', drag)[0]);
+       
+       
+                   var node = source.insertNodes(false, [{ 
+                       data : drag.innerHTML.replace(/GRPID/g, group.id()).replace(/GRPNAME/g, group.name()),
+                       type : [group.id()+''] // use the type field to store the ID
+                   }]);
+               }
+           );
+       
+           if (groups.length == 0) {
+               selectedGrp = null
+           } else if (selectedGrp == null) {
+               selectedGrp = groups[0].id();
+           }
+       
+           drawGroupEntries(selectedGrp);
+       }
+       
+       function drawLocations() {
+       
+           if (!locTbody) {
+               locTbody = dojo.byId('acplg-loc-tbody');
+               locRowTemplate = locTbody.removeChild(dojo.byId('acplg-loc-row'));
+           } else {
+               // clear out the previous table
+               while (node = locTbody.childNodes[0])
+                   locTbody.removeChild(node);
+           }
+       
+           var allMyOrgs = fieldmapper.aou.fullPath(currentOrg, true);
+       
+           dojo.forEach(locations,
+               function(loc) {
+                   if (allMyOrgs.indexOf(loc.owning_lib()) == -1) return;
+       
+                   // don't show locations contained in the current group
+                   if (currentGroupMaps.length) {
+                       var existing = currentGroupMaps.filter(
+                           function(map) { return (map.location() == loc.id()) });
+                       if (existing.length > 0) return;
+                   }
+       
+                   var row = locRowTemplate.cloneNode(true);
+                   row.setAttribute('location', loc.id());
+                   dojo.query('[name=name]', row)[0].innerHTML = loc.name();
+                   dojo.query('[name=owning_lib]', row)[0].innerHTML = fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname();
+                   locTbody.appendChild(row);
+               }
+           );
+       }
+       
+       function updateGroupOrder() {
+           var pos = 0;
+           var toUpdate = [];
+       
+           // find any groups that have changed position and send them off for update
+           dojo.forEach(
+               source.getAllNodes(),
+               function(node) {
+                   var item = source.getItem(node.id);
+                   var grpId = item.type[0];
+                   var grp = groups.filter(function(g) { return g.id() == grpId })[0];
+                   if (grp.pos() != pos) {
+                       grp.pos(pos);
+                       toUpdate.push(grp);
+                   }
+                   pos++;
+               }
+           );
+       
+           if (toUpdate.length == 0) return;
+       
+           var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+           pcrud.update(toUpdate); // run sync to prevent UI changes mid-update 
+       }
+       
+       function newGroup() {
+       
+           var dialog = new openils_widget_EditDialog({
+               fmClass : 'acplg',
+               mode : 'create',
+               parentNode : dojo.byId('acplg-edit-dialog'),
+               suppressFields : ['id'],
+               // note: when 'pos' is suppressed, the value is not propagated.
+               overrideWidgetArgs : {
+                   pos : {widgetValue : groups.length, dijitArgs : {disabled : true}},
+                   owner : {widgetValue : currentOrg, dijitArgs : {disabled : true}}
+               },
+               onPostSubmit : function(req, cudResults) {
+                   if (cudResults && cudResults.length) {
+                       // refresh the group display
+                       drawGroupList(cudResults[0].id());
+                   }
+               }
+           });
+       
+           dialog.startup();
+           dialog.show();
+       }
+       
+       function editGroup(grpId) {
+           var grp = groups.filter(function(g) { return g.id() == grpId })[0];
+       
+           var dialog = new openils_widget_EditDialog({
+               fmObject : grp,
+               mode : 'update',
+               parentNode : dojo.byId('acplg-edit-dialog'),
+               suppressFields : ['id', 'pos', 'owner'],
+               onPostSubmit : function(req, cudResults) {
+                   if (cudResults && cudResults.length) {
+                       // refresh the group display
+                       // pcrud.update returns ID only
+                       drawGroupList(cudResults[0]);
+                   }
+               }
+           });
+       
+           dialog.startup();
+           dialog.show();
+       }
+       
+       function deleteGroup(grpId) {
+           // confirm and delete
+           var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+           var grp = groups.filter(function(g) { return g.id() == grpId })[0];
+           pcrud.eliminate(grp, {oncomplete : function() { drawGroupList() }});
+       }
+       
+       function drawGroupEntries(grpId) {
+           currentGroupId = grpId;
+       
+           // init/reset the table of mapped copy locations
+           if (!locMapTbody) {
+               locMapTbody = dojo.byId('acplg-loc-map-tbody');
+               locMapRowTemplate = locMapTbody.removeChild(dojo.byId('acplg-loc-map-row'));
+           } else {
+               // clear out the previous table
+               while (node = locMapTbody.childNodes[0])
+                   locMapTbody.removeChild(node);
+           }
+           
+           // update the 'selected' status
+           dojo.query('[group]').forEach(
+               function(node) {
+                   if (node.getAttribute('group') == grpId) {
+                       openils_Util.addCSSClass(node, 'acplg-group-selected');
+                   } else {
+                       openils_Util.removeCSSClass(node, 'acplg-group-selected');
+                   }
+               }
+           );
+       
+           currentGroupMaps = [];
+       
+           // fetch the group
+           if (grpId) {
+               var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+               currentGroupMaps = pcrud.search('acplgm', {lgroup : grpId});
+           } 
+       
+           // update the location selector to remove the already-selected orgs
+           drawLocations();
+       
+           // draw the mapped copy locations
+           // remove any mapped locations from the location selector
+           dojo.forEach(currentGroupMaps,
+               function(map) {
+                   var row = locMapRowTemplate.cloneNode(true);
+                   row.setAttribute('map', map.id());
+                   var loc = locations.filter(
+                       function(loc) { return (loc.id() == map.location()) })[0];
+                   dojo.query('[name=name]', row)[0].innerHTML = loc.name();
+                   dojo.query('[name=owning_lib]', row)[0].innerHTML = 
+                       fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname();
+                   locMapTbody.appendChild(row);
+       
+                   // if the location is in the group, remove it from the location selection list
+                   //removeLocationRow(loc.id());
+               }
+           );
+       }
+       
+       function editLocations(action) {
+           var maps = [];
+           var tbody = (action == 'create') ? locTbody : locMapTbody;
+           dojo.forEach(tbody.getElementsByTagName('tr'),
+               function(row) {
+                   var selector = dojo.query('[name=selector]', row)[0];
+                   if (selector.checked) {
+                       var map = new fieldmapper.acplgm();
+                       map.lgroup(currentGroupId);
+                       if (action == 'create') {
+                           map.location(row.getAttribute('location'));
+                       } else {
+                           map.id(row.getAttribute('map'));
+                       }
+                       maps.push(map);
+                   }
+               }
+           );
+       
+           if (maps.length == 0) return;
+       
+           // check for dupes
+           var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+           pcrud[action](maps, {
+               oncomplete : function() { 
+                   drawGroupEntries(currentGroupId) 
+                   /*
+                   if (action != 'create') {
+                       drawLocations();
+                   }
+                   */
+               }
+           });
+       }
+       
+       function deSelectAll(node) {
+           dojo.query('[name=selector]', node).forEach(
+               function(selector) {
+                   selector.checked = false;
+               }
+           );
+       }
+       
+       /*
+       function removeLocationRow(locId) {
+           var row = dojo.query('[location=' + locId + ']', locTbody)[0];
+           if (row) locTbody.removeChild(row);
+       }
+       */
+       
+       openils_Util.addOnLoad(init);
+       
+
+});
\ No newline at end of file
index 5032b34..6891e67 100644 (file)
-dojo.require('dijit.layout.ContentPane');
-dojo.require("dojo.dnd.Container");
-dojo.require("dojo.dnd.Source");
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.User');
-dojo.require('openils.Util');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.ProgressDialog');
-
-var user;
-var orders;
-var locations;
-var source;
-
-function init() {
-
-     user = new openils.User();
-     source = new dojo.dnd.Source('acl-ol');
-
-     user.buildPermOrgSelector(
-        'ADMIN_COPY_LOCATION_ORDER', 
-        contextOrgSelector, 
-        null, 
-        function() {
-              dojo.connect(contextOrgSelector, 'onChange', filterGrid);
-        }
-    );
-
-    filterGrid(user.user.ws_ou());
-}
-
-function filterGrid(org) {
-
-    // fetch the locations and order entries
-    if(!orders) {
-        var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-        orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
-        locations = pcrud.search('acpl', 
-            {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)}, 
-            {order_by : {acpl : 'name'}}
-        ); 
-    }
-
-    // init the DnD environment
-    source.selectAll();
-    source.deleteSelectedNodes();
-    source.clearItems();
-
-    var locs = [];
-
-    // sort and append by existing order settings
-    dojo.forEach(orders, 
-        function(order) {
-            locs.push( 
-                locations.filter(function(l) {return l.id() == order.location()})[0] 
-            );
-        }
-    );
-
-    // append any non-sorted locations
-    dojo.forEach(locations, 
-        function(l) {
-            if(!locs.filter(function(ll) { return ll.id() == l.id() })[0])
-                locs.push(l);
-        }
-    );
-
-    // shove them into the DnD environment
-    dojo.forEach(locs,
-        function(loc) {
-            if(!loc) return;
-            var node = source.insertNodes(false, [ 
-                { 
-                    data : loc.name() + ' (' + fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname()+')',
-                    type : [loc.id()+''] // use the type field to store the ID
-                }
-            ]);
-        }
-    );
-}
-
-function applyChanges() {
-    progressDialog.show();
-
-    var newOrders = [];
-    var contextOrg = contextOrgSelector.attr('value');
-
-    // pull the locations out of the DnD environment and create order entries for them
-    dojo.forEach(
-        source.getAllNodes(),
-        function(node) {
-            var item = source.getItem(node.id);
-            var o = new fieldmapper.acplo();
-            o.position(newOrders.length + 1);
-            o.location(item.type[0]); // location.id() is stored in DnD item type
-            o.org(contextOrg);
-            newOrders.push(o);
-        }
-    );
-
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.copy_location_order.update'],
-        {
-            async : true,
-            params : [openils.User.authtoken, newOrders],
-            onresponse : function(r) {
-                if(r = openils.Util.readResponse(r)) {
-                    if(r.orders) {
-                        orders = r.order;
-                        progressDialog.hide();
-                        filterGrid(contextOrg);
-                        return;
-                    } 
-                    progressDialog.update(r);
-                }
-            },
-        }
-    );
-}
-
-openils.Util.addOnLoad(init);
-
+require([
+       "dijit/layout/ContentPane",
+       "dojo/dnd/Container",
+       "dojo/dnd/Source",
+       "openils/widget/OrgUnitFilteringSelect",
+       "fieldmapper/OrgUtils",
+       "openils/User",
+       "openils/Util",
+       "openils/widget/AutoGrid",
+       "openils/PermaCrud",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_layout_ContentPane,
+       dojo_dnd_Container,
+       dojo_dnd_Source,
+       openils_widget_OrgUnitFilteringSelect,
+       fieldmapper_OrgUtils,
+       openils_User,
+       openils_Util,
+       openils_widget_AutoGrid,
+       openils_PermaCrud,
+       openils_widget_ProgressDialog){
+       
+       var user;
+       var orders;
+       var locations;
+       var source;
+       
+       function init() {
+       
+            user = new openils_User();
+            source = new dojo_dnd_Source('acl-ol');
+       
+            user.buildPermOrgSelector(
+               'ADMIN_COPY_LOCATION_ORDER', 
+               contextOrgSelector, 
+               null, 
+               function() {
+                     dojo.connect(contextOrgSelector, 'onChange', filterGrid);
+               }
+           );
+       
+           filterGrid(user.user.ws_ou());
+       }
+       
+       function filterGrid(org) {
+       
+           // fetch the locations and order entries
+           if(!orders) {
+               var pcrud = new openils_PermaCrud({authtoken : user.authtoken});
+               orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
+               locations = pcrud.search('acpl', 
+                   {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)}, 
+                   {order_by : {acpl : 'name'}}
+               ); 
+           }
+       
+           // init the DnD environment
+           source.selectAll();
+           source.deleteSelectedNodes();
+           source.clearItems();
+       
+           var locs = [];
+       
+           // sort and append by existing order settings
+           dojo.forEach(orders, 
+               function(order) {
+                   locs.push( 
+                       locations.filter(function(l) {return l.id() == order.location()})[0] 
+                   );
+               }
+           );
+       
+           // append any non-sorted locations
+           dojo.forEach(locations, 
+               function(l) {
+                   if(!locs.filter(function(ll) { return ll.id() == l.id() })[0])
+                       locs.push(l);
+               }
+           );
+       
+           // shove them into the DnD environment
+           dojo.forEach(locs,
+               function(loc) {
+                   if(!loc) return;
+                   var node = source.insertNodes(false, [ 
+                       { 
+                           data : loc.name() + ' (' + fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname()+')',
+                           type : [loc.id()+''] // use the type field to store the ID
+                       }
+                   ]);
+               }
+           );
+       }
+       
+       function applyChanges() {
+           progressDialog.show();
+       
+           var newOrders = [];
+           var contextOrg = contextOrgSelector.attr('value');
+       
+           // pull the locations out of the DnD environment and create order entries for them
+           dojo.forEach(
+               source.getAllNodes(),
+               function(node) {
+                   var item = source.getItem(node.id);
+                   var o = new fieldmapper.acplo();
+                   o.position(newOrders.length + 1);
+                   o.location(item.type[0]); // location.id() is stored in DnD item type
+                   o.org(contextOrg);
+                   newOrders.push(o);
+               }
+           );
+       
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.copy_location_order.update'],
+               {
+                   async : true,
+                   params : [openils_User.authtoken, newOrders],
+                   onresponse : function(r) {
+                       if(r = openils_Util.readResponse(r)) {
+                           if(r.orders) {
+                               orders = r.order;
+                               progressDialog.hide();
+                               filterGrid(contextOrg);
+                               return;
+                           } 
+                           progressDialog.update(r);
+                       }
+                   },
+               }
+           );
+       }
+       
+       openils_Util.addOnLoad(init);
+       
+       
+
+});
\ No newline at end of file
index ff01590..46d02fd 100644 (file)
@@ -1,81 +1,92 @@
-dojo.require("dojo.data.ItemFileWriteStore");
-dojo.require("dijit.form.CurrencyTextBox");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
+require([
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/CurrencyTextBox",
+       "dijit/form/FilteringSelect",
+       "openils/widget/AutoGrid",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojo_data_ItemFileWriteStore,
+       dijit_form_CurrencyTextBox,
+       dijit_form_FilteringSelect,
+       openils_widget_AutoGrid,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var pcrud;
+       var actOwner;
+       var actList;
+       
+       function create_or_update_act(obj, opts, edit_pane) {
+           fieldmapper.standardRequest(
+               ["open-ils.cat", "open-ils.cat.asset.copy_template.create_or_update"], {
+                   "params": [openils.User.authtoken, obj],
+                   "async": true,
+                   "oncomplete": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           if (edit_pane.onPostSubmit)
+                               edit_pane.onPostSubmit(null, [r]);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function actInit() {
+           actGrid.overrideEditWidgets.fine_level = special_fine_level;
+           actGrid.overrideEditWidgets.fine_level.shove = {"create": 2};
+       
+           actGrid.overrideEditWidgets.loan_duration = special_loan_duration;
+           actGrid.overrideEditWidgets.loan_duration.shove = {"create": 2};
+       
+           pcrud = new openils_PermaCrud();
+       
+           new openils.User().buildPermOrgSelector(
+               "ADMIN_ASSET_COPY_TEMPLATE",
+               actOwnerSelect,
+               null,
+               function() {
+                   dojo.connect(
+                       actOwnerSelect,
+                       "onChange",
+                       function() {
+                           actOwner = fieldmapper.aou.findOrgUnit(this.attr("value"));
+                           actGrid.resetStore();
+                           buildActGrid();
+                       }
+                   );
+                   buildActGrid();
+               }
+           );
+       }
+       
+       function buildActGrid() {
+           if (!actOwner)
+               actOwner = fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou());
+       
+           pcrud.search(
+               "act", {
+                   "owning_lib": fieldmapper.aou.orgNodeTrail(actOwner, true /* asId */)
+               }, {
+                   "async": true,
+                   "onresponse": function(r) {
+                       if ((actList = openils.Util.readResponse(r))) {
+                           actList = openils.Util.objectSort(actList);
+                           actList.forEach(
+                               function(o) {
+                                   actGrid.store.newItem(act.toStoreItem(o));
+                               }
+                           );
+                       }
+                   },
+                   "oncomplete": function() {
+                       actGrid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(actInit);
+       
 
-var pcrud;
-var actOwner;
-var actList;
-
-function create_or_update_act(obj, opts, edit_pane) {
-    fieldmapper.standardRequest(
-        ["open-ils.cat", "open-ils.cat.asset.copy_template.create_or_update"], {
-            "params": [openils.User.authtoken, obj],
-            "async": true,
-            "oncomplete": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    if (edit_pane.onPostSubmit)
-                        edit_pane.onPostSubmit(null, [r]);
-                }
-            }
-        }
-    );
-}
-
-function actInit() {
-    actGrid.overrideEditWidgets.fine_level = special_fine_level;
-    actGrid.overrideEditWidgets.fine_level.shove = {"create": 2};
-
-    actGrid.overrideEditWidgets.loan_duration = special_loan_duration;
-    actGrid.overrideEditWidgets.loan_duration.shove = {"create": 2};
-
-    pcrud = new openils.PermaCrud();
-
-    new openils.User().buildPermOrgSelector(
-        "ADMIN_ASSET_COPY_TEMPLATE",
-        actOwnerSelect,
-        null,
-        function() {
-            dojo.connect(
-                actOwnerSelect,
-                "onChange",
-                function() {
-                    actOwner = fieldmapper.aou.findOrgUnit(this.attr("value"));
-                    actGrid.resetStore();
-                    buildActGrid();
-                }
-            );
-            buildActGrid();
-        }
-    );
-}
-
-function buildActGrid() {
-    if (!actOwner)
-        actOwner = fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou());
-
-    pcrud.search(
-        "act", {
-            "owning_lib": fieldmapper.aou.orgNodeTrail(actOwner, true /* asId */)
-        }, {
-            "async": true,
-            "onresponse": function(r) {
-                if ((actList = openils.Util.readResponse(r))) {
-                    actList = openils.Util.objectSort(actList);
-                    actList.forEach(
-                        function(o) {
-                            actGrid.store.newItem(act.toStoreItem(o));
-                        }
-                    );
-                }
-            },
-            "oncomplete": function() {
-                actGrid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(actInit);
+});
\ No newline at end of file
index 2d307a8..db3d94a 100644 (file)
@@ -1,72 +1,88 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var thingContextOrg;
-var thingList;
-
-/** really need to put this in a shared location... */
-function getOrgInfo(rowIndex, item) {
-    if(!item) return '';
-    var orgId = this.grid.store.getValue(item, this.field);
-    return fieldmapper.aou.findOrgUnit(orgId).shortname();
-}
-
-function thingInit() {
-
-    thingGrid.disableSelectorForRow = function(rowIdx) {
-        var item = thingGrid.getItem(rowIdx);
-        return (thingGrid.store.getValue(item, 'id') < 0);
-    }
-
-    buildGrid();
-    var connect = function() {
-        dojo.connect(thingContextOrgSelect, 'onChange',
-                     function() {
-                         thingContextOrg = this.getValue();
-                         thingGrid.resetStore();
-                         buildGrid();
-                     }
-                    );
-    };
-    // go ahead and let staff see everything
-    new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect);
-}
-
-function buildGrid() {
-    if(thingContextOrg == null)
-        thingContextOrg = openils.User.user.ws_ou();
-
-    fieldmapper.standardRequest(
-        ['open-ils.pcrud', 'open-ils.pcrud.search.acnp.atomic'],
-        {   async: true,
-            params: [
-                openils.User.authtoken,
-                {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)},
-                {"order_by":{"acnp":"label_sortkey"}}
-            ],
-            oncomplete: function(r) {
-                if(thingList = openils.Util.readResponse(r)) {
-                    thingList = openils.Util.objectSort(thingList);
-                    dojo.forEach(thingList,
-                                 function(e) {
-                                     thingGrid.store.newItem(acnp.toStoreItem(e));
-                                 }
-                                );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(thingInit);
-
+require([
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/CurrencyTextBox",
+       "dijit/Dialog",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_form_CurrencyTextBox,
+       dijit_Dialog,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var thingContextOrg;
+       var thingList;
+       
+       /** really need to put this in a shared location... */
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return '';
+           var orgId = this.grid.store.getValue(item, this.field);
+           return fieldmapper.aou.findOrgUnit(orgId).shortname();
+       }
+       
+       function thingInit() {
+       
+           thingGrid.disableSelectorForRow = function(rowIdx) {
+               var item = thingGrid.getItem(rowIdx);
+               return (thingGrid.store.getValue(item, 'id') < 0);
+           }
+       
+           buildGrid();
+           var connect = function() {
+               dojo.connect(thingContextOrgSelect, 'onChange',
+                            function() {
+                                thingContextOrg = this.getValue();
+                                thingGrid.resetStore();
+                                buildGrid();
+                            }
+                           );
+           };
+           // go ahead and let staff see everything
+           new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect);
+       }
+       
+       function buildGrid() {
+           if(thingContextOrg == null)
+               thingContextOrg = openils.User.user.ws_ou();
+       
+           fieldmapper.standardRequest(
+               ['open-ils.pcrud', 'open-ils.pcrud.search.acnp.atomic'],
+               {   async: true,
+                   params: [
+                       openils.User.authtoken,
+                       {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)},
+                       {"order_by":{"acnp":"label_sortkey"}}
+                   ],
+                   oncomplete: function(r) {
+                       if(thingList = openils.Util.readResponse(r)) {
+                           thingList = openils.Util.objectSort(thingList);
+                           dojo.forEach(thingList,
+                                        function(e) {
+                                            thingGrid.store.newItem(acnp.toStoreItem(e));
+                                        }
+                                       );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(thingInit);
+       
+       
+       
 
+});
\ No newline at end of file
index c79cd0e..9bfacd4 100644 (file)
@@ -1,72 +1,88 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var thingContextOrg;
-var thingList;
-
-/** really need to put this in a shared location... */
-function getOrgInfo(rowIndex, item) {
-    if(!item) return '';
-    var orgId = this.grid.store.getValue(item, this.field);
-    return fieldmapper.aou.findOrgUnit(orgId).shortname();
-}
-
-function thingInit() {
-
-    thingGrid.disableSelectorForRow = function(rowIdx) {
-        var item = thingGrid.getItem(rowIdx);
-        return (thingGrid.store.getValue(item, 'id') < 0);
-    }
-
-    buildGrid();
-    var connect = function() {
-        dojo.connect(thingContextOrgSelect, 'onChange',
-                     function() {
-                         thingContextOrg = this.getValue();
-                         thingGrid.resetStore();
-                         buildGrid();
-                     }
-                    );
-    };
-    // go ahead and let staff see everything
-    new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect);
-}
-
-function buildGrid() {
-    if(thingContextOrg == null)
-        thingContextOrg = openils.User.user.ws_ou();
-
-    fieldmapper.standardRequest(
-        ['open-ils.pcrud', 'open-ils.pcrud.search.acns.atomic'],
-        {   async: true,
-            params: [
-                openils.User.authtoken,
-                {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)},
-                {"order_by":{"acns":"label_sortkey"}}
-            ],
-            oncomplete: function(r) {
-                if(thingList = openils.Util.readResponse(r)) {
-                    thingList = openils.Util.objectSort(thingList);
-                    dojo.forEach(thingList,
-                                 function(e) {
-                                     thingGrid.store.newItem(acns.toStoreItem(e));
-                                 }
-                                );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(thingInit);
-
+require([
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/CurrencyTextBox",
+       "dijit/Dialog",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_form_CurrencyTextBox,
+       dijit_Dialog,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var thingContextOrg;
+       var thingList;
+       
+       /** really need to put this in a shared location... */
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return '';
+           var orgId = this.grid.store.getValue(item, this.field);
+           return fieldmapper.aou.findOrgUnit(orgId).shortname();
+       }
+       
+       function thingInit() {
+       
+           thingGrid.disableSelectorForRow = function(rowIdx) {
+               var item = thingGrid.getItem(rowIdx);
+               return (thingGrid.store.getValue(item, 'id') < 0);
+           }
+       
+           buildGrid();
+           var connect = function() {
+               dojo.connect(thingContextOrgSelect, 'onChange',
+                            function() {
+                                thingContextOrg = this.getValue();
+                                thingGrid.resetStore();
+                                buildGrid();
+                            }
+                           );
+           };
+           // go ahead and let staff see everything
+           new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect);
+       }
+       
+       function buildGrid() {
+           if(thingContextOrg == null)
+               thingContextOrg = openils.User.user.ws_ou();
+       
+           fieldmapper.standardRequest(
+               ['open-ils.pcrud', 'open-ils.pcrud.search.acns.atomic'],
+               {   async: true,
+                   params: [
+                       openils.User.authtoken,
+                       {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)},
+                       {"order_by":{"acns":"label_sortkey"}}
+                   ],
+                   oncomplete: function(r) {
+                       if(thingList = openils.Util.readResponse(r)) {
+                           thingList = openils.Util.objectSort(thingList);
+                           dojo.forEach(thingList,
+                                        function(e) {
+                                            thingGrid.store.newItem(acns.toStoreItem(e));
+                                        }
+                                       );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(thingInit);
+       
+       
+       
 
+});
\ No newline at end of file
index 0979c49..a8ad97b 100644 (file)
@@ -1,15 +1,26 @@
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dijit.form.Button');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.ProgressDialog');
-
-function load(){
-    cmGrid.overrideWidgetArgs.prefix = {hrbefore : true};
-    cmGrid.overrideWidgetArgs.asset = {hrbefore: true};
-    cmGrid.loadAll({order_by:{cbc:'org_unit'}});
-}
-
-openils.Util.addOnLoad(load);
+require([
+       "dijit/layout/ContentPane",
+       "dijit/form/Button",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/PermaCrud",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_layout_ContentPane,
+       dijit_form_Button,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_PermaCrud,
+       openils_widget_ProgressDialog){
+       
+       function load(){
+           cmGrid.overrideWidgetArgs.prefix = {hrbefore : true};
+           cmGrid.overrideWidgetArgs.asset = {hrbefore: true};
+           cmGrid.loadAll({order_by:{cbc:'org_unit'}});
+       }
+       
+       openils.Util.addOnLoad(load);
+       
+       
 
+});
\ No newline at end of file
index de27506..3863fad 100644 (file)
@@ -1,66 +1,82 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.form.CurrencyTextBox');
-dojo.require('dijit.Dialog');
-dojo.require('dojox.widget.PlaceholderMenuItem');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-
-var btContextOrg;
-var btList;
-
-/** really need to put this in a shared location... */
-function getOrgInfo(rowIndex, item) {
-    if(!item) return '';
-    var orgId = this.grid.store.getValue(item, this.field);
-    return fieldmapper.aou.findOrgUnit(orgId).shortname();
-}
-
-function btInit() {
-
-    btGrid.disableSelectorForRow = function(rowIdx) {
-        var item = btGrid.getItem(rowIdx);
-        return (btGrid.store.getValue(item, 'id') < 100);
-    }
-
-    buildBTGrid();
-    var connect = function() {
-        dojo.connect(btContextOrgSelect, 'onChange',
-                     function() {
-                         btContextOrg = this.getValue();
-                         btGrid.resetStore();
-                         buildBTGrid();
-                     }
-                    );
-    };
-    new openils.User().buildPermOrgSelector('VIEW_BILLING_TYPE', btContextOrgSelect, null, connect);
-}
-
-function buildBTGrid() {
-    if(btContextOrg == null)
-        btContextOrg = openils.User.user.ws_ou();
-    fieldmapper.standardRequest(
-        ['open-ils.circ', 'open-ils.circ.billing_type.ranged.retrieve.all'],
-        {   async: true,
-            params: [openils.User.authtoken, btContextOrg, fieldmapper.aou.findOrgDepth(btContextOrg)],
-            oncomplete: function(r) {
-                if(btList = openils.Util.readResponse(r)) {
-                    btList = openils.Util.objectSort(btList);
-                    dojo.forEach(btList,
-                                 function(e) {
-                                     btGrid.store.newItem(cbt.toStoreItem(e));
-                                 }
-                                );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(btInit);
-
+require([
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/form/CurrencyTextBox",
+       "dijit/Dialog",
+       "dojox/widget/PlaceholderMenuItem",
+       "fieldmapper/OrgUtils",
+       "dijit/form/FilteringSelect",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_form_CurrencyTextBox,
+       dijit_Dialog,
+       dojox_widget_PlaceholderMenuItem,
+       fieldmapper_OrgUtils,
+       dijit_form_FilteringSelect,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect){
+       
+       var btContextOrg;
+       var btList;
+       
+       /** really need to put this in a shared location... */
+       function getOrgInfo(rowIndex, item) {
+           if(!item) return '';
+           var orgId = this.grid.store.getValue(item, this.field);
+           return fieldmapper.aou.findOrgUnit(orgId).shortname();
+       }
+       
+       function btInit() {
+       
+           btGrid.disableSelectorForRow = function(rowIdx) {
+               var item = btGrid.getItem(rowIdx);
+               return (btGrid.store.getValue(item, 'id') < 100);
+           }
+       
+           buildBTGrid();
+           var connect = function() {
+               dojo.connect(btContextOrgSelect, 'onChange',
+                            function() {
+                                btContextOrg = this.getValue();
+                                btGrid.resetStore();
+                                buildBTGrid();
+                            }
+                           );
+           };
+           new openils.User().buildPermOrgSelector('VIEW_BILLING_TYPE', btContextOrgSelect, null, connect);
+       }
+       
+       function buildBTGrid() {
+           if(btContextOrg == null)
+               btContextOrg = openils.User.user.ws_ou();
+           fieldmapper.standardRequest(
+               ['open-ils.circ', 'open-ils.circ.billing_type.ranged.retrieve.all'],
+               {   async: true,
+                   params: [openils.User.authtoken, btContextOrg, fieldmapper.aou.findOrgDepth(btContextOrg)],
+                   oncomplete: function(r) {
+                       if(btList = openils.Util.readResponse(r)) {
+                           btList = openils.Util.objectSort(btList);
+                           dojo.forEach(btList,
+                                        function(e) {
+                                            btGrid.store.newItem(cbt.toStoreItem(e));
+                                        }
+                                       );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(btInit);
+       
+       
+       
 
+});
\ No newline at end of file
index 49d6aa8..23a2fee 100644 (file)
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dijit.form.Button');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.ProgressDialog');
-
-var linkedEditor = null;
-var circModEntryCache = [];
-var limitGroupEntryCache = [];
-var circModCache = {};
-var limitGroupCache = {};
-var curLinkedEditor;
-
-function load(){
-    clsGrid.loadAll({order_by:{ccls:'name'}});
-    clsGrid.onEditPane = buildEditPaneAdditions;
-    clsGrid.onPostUpdate = updateLinked;
-    clsGrid.onPostCreate = updateLinked;
-    linkedEditor = dojo.byId('linked-editor').parentNode.removeChild(dojo.byId('linked-editor'));
-
-    // Cache circ mod/limit group info for later display
-    var pcrud = new openils.PermaCrud();
-    var temp = pcrud.retrieveAll('ccm');
-    dojo.forEach(temp, function(g) { circModCache[g.code()] = g; } );
-    temp = pcrud.retrieveAll('cclg');
-    dojo.forEach(temp, function(g) { limitGroupCache[g.id()] = g; } );
-}
-
-function byName(name, ctxt) {
-    return dojo.query('[name=' + name + ']', ctxt)[0];
-}
-
-function buildEditPaneAdditions(editPane) {
-    circModEntryCache = [];
-    limitGroupEntryCache = [];
-    var tr = document.createElement('tr');
-    var td = document.createElement('td');
-    td.setAttribute('colspan','2');
-    // Explanation....
-    // editPane.domNode.lastChild = Table
-    // .lastChild = Table Body
-    // .lastChild = Table Row containing Action Buttons
-    editPane.domNode.lastChild.lastChild.insertBefore(tr, editPane.domNode.lastChild.lastChild.lastChild);
-    tr.appendChild(td);
-    curLinkedEditor = linkedEditor.cloneNode(true);
-    td.appendChild(curLinkedEditor);
-    var circModTmpl = byName('circ-mod-entry-tbody', curLinkedEditor).removeChild(byName('circ-mod-entry-row', curLinkedEditor));
-    var limitGroupTmpl = byName('limit-group-entry-tbody', curLinkedEditor).removeChild(byName('limit-group-entry-row', curLinkedEditor));
-
-    var cm_selector = new openils.widget.AutoFieldWidget({
-        fmClass : 'cclscmm',
-        fmField : 'circ_mod',
-        parentNode : byName('circ-mod-selector', curLinkedEditor)
-    });
-    cm_selector.build();
-
-    var lg_selector = new openils.widget.AutoFieldWidget({
-        fmClass : 'cclsgm',
-        fmField : 'limit_group',
-        parentNode : byName('limit-group-selector', curLinkedEditor)
-    });
-    lg_selector.build();
-
-    function addMod(code) {
-        var row = circModTmpl.cloneNode(true);
-        row.setAttribute('code', code);
-        byName('circ-mod', row).innerHTML = code + ' : ' + circModCache[code].name();
-        byName('remove-circ-mod', row).onclick = function() {
-            byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
-        }
-        byName('circ-mod-entry-tbody', editPane.domNode).appendChild(row);
-    }
-
-    function addGroup(group) {
-        var row = limitGroupTmpl.cloneNode(true);
-        row.setAttribute('limit_group', group);
-        byName('limit-group', row).innerHTML = limitGroupCache[group].name();
-        byName('remove-limit-group', row).onclick = function() {
-            byName('limit-group-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
-        }
-        byName('limit-group-entry-tbody', editPane.domNode).appendChild(row);
-    }
-
-    byName('add-circ-mod', editPane.domNode).onclick = function() {
-        addMod(cm_selector.widget.attr('value'));
-    }
-
-    byName('add-limit-group', editPane.domNode).onclick = function() {
-        addGroup(lg_selector.widget.attr('value'));
-    }
-
-    // On edit we need to load existing entries.
-    // On create, not so much.
-    if(!editPane.fmObject) return; 
-    var limitSet = editPane.fmObject.id();
-
-    if(editPane.mode == 'update') {
-        var pcrud = new openils.PermaCrud();
-        circModEntryCache = pcrud.search('cclscmm', {limit_set: limitSet});
-        limitGroupEntryCache = pcrud.search('cclsgm', {limit_set: limitSet});
-        dojo.forEach(circModEntryCache, function(g) { addCircMod(circModTmpl, g); } );
-        dojo.forEach(limitGroupEntryCache, function(g) { addLimitGroup(limitGroupTmpl, g); } );
-    } 
-}
-
-function addCircMod(tmpl, circ_mod_entry) {
-    var row = tmpl.cloneNode(true);
-    var code = circ_mod_entry.circ_mod();
-    row.setAttribute('code', code);
-    byName('circ-mod', row).innerHTML = code + ' : ' + circModCache[code].name();
-    byName('remove-circ-mod', row).onclick = function() {
-        byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
-    }
-    byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).appendChild(row);
-}
-
-function addLimitGroup(tmpl, limit_group_entry) {
-    var row = tmpl.cloneNode(true);
-    var group = limit_group_entry.limit_group();
-    row.setAttribute('limit_group', group);
-    byName('limit-group', row).innerHTML = limitGroupCache[group].name();
-    if(limit_group_entry.check_only() == 't') {
-        byName('limit-group-check-only', row).setAttribute('checked', 'true');
-    }
-    byName('remove-limit-group', row).onclick = function() {
-        byName('limit-group-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
-    }
-    byName('limit-group-entry-tbody', clsGrid.editPane.domNode).appendChild(row);
-}
-
-function updateLinked(fmObject, rowindex) {
-    var id = null;
-    if(rowindex != undefined && this.editPane && this.editPane.fmObject) {
-        // Edit, grab existing ID
-        id = this.editPane.fmObject.id();
-    } else if(fmObject.id) {
-        // Create, grab new ID
-        id = fmObject.id();
-    }
-    // If we don't have an ID, drop out.
-    if(id == null) return;
-    var pcrud = new openils.PermaCrud();
-    progressDialog.show(true);
-
-    var add = [];
-    var remove = [];
-    var update = [];
-
-    // First up, circ mods.
-    var circ_mods = [];
-    dojo.query('[name=circ-mod-entry-row]', this.editPane.domNode).forEach(
-        function(row) {
-            var mod = row.getAttribute('code');
-            circ_mods.push(mod);
-            if(!circModEntryCache.filter(function(i) { return (i.circ_mod() == mod); })[0]) {
-                var entry = new fieldmapper.cclscmm();
-                entry.isnew(true);
-                entry.limit_set(id);
-                entry.circ_mod(mod);
-                add.push(entry);
-            }
-        }
-    );
-    dojo.forEach(circModEntryCache, function(eMod) {
-            if(!circ_mods.filter(function(i) { return (i == eMod.circ_mod()); })[0]) {
-                eMod.isdeleted(true);
-                remove.push(eMod);
-            }
-        }
-    );
-
-    // Next, limit groups
-    var limit_groups = [];
-    dojo.query('[name=limit-group-entry-row]', this.editPane.domNode).forEach(
-        function(row) {
-            var group = row.getAttribute('limit_group');
-            limit_groups.push(group);
-            var cached = limitGroupEntryCache.filter(function(i) { return (i.limit_group() == group); })[0];
-            if(!cached) {
-                var entry = new fieldmapper.cclsgm();
-                entry.isnew(true);
-                entry.limit_set(id);
-                entry.limit_group(group);
-                entry.check_only(byName('limit-group-check-only', row).checked ? 't' : 'f');
-                add.push(entry);
-            } else {
-                var check_only = byName('limit-group-check-only', row).checked;
-                if(check_only != (cached.check_only() == 't')) {
-                    cached.check_only(check_only ? 't' : 'f');
-                    cached.ischanged(true);
-                    update.push(cached);
-                }
-            }
-        }
-    );
-    dojo.forEach(limitGroupEntryCache, function(eGroup) {
-            if(!limit_groups.filter(function(i) { return (i == eGroup.limit_group()); })[0]) {
-                eGroup.isdeleted(true);
-                remove.push(eGroup);
-            }
-        }
-    );
-
-    function updateEntries() {
-        pcrud.update(update, {
-            oncomplete : function () {
-                progressDialog.hide();
-            }
-        });
-    }
-
-    function removeEntries() {
-        pcrud.eliminate(remove, {
-            oncomplete : function () {
-                if(update.length) {
-                    updateEntries();
-                } else {
-                    progressDialog.hide();
-                }
-            }
-        });
-    }
-
-    function addEntries() {
-        pcrud.create(add, {
-            oncomplete : function () {
-                if(remove.length) {
-                    removeEntries();
-                } else if (update.length) {
-                    updateEntries();
-                } else {
-                    progressDialog.hide();
-                }
-            }
-        });
-    }
-
-    if(add.length)
-        addEntries();
-    else if (remove.length)
-        removeEntries();
-    else if (update.length)
-        updateEntries();
-    else
-        progressDialog.hide();
-}
-
-openils.Util.addOnLoad(load);
+require([
+       "dijit/layout/ContentPane",
+       "dijit/form/Button",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/PermaCrud",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_layout_ContentPane,
+       dijit_form_Button,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_PermaCrud,
+       openils_widget_ProgressDialog){
+       
+       var linkedEditor = null;
+       var circModEntryCache = [];
+       var limitGroupEntryCache = [];
+       var circModCache = {};
+       var limitGroupCache = {};
+       var curLinkedEditor;
+       
+       function load(){
+           clsGrid.loadAll({order_by:{ccls:'name'}});
+           clsGrid.onEditPane = buildEditPaneAdditions;
+           clsGrid.onPostUpdate = updateLinked;
+           clsGrid.onPostCreate = updateLinked;
+           linkedEditor = dojo.byId('linked-editor').parentNode.removeChild(dojo.byId('linked-editor'));
+       
+           // Cache circ mod/limit group info for later display
+           var pcrud = new openils_PermaCrud();
+           var temp = pcrud.retrieveAll('ccm');
+           dojo.forEach(temp, function(g) { circModCache[g.code()] = g; } );
+           temp = pcrud.retrieveAll('cclg');
+           dojo.forEach(temp, function(g) { limitGroupCache[g.id()] = g; } );
+       }
+       
+       function byName(name, ctxt) {
+           return dojo.query('[name=' + name + ']', ctxt)[0];
+       }
+       
+       function buildEditPaneAdditions(editPane) {
+           circModEntryCache = [];
+           limitGroupEntryCache = [];
+           var tr = document.createElement('tr');
+           var td = document.createElement('td');
+           td.setAttribute('colspan','2');
+           // Explanation....
+           // editPane.domNode.lastChild = Table
+           // .lastChild = Table Body
+           // .lastChild = Table Row containing Action Buttons
+           editPane.domNode.lastChild.lastChild.insertBefore(tr, editPane.domNode.lastChild.lastChild.lastChild);
+           tr.appendChild(td);
+           curLinkedEditor = linkedEditor.cloneNode(true);
+           td.appendChild(curLinkedEditor);
+           var circModTmpl = byName('circ-mod-entry-tbody', curLinkedEditor).removeChild(byName('circ-mod-entry-row', curLinkedEditor));
+           var limitGroupTmpl = byName('limit-group-entry-tbody', curLinkedEditor).removeChild(byName('limit-group-entry-row', curLinkedEditor));
+       
+           var cm_selector = new openils_widget_AutoFieldWidget({
+               fmClass : 'cclscmm',
+               fmField : 'circ_mod',
+               parentNode : byName('circ-mod-selector', curLinkedEditor)
+           });
+           cm_selector.build();
+       
+           var lg_selector = new openils_widget_AutoFieldWidget({
+               fmClass : 'cclsgm',
+               fmField : 'limit_group',
+               parentNode : byName('limit-group-selector', curLinkedEditor)
+           });
+           lg_selector.build();
+       
+           function addMod(code) {
+               var row = circModTmpl.cloneNode(true);
+               row.setAttribute('code', code);
+               byName('circ-mod', row).innerHTML = code + ' : ' + circModCache[code].name();
+               byName('remove-circ-mod', row).onclick = function() {
+                   byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
+               }
+               byName('circ-mod-entry-tbody', editPane.domNode).appendChild(row);
+           }
+       
+           function addGroup(group) {
+               var row = limitGroupTmpl.cloneNode(true);
+               row.setAttribute('limit_group', group);
+               byName('limit-group', row).innerHTML = limitGroupCache[group].name();
+               byName('remove-limit-group', row).onclick = function() {
+                   byName('limit-group-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
+               }
+               byName('limit-group-entry-tbody', editPane.domNode).appendChild(row);
+           }
+       
+           byName('add-circ-mod', editPane.domNode).onclick = function() {
+               addMod(cm_selector.widget.attr('value'));
+           }
+       
+           byName('add-limit-group', editPane.domNode).onclick = function() {
+               addGroup(lg_selector.widget.attr('value'));
+           }
+       
+           // On edit we need to load existing entries.
+           // On create, not so much.
+           if(!editPane.fmObject) return; 
+           var limitSet = editPane.fmObject.id();
+       
+           if(editPane.mode == 'update') {
+               var pcrud = new openils_PermaCrud();
+               circModEntryCache = pcrud.search('cclscmm', {limit_set: limitSet});
+               limitGroupEntryCache = pcrud.search('cclsgm', {limit_set: limitSet});
+               dojo.forEach(circModEntryCache, function(g) { addCircMod(circModTmpl, g); } );
+               dojo.forEach(limitGroupEntryCache, function(g) { addLimitGroup(limitGroupTmpl, g); } );
+           } 
+       }
+       
+       function addCircMod(tmpl, circ_mod_entry) {
+           var row = tmpl.cloneNode(true);
+           var code = circ_mod_entry.circ_mod();
+           row.setAttribute('code', code);
+           byName('circ-mod', row).innerHTML = code + ' : ' + circModCache[code].name();
+           byName('remove-circ-mod', row).onclick = function() {
+               byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
+           }
+           byName('circ-mod-entry-tbody', clsGrid.editPane.domNode).appendChild(row);
+       }
+       
+       function addLimitGroup(tmpl, limit_group_entry) {
+           var row = tmpl.cloneNode(true);
+           var group = limit_group_entry.limit_group();
+           row.setAttribute('limit_group', group);
+           byName('limit-group', row).innerHTML = limitGroupCache[group].name();
+           if(limit_group_entry.check_only() == 't') {
+               byName('limit-group-check-only', row).setAttribute('checked', 'true');
+           }
+           byName('remove-limit-group', row).onclick = function() {
+               byName('limit-group-entry-tbody', clsGrid.editPane.domNode).removeChild(row);
+           }
+           byName('limit-group-entry-tbody', clsGrid.editPane.domNode).appendChild(row);
+       }
+       
+       function updateLinked(fmObject, rowindex) {
+           var id = null;
+           if(rowindex != undefined && this.editPane && this.editPane.fmObject) {
+               // Edit, grab existing ID
+               id = this.editPane.fmObject.id();
+           } else if(fmObject.id) {
+               // Create, grab new ID
+               id = fmObject.id();
+           }
+           // If we don't have an ID, drop out.
+           if(id == null) return;
+           var pcrud = new openils_PermaCrud();
+           progressDialog.show(true);
+       
+           var add = [];
+           var remove = [];
+           var update = [];
+       
+           // First up, circ mods.
+           var circ_mods = [];
+           dojo.query('[name=circ-mod-entry-row]', this.editPane.domNode).forEach(
+               function(row) {
+                   var mod = row.getAttribute('code');
+                   circ_mods.push(mod);
+                   if(!circModEntryCache.filter(function(i) { return (i.circ_mod() == mod); })[0]) {
+                       var entry = new fieldmapper.cclscmm();
+                       entry.isnew(true);
+                       entry.limit_set(id);
+                       entry.circ_mod(mod);
+                       add.push(entry);
+                   }
+               }
+           );
+           dojo.forEach(circModEntryCache, function(eMod) {
+                   if(!circ_mods.filter(function(i) { return (i == eMod.circ_mod()); })[0]) {
+                       eMod.isdeleted(true);
+                       remove.push(eMod);
+                   }
+               }
+           );
+       
+           // Next, limit groups
+           var limit_groups = [];
+           dojo.query('[name=limit-group-entry-row]', this.editPane.domNode).forEach(
+               function(row) {
+                   var group = row.getAttribute('limit_group');
+                   limit_groups.push(group);
+                   var cached = limitGroupEntryCache.filter(function(i) { return (i.limit_group() == group); })[0];
+                   if(!cached) {
+                       var entry = new fieldmapper.cclsgm();
+                       entry.isnew(true);
+                       entry.limit_set(id);
+                       entry.limit_group(group);
+                       entry.check_only(byName('limit-group-check-only', row).checked ? 't' : 'f');
+                       add.push(entry);
+                   } else {
+                       var check_only = byName('limit-group-check-only', row).checked;
+                       if(check_only != (cached.check_only() == 't')) {
+                           cached.check_only(check_only ? 't' : 'f');
+                           cached.ischanged(true);
+                           update.push(cached);
+                       }
+                   }
+               }
+           );
+           dojo.forEach(limitGroupEntryCache, function(eGroup) {
+                   if(!limit_groups.filter(function(i) { return (i == eGroup.limit_group()); })[0]) {
+                       eGroup.isdeleted(true);
+                       remove.push(eGroup);
+                   }
+               }
+           );
+       
+           function updateEntries() {
+               pcrud.update(update, {
+                   oncomplete : function () {
+                       progressDialog.hide();
+                   }
+               });
+           }
+       
+           function removeEntries() {
+               pcrud.eliminate(remove, {
+                   oncomplete : function () {
+                       if(update.length) {
+                           updateEntries();
+                       } else {
+                           progressDialog.hide();
+                       }
+                   }
+               });
+           }
+       
+           function addEntries() {
+               pcrud.create(add, {
+                   oncomplete : function () {
+                       if(remove.length) {
+                           removeEntries();
+                       } else if (update.length) {
+                           updateEntries();
+                       } else {
+                           progressDialog.hide();
+                       }
+                   }
+               });
+           }
+       
+           if(add.length)
+               addEntries();
+           else if (remove.length)
+               removeEntries();
+           else if (update.length)
+               updateEntries();
+           else
+               progressDialog.hide();
+       }
+       
+       openils.Util.addOnLoad(load);
+       
+       
 
+});
\ No newline at end of file
index 175a15d..12dbe07 100644 (file)
-dojo.require('dijit.layout.ContentPane');
-dojo.require('dijit.form.Button');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.ProgressDialog');
-
-var limitSetEditor = null;
-var limitSetEntryCache = [];
-var limitSetCache = {};
-
-function load(){
-    cmGrid.overrideWidgetArgs.grp = {hrbefore : true};
-    cmGrid.overrideWidgetArgs.is_renewal = {ternary : true};
-    cmGrid.overrideWidgetArgs.ref_flag = {ternary : true};
-    cmGrid.overrideWidgetArgs.juvenile_flag = {ternary : true};
-    cmGrid.overrideWidgetArgs.circulate = {inherits : true, hrbefore : true};
-    cmGrid.overrideWidgetArgs.duration_rule = {inherits : true};
-    cmGrid.overrideWidgetArgs.recurring_fine_rule = {inherits : true};
-    cmGrid.overrideWidgetArgs.max_fine_rule = {inherits : true};
-    cmGrid.overrideWidgetArgs.available_copy_hold_ratio = {inherits : true};
-    cmGrid.overrideWidgetArgs.total_copy_hold_ratio = {inherits : true};
-    cmGrid.overrideWidgetArgs.renewals = {inherits : true};
-    cmGrid.overrideWidgetArgs.grace_period = {inherits : true};
-    cmGrid.overrideWidgetArgs.hard_due_date = {inherits : true};
-    cmGrid.loadAll({order_by:{ccmm:'circ_modifier'}});
-    cmGrid.onEditPane = buildEditPaneAdditions;
-    cmGrid.onPostUpdate = updateLinked;
-    cmGrid.onPostCreate = updateLinked;
-    limitSetEditor = dojo.byId('limit-set-editor').parentNode.removeChild(dojo.byId('limit-set-editor'));
-
-    // Cache limit set info for later display
-    var pcrud = new openils.PermaCrud();
-    var temp = pcrud.retrieveAll('ccls');
-    dojo.forEach(temp, function(g) { limitSetCache[g.id()] = g; } );
-}
-
-function byName(name, ctxt) {
-    return dojo.query('[name=' + name + ']', ctxt)[0];
-}
-
-function buildEditPaneAdditions(editPane) {
-    limitSetEntryCache = [];
-    var tr = document.createElement('tr');
-    var td = document.createElement('td');
-    td.setAttribute('colspan','2');
-    // Explanation....
-    // editPane.domNode.lastChild = Table
-    // .lastChild = Table Body
-    // .lastChild = Table Row containing Action Buttons
-    editPane.domNode.lastChild.lastChild.insertBefore(tr, editPane.domNode.lastChild.lastChild.lastChild);
-    tr.appendChild(td);
-    curLimitSetEditor = limitSetEditor.cloneNode(true);
-    td.appendChild(curLimitSetEditor);
-    var limitSetTmpl = byName('limit-set-entry-tbody', curLimitSetEditor).removeChild(byName('limit-set-entry-row', curLimitSetEditor));
-
-    var selector = new openils.widget.AutoFieldWidget({
-        fmClass : 'ccmlsm',
-        fmField : 'limit_set',
-        parentNode : byName('limit-set-selector', curLimitSetEditor)
-    });
-    selector.build();
-
-    function addSet(lset) {
-        var row = limitSetTmpl.cloneNode(true);
-        row.setAttribute('limit_set', lset);
-        byName('limit-set', row).innerHTML = limitSetCache[lset].name();
-        byName('remove-limit-set', row).onclick = function() {
-            byName('limit-set-entry-tbody', cmGrid.editPane.domNode).removeChild(row);
-        }
-        byName('limit-set-active', row).setAttribute('checked', 'true');
-        byName('limit-set-entry-tbody', editPane.domNode).appendChild(row);
-    }
-
-    byName('add-limit-set', editPane.domNode).onclick = function() {
-        addSet(selector.widget.attr('value'));
-    }
-
-    // On edit we need to load existing entries.
-    // On create, not so much.
-    if(!editPane.fmObject) return; 
-    var matchpoint = editPane.fmObject.id();
-
-    if(editPane.mode == 'update') {
-        var pcrud = new openils.PermaCrud();
-        limitSetEntryCache = pcrud.search('ccmlsm', {matchpoint: editPane.fmObject.id()});
-        dojo.forEach(limitSetEntryCache, function(g) { addLimitSet(limitSetTmpl, g); } );
-    } 
-}
-
-function addLimitSet(tmpl, limit_set_entry) {
-    var row = tmpl.cloneNode(true);
-    var lset = limit_set_entry.limit_set();
-    row.setAttribute('limit_set', lset);
-    byName('limit-set', row).innerHTML = limitSetCache[lset].name();
-    if(limit_set_entry.active() == 't') {
-        byName('limit-set-active', row).setAttribute('checked', 'true');
-    }
-    if(limit_set_entry.fallthrough() == 't') {
-        byName('limit-set-fallthrough', row).setAttribute('checked', 'true');
-    }
-    byName('remove-limit-set', row).onclick = function() {
-        byName('limit-set-entry-tbody', cmGrid.editPane.domNode).removeChild(row);
-    }
-    byName('limit-set-entry-tbody', cmGrid.editPane.domNode).appendChild(row);
-}
-
-function format_hard_due_date(name, id) {
-    var item=this.grid.getItem(id);
-    if(!item) return name;
-    switch (this.grid.store.getValue(this.grid.getItem(id), 'hard_due_date')) {
-        case null :
-        case undefined :
-        case 'unset' :
-            return name;
-        default:
-            return "<a href='" + oilsBasePath +
-                "/conify/global/config/hard_due_date?name=" +
-                encodeURIComponent(name) + "'>" + name + "</a>";
-    }
-}
-
-function updateLinked(fmObject, rowindex) {
-    var id = null;
-    if(rowindex != undefined && this.editPane && this.editPane.fmObject) {
-        // Edit, grab existing ID
-        id = this.editPane.fmObject.id();
-    } else if(fmObject.id) {
-        // Create, grab new ID
-        id = fmObject.id();
-    }
-    // If we don't have an ID, drop out.
-    if(id == null) return;
-    var pcrud = new openils.PermaCrud();
-    progressDialog.show(true);
-
-    var add = [];
-    var remove = [];
-    var update = [];
-
-    var limit_sets = [];
-    dojo.query('[name=limit-set-entry-row]', this.editPane.domNode).forEach(
-        function(row) {
-            var lset = row.getAttribute('limit_set');
-            limit_sets.push(lset);
-            var cached = limitSetEntryCache.filter(function(i) { return (i.limit_set() == lset); })[0];
-            if(!cached) {
-                var entry = new fieldmapper.ccmlsm();
-                entry.isnew(true);
-                entry.matchpoint(id);
-                entry.limit_set(lset);
-                entry.active(byName('limit-set-active', row).checked ? 't' : 'f');
-                entry.fallthrough(byName('limit-set-fallthrough', row).checked ? 't' : 'f');
-                add.push(entry);
-            } else {
-                var active = byName('limit-set-active', row).checked;
-                var fallthrough = byName('limit-set-fallthrough', row).checked;
-                if((active != (cached.active() == 't')) || (fallthrough != (cached.fallthrough() == 't'))) {
-                    cached.active(active ? 't' : 'f');
-                    cached.fallthrough(fallthrough ? 't' : 'f');
-                    cached.ischanged(true);
-                    update.push(cached);
-                }
-            }
-        }
-    );
-    dojo.forEach(limitSetEntryCache, function(eSet) {
-            if(!limit_sets.filter(function(i) { return (i == eSet.limit_set()); })[0]) {
-                eSet.isdeleted(true);
-                remove.push(eSet);
-            }
-        }
-    );
-
-    function updateEntries() {
-        pcrud.update(update, {
-            oncomplete : function () {
-                progressDialog.hide();
-            }
-        });
-    }
-
-    function removeEntries() {
-        pcrud.eliminate(remove, {
-            oncomplete : function () {
-                if(update.length) {
-                    updateEntries();
-                } else {
-                    progressDialog.hide();
-                }
-            }
-        });
-    }
-
-    function addEntries() {
-        pcrud.create(add, {
-            oncomplete : function () {
-                if(remove.length) {
-                    removeEntries();
-                } else if (update.length) {
-                    updateEntries();
-                } else {
-                    progressDialog.hide();
-                }
-            }
-        });
-    }
-
-    if(add.length)
-        addEntries();
-    else if (remove.length)
-        removeEntries();
-    else if (update.length)
-        updateEntries();
-    else
-        progressDialog.hide();
-}
-
-openils.Util.addOnLoad(load);
-
+require([
+       "dijit/layout/ContentPane",
+       "dijit/form/Button",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/PermaCrud",
+       "openils/widget/ProgressDialog"
+       ],
+function(dijit_layout_ContentPane,
+       dijit_form_Button,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_PermaCrud,
+       openils_widget_ProgressDialog){
+       
+       var limitSetEditor = null;
+       var limitSetEntryCache = [];
+       var limitSetCache = {};
+       
+       function load(){
+           cmGrid.overrideWidgetArgs.grp = {hrbefore : true};
+           cmGrid.overrideWidgetArgs.is_renewal = {ternary : true};
+           cmGrid.overrideWidgetArgs.ref_flag = {ternary : true};
+           cmGrid.overrideWidgetArgs.juvenile_flag = {ternary : true};
+           cmGrid.overrideWidgetArgs.circulate = {inherits : true, hrbefore : true};
+           cmGrid.overrideWidgetArgs.duration_rule = {inherits : true};
+           cmGrid.overrideWidgetArgs.recurring_fine_rule = {inherits : true};
+           cmGrid.overrideWidgetArgs.max_fine_rule = {inherits : true};
+           cmGrid.overrideWidgetArgs.available_copy_hold_ratio = {inherits : true};
+           cmGrid.overrideWidgetArgs.total_copy_hold_ratio = {inherits : true};
+           cmGrid.overrideWidgetArgs.renewals = {inherits : true};
+           cmGrid.overrideWidgetArgs.grace_period = {inherits : true};
+           cmGrid.overrideWidgetArgs.hard_due_date = {inherits : true};
+           cmGrid.loadAll({order_by:{ccmm:'circ_modifier'}});
+           cmGrid.onEditPane = buildEditPaneAdditions;
+           cmGrid.onPostUpdate = updateLinked;
+           cmGrid.onPostCreate = updateLinked;
+           limitSetEditor = dojo.byId('limit-set-editor').parentNode.removeChild(dojo.byId('limit-set-editor'));
+       
+           // Cache limit set info for later display
+           var pcrud = new openils_PermaCrud();
+           var temp = pcrud.retrieveAll('ccls');
+           dojo.forEach(temp, function(g) { limitSetCache[g.id()] = g; } );
+       }
+       
+       function byName(name, ctxt) {
+           return dojo.query('[name=' + name + ']', ctxt)[0];
+       }
+       
+       function buildEditPaneAdditions(editPane) {
+           limitSetEntryCache = [];
+           var tr = document.createElement('tr');
+           var td = document.createElement('td');
+           td.setAttribute('colspan','2');
+           // Explanation....
+           // editPane.domNode.lastChild = Table
+           // .lastChild = Table Body
+           // .lastChild = Table Row containing Action Buttons
+           editPane.domNode.lastChild.lastChild.insertBefore(tr, editPane.domNode.lastChild.lastChild.lastChild);
+           tr.appendChild(td);
+           curLimitSetEditor = limitSetEditor.cloneNode(true);
+           td.appendChild(curLimitSetEditor);
+           var limitSetTmpl = byName('limit-set-entry-tbody', curLimitSetEditor).removeChild(byName('limit-set-entry-row', curLimitSetEditor));
+       
+           var selector = new openils_widget_AutoFieldWidget({
+               fmClass : 'ccmlsm',
+               fmField : 'limit_set',
+               parentNode : byName('limit-set-selector', curLimitSetEditor)
+           });
+           selector.build();
+       
+           function addSet(lset) {
+               var row = limitSetTmpl.cloneNode(true);
+               row.setAttribute('limit_set', lset);
+               byName('limit-set', row).innerHTML = limitSetCache[lset].name();
+               byName('remove-limit-set', row).onclick = function() {
+                   byName('limit-set-entry-tbody', cmGrid.editPane.domNode).removeChild(row);
+               }
+               byName('limit-set-active', row).setAttribute('checked', 'true');
+               byName('limit-set-entry-tbody', editPane.domNode).appendChild(row);
+           }
+       
+           byName('add-limit-set', editPane.domNode).onclick = function() {
+               addSet(selector.widget.attr('value'));
+           }
+       
+           // On edit we need to load existing entries.
+           // On create, not so much.
+           if(!editPane.fmObject) return; 
+           var matchpoint = editPane.fmObject.id();
+       
+           if(editPane.mode == 'update') {
+               var pcrud = new openils_PermaCrud();
+               limitSetEntryCache = pcrud.search('ccmlsm', {matchpoint: editPane.fmObject.id()});
+               dojo.forEach(limitSetEntryCache, function(g) { addLimitSet(limitSetTmpl, g); } );
+           } 
+       }
+       
+       function addLimitSet(tmpl, limit_set_entry) {
+           var row = tmpl.cloneNode(true);
+           var lset = limit_set_entry.limit_set();
+           row.setAttribute('limit_set', lset);
+           byName('limit-set', row).innerHTML = limitSetCache[lset].name();
+           if(limit_set_entry.active() == 't') {
+               byName('limit-set-active', row).setAttribute('checked', 'true');
+           }
+           if(limit_set_entry.fallthrough() == 't') {
+               byName('limit-set-fallthrough', row).setAttribute('checked', 'true');
+           }
+           byName('remove-limit-set', row).onclick = function() {
+               byName('limit-set-entry-tbody', cmGrid.editPane.domNode).removeChild(row);
+           }
+           byName('limit-set-entry-tbody', cmGrid.editPane.domNode).appendChild(row);
+       }
+       
+       function format_hard_due_date(name, id) {
+           var item=this.grid.getItem(id);
+           if(!item) return name;
+           switch (this.grid.store.getValue(this.grid.getItem(id), 'hard_due_date')) {
+               case null :
+               case undefined :
+               case 'unset' :
+                   return name;
+               default:
+                   return "<a href='" + oilsBasePath +
+                       "/conify/global/config/hard_due_date?name=" +
+                       encodeURIComponent(name) + "'>" + name + "</a>";
+           }
+       }
+       
+       function updateLinked(fmObject, rowindex) {
+           var id = null;
+           if(rowindex != undefined && this.editPane && this.editPane.fmObject) {
+               // Edit, grab existing ID
+               id = this.editPane.fmObject.id();
+           } else if(fmObject.id) {
+               // Create, grab new ID
+               id = fmObject.id();
+           }
+           // If we don't have an ID, drop out.
+           if(id == null) return;
+           var pcrud = new openils_PermaCrud();
+           progressDialog.show(true);
+       
+           var add = [];
+           var remove = [];
+           var update = [];
+       
+           var limit_sets = [];
+           dojo.query('[name=limit-set-entry-row]', this.editPane.domNode).forEach(
+               function(row) {
+                   var lset = row.getAttribute('limit_set');
+                   limit_sets.push(lset);
+                   var cached = limitSetEntryCache.filter(function(i) { return (i.limit_set() == lset); })[0];
+                   if(!cached) {
+                       var entry = new fieldmapper.ccmlsm();
+                       entry.isnew(true);
+                       entry.matchpoint(id);
+                       entry.limit_set(lset);
+                       entry.active(byName('limit-set-active', row).checked ? 't' : 'f');
+                       entry.fallthrough(byName('limit-set-fallthrough', row).checked ? 't' : 'f');
+                       add.push(entry);
+                   } else {
+                       var active = byName('limit-set-active', row).checked;
+                       var fallthrough = byName('limit-set-fallthrough', row).checked;
+                       if((active != (cached.active() == 't')) || (fallthrough != (cached.fallthrough() == 't'))) {
+                           cached.active(active ? 't' : 'f');
+                           cached.fallthrough(fallthrough ? 't' : 'f');
+                           cached.ischanged(true);
+                           update.push(cached);
+                       }
+                   }
+               }
+           );
+           dojo.forEach(limitSetEntryCache, function(eSet) {
+                   if(!limit_sets.filter(function(i) { return (i == eSet.limit_set()); })[0]) {
+                       eSet.isdeleted(true);
+                       remove.push(eSet);
+                   }
+               }
+           );
+       
+           function updateEntries() {
+               pcrud.update(update, {
+                   oncomplete : function () {
+                       progressDialog.hide();
+                   }
+               });
+           }
+       
+           function removeEntries() {
+               pcrud.eliminate(remove, {
+                   oncomplete : function () {
+                       if(update.length) {
+                           updateEntries();
+                       } else {
+                           progressDialog.hide();
+                       }
+                   }
+               });
+           }
+       
+           function addEntries() {
+               pcrud.create(add, {
+                   oncomplete : function () {
+                       if(remove.length) {
+                           removeEntries();
+                       } else if (update.length) {
+                           updateEntries();
+                       } else {
+                           progressDialog.hide();
+                       }
+                   }
+               });
+           }
+       
+           if(add.length)
+               addEntries();
+           else if (remove.length)
+               removeEntries();
+           else if (update.length)
+               updateEntries();
+           else
+               progressDialog.hide();
+       }
+       
+       openils.Util.addOnLoad(load);
+       
+       
+
+});
\ No newline at end of file
index dbfc80e..7780c59 100644 (file)
@@ -1,50 +1,60 @@
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('fieldmapper.IDL');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.AutoGrid');
-
-function updateFieldSelector() {
-    var cls = this.attr('value');
-    if(!cls) return;
-    var flist = fieldmapper.IDL.fmclasses[cls];
-    var fields = [];
-    for(var f in flist.fields) {
-        var field = flist.fields[f];
-        if(field.virtual) continue;
-        fields.push({name:field.label, value:field.name});
-    }
-    fdocGrid.overrideEditWidgets.field.store = new dojo.data.ItemFileReadStore(
-        {data:{identifier:'value', label:'name', items:fields}});
-}
-
-function load() {
-    var slist = fieldmapper.IDL.fmclasses;
-    var dlist = [];
-
-    fdocGrid.overrideEditWidgets.field = editFieldSelector;
-    fdocGrid.overrideEditWidgets.fm_class = editClassSelector;
-    dojo.connect(fdocGrid.overrideEditWidgets.fm_class, 'onChange', updateFieldSelector);
-
-    for(var f in slist) {
-        if(slist[f].label != slist[f].name) // only show tables that have an actual label
-            dlist.push({value:slist[f].name, name:slist[f].label});
-    }
-    dlist = dlist.sort(function(a, b){return (a.name < b.name) ? -1 : 1;});
-
-    fmClassSelector.store = 
-        fdocGrid.overrideEditWidgets.fm_class.store = 
-            new dojo.data.ItemFileReadStore({data:{identifier:'value', label:'name', items:dlist}});
-
-    fmClassSelector.startup();
-    dojo.connect(fmClassSelector, 'onChange',
-        function() {
-            fdocGrid.resetStore();
-            fdocGrid.loadAll({order_by:{fdoc : 'field'}}, {fm_class: this.attr('value')});
-        }
-    );
-}
-
-
-openils.Util.addOnLoad(load);
-
+require([
+       "dijit/form/FilteringSelect",
+       "dojo/data/ItemFileReadStore",
+       "fieldmapper/IDL",
+       "openils/PermaCrud",
+       "openils/widget/AutoGrid"
+       ],
+function(dijit_form_FilteringSelect,
+       dojo_data_ItemFileReadStore,
+       fieldmapper_IDL,
+       openils_PermaCrud,
+       openils_widget_AutoGrid){
+       
+       function updateFieldSelector() {
+           var cls = this.attr('value');
+           if(!cls) return;
+           var flist = fieldmapper_IDL.fmclasses[cls];
+           var fields = [];
+           for(var f in flist.fields) {
+               var field = flist.fields[f];
+               if(field.virtual) continue;
+               fields.push({name:field.label, value:field.name});
+           }
+           fdocGrid.overrideEditWidgets.field.store = new dojo_data_ItemFileReadStore(
+               {data:{identifier:'value', label:'name', items:fields}});
+       }
+       
+       function load() {
+           var slist = fieldmapper_IDL.fmclasses;
+           var dlist = [];
+       
+           fdocGrid.overrideEditWidgets.field = editFieldSelector;
+           fdocGrid.overrideEditWidgets.fm_class = editClassSelector;
+           dojo.connect(fdocGrid.overrideEditWidgets.fm_class, 'onChange', updateFieldSelector);
+       
+           for(var f in slist) {
+               if(slist[f].label != slist[f].name) // only show tables that have an actual label
+                   dlist.push({value:slist[f].name, name:slist[f].label});
+           }
+           dlist = dlist.sort(function(a, b){return (a.name < b.name) ? -1 : 1;});
+       
+           fmClassSelector.store = 
+               fdocGrid.overrideEditWidgets.fm_class.store = 
+                   new dojo_data_ItemFileReadStore({data:{identifier:'value', label:'name', items:dlist}});
+       
+           fmClassSelector.startup();
+           dojo.connect(fmClassSelector, 'onChange',
+               function() {
+                   fdocGrid.resetStore();
+                   fdocGrid.loadAll({order_by:{fdoc : 'field'}}, {fm_class: this.attr('value')});
+               }
+           );
+       }
+       
+       
+       openils.Util.addOnLoad(load);
+       
+       
+
+});
\ No newline at end of file
index 80407e5..35b80ff 100644 (file)
@@ -1,46 +1,57 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dijit.Dialog');
-dojo.require('openils.PermaCrud');
-
-var thingList;
-
-function thingInit() {
-
-    thingGrid.disableSelectorForRow = function(rowIdx) {
-        var item = thingGrid.getItem(rowIdx);
-        return (thingGrid.store.getValue(item, 'id') < 0);
-    }
-
-    buildGrid();
-}
-
-function buildGrid() {
-
-    fieldmapper.standardRequest(
-        ['open-ils.pcrud', 'open-ils.pcrud.search.csc.atomic'],
-        {   async: true,
-            params: [
-                openils.User.authtoken,
-                {"id":{"!=":null}},
-                {"order_by":{"csc":"name"}}
-            ],
-            oncomplete: function(r) {
-                if(thingList = openils.Util.readResponse(r)) {
-                    thingList = openils.Util.objectSort(thingList,'name');
-                    dojo.forEach(thingList,
-                                 function(e) {
-                                     thingGrid.store.newItem(csc.toStoreItem(e));
-                                 }
-                                );
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(thingInit);
-
-
+require([
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "dojox/grid/cells/dijit",
+       "dojo/data/ItemFileWriteStore",
+       "dijit/Dialog",
+       "openils/PermaCrud"
+       ],
+function(dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       dojox_grid_cells_dijit,
+       dojo_data_ItemFileWriteStore,
+       dijit_Dialog,
+       openils_PermaCrud){
+       
+       var thingList;
+       
+       function thingInit() {
+       
+           thingGrid.disableSelectorForRow = function(rowIdx) {
+               var item = thingGrid.getItem(rowIdx);
+               return (thingGrid.store.getValue(item, 'id') < 0);
+           }
+       
+           buildGrid();
+       }
+       
+       function buildGrid() {
+       
+           fieldmapper.standardRequest(
+               ['open-ils.pcrud', 'open-ils.pcrud.search.csc.atomic'],
+               {   async: true,
+                   params: [
+                       openils.User.authtoken,
+                       {"id":{"!=":null}},
+                       {"order_by":{"csc":"name"}}
+                   ],
+                   oncomplete: function(r) {
+                       if(thingList = openils.Util.readResponse(r)) {
+                           thingList = openils.Util.objectSort(thingList,'name');
+                           dojo.forEach(thingList,
+                                        function(e) {
+                                            thingGrid.store.newItem(csc.toStoreItem(e));
+                                        }
+                                       );
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(thingInit);
+       
+       
+       
+
+});
\ No newline at end of file
index b560da7..4348bed 100644 (file)
@@ -1,28 +1,39 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('dojox.form.CheckedMultiSelect');
-dojo.require('dijit.form.TextBox');
-dojo.require('dojox.grid.cells.dijit');
-dojo.require('openils.widget.AutoGrid');
-
- var spCache = {};
-
-function spBuildGrid() {
-    spGrid.disableWidgetTest = function(field, obj) {
-        if(obj && obj.id() < 100 && field == 'name')
-            return true;
-        return false;
-    }
-    spGrid.loadAll({order_by:{csp : 'name'}});
-}
-
-function formatId(inDatum) {
-    if(inDatum < 100){
-        return "<span style='color:red;'>"+ inDatum +"</span>";
-    }
-    return inDatum;
-}
-
-openils.Util.addOnLoad(spBuildGrid);
-
+require([
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileWriteStore",
+       "dojox/form/CheckedMultiSelect",
+       "dijit/form/TextBox",
+       "dojox/grid/cells/dijit",
+       "openils/widget/AutoGrid"
+       ],
+function(dojox_grid_DataGrid,
+       dojo_data_ItemFileWriteStore,
+       dojox_form_CheckedMultiSelect,
+       dijit_form_TextBox,
+       dojox_grid_cells_dijit,
+       openils_widget_AutoGrid){
+       
+        var spCache = {};
+       
+       function spBuildGrid() {
+           spGrid.disableWidgetTest = function(field, obj) {
+               if(obj && obj.id() < 100 && field == 'name')
+                   return true;
+               return false;
+           }
+           spGrid.loadAll({order_by:{csp : 'name'}});
+       }
+       
+       function formatId(inDatum) {
+           if(inDatum < 100){
+               return "<span style='color:red;'>"+ inDatum +"</span>";
+           }
+           return inDatum;
+       }
+       
+       openils.Util.addOnLoad(spBuildGrid);
+       
+       
+       
 
+});
\ No newline at end of file
index ff2de10..e623a42 100644 (file)
@@ -1,16 +1,28 @@
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileReadStore');
-dojo.require('dijit.form.NumberTextBox');
-dojo.require('dijit.form.CheckBox');
-dojo.require('fieldmapper.OrgUtils');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.widget.AutoGrid');
-var zsList;
-
-function buildZSGrid() {
-    zsGrid.loadAll({order_by:{czs : 'name'}});
-}
-
-openils.Util.addOnLoad(buildZSGrid);
-
+require([
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileReadStore",
+       "dijit/form/NumberTextBox",
+       "dijit/form/CheckBox",
+       "fieldmapper/OrgUtils",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/AutoGrid"
+       ],
+function(dojox_grid_DataGrid,
+       dojo_data_ItemFileReadStore,
+       dijit_form_NumberTextBox,
+       dijit_form_CheckBox,
+       fieldmapper_OrgUtils,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_AutoGrid){
+       var zsList;
+       
+       function buildZSGrid() {
+           zsGrid.loadAll({order_by:{czs : 'name'}});
+       }
+       
+       openils.Util.addOnLoad(buildZSGrid);
+       
+       
+       
 
+});
\ No newline at end of file
index e944c66..d4ed9de 100644 (file)
@@ -1,31 +1,40 @@
-dojo.require('dijit.form.FilteringSelect');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.widget.PermGrpFilteringSelect');
+require([
+       "dijit/form/FilteringSelect",
+       "openils/widget/AutoGrid",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/PermGrpFilteringSelect"
+       ],
+function(dijit_form_FilteringSelect,
+       openils_widget_AutoGrid,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_PermGrpFilteringSelect){
+       
+       
+       function buildGrid(org_id) {
+           var org_id = openils.User.user.ws_ou();
+           var list = fieldmapper.aou.findOrgUnit(org_id).orgNodeTrail().map( function (i) { 
+                   return i.id() } );       
+           
+            gptGrid.loadAll({order_by:{pgpt : 'grp'}},{org_unit:list});   
+       
+            new openils.User().buildPermOrgSelector('VIEW_GROUP_PENALTY_THRESHOLD', contextOrgSelector, null, function() {
+                    dojo.connect(contextOrgSelector, 'onChange', filterGrid);});   
+       }
+       
+       function filterGrid() {
+           gptGrid.resetStore();
+           var unit = contextOrgSelector.getValue();   
+           var list = fieldmapper.aou.findOrgUnit(unit).orgNodeTrail().map( function (i) { 
+                   return i.id() } );       
+       
+           if(unit) 
+               gptGrid.loadAll({order_by:{pgpt: 'grp'}}, {org_unit:list});
+           else
+               gptGrid.loadAll({order_by:{pgpt : 'grp'}});
+           
+       }
+       
+       openils.Util.addOnLoad(buildGrid);
+       
 
-
-function buildGrid(org_id) {
-    var org_id = openils.User.user.ws_ou();
-    var list = fieldmapper.aou.findOrgUnit(org_id).orgNodeTrail().map( function (i) { 
-            return i.id() } );       
-    
-     gptGrid.loadAll({order_by:{pgpt : 'grp'}},{org_unit:list});   
-
-     new openils.User().buildPermOrgSelector('VIEW_GROUP_PENALTY_THRESHOLD', contextOrgSelector, null, function() {
-             dojo.connect(contextOrgSelector, 'onChange', filterGrid);});   
-}
-
-function filterGrid() {
-    gptGrid.resetStore();
-    var unit = contextOrgSelector.getValue();   
-    var list = fieldmapper.aou.findOrgUnit(unit).orgNodeTrail().map( function (i) { 
-            return i.id() } );       
-
-    if(unit) 
-        gptGrid.loadAll({order_by:{pgpt: 'grp'}}, {org_unit:list});
-    else
-        gptGrid.loadAll({order_by:{pgpt : 'grp'}});
-    
-}
-
-openils.Util.addOnLoad(buildGrid);
+});
\ No newline at end of file
index 1de99e4..6de6f25 100644 (file)
-dojo.require("dijit.Tree");
-dojo.require("dijit.form.Button");
-dojo.require("dojo.data.ItemFileWriteStore");
-dojo.require("dojo.dnd.Source");
-dojo.require("openils.vandelay.TreeDndSource");
-dojo.require("openils.vandelay.TreeStoreModel");
-dojo.require("openils.CGI");
-dojo.require("openils.User");
-dojo.require("openils.Util");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.widget.ProgressDialog");
-dojo.require("openils.widget.AutoGrid");
-
-var localeStrings, node_editor, qnode_editor, _crads, CGI, tree, match_set;
-
-var NodeEditorAbstract = {
-    "_svf_select_template": null,
-    "_simple_value_getter": function(control) {
-        if (typeof control.selectedIndex != "undefined")
-            return control.options[control.selectedIndex].value;
-        else if (dojo.attr(control, "type") == "checkbox")
-            return control.checked;
-        else
-            return control.value;
-    },
-    "is_sensible": function(thing) {
-        var need_one = 0;
-        this.foi.forEach(function(field) { if (thing[field]()) need_one++; });
-
-        if (need_one != 1) {
-            alert(localeStrings.POINT_NEEDS_ONE);
-            return false;
-        }
-
-        if (thing.tag()) {
-            if (
-                !thing.tag().match(/^\d{3}$/) ||
-                thing.subfield().length != 1 ||
-                !thing.subfield().match(/\S/) ||
-                thing.subfield().charCodeAt(0) < 32
-            ) {
-                alert(localeStrings.FAULTY_MARC);
-                return false;
-            }
-        }
-
-        return true;
-    },
-    "_add_consistent_controls": function(tgt) {
-        if (!this._consistent_controls) {
-            var trs = dojo.query(this._consistent_controls_query);
-            this._consistent_controls = [];
-            for (var i = 0; i < trs.length; i++)
-                this._consistent_controls[i] = dojo.clone(trs[i]);
-        }
-
-        this._consistent_controls.forEach(
-            function(node) { dojo.place(dojo.clone(node), tgt); }
-        );
-    },
-    "_factories_by_type": {
-        "svf": function() {
-            if (!self._svf_select_template) {
-                self._svf_select_template = dojo.create(
-                    "select", {"fmfield": "svf"}
-                );
-                for (var i=0; i<_crads.length; i++) {
-                    dojo.create(
-                        "option", {
-                            "value": _crads[i].name(),
-                            "innerHTML": _crads[i].label()
-                        }, self._svf_select_template
-                    );
-                }
-            }
-
-            var select = dojo.clone(self._svf_select_template);
-            dojo.attr(select, "id", "svf-select");
-            var label = dojo.create(
-                "label", {
-                    "for": "svf-select", "innerHTML": localeStrings.SVF + ":"
-                }
-            );
-
-            var tr = dojo.create("tr");
-            dojo.place(label, dojo.create("td", null, tr));
-            dojo.place(select, dojo.create("td", null, tr));
-
-            return [tr];
-        },
-        "tag": function() {
-            var rows = [dojo.create("tr"), dojo.create("tr")];
-            dojo.create(
-                "label", {
-                    "for": "tag-input", "innerHTML": "Tag:"
-                }, dojo.create("td", null, rows[0])
-            );
-            dojo.create(
-                "input", {
-                    "id": "tag-input",
-                    "type": "text",
-                    "size": 4,
-                    "maxlength": 3,
-                    "fmfield": "tag"
-                }, dojo.create("td", null, rows[0])
-            );
-            dojo.create(
-                "label", {
-                    "for": "subfield-input", "innerHTML": "Subfield: \u2021"
-                }, dojo.create("td", null, rows[1])
-            );
-            dojo.create(
-                "input", {
-                    "id": "subfield-input",
-                    "type": "text",
-                    "size": 2,
-                    "maxlength": 1,
-                    "fmfield": "subfield"
-                }, dojo.create("td", null, rows[1])
-            );
-            return rows;
-        },
-        "bool_op": function() {
-            var tr = dojo.create("tr");
-            dojo.create(
-                "label",
-                {"for": "operator-select", "innerHTML": "Operator:"},
-                dojo.create("td", null, tr)
-            );
-            var select = dojo.create(
-                "select", {"fmfield": "bool_op", "id": "operator-select"},
-                dojo.create("td", null, tr)
-            );
-            dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
-            dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
-
-            return [tr];
-        }
-    }
-};
-
-function apply_base_class(cls, basecls) {
-    openils.Util.objectProperties(basecls).forEach(
-        function(m) { cls[m] = basecls[m]; }
-    );
-}
-
-function QualityNodeEditor() {
-    var self = this;
-    this.foi = ["tag", "svf"]; /* Fields of Interest - starting points for UI */
-
-    this._init = function(qnode_editor_container) {
-        this._consistent_controls_query =
-            "[consistent-controls], [quality-controls]";
-        this.qnode_editor_container = dojo.byId(qnode_editor_container);
-        this.clear();
-    };
-
-    this.clear = function() {
-        dojo.create(
-            "em", {"innerHTML": localeStrings.WORKING_QM_HERE},
-            this.qnode_editor_container, "only"
-        );
-    };
-
-    this.build_vmsq = function() {
-        var metric = new vmsq();
-        metric.match_set(match_set.id());   /* using global */
-        var controls = dojo.query("[fmfield]", this.qnode_editor_container);
-        for (var i = 0; i < controls.length; i++) {
-            var field = dojo.attr(controls[i], "fmfield");
-            var value = this._simple_value_getter(controls[i]);
-            metric[field](value);
-        }
-
-        if (!this.is_sensible(metric)) return null;    /* will alert() */
-        else return metric;
-    };
-
-    this.add = function(type) {
-        this.clear();
-
-        /* these are the editing widgets */
-        var table = dojo.create("table", {"className": "node-editor"});
-
-        var nodes = this._factories_by_type[type]();
-        for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
-
-        this._add_consistent_controls(table);
-
-        var ok_cxl_td = dojo.create(
-            "td", {"colspan": 2, "align": "center", "className": "space-me"},
-            dojo.create("tr", null, table)
-        );
-
-        dojo.create(
-            "input", {
-                "type": "submit", "value": localeStrings.OK,
-                "onclick": function() {
-                    var metric = self.build_vmsq();
-                    if (metric) {
-                        self.clear();
-                        pcrud.create(
-                            metric, {
-                                /* borrowed from openils.widget.AutoGrid */
-                                "oncomplete": function(req, cudResults) {
-                                    var fmObject = cudResults[0];
-                                    if (vmsq_grid.onPostCreate)
-                                        vmsq_grid.onPostCreate(fmObject);
-                                    if (fmObject) {
-                                        vmsq_grid.store.newItem(
-                                            fmObject.toStoreItem()
-                                        );
-                                    }
-                                    setTimeout(function() {
-                                        try {
-                                            vmsq_grid.selection.select(vmsq_grid.rowCount-1);
-                                            vmsq_grid.views.views[0].getCellNode(vmsq_grid.rowCount-1, 1).focus();
-                                        } catch (E) {}
-                                    },200);
-                                }
-                            }
-                        );
-                    }
-                }
-            }, ok_cxl_td
-        );
-        dojo.create(
-            "input", {
-                "type": "reset", "value": localeStrings.CANCEL,
-                "onclick": function() { self.clear(); }
-            }, ok_cxl_td
-        );
-
-        dojo.place(table, this.qnode_editor_container, "only");
-
-        /* nice */
-        try { dojo.query("select, input", table)[0].focus(); }
-        catch(E) { console.log(String(E)); }
-
-    };
-
-    apply_base_class(self, NodeEditorAbstract);
-    this._init.apply(this, arguments);
-}
-
-function NodeEditor() {
-    var self = this;
-    this.foi = ["tag", "svf", "bool_op"]; /* Fields of Interest - starting points for UI */
-
-    this._init = function(dnd_source, node_editor_container) {
-        this._consistent_controls_query =
-            "[consistent-controls], [point-controls]";
-        this.dnd_source = dnd_source;
-        this.node_editor_container = dojo.byId(node_editor_container);
-    };
-
-    this.clear = function() {
-        this.dnd_source.selectAll().deleteSelectedNodes();
-        dojo.create(
-            "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
-            this.node_editor_container, "only"
-        );
-        this.dnd_source._ready = false;
-    };
-
-    this.build_vmsp = function() {
-        var match_point = new vmsp();
-        var controls = dojo.query("[fmfield]", this.node_editor_container);
-        for (var i = 0; i < controls.length; i++) {
-            var field = dojo.attr(controls[i], "fmfield");
-            var value = this._simple_value_getter(controls[i]);
-            match_point[field](value);
-        }
-
-        if (!this.is_sensible(match_point)) return null;    /* will alert() */
-        else return match_point;
-    };
-
-    this.update_draggable = function(draggable) {
-        var mp;
-
-        if (!(mp = this.build_vmsp())) return;  /* will alert() */
-
-        draggable.match_point = mp;
-        dojo.attr(draggable, "innerHTML", render_vmsp_label(mp));
-        this.dnd_source._ready = true;
-    };
-
-    this.add = function(type) {
-        this.clear();
-
-        /* a representation, not the editing widgets, but will also carry
-         * the fieldmapper object when dragged to the tree */
-        var draggable = dojo.create(
-            "li", {"innerHTML": localeStrings.DEFINE_MP}
-        );
-
-        /* these are the editing widgets */
-        var table = dojo.create("table", {"className": "node-editor"});
-
-        var nodes = this._factories_by_type[type]();
-        for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
-
-        if (type != "bool_op")
-            this._add_consistent_controls(table);
-
-        dojo.create(
-            "input", {
-                "type": "submit", "value": localeStrings.OK,
-                "onclick": function() { self.update_draggable(draggable); }
-            }, dojo.create(
-                "td", {"colspan": 2, "align": "center"},
-                dojo.create("tr", null, table)
-            )
-        );
-
-        dojo.place(table, this.node_editor_container, "only");
-
-        this.dnd_source.insertNodes(false, [draggable]);
-
-        /* nice */
-        try { dojo.query("select, input", table)[0].focus(); }
-        catch(E) { console.log(String(E)); }
-
-    };
-
-    apply_base_class(self, NodeEditorAbstract);
-
-    this._init.apply(this, arguments);
-}
-
-function find_crad_by_name(name) {
-    for (var i = 0; i < _crads.length; i++) {
-        if (_crads[i].name() == name)
-            return _crads[i];
-    }
-    return null;
-}
-
-function render_vmsp_label(point, minimal) {
-    /* "minimal" has these implications:
-     * for svf, only show the code, not the longer label.
-     * no quality display
-     */
-    if (point.bool_op()) {
-        return point.bool_op();
-    } else if (point.svf()) {
-        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
-            minimal ?  point.svf() :
-                (point.svf() + " / " + find_crad_by_name(point.svf()).label()) +
-                " | " + dojo.string.substitute(
-                    localeStrings.MATCH_SCORE, [point.quality()]
-                )
-        );
-    } else {
-        return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
-            point.tag() + " \u2021" + point.subfield() + (minimal ? "" : " | " +
-                dojo.string.substitute(
-                    localeStrings.MATCH_SCORE, [point.quality()]
-                )
-            );
-    }
-}
-
-function replace_mode(explicit) {
-    if (typeof explicit == "undefined")
-        tree.model.replace_mode ^= 1;
-    else
-        tree.model.replace_mode = explicit;
-
-    dojo.attr(
-        "replacer", "innerHTML",
-        localeStrings[
-            (tree.model.replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
-        ]
-    );
-    dojo[tree.model.replace_mode ? "addClass" : "removeClass"](
-        "replacer", "replace-mode"
-    );
-}
-
-function delete_selected_in_tree() {
-    /* relies on the fact that we only have one tree that would have
-     * registered a dnd controller. */
-    _tree_dnd_controllers[0].getSelectedItems().forEach(
-        function(item) {
-            if (item === tree.model.root)
-                alert(localeStrings.LEAVE_ROOT_ALONE);
-            else
-                tree.model.store.deleteItem(item);
-        }
-    );
-}
-
-function new_match_set_tree() {
-    var point = new vmsp();
-    point.bool_op("AND");
-    return [
-        {
-            "id": "root",
-            "children": [],
-            "name": render_vmsp_label(point),
-            "match_point": point
-        }
-    ];
-}
-
-/* dojoize_match_set_tree() takes an argument, "point", that is actually a
- * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
- * that into a syntactically flat array but preserving the hierarchy
- * semantically in the language used by dojo data stores, i.e.,
- *
- * [
- *  {'id': 'root', children:[{'_reference': '0'}, {'_reference': '1'}]},
- *  {'id': '0', children:[]},
- *  {'id': '1', children:[]}
- * ],
- *
- */
-function dojoize_match_set_tree(point, depth) {
-    var root = false;
-    if (!depth) {
-        if (!point) {
-            return new_match_set_tree();
-        }
-        depth = 0;
-        root = true;
-    }
-
-    var bathwater = point.children();
-    point.children([]);
-    var item = {
-        "id": (root ? "root" : point.id()),
-        "name": render_vmsp_label(point),
-        "match_point": point.clone(),
-        "children": []
-    };
-    point.children(bathwater);
-
-    var results = [item];
-
-    if (point.children()) {
-        for (var i = 0; i < point.children().length; i++) {
-            var child = point.children()[i];
-            item.children.push({"_reference": child.id()});
-            results = results.concat(
-                dojoize_match_set_tree(child, ++depth)
-            );
-        }
-    }
-
-    return results;
-}
-
-function render_vms_metadata(match_set) {
-    dojo.byId("vms-name").innerHTML = match_set.name();
-    dojo.byId("vms-owner").innerHTML =
-        aou.findOrgUnit(match_set.owner()).name();
-    dojo.byId("vms-mtype").innerHTML = match_set.mtype();
-}
-
-function redraw_expression_preview() {
-    tree.model.getRoot(
-        function(root) {
-            tree.model.get_simple_tree(
-                root, function(r) {
-                    dojo.attr(
-                        "expr-preview",
-                        "innerHTML",
-                        render_expression_preview(r)
-                    );
-                }
-            );
-        }
-    );
-}
-
-function render_expression_preview(r) {
-    if (r.children().length) {
-        return "(" + r.children().map(render_expression_preview).join(
-            " " + render_vmsp_label(r) + " "
-        ) + ")";
-    } else if (!r.bool_op()) {
-        return render_vmsp_label(r, true /* minimal */);
-    } else {
-        return "()";
-    }
-}
-
-function save_tree() {
-    progress_dialog.show(true);
-
-    tree.model.getRoot(
-        function(root) {
-            tree.model.get_simple_tree(
-                root, function(r) {
-                    fieldmapper.standardRequest(
-                        ["open-ils.vandelay",
-                            "open-ils.vandelay.match_set.update"], {
-                            "params": [
-                                openils.User.authtoken, match_set.id(), r
-                            ],
-                            "async": true,
-                            "oncomplete": function(r) {
-                                progress_dialog.hide();
-                                /* catch exceptions */
-                                r = openils.Util.readResponse(r);
-
-                                location.href = location.href;
-                            }
-                        }
-                    );
-                }
-            );
-        }
-    );
-}
-
-function init_vmsq_grid() {
-    vmsq_grid.loadAll(
-        {"order_by": {"vmsq": "quality"}},
-        {"match_set": match_set.id()}
-    );
-}
-
-function my_init() {
-    progress_dialog.show(true);
-
-    dojo.requireLocalization("openils.vandelay", "match_set");
-    localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
-
-    pcrud = new openils.PermaCrud();
-    CGI = new openils.CGI();
-
-    if (!CGI.param("match_set")) {
-        alert(localeStrings.NO_CAN_DO);
-        progress_dialog.hide();
-        return;
-    }
-
-    render_vms_metadata(
-        match_set = pcrud.retrieve("vms", CGI.param("match_set"))
-    );
-
-    /* No-one should have hundreds of these or anything, but theoretically
-     * this could be problematic with a big enough list of crad objects. */
-    _crads = pcrud.retrieveAll("crad", {"order_by": {"crad": "label"}});
-
-    var match_set_tree = fieldmapper.standardRequest(
-        ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
-        [openils.User.authtoken, CGI.param("match_set")]
-    );
-
-    var store = new dojo.data.ItemFileWriteStore({
-        "data": {
-            "identifier": "id",
-            "label": "name",
-            "items": dojoize_match_set_tree(match_set_tree)
-        }
-    });
-
-    var tree_model = new openils.vandelay.TreeStoreModel({
-        "store": store, "query": {"id": "root"}
-    });
-
-    var src = new dojo.dnd.Source("src-here");
-    tree = new dijit.Tree(
-        {
-            "model": tree_model,
-            "dndController": openils.vandelay.TreeDndSource,
-            "dragThreshold": 8,
-            "betweenThreshold": 5,
-            "persist": false
-        }, "tree-here"
-    );
-
-    node_editor = new NodeEditor(src, "node-editor-container");
-    qnode_editor = new QualityNodeEditor("qnode-editor-container");
-
-    replace_mode(0);
-
-    dojo.connect(
-        src, "onDndDrop", null,
-        function(source, nodes, copy, target) {
-            /* Because of the... interesting... characteristics of DnD
-             * design in dojo/dijit (at least as of 1.3), this callback will
-             * fire both for our working node dndSource and for the tree!
-             */
-            if (source == this)
-                node_editor.clear();  /* ... because otherwise this acts like a
-                                         copy operation no matter what the user
-                                         does, even though we really want a
-                                         "move." */
-        }
-    );
-
-    redraw_expression_preview();
-    node_editor.clear();
-
-    init_vmsq_grid();
-
-    progress_dialog.hide();
-}
-
-openils.Util.addOnLoad(my_init);
+require([
+       "dijit/Tree",
+       "dijit/form/Button",
+       "dojo/data/ItemFileWriteStore",
+       "dojo/dnd/Source",
+       "openils/vandelay/TreeDndSource",
+       "openils/vandelay/TreeStoreModel",
+       "openils/CGI",
+       "openils/User",
+       "openils/Util",
+       "openils/PermaCrud",
+       "openils/widget/ProgressDialog",
+       "openils/widget/AutoGrid"
+       ],
+function(dijit_Tree,
+       dijit_form_Button,
+       dojo_data_ItemFileWriteStore,
+       dojo_dnd_Source,
+       openils_vandelay_TreeDndSource,
+       openils_vandelay_TreeStoreModel,
+       openils_CGI,
+       openils_User,
+       openils_Util,
+       openils_PermaCrud,
+       openils_widget_ProgressDialog,
+       openils_widget_AutoGrid){
+       
+       var localeStrings, node_editor, qnode_editor, _crads, CGI, tree, match_set;
+       
+       var NodeEditorAbstract = {
+           "_svf_select_template": null,
+           "_simple_value_getter": function(control) {
+               if (typeof control.selectedIndex != "undefined")
+                   return control.options[control.selectedIndex].value;
+               else if (dojo.attr(control, "type") == "checkbox")
+                   return control.checked;
+               else
+                   return control.value;
+           },
+           "is_sensible": function(thing) {
+               var need_one = 0;
+               this.foi.forEach(function(field) { if (thing[field]()) need_one++; });
+       
+               if (need_one != 1) {
+                   alert(localeStrings.POINT_NEEDS_ONE);
+                   return false;
+               }
+       
+               if (thing.tag()) {
+                   if (
+                       !thing.tag().match(/^\d{3}$/) ||
+                       thing.subfield().length != 1 ||
+                       !thing.subfield().match(/\S/) ||
+                       thing.subfield().charCodeAt(0) < 32
+                   ) {
+                       alert(localeStrings.FAULTY_MARC);
+                       return false;
+                   }
+               }
+       
+               return true;
+           },
+           "_add_consistent_controls": function(tgt) {
+               if (!this._consistent_controls) {
+                   var trs = dojo.query(this._consistent_controls_query);
+                   this._consistent_controls = [];
+                   for (var i = 0; i < trs.length; i++)
+                       this._consistent_controls[i] = dojo.clone(trs[i]);
+               }
+       
+               this._consistent_controls.forEach(
+                   function(node) { dojo.place(dojo.clone(node), tgt); }
+               );
+           },
+           "_factories_by_type": {
+               "svf": function() {
+                   if (!self._svf_select_template) {
+                       self._svf_select_template = dojo.create(
+                           "select", {"fmfield": "svf"}
+                       );
+                       for (var i=0; i<_crads.length; i++) {
+                           dojo.create(
+                               "option", {
+                                   "value": _crads[i].name(),
+                                   "innerHTML": _crads[i].label()
+                               }, self._svf_select_template
+                           );
+                       }
+                   }
+       
+                   var select = dojo.clone(self._svf_select_template);
+                   dojo.attr(select, "id", "svf-select");
+                   var label = dojo.create(
+                       "label", {
+                           "for": "svf-select", "innerHTML": localeStrings.SVF + ":"
+                       }
+                   );
+       
+                   var tr = dojo.create("tr");
+                   dojo.place(label, dojo.create("td", null, tr));
+                   dojo.place(select, dojo.create("td", null, tr));
+       
+                   return [tr];
+               },
+               "tag": function() {
+                   var rows = [dojo.create("tr"), dojo.create("tr")];
+                   dojo.create(
+                       "label", {
+                           "for": "tag-input", "innerHTML": "Tag:"
+                       }, dojo.create("td", null, rows[0])
+                   );
+                   dojo.create(
+                       "input", {
+                           "id": "tag-input",
+                           "type": "text",
+                           "size": 4,
+                           "maxlength": 3,
+                           "fmfield": "tag"
+                       }, dojo.create("td", null, rows[0])
+                   );
+                   dojo.create(
+                       "label", {
+                           "for": "subfield-input", "innerHTML": "Subfield: \u2021"
+                       }, dojo.create("td", null, rows[1])
+                   );
+                   dojo.create(
+                       "input", {
+                           "id": "subfield-input",
+                           "type": "text",
+                           "size": 2,
+                           "maxlength": 1,
+                           "fmfield": "subfield"
+                       }, dojo.create("td", null, rows[1])
+                   );
+                   return rows;
+               },
+               "bool_op": function() {
+                   var tr = dojo.create("tr");
+                   dojo.create(
+                       "label",
+                       {"for": "operator-select", "innerHTML": "Operator:"},
+                       dojo.create("td", null, tr)
+                   );
+                   var select = dojo.create(
+                       "select", {"fmfield": "bool_op", "id": "operator-select"},
+                       dojo.create("td", null, tr)
+                   );
+                   dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
+                   dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
+       
+                   return [tr];
+               }
+           }
+       };
+       
+       function apply_base_class(cls, basecls) {
+           openils_Util.objectProperties(basecls).forEach(
+               function(m) { cls[m] = basecls[m]; }
+           );
+       }
+       
+       function QualityNodeEditor() {
+           var self = this;
+           this.foi = ["tag", "svf"]; /* Fields of Interest - starting points for UI */
+       
+           this._init = function(qnode_editor_container) {
+               this._consistent_controls_query =
+                   "[consistent-controls], [quality-controls]";
+               this.qnode_editor_container = dojo.byId(qnode_editor_container);
+               this.clear();
+           };
+       
+           this.clear = function() {
+               dojo.create(
+                   "em", {"innerHTML": localeStrings.WORKING_QM_HERE},
+                   this.qnode_editor_container, "only"
+               );
+           };
+       
+           this.build_vmsq = function() {
+               var metric = new vmsq();
+               metric.match_set(match_set.id());   /* using global */
+               var controls = dojo.query("[fmfield]", this.qnode_editor_container);
+               for (var i = 0; i < controls.length; i++) {
+                   var field = dojo.attr(controls[i], "fmfield");
+                   var value = this._simple_value_getter(controls[i]);
+                   metric[field](value);
+               }
+       
+               if (!this.is_sensible(metric)) return null;    /* will alert() */
+               else return metric;
+           };
+       
+           this.add = function(type) {
+               this.clear();
+       
+               /* these are the editing widgets */
+               var table = dojo.create("table", {"className": "node-editor"});
+       
+               var nodes = this._factories_by_type[type]();
+               for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
+       
+               this._add_consistent_controls(table);
+       
+               var ok_cxl_td = dojo.create(
+                   "td", {"colspan": 2, "align": "center", "className": "space-me"},
+                   dojo.create("tr", null, table)
+               );
+       
+               dojo.create(
+                   "input", {
+                       "type": "submit", "value": localeStrings.OK,
+                       "onclick": function() {
+                           var metric = self.build_vmsq();
+                           if (metric) {
+                               self.clear();
+                               pcrud.create(
+                                   metric, {
+                                       /* borrowed from openils_widget_AutoGrid */
+                                       "oncomplete": function(req, cudResults) {
+                                           var fmObject = cudResults[0];
+                                           if (vmsq_grid.onPostCreate)
+                                               vmsq_grid.onPostCreate(fmObject);
+                                           if (fmObject) {
+                                               vmsq_grid.store.newItem(
+                                                   fmObject.toStoreItem()
+                                               );
+                                           }
+                                           setTimeout(function() {
+                                               try {
+                                                   vmsq_grid.selection.select(vmsq_grid.rowCount-1);
+                                                   vmsq_grid.views.views[0].getCellNode(vmsq_grid.rowCount-1, 1).focus();
+                                               } catch (E) {}
+                                           },200);
+                                       }
+                                   }
+                               );
+                           }
+                       }
+                   }, ok_cxl_td
+               );
+               dojo.create(
+                   "input", {
+                       "type": "reset", "value": localeStrings.CANCEL,
+                       "onclick": function() { self.clear(); }
+                   }, ok_cxl_td
+               );
+       
+               dojo.place(table, this.qnode_editor_container, "only");
+       
+               /* nice */
+               try { dojo.query("select, input", table)[0].focus(); }
+               catch(E) { console.log(String(E)); }
+       
+           };
+       
+           apply_base_class(self, NodeEditorAbstract);
+           this._init.apply(this, arguments);
+       }
+       
+       function NodeEditor() {
+           var self = this;
+           this.foi = ["tag", "svf", "bool_op"]; /* Fields of Interest - starting points for UI */
+       
+           this._init = function(dnd_source, node_editor_container) {
+               this._consistent_controls_query =
+                   "[consistent-controls], [point-controls]";
+               this.dnd_source = dnd_source;
+               this.node_editor_container = dojo.byId(node_editor_container);
+           };
+       
+           this.clear = function() {
+               this.dnd_source.selectAll().deleteSelectedNodes();
+               dojo.create(
+                   "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
+                   this.node_editor_container, "only"
+               );
+               this.dnd_source._ready = false;
+           };
+       
+           this.build_vmsp = function() {
+               var match_point = new vmsp();
+               var controls = dojo.query("[fmfield]", this.node_editor_container);
+               for (var i = 0; i < controls.length; i++) {
+                   var field = dojo.attr(controls[i], "fmfield");
+                   var value = this._simple_value_getter(controls[i]);
+                   match_point[field](value);
+               }
+       
+               if (!this.is_sensible(match_point)) return null;    /* will alert() */
+               else return match_point;
+           };
+       
+           this.update_draggable = function(draggable) {
+               var mp;
+       
+               if (!(mp = this.build_vmsp())) return;  /* will alert() */
+       
+               draggable.match_point = mp;
+               dojo.attr(draggable, "innerHTML", render_vmsp_label(mp));
+               this.dnd_source._ready = true;
+           };
+       
+           this.add = function(type) {
+               this.clear();
+       
+               /* a representation, not the editing widgets, but will also carry
+                * the fieldmapper object when dragged to the tree */
+               var draggable = dojo.create(
+                   "li", {"innerHTML": localeStrings.DEFINE_MP}
+               );
+       
+               /* these are the editing widgets */
+               var table = dojo.create("table", {"className": "node-editor"});
+       
+               var nodes = this._factories_by_type[type]();
+               for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
+       
+               if (type != "bool_op")
+                   this._add_consistent_controls(table);
+       
+               dojo.create(
+                   "input", {
+                       "type": "submit", "value": localeStrings.OK,
+                       "onclick": function() { self.update_draggable(draggable); }
+                   }, dojo.create(
+                       "td", {"colspan": 2, "align": "center"},
+                       dojo.create("tr", null, table)
+                   )
+               );
+       
+               dojo.place(table, this.node_editor_container, "only");
+       
+               this.dnd_source.insertNodes(false, [draggable]);
+       
+               /* nice */
+               try { dojo.query("select, input", table)[0].focus(); }
+               catch(E) { console.log(String(E)); }
+       
+           };
+       
+           apply_base_class(self, NodeEditorAbstract);
+       
+           this._init.apply(this, arguments);
+       }
+       
+       function find_crad_by_name(name) {
+           for (var i = 0; i < _crads.length; i++) {
+               if (_crads[i].name() == name)
+                   return _crads[i];
+           }
+           return null;
+       }
+       
+       function render_vmsp_label(point, minimal) {
+           /* "minimal" has these implications:
+            * for svf, only show the code, not the longer label.
+            * no quality display
+            */
+           if (point.bool_op()) {
+               return point.bool_op();
+           } else if (point.svf()) {
+               return (openils_Util.isTrue(point.negate()) ? "NOT " : "") + (
+                   minimal ?  point.svf() :
+                       (point.svf() + " / " + find_crad_by_name(point.svf()).label()) +
+                       " | " + dojo.string.substitute(
+                           localeStrings.MATCH_SCORE, [point.quality()]
+                       )
+               );
+           } else {
+               return (openils_Util.isTrue(point.negate()) ? "NOT " : "") +
+                   point.tag() + " \u2021" + point.subfield() + (minimal ? "" : " | " +
+                       dojo.string.substitute(
+                           localeStrings.MATCH_SCORE, [point.quality()]
+                       )
+                   );
+           }
+       }
+       
+       function replace_mode(explicit) {
+           if (typeof explicit == "undefined")
+               tree.model.replace_mode ^= 1;
+           else
+               tree.model.replace_mode = explicit;
+       
+           dojo.attr(
+               "replacer", "innerHTML",
+               localeStrings[
+                   (tree.model.replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
+               ]
+           );
+           dojo[tree.model.replace_mode ? "addClass" : "removeClass"](
+               "replacer", "replace-mode"
+           );
+       }
+       
+       function delete_selected_in_tree() {
+           /* relies on the fact that we only have one tree that would have
+            * registered a dnd controller. */
+           _tree_dnd_controllers[0].getSelectedItems().forEach(
+               function(item) {
+                   if (item === tree.model.root)
+                       alert(localeStrings.LEAVE_ROOT_ALONE);
+                   else
+                       tree.model.store.deleteItem(item);
+               }
+           );
+       }
+       
+       function new_match_set_tree() {
+           var point = new vmsp();
+           point.bool_op("AND");
+           return [
+               {
+                   "id": "root",
+                   "children": [],
+                   "name": render_vmsp_label(point),
+                   "match_point": point
+               }
+           ];
+       }
+       
+       /* dojoize_match_set_tree() takes an argument, "point", that is actually a
+        * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
+        * that into a syntactically flat array but preserving the hierarchy
+        * semantically in the language used by dojo data stores, i.e.,
+        *
+        * [
+        *  {'id': 'root', children:[{'_reference': '0'}, {'_reference': '1'}]},
+        *  {'id': '0', children:[]},
+        *  {'id': '1', children:[]}
+        * ],
+        *
+        */
+       function dojoize_match_set_tree(point, depth) {
+           var root = false;
+           if (!depth) {
+               if (!point) {
+                   return new_match_set_tree();
+               }
+               depth = 0;
+               root = true;
+           }
+       
+           var bathwater = point.children();
+           point.children([]);
+           var item = {
+               "id": (root ? "root" : point.id()),
+               "name": render_vmsp_label(point),
+               "match_point": point.clone(),
+               "children": []
+           };
+           point.children(bathwater);
+       
+           var results = [item];
+       
+           if (point.children()) {
+               for (var i = 0; i < point.children().length; i++) {
+                   var child = point.children()[i];
+                   item.children.push({"_reference": child.id()});
+                   results = results.concat(
+                       dojoize_match_set_tree(child, ++depth)
+                   );
+               }
+           }
+       
+           return results;
+       }
+       
+       function render_vms_metadata(match_set) {
+           dojo.byId("vms-name").innerHTML = match_set.name();
+           dojo.byId("vms-owner").innerHTML =
+               aou.findOrgUnit(match_set.owner()).name();
+           dojo.byId("vms-mtype").innerHTML = match_set.mtype();
+       }
+       
+       function redraw_expression_preview() {
+           tree.model.getRoot(
+               function(root) {
+                   tree.model.get_simple_tree(
+                       root, function(r) {
+                           dojo.attr(
+                               "expr-preview",
+                               "innerHTML",
+                               render_expression_preview(r)
+                           );
+                       }
+                   );
+               }
+           );
+       }
+       
+       function render_expression_preview(r) {
+           if (r.children().length) {
+               return "(" + r.children().map(render_expression_preview).join(
+                   " " + render_vmsp_label(r) + " "
+               ) + ")";
+           } else if (!r.bool_op()) {
+               return render_vmsp_label(r, true /* minimal */);
+           } else {
+               return "()";
+           }
+       }
+       
+       function save_tree() {
+           progress_dialog.show(true);
+       
+           tree.model.getRoot(
+               function(root) {
+                   tree.model.get_simple_tree(
+                       root, function(r) {
+                           fieldmapper.standardRequest(
+                               ["open-ils.vandelay",
+                                   "open-ils.vandelay.match_set.update"], {
+                                   "params": [
+                                       openils_User.authtoken, match_set.id(), r
+                                   ],
+                                   "async": true,
+                                   "oncomplete": function(r) {
+                                       progress_dialog.hide();
+                                       /* catch exceptions */
+                                       r = openils_Util.readResponse(r);
+       
+                                       location.href = location.href;
+                                   }
+                               }
+                           );
+                       }
+                   );
+               }
+           );
+       }
+       
+       function init_vmsq_grid() {
+           vmsq_grid.loadAll(
+               {"order_by": {"vmsq": "quality"}},
+               {"match_set": match_set.id()}
+           );
+       }
+       
+       function my_init() {
+           progress_dialog.show(true);
+       
+           dojo.requireLocalization("openils.vandelay", "match_set");
+           localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
+       
+           pcrud = new openils_PermaCrud();
+           CGI = new openils_CGI();
+       
+           if (!CGI.param("match_set")) {
+               alert(localeStrings.NO_CAN_DO);
+               progress_dialog.hide();
+               return;
+           }
+       
+           render_vms_metadata(
+               match_set = pcrud.retrieve("vms", CGI.param("match_set"))
+           );
+       
+           /* No-one should have hundreds of these or anything, but theoretically
+            * this could be problematic with a big enough list of crad objects. */
+           _crads = pcrud.retrieveAll("crad", {"order_by": {"crad": "label"}});
+       
+           var match_set_tree = fieldmapper.standardRequest(
+               ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
+               [openils_User.authtoken, CGI.param("match_set")]
+           );
+       
+           var store = new dojo_data_ItemFileWriteStore({
+               "data": {
+                   "identifier": "id",
+                   "label": "name",
+                   "items": dojoize_match_set_tree(match_set_tree)
+               }
+           });
+       
+           var tree_model = new openils_vandelay_TreeStoreModel({
+               "store": store, "query": {"id": "root"}
+           });
+       
+           var src = new dojo_dnd_Source("src-here");
+           tree = new dijit_Tree(
+               {
+                   "model": tree_model,
+                   "dndController": openils_vandelay_TreeDndSource,
+                   "dragThreshold": 8,
+                   "betweenThreshold": 5,
+                   "persist": false
+               }, "tree-here"
+           );
+       
+           node_editor = new NodeEditor(src, "node-editor-container");
+           qnode_editor = new QualityNodeEditor("qnode-editor-container");
+       
+           replace_mode(0);
+       
+           dojo.connect(
+               src, "onDndDrop", null,
+               function(source, nodes, copy, target) {
+                   /* Because of the... interesting... characteristics of DnD
+                    * design in dojo/dijit (at least as of 1.3), this callback will
+                    * fire both for our working node dndSource and for the tree!
+                    */
+                   if (source == this)
+                       node_editor.clear();  /* ... because otherwise this acts like a
+                                                copy operation no matter what the user
+                                                does, even though we really want a
+                                                "move." */
+               }
+           );
+       
+           redraw_expression_preview();
+           node_editor.clear();
+       
+           init_vmsq_grid();
+       
+           progress_dialog.hide();
+       }
+       
+       openils_Util.addOnLoad(my_init);
+       
+
+});
\ No newline at end of file
index c4c7654..37e586c 100644 (file)
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.DateTextBox");
-dojo.require("dijit.form.TextBox");
-dojo.require("dijit.form.NumberSpinner");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("openils.widget.PCrudAutocompleteBox");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.ProgressDialog");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.CGI");
-
-var pcrud, cgi, issuance_id;
-var sitem_cache = {};
-
-function load_sitem_grid() {
-    sitem_grid.overrideEditWidgets.status = status_selector;
-    sitem_grid.overrideEditWidgets.status.shove = {};   /* sic */
-
-    sitem_grid.dataLoader = sitem_data_loader;
-    sitem_grid.dataLoader();
-}
-
-function load_siss_display() {
-    pcrud.retrieve(
-        "siss", issuance_id, {
-            "onresponse": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    var link = dojo.byId("siss_label_here");
-                    link.onclick = function() {
-                        location.href = oilsBasePath +
-                            "/serial/subscription?id=" +
-                            r.subscription() + "&tab=issuances";
-                    }
-                    link.innerHTML = r.label();
-                    prepare_create_dialog(r.subscription());
-                }
-            }
-        }
-    );
-}
-
-function sitem_data_loader() {
-    sitem_grid.resetStore();
-    sitem_grid.showLoadProgressIndicator();
-
-    fieldmapper.standardRequest(
-        ["open-ils.serial", "open-ils.serial.items.by_issuance"], {
-            "params": [
-                openils.User.authtoken, issuance_id, {
-                    "limit": sitem_grid.displayLimit,
-                    "offset": sitem_grid.displayOffset
-                }
-            ],
-            "async": true,
-            "onresponse": function(r) {
-                var item = openils.Util.readResponse(r);
-                sitem_cache[item.id()] = item;
-                sitem_grid.store.newItem(item.toStoreItem());
-            },
-            "oncomplete": function(r) {
-                sitem_grid.hideLoadProgressIndicator();
-            }
-        }
-    );
-}
-
-function _get_field(store, item, field) {
-    if (!item) return "";
-    var id = store.getValue(item, "id");
-    return sitem_cache[id][field]();
-}
-
-/* create the get_foo() functions used by our AutoGrid */
-["creator", "editor", "stream", "unit"].forEach(
-    function(field) {
-        window["get_" + field] = function(row_index, item) {
-            return _get_field(this.grid.store, item, field);
-        };
-    }
-);
-
-function format_user(user) {
-    return user ? user.usrname() : "";
-}
-
-function format_stream(stream) {
-    return stream ? (stream.routing_label() || "[None]") : ""; /* XXX i18n */
-}
-
-function format_unit(unit) {
-    return unit ? (unit.barcode() || "[None]") : ""; /* XXX i18n */
-}
-
-function update_sitem_safely(obj, opts, edit_pane) {
-    fieldmapper.standardRequest(
-        ["open-ils.serial", "open-ils.serial.item.update"], {
-            "params": [openils.User.authtoken, obj],
-            "async": true,
-            "oncomplete": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    if (edit_pane.onPostSubmit)
-                        edit_pane.onPostSubmit(null, [r]);
-                }
-            }
-        }
-    );
-}
-
-function prepare_create_dialog(sub_id) {
-    pcrud.search(
-        "sdist", {"subscription": sub_id}, {
-            "id_list": true,
-            "async": true,
-            "oncomplete": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    new openils.widget.PCrudAutocompleteBox({
-                        "fmclass": "sstr",
-                        "searchAttr": "routing_label",
-                        "hasDownArrow": true,
-                        "name": "stream",
-                        "store_options": {
-                            "base_filter": {"distribution": r},
-                            "honor_retrieve_all": true
-                        }
-                    }, "stream_selector");
-                }
-            }
-        }
-    );
-}
-
-function create_new_items(form) {
-    var item = new sitem();
-    item.issuance(issuance_id);    /* from global */
-    item.stream(form.stream);
-    item.status(form.status);
-    item.date_expected(
-        form.date_expected ?
-            dojo.date.stamp.toISOString(form.date_expected) : null
-    );
-    item.date_received(
-        form.date_received ?
-            dojo.date.stamp.toISOString(form.date_received) : null
-    );
-
-    progress_dialog.show(true);
-    fieldmapper.standardRequest(
-        ["open-ils.serial", "open-ils.serial.item.create"], {
-            "params": [openils.User.authtoken, item, form.count],
-            "async": true,
-            "oncomplete": function(r) {
-                progress_dialog.hide();
-                if (r = openils.Util.readResponse(r)) {
-                    sitem_grid.refresh();
-                }
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(
-    function() {
-        cgi = new openils.CGI();
-        pcrud = new openils.PermaCrud();
-
-        issuance_id = cgi.param("issuance");
-        load_siss_display();
-        load_sitem_grid();
-    }
-);
+require([
+       "dijit/form/Button",
+       "dijit/form/DateTextBox",
+       "dijit/form/TextBox",
+       "dijit/form/NumberSpinner",
+       "dijit/form/FilteringSelect",
+       "openils/widget/PCrudAutocompleteBox",
+       "openils/widget/AutoGrid",
+       "openils/widget/ProgressDialog",
+       "openils/PermaCrud",
+       "openils/CGI"
+       ],
+function(dijit_form_Button,
+       dijit_form_DateTextBox,
+       dijit_form_TextBox,
+       dijit_form_NumberSpinner,
+       dijit_form_FilteringSelect,
+       openils_widget_PCrudAutocompleteBox,
+       openils_widget_AutoGrid,
+       openils_widget_ProgressDialog,
+       openils_PermaCrud,
+       openils_CGI){
+       
+       var pcrud, cgi, issuance_id;
+       var sitem_cache = {};
+       
+       function load_sitem_grid() {
+           sitem_grid.overrideEditWidgets.status = status_selector;
+           sitem_grid.overrideEditWidgets.status.shove = {};   /* sic */
+       
+           sitem_grid.dataLoader = sitem_data_loader;
+           sitem_grid.dataLoader();
+       }
+       
+       function load_siss_display() {
+           pcrud.retrieve(
+               "siss", issuance_id, {
+                   "onresponse": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           var link = dojo.byId("siss_label_here");
+                           link.onclick = function() {
+                               location.href = oilsBasePath +
+                                   "/serial/subscription?id=" +
+                                   r.subscription() + "&tab=issuances";
+                           }
+                           link.innerHTML = r.label();
+                           prepare_create_dialog(r.subscription());
+                       }
+                   }
+               }
+           );
+       }
+       
+       function sitem_data_loader() {
+           sitem_grid.resetStore();
+           sitem_grid.showLoadProgressIndicator();
+       
+           fieldmapper.standardRequest(
+               ["open-ils.serial", "open-ils.serial.items.by_issuance"], {
+                   "params": [
+                       openils.User.authtoken, issuance_id, {
+                           "limit": sitem_grid.displayLimit,
+                           "offset": sitem_grid.displayOffset
+                       }
+                   ],
+                   "async": true,
+                   "onresponse": function(r) {
+                       var item = openils.Util.readResponse(r);
+                       sitem_cache[item.id()] = item;
+                       sitem_grid.store.newItem(item.toStoreItem());
+                   },
+                   "oncomplete": function(r) {
+                       sitem_grid.hideLoadProgressIndicator();
+                   }
+               }
+           );
+       }
+       
+       function _get_field(store, item, field) {
+           if (!item) return "";
+           var id = store.getValue(item, "id");
+           return sitem_cache[id][field]();
+       }
+       
+       /* create the get_foo() functions used by our AutoGrid */
+       ["creator", "editor", "stream", "unit"].forEach(
+           function(field) {
+               window["get_" + field] = function(row_index, item) {
+                   return _get_field(this.grid.store, item, field);
+               };
+           }
+       );
+       
+       function format_user(user) {
+           return user ? user.usrname() : "";
+       }
+       
+       function format_stream(stream) {
+           return stream ? (stream.routing_label() || "[None]") : ""; /* XXX i18n */
+       }
+       
+       function format_unit(unit) {
+           return unit ? (unit.barcode() || "[None]") : ""; /* XXX i18n */
+       }
+       
+       function update_sitem_safely(obj, opts, edit_pane) {
+           fieldmapper.standardRequest(
+               ["open-ils.serial", "open-ils.serial.item.update"], {
+                   "params": [openils.User.authtoken, obj],
+                   "async": true,
+                   "oncomplete": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           if (edit_pane.onPostSubmit)
+                               edit_pane.onPostSubmit(null, [r]);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function prepare_create_dialog(sub_id) {
+           pcrud.search(
+               "sdist", {"subscription": sub_id}, {
+                   "id_list": true,
+                   "async": true,
+                   "oncomplete": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           new openils_widget_PCrudAutocompleteBox({
+                               "fmclass": "sstr",
+                               "searchAttr": "routing_label",
+                               "hasDownArrow": true,
+                               "name": "stream",
+                               "store_options": {
+                                   "base_filter": {"distribution": r},
+                                   "honor_retrieve_all": true
+                               }
+                           }, "stream_selector");
+                       }
+                   }
+               }
+           );
+       }
+       
+       function create_new_items(form) {
+           var item = new sitem();
+        
+           item.issuance(issuance_id);    /* from global */
+           item.stream(form.stream);
+           item.status(form.status);
+           item.date_expected(
+               form.date_expected ?
+                   dojo.date.stamp.toISOString(form.date_expected) : null
+           );
+           item.date_received(
+               form.date_received ?
+                   dojo.date.stamp.toISOString(form.date_received) : null
+           );
+       
+           progress_dialog.show(true);
+           fieldmapper.standardRequest(
+               ["open-ils.serial", "open-ils.serial.item.create"], {
+                   "params": [openils.User.authtoken, item, form.count],
+                   "async": true,
+                   "oncomplete": function(r) {
+                       progress_dialog.hide();
+                       if (r = openils.Util.readResponse(r)) {
+                           sitem_grid.refresh();
+                       }
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               cgi = new openils_CGI();
+               pcrud = new openils_PermaCrud();
+       
+               issuance_id = cgi.param("issuance");
+               load_siss_display();
+               load_sitem_grid();
+           }
+       );
+       
+
+});
\ No newline at end of file
index 79bf05d..1fcb79d 100644 (file)
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.RadioButton");
-dojo.require("dijit.form.NumberSpinner");
-dojo.require("dijit.form.TextBox");
-dojo.require("dojo.dnd.Source");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.ProgressDialog");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.CGI");
-
-var pcrud;
-var dist_id;
-var rlu_editor;
-var cgi;
-
-function format_routing_label(routing_label) {
-    return routing_label ? routing_label : "[None]";
-}
-
-function load_sstr_grid() {
-    sstr_grid.overrideEditWidgets.distribution =
-        new dijit.form.TextBox({"disabled": true, "value": dist_id});
-
-    sstr_grid.resetStore();
-    sstr_grid.loadAll(
-        {"order_by": {"ssub": "start_date DESC"}},
-        {"distribution": dist_id}
-    );
-}
-
-function load_sdist_display() {
-    pcrud.retrieve(
-        "sdist", dist_id, {
-            "onresponse": function(r) {
-                if (r = openils.Util.readResponse(r)) {
-                    var link = dojo.byId("sdist_label_here");
-                    link.onclick = function() {
-                        location.href = oilsBasePath +
-                            "/serial/subscription?id=" +
-                            r.subscription() + "&tab=distributions";
-                    }
-                    link.innerHTML = r.label();
-                    load_sdist_org_unit_display(r);
-                }
-            }
-        }
-    );
-}
-
-function load_sdist_org_unit_display(dist) {
-    dojo.byId("sdist_org_unit_name_here").innerHTML =
-        aou.findOrgUnit(dist.holding_lib()).name();
-}
-
-function create_many_streams(fields) {
-    var streams = [];
-    for (var i = 0; i < fields.quantity; i++) {
-        var stream = new sstr();
-        stream.distribution(dist_id);
-        streams.push(stream);
-    }
-
-    progress_dialog.show(true);
-    this.pcrud.create(
-        streams, {
-            "oncomplete": function(r, list) {
-                progress_dialog.hide();
-                sstr_grid.refresh();
-            },
-            "onerror": function(r) {
-                progress_dialog.hide();
-                alert("Error creating streams!"); /* XXX i18n */
-            }
-        }
-    );
-}
-
-function RLUEditor() {
-    var self = this;
-
-    function _reader_xor_dept_toggle(value) {
-        var reader = dijit.byId("reader");
-        var department = dijit.byId("department");
-
-        if (this.id.match(/\w+$/).pop() == "reader")
-            _reader_toggle(value, reader, department);
-        else
-            _department_toggle(value, reader, department);
-    }
-
-    function _reader_toggle(value, reader, department) {
-        if (value) {
-            reader.attr("disabled", false);
-            department.attr("disabled", true);
-            setTimeout(function() { reader.focus(); }, 125);
-        }
-    }
-
-    function _department_toggle(value, reader, department) {
-        if (value) {
-            reader.attr("disabled", true);
-            department.attr("disabled", false);
-            setTimeout(function() { department.focus(); }, 125);
-        }
-    }
-
-    this.user_to_source_entry = function(user) {
-        var node = dojo.create("li");
-        var s;
-        if (user.reader()) {
-            s = dojo.string.substitute(
-                this.template.reader, [
-                    user.reader().card().barcode(),
-                    user.reader().family_name(),
-                    user.reader().first_given_name(),
-                    user.reader().second_given_name(),
-                    user.reader().home_ou().shortname()
-                ].map(function(o) { return o == null ? "" : o; })
-            );
-        } else {
-            s = dojo.string.substitute(
-                this.template.department, [user.department()]
-            );
-        }
-
-        if (user.note()) {
-            s += dojo.string.substitute(this.template.note, [user.note()]);
-        }
-
-        node.innerHTML = "&nbsp;" + s;
-
-        dojo.create(
-            "a", {
-                "href": "javascript:void(0);",
-                "onclick": function() { self.toggle_deleted(node); },
-                "innerHTML": this.template.remove
-            }, node, "first"
-        );
-
-        node._user = user;
-        return node;
-    };
-
-    this.toggle_deleted = function(node) {
-        if (node._user.isdeleted()) {
-            dojo.style(node, "textDecoration", "none");
-            node._user.isdeleted(false);
-        } else {
-            dojo.style(node, "textDecoration", "line-through");
-            node._user.isdeleted(true);
-        }
-    };
-
-    this.new_user = function() {
-        var form = this.dialog.attr("value");
-        var user = new fieldmapper.srlu();
-        user.isnew(true);
-        user.stream(this.stream);
-
-        if (form.note)
-            user.note(form.note);
-
-        if (form.department) {
-            user.department(form.department);
-        } else if (form.reader) {
-            this.add_button.attr("disabled", true);
-            fieldmapper.standardRequest(
-                ["open-ils.actor",
-                    "open-ils.actor.user.fleshed.retrieve_by_barcode"], {
-                    "params": [openils.User.authtoken, form.reader, true],
-                    "timeout": 10, /* sync */
-                    "onresponse": function(r) {
-                        if (r = openils.Util.readResponse(r)) {
-                            user.reader(r);
-                        }
-                    }
-                }
-            );
-            this.add_button.attr("disabled", false);
-        } else {
-            alert("Provide either a reader or a department."); /* XXX i18n */
-            return;
-        }
-
-        ["reader", "department", "note"].forEach(
-            function(s) { dijit.byId(s).attr("value", ""); }
-        );
-
-        this.source.insertNodes(false, [self.user_to_source_entry(user)]);
-    }
-
-    this.show = function() {
-        if (sstr_grid.getSelectedRows().length != 1) {
-            alert(
-                "Use the checkboxes to select exactly one stream " +
-                "for this operation."   /* XXX i18n */
-            );
-        } else {
-            /* AutoGrid.getSelectedItems() yields a weird, non-FM object */
-            this.stream = sstr_grid.getSelectedItems()[0].id[0];
-
-            this.source.selectAll();
-            this.source.deleteSelectedNodes();
-            this.source.clearItems();
-
-            this.dialog.show();
-
-            fieldmapper.standardRequest(
-                ["open-ils.serial",
-                    "open-ils.serial.routing_list_users.fleshed_and_ordered"], {
-                    "params": [openils.User.authtoken, this.stream],
-                    "async": true,
-                    "onresponse": function(r) {
-                        if (r = openils.Util.readResponse(r)) {
-                            self.source.insertNodes(
-                                false, [self.user_to_source_entry(r)]
-                            );
-                        }
-                    },
-                    "oncomplete": function() {
-                        setTimeout(
-                            function() { self.save_button.focus(); }, 125
-                        );
-                    }
-                }
-            );
-        }
-    };
-
-    this.save = function() {
-        var obj_list = this.source.getAllNodes().map(
-            function(node) {
-                var obj = node._user;
-                if (obj.reader())
-                    obj.reader(obj.reader().id());
-
-                return obj;
-            }
-        );
-
-        this.save_button.attr("disabled", true);
-
-        /* pcrud.apply *almost* could have handled this, but there's a reason
-         * it doesn't, and it has to do with the unique key constraint on the
-         * pos field in srlu objects.
-         */
-        try {
-            fieldmapper.standardRequest(
-                /* This method will set pos in ascending order. */
-                ["open-ils.serial",
-                    "open-ils.serial.routing_list_users.replace"], {
-                    "params": [openils.User.authtoken, obj_list],
-                    "timeout": 10, /* sync */
-                    "oncomplete": function(r) {
-                        openils.Util.readResponse(r);   /* display exceptions */
-                    }
-                }
-            );
-        } catch (E) {
-            alert(E); /* XXX i18n */
-        }
-
-        this.save_button.attr("disabled", false);
-    };
-
-    this._init = function(dialog) {
-        this.dialog = dijit.byId("routing_list_dialog");
-        this.source = routing_list_source;
-
-        this.template = {};
-        ["reader", "department", "note", "remove"].forEach(
-            function(n) {
-                self.template[n] =
-                    dojo.byId("routing_list_user_template_" + n).innerHTML;
-            }
-        );
-
-        this.add_button = dijit.byId("routing_list_add_button");
-        this.save_button = dijit.byId("routing_list_save_button");
-
-        dijit.byId("reader_xor_dept-reader").onChange =
-            _reader_xor_dept_toggle;
-        dijit.byId("reader_xor_dept-department").onChange =
-            _reader_xor_dept_toggle;
-    };
-
-    this._init.apply(this, arguments);
-}
-
-openils.Util.addOnLoad(
-    function() {
-        cgi = new openils.CGI();
-        pcrud = new openils.PermaCrud();
-        rlu_editor = new RLUEditor();
-
-        dist_id = cgi.param("distribution");
-        load_sdist_display();
-        load_sstr_grid();
-    }
-);
+require([
+       "dijit/form/Button",
+       "dijit/form/RadioButton",
+       "dijit/form/NumberSpinner",
+       "dijit/form/TextBox",
+       "dojo/dnd/Source",
+       "openils/widget/AutoGrid",
+       "openils/widget/ProgressDialog",
+       "openils/PermaCrud",
+       "openils/CGI"
+       ],
+function(dijit_form_Button,
+       dijit_form_RadioButton,
+       dijit_form_NumberSpinner,
+       dijit_form_TextBox,
+       dojo_dnd_Source,
+       openils_widget_AutoGrid,
+       openils_widget_ProgressDialog,
+       openils_PermaCrud,
+       openils_CGI){
+       
+       var pcrud;
+       var dist_id;
+       var rlu_editor;
+       var cgi;
+       
+       function format_routing_label(routing_label) {
+           return routing_label ? routing_label : "[None]";
+       }
+       
+       function load_sstr_grid() {
+           sstr_grid.overrideEditWidgets.distribution =
+               new dijit_form_TextBox({"disabled": true, "value": dist_id});
+       
+           sstr_grid.resetStore();
+           sstr_grid.loadAll(
+               {"order_by": {"ssub": "start_date DESC"}},
+               {"distribution": dist_id}
+           );
+       }
+       
+       function load_sdist_display() {
+           pcrud.retrieve(
+               "sdist", dist_id, {
+                   "onresponse": function(r) {
+                       if (r = openils.Util.readResponse(r)) {
+                           var link = dojo.byId("sdist_label_here");
+                           link.onclick = function() {
+                               location.href = oilsBasePath +
+                                   "/serial/subscription?id=" +
+                                   r.subscription() + "&tab=distributions";
+                           }
+                           link.innerHTML = r.label();
+                           load_sdist_org_unit_display(r);
+                       }
+                   }
+               }
+           );
+       }
+       
+       function load_sdist_org_unit_display(dist) {
+           dojo.byId("sdist_org_unit_name_here").innerHTML =
+               aou.findOrgUnit(dist.holding_lib()).name();
+       }
+       
+       function create_many_streams(fields) {
+           var streams = [];
+           for (var i = 0; i < fields.quantity; i++) {
+               var stream = new sstr();
+               stream.distribution(dist_id);
+               streams.push(stream);
+           }
+       
+           progress_dialog.show(true);
+           this.pcrud.create(
+               streams, {
+                   "oncomplete": function(r, list) {
+                       progress_dialog.hide();
+                       sstr_grid.refresh();
+                   },
+                   "onerror": function(r) {
+                       progress_dialog.hide();
+                       alert("Error creating streams!"); /* XXX i18n */
+                   }
+               }
+           );
+       }
+       
+       function RLUEditor() {
+           var self = this;
+       
+           function _reader_xor_dept_toggle(value) {
+               var reader = dijit.byId("reader");
+               var department = dijit.byId("department");
+       
+               if (this.id.match(/\w+$/).pop() == "reader")
+                   _reader_toggle(value, reader, department);
+               else
+                   _department_toggle(value, reader, department);
+           }
+       
+           function _reader_toggle(value, reader, department) {
+               if (value) {
+                   reader.attr("disabled", false);
+                   department.attr("disabled", true);
+                   setTimeout(function() { reader.focus(); }, 125);
+               }
+           }
+       
+           function _department_toggle(value, reader, department) {
+               if (value) {
+                   reader.attr("disabled", true);
+                   department.attr("disabled", false);
+                   setTimeout(function() { department.focus(); }, 125);
+               }
+           }
+       
+           this.user_to_source_entry = function(user) {
+               var node = dojo.create("li");
+               var s;
+               if (user.reader()) {
+                   s = dojo.string.substitute(
+                       this.template.reader, [
+                           user.reader().card().barcode(),
+                           user.reader().family_name(),
+                           user.reader().first_given_name(),
+                           user.reader().second_given_name(),
+                           user.reader().home_ou().shortname()
+                       ].map(function(o) { return o == null ? "" : o; })
+                   );
+               } else {
+                   s = dojo.string.substitute(
+                       this.template.department, [user.department()]
+                   );
+               }
+       
+               if (user.note()) {
+                   s += dojo.string.substitute(this.template.note, [user.note()]);
+               }
+       
+               node.innerHTML = "&nbsp;" + s;
+       
+               dojo.create(
+                   "a", {
+                       "href": "javascript:void(0);",
+                       "onclick": function() { self.toggle_deleted(node); },
+                       "innerHTML": this.template.remove
+                   }, node, "first"
+               );
+       
+               node._user = user;
+               return node;
+           };
+       
+           this.toggle_deleted = function(node) {
+               if (node._user.isdeleted()) {
+                   dojo.style(node, "textDecoration", "none");
+                   node._user.isdeleted(false);
+               } else {
+                   dojo.style(node, "textDecoration", "line-through");
+                   node._user.isdeleted(true);
+               }
+           };
+       
+           this.new_user = function() {
+               var form = this.dialog.attr("value");
+               var user = new fieldmapper.srlu();
+               user.isnew(true);
+               user.stream(this.stream);
+       
+               if (form.note)
+                   user.note(form.note);
+       
+               if (form.department) {
+                   user.department(form.department);
+               } else if (form.reader) {
+                   this.add_button.attr("disabled", true);
+                   fieldmapper.standardRequest(
+                       ["open-ils.actor",
+                           "open-ils.actor.user.fleshed.retrieve_by_barcode"], {
+                           "params": [openils.User.authtoken, form.reader, true],
+                           "timeout": 10, /* sync */
+                           "onresponse": function(r) {
+                               if (r = openils.Util.readResponse(r)) {
+                                   user.reader(r);
+                               }
+                           }
+                       }
+                   );
+                   this.add_button.attr("disabled", false);
+               } else {
+                   alert("Provide either a reader or a department."); /* XXX i18n */
+                   return;
+               }
+       
+               ["reader", "department", "note"].forEach(
+                   function(s) { dijit.byId(s).attr("value", ""); }
+               );
+       
+               this.source.insertNodes(false, [self.user_to_source_entry(user)]);
+           }
+       
+           this.show = function() {
+               if (sstr_grid.getSelectedRows().length != 1) {
+                   alert(
+                       "Use the checkboxes to select exactly one stream " +
+                       "for this operation."   /* XXX i18n */
+                   );
+               } else {
+                   /* AutoGrid.getSelectedItems() yields a weird, non-FM object */
+                   this.stream = sstr_grid.getSelectedItems()[0].id[0];
+       
+                   this.source.selectAll();
+                   this.source.deleteSelectedNodes();
+                   this.source.clearItems();
+       
+                   this.dialog.show();
+       
+                   fieldmapper.standardRequest(
+                       ["open-ils.serial",
+                           "open-ils.serial.routing_list_users.fleshed_and_ordered"], {
+                           "params": [openils.User.authtoken, this.stream],
+                           "async": true,
+                           "onresponse": function(r) {
+                               if (r = openils.Util.readResponse(r)) {
+                                   self.source.insertNodes(
+                                       false, [self.user_to_source_entry(r)]
+                                   );
+                               }
+                           },
+                           "oncomplete": function() {
+                               setTimeout(
+                                   function() { self.save_button.focus(); }, 125
+                               );
+                           }
+                       }
+                   );
+               }
+           };
+       
+           this.save = function() {
+               var obj_list = this.source.getAllNodes().map(
+                   function(node) {
+                       var obj = node._user;
+                       if (obj.reader())
+                           obj.reader(obj.reader().id());
+       
+                       return obj;
+                   }
+               );
+       
+               this.save_button.attr("disabled", true);
+       
+               /* pcrud.apply *almost* could have handled this, but there's a reason
+                * it doesn't, and it has to do with the unique key constraint on the
+                * pos field in srlu objects.
+                */
+               try {
+                   fieldmapper.standardRequest(
+                       /* This method will set pos in ascending order. */
+                       ["open-ils.serial",
+                           "open-ils.serial.routing_list_users.replace"], {
+                           "params": [openils.User.authtoken, obj_list],
+                           "timeout": 10, /* sync */
+                           "oncomplete": function(r) {
+                               openils.Util.readResponse(r);   /* display exceptions */
+                           }
+                       }
+                   );
+               } catch (E) {
+                   alert(E); /* XXX i18n */
+               }
+       
+               this.save_button.attr("disabled", false);
+           };
+       
+           this._init = function(dialog) {
+               this.dialog = dijit.byId("routing_list_dialog");
+               this.source = routing_list_source;
+       
+               this.template = {};
+               ["reader", "department", "note", "remove"].forEach(
+                   function(n) {
+                       self.template[n] =
+                           dojo.byId("routing_list_user_template_" + n).innerHTML;
+                   }
+               );
+       
+               this.add_button = dijit.byId("routing_list_add_button");
+               this.save_button = dijit.byId("routing_list_save_button");
+       
+               dijit.byId("reader_xor_dept-reader").onChange =
+                   _reader_xor_dept_toggle;
+               dijit.byId("reader_xor_dept-department").onChange =
+                   _reader_xor_dept_toggle;
+           };
+       
+           this._init.apply(this, arguments);
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               cgi = new openils_CGI();
+               pcrud = new openils_PermaCrud();
+               rlu_editor = new RLUEditor();
+       
+               dist_id = cgi.param("distribution");
+               load_sdist_display();
+               load_sstr_grid();
+           }
+       );
+       
+
+});
\ No newline at end of file
index 0f166fe..c2cd6a7 100644 (file)
@@ -1,60 +1,70 @@
-dojo.require("dijit.form.Button");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.OrgUnitFilteringSelect");
-dojo.require("openils.BibTemplate");
-dojo.require("openils.CGI");
+require([
+       "dijit/form/Button",
+       "openils/widget/AutoGrid",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/BibTemplate",
+       "openils/CGI"
+       ],
+function(dijit_form_Button,
+       openils_widget_AutoGrid,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_BibTemplate,
+       openils_CGI){
+       
+       var terms;
+       var cgi;
+       
+       function format_ssub_link(id) {
+           return "<a href='" + oilsBasePath + "/serial/subscription?id=" +
+               id + "'>" + id + "</a>";
+       }
+       
+       function load_ssub_grid() {
+           ssub_grid.resetStore();
+           ssub_grid.loadAll({"order_by": {"ssub": "start_date DESC"}}, terms);
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               cgi = new openils_CGI();
+       
+               terms = {
+                   "owning_lib": aou.orgNodeTrail(
+                       aou.findOrgUnit(openils.User.user.ws_ou()),
+                       true /* asId */
+                   ),
+                   "record_entry": cgi.param("record_entry") || _fallback_record_entry
+               };
+       
+               if (terms.record_entry)
+                   new openils_BibTemplate({"record": terms.record_entry}).render();
+       
+               /* This should be present even if terms.record_entry is undef */
+               ssub_grid.overrideEditWidgets.record_entry = new dijit.form.TextBox(
+                   {"value": terms.record_entry, "disabled": true}
+               );
+       
+               new openils.User().buildPermOrgSelector(
+                   "ADMIN_SERIAL_SUBSCRIPTION",
+                   ssub_owner_select,
+                   null,
+                   function() {
+                       dojo.connect(
+                           ssub_owner_select,
+                           "onChange",
+                           function() {
+                               terms.owning_lib = aou.orgNodeTrail(
+                                   aou.findOrgUnit(this.attr("value")),
+                                   true /* asId */
+                               );
+                               load_ssub_grid();
+                           }
+                       );
+                       load_ssub_grid();
+                   }
+               );
+           }
+       );
+       
 
-var terms;
-var cgi;
-
-function format_ssub_link(id) {
-    return "<a href='" + oilsBasePath + "/serial/subscription?id=" +
-        id + "'>" + id + "</a>";
-}
-
-function load_ssub_grid() {
-    ssub_grid.resetStore();
-    ssub_grid.loadAll({"order_by": {"ssub": "start_date DESC"}}, terms);
-}
-
-openils.Util.addOnLoad(
-    function() {
-        cgi = new openils.CGI();
-
-        terms = {
-            "owning_lib": aou.orgNodeTrail(
-                aou.findOrgUnit(openils.User.user.ws_ou()),
-                true /* asId */
-            ),
-            "record_entry": cgi.param("record_entry") || _fallback_record_entry
-        };
-
-        if (terms.record_entry)
-            new openils.BibTemplate({"record": terms.record_entry}).render();
-
-        /* This should be present even if terms.record_entry is undef */
-        ssub_grid.overrideEditWidgets.record_entry = new dijit.form.TextBox(
-            {"value": terms.record_entry, "disabled": true}
-        );
-
-        new openils.User().buildPermOrgSelector(
-            "ADMIN_SERIAL_SUBSCRIPTION",
-            ssub_owner_select,
-            null,
-            function() {
-                dojo.connect(
-                    ssub_owner_select,
-                    "onChange",
-                    function() {
-                        terms.owning_lib = aou.orgNodeTrail(
-                            aou.findOrgUnit(this.attr("value")),
-                            true /* asId */
-                        );
-                        load_ssub_grid();
-                    }
-                );
-                load_ssub_grid();
-            }
-        );
-    }
-);
+});
\ No newline at end of file
index b8b05e7..de6a1b3 100644 (file)
-dojo.require("dojo.string");
-
-var list_renderer;
-
-function n(name, ctx) { return dojo.query("[name='" + name + "']", ctx)[0]; }
-
-function ListRenderer() {
-    var self = this;
-
-    this.render = function() {
-        for (var i = 0; i < this.streams.length; i++) {
-            var stream = this.streams[i];
-
-            if (!this.users_by_stream[stream.id()])
-                continue; /* no users on this stream */
-
-            var list = dojo.clone(this.list_template);
-            n("title", list).innerHTML = this.mvr.title();
-            n("issuance_label", list).innerHTML = this.issuance.label();
-            n("distribution_holding_lib", list).innerHTML =
-                stream.distribution().holding_lib().shortname();
-            n("distribution_label", list).innerHTML =
-                stream.distribution().label();
-            if (stream.routing_label()) {
-                n("stream_routing_label", list).innerHTML =
-                    stream.routing_label();
-                openils.Util.show(
-                    n("stream_routing_label", list), "inline"
-                );
-            } else {
-                n("stream_id", list).innerHTML = stream.id();
-                openils.Util.show(n("stream_id_container", list), "inline");
-            }
-
-            this.render_users(stream, list);
-
-            if (i) {
-                dojo.create(
-                    "hr",
-                    {"style": "page-break-after: always"}, this.target, "last"
-                );
-            }
-
-            dojo.place(list, this.target, "last");
-        }
-
-        return this; /* for chaining */
-    };
-
-    this.render_users = function(stream, list) {
-        for (var i = 0; i < this.users_by_stream[stream.id()].length; i++) {
-            var user = this.users_by_stream[stream.id()][i];
-            var node = dojo.clone(this.user_template);
-
-            if (user.reader()) {
-                n("barcode", node).innerHTML = user.reader().card().barcode();
-                n("name", node).innerHTML = dojo.string.substitute(
-                    "${0}, ${1} ${2}", [
-                        user.reader().family_name(),
-                        user.reader().first_given_name(),
-                        user.reader().second_given_name()
-                    ].map(function(n) { return n || ""; })
-                );
-                n("ou", node).innerHTML = user.reader().home_ou().shortname();
-                openils.Util.show(n("reader_container", node), "inline");
-            } else if (user.department()) {
-                n("department", node).innerHTML = user.department();
-                openils.Util.show(n("department_container", node), "inline");
-            }
-
-            if (user.note()) {
-                n("note", node).innerHTML = user.note();
-                openils.Util.show(n("note_container", node), "inline");
-            }
-
-            dojo.place(node, n("users", list), "last");
-        }
-    };
-
-    this.print = function() {
-        this.print_target.print();
-    }
-
-    this._sort_users = function() {
-        this.users_by_stream = {};
-        this.users.forEach(
-            function(user) {
-                var key = user.stream();
-                if (!self.users_by_stream[key])
-                    self.users_by_stream[key] = [];
-                self.users_by_stream[key].push(user);
-            }
-        );
-    };
-
-    /* Unfortunately, when we print the main window with dijits
-     * wrapping everything, the page-break-* CSS properties don't work
-     * inside of there, so we need an iframe to print from.
-     */
-    this._prepare_iframe = function() {
-        var iframe = dojo.create(
-            "iframe", {
-                "src": "", "width": "100%", "height": "500", "frameborder": 0
-            }, "iframe_in_here", "only"
-        );
-
-        iframe.contentWindow.document.open();
-        iframe.contentWindow.document.write(
-            "<html><head><style type='text/css'>" +
-            ".item-title { font-size: 130%; font-weight: bold; }\n" +
-            ".item-issuance-label { font-size: 120%; }\n" +
-            ".item-dist-and-stream { font-size: 110%; }\n" +
-            ".hidden { display: none; }\n" +
-            "</style></head>\n<body></body></html>"
-        );
-        iframe.contentWindow.document.close();
-        this.target = iframe.contentWindow.document.body;
-        this.print_target = iframe.contentWindow;
-    };
-
-    this._init = function(data) {
-        this.user_template = dojo.byId("user_template");
-        this.user_template.removeAttribute("id");
-        this.user_template.parentNode.removeChild(this.user_template);
-
-        this.list_template = dojo.byId("list_template");
-        this.list_template.removeAttribute("id");
-        this.list_template.parentNode.removeChild(this.list_template);
-
-        dojo.mixin(this, data);
-
-        this._sort_users();
-        this._prepare_iframe();
-    }
-
-    this._init.apply(this, arguments);
-}
-
-openils.Util.addOnLoad(
-    function() {
-        if (!xulG) {
-            alert(
-                "This interface is not designed for use outside " +
-                "the staff client." /* XXX i18n */
-            );
-        } else {
-            list_renderer = new ListRenderer(xulG.routing_list_data);
-            list_renderer.render().print();
-        }
-    }
-);
+require([
+       "dojo/string"
+       ],
+function(dojo_string){
+       
+       var list_renderer;
+       
+       function n(name, ctx) { return dojo.query("[name='" + name + "']", ctx)[0]; }
+       
+       function ListRenderer() {
+           var self = this;
+       
+           this.render = function() {
+               for (var i = 0; i < this.streams.length; i++) {
+                   var stream = this.streams[i];
+       
+                   if (!this.users_by_stream[stream.id()])
+                       continue; /* no users on this stream */
+       
+                   var list = dojo.clone(this.list_template);
+                   n("title", list).innerHTML = this.mvr.title();
+                   n("issuance_label", list).innerHTML = this.issuance.label();
+                   n("distribution_holding_lib", list).innerHTML =
+                       stream.distribution().holding_lib().shortname();
+                   n("distribution_label", list).innerHTML =
+                       stream.distribution().label();
+                   if (stream.routing_label()) {
+                       n("stream_routing_label", list).innerHTML =
+                           stream.routing_label();
+                       openils.Util.show(
+                           n("stream_routing_label", list), "inline"
+                       );
+                   } else {
+                       n("stream_id", list).innerHTML = stream.id();
+                       openils.Util.show(n("stream_id_container", list), "inline");
+                   }
+       
+                   this.render_users(stream, list);
+       
+                   if (i) {
+                       dojo.create(
+                           "hr",
+                           {"style": "page-break-after: always"}, this.target, "last"
+                       );
+                   }
+       
+                   dojo.place(list, this.target, "last");
+               }
+       
+               return this; /* for chaining */
+           };
+       
+           this.render_users = function(stream, list) {
+               for (var i = 0; i < this.users_by_stream[stream.id()].length; i++) {
+                   var user = this.users_by_stream[stream.id()][i];
+                   var node = dojo.clone(this.user_template);
+       
+                   if (user.reader()) {
+                       n("barcode", node).innerHTML = user.reader().card().barcode();
+                       n("name", node).innerHTML = dojo_string.substitute(
+                           "${0}, ${1} ${2}", [
+                               user.reader().family_name(),
+                               user.reader().first_given_name(),
+                               user.reader().second_given_name()
+                           ].map(function(n) { return n || ""; })
+                       );
+                       n("ou", node).innerHTML = user.reader().home_ou().shortname();
+                       openils.Util.show(n("reader_container", node), "inline");
+                   } else if (user.department()) {
+                       n("department", node).innerHTML = user.department();
+                       openils.Util.show(n("department_container", node), "inline");
+                   }
+       
+                   if (user.note()) {
+                       n("note", node).innerHTML = user.note();
+                       openils.Util.show(n("note_container", node), "inline");
+                   }
+       
+                   dojo.place(node, n("users", list), "last");
+               }
+           };
+       
+           this.print = function() {
+               this.print_target.print();
+           }
+       
+           this._sort_users = function() {
+               this.users_by_stream = {};
+               this.users.forEach(
+                   function(user) {
+                       var key = user.stream();
+                       if (!self.users_by_stream[key])
+                           self.users_by_stream[key] = [];
+                       self.users_by_stream[key].push(user);
+                   }
+               );
+           };
+       
+           /* Unfortunately, when we print the main window with dijits
+            * wrapping everything, the page-break-* CSS properties don't work
+            * inside of there, so we need an iframe to print from.
+            */
+           this._prepare_iframe = function() {
+               var iframe = dojo.create(
+                   "iframe", {
+                       "src": "", "width": "100%", "height": "500", "frameborder": 0
+                   }, "iframe_in_here", "only"
+               );
+       
+               iframe.contentWindow.document.open();
+               iframe.contentWindow.document.write(
+                   "<html><head><style type='text/css'>" +
+                   ".item-title { font-size: 130%; font-weight: bold; }\n" +
+                   ".item-issuance-label { font-size: 120%; }\n" +
+                   ".item-dist-and-stream { font-size: 110%; }\n" +
+                   ".hidden { display: none; }\n" +
+                   "</style></head>\n<body></body></html>"
+               );
+               iframe.contentWindow.document.close();
+               this.target = iframe.contentWindow.document.body;
+               this.print_target = iframe.contentWindow;
+           };
+       
+           this._init = function(data) {
+               this.user_template = dojo.byId("user_template");
+               this.user_template.removeAttribute("id");
+               this.user_template.parentNode.removeChild(this.user_template);
+       
+               this.list_template = dojo.byId("list_template");
+               this.list_template.removeAttribute("id");
+               this.list_template.parentNode.removeChild(this.list_template);
+       
+               dojo.mixin(this, data);
+       
+               this._sort_users();
+               this._prepare_iframe();
+           }
+       
+           this._init.apply(this, arguments);
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               if (!xulG) {
+                   alert(
+                       "This interface is not designed for use outside " +
+                       "the staff client." /* XXX i18n */
+                   );
+               } else {
+                   list_renderer = new ListRenderer(xulG.routing_list_data);
+                   list_renderer.render().print();
+               }
+           }
+       );
+       
+
+});
\ No newline at end of file
index d3e3d4b..2103f2c 100644 (file)
-dojo.require("dijit.form.Button");
-dojo.require("dijit.form.RadioButton");
-dojo.require("dijit.form.FilteringSelect");
-dojo.require("dijit.form.DropDownButton");
-dojo.require("dijit.TooltipDialog");
-dojo.require("dijit.layout.TabContainer");
-dojo.require("dijit.layout.ContentPane");
-dojo.require("dojox.grid.DataGrid");
-dojo.require("openils.widget.AutoGrid");
-dojo.require("openils.widget.ProgressDialog");
-dojo.require("openils.widget.HoldingCode");
-dojo.require("openils.PermaCrud");
-dojo.require("openils.CGI");
+require([
+       "dijit/form/Button",
+       "dijit/form/RadioButton",
+       "dijit/form/FilteringSelect",
+       "dijit/form/DropDownButton",
+       "dijit/TooltipDialog",
+       "dijit/layout/TabContainer",
+       "dijit/layout/ContentPane",
+       "dojox/grid/DataGrid",
+       "openils/widget/AutoGrid",
+       "openils/widget/ProgressDialog",
+       "openils/widget/HoldingCode",
+       "openils/PermaCrud",
+       "openils/CGI"
+       ],
+function(dijit_form_Button,
+       dijit_form_RadioButton,
+       dijit_form_FilteringSelect,
+       dijit_form_DropDownButton,
+       dijit_TooltipDialog,
+       dijit_layout_TabContainer,
+       dijit_layout_ContentPane,
+       dojox_grid_DataGrid,
+       openils_widget_AutoGrid,
+       openils_widget_ProgressDialog,
+       openils_widget_HoldingCode,
+       openils_PermaCrud,
+       openils_CGI){
+       
+       var pcrud;
+       var cgi;
+       var sub;
+       var sub_id;
+       
+       function node_by_name(name, ctx) {
+           return dojo.query("[name='" + name + "']", ctx)[0];
+       }
+       
+       /* typing save: add {get,set}Value() to all HTML <select> elements */
+       HTMLSelectElement.prototype.getValue = function() {
+           return this.options[this.selectedIndex].value;
+       }
+       HTMLSelectElement.prototype.setValue = function(s) {
+           for (var i = 0; i < this.options.length; i++) {
+               if (s == this.options[i].value) {
+                   this.selectedIndex = i;
+                   break;
+               }
+           }
+       }
+       
+       function load_sub_grid(id, oncomplete) {
+           if (!pcrud) return; /* first run, onLoad hasn't fired yet */
+           if (!sub_grid._fresh) {
+               var dist_ids = pcrud.search(
+                   "sdist", {"subscription": id}, {"id_list": true}
+               );
+               pcrud.retrieve(
+                   "ssub", id, {
+                       "onresponse": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               sub = r;
+                               var data = ssub.toStoreData([r]);
+                               data.items[0].num_dist = dist_ids ? dist_ids.length : 0;
+                               sub_grid.setStore(
+                                   new dojo.data.ItemFileReadStore({"data": data})
+                               );
+                               sub_grid._fresh = true;
+                           }
+                       },
+                       "oncomplete": function() {
+                           if (oncomplete) oncomplete();
+                       }
+                   }
+               );
+           }
+       }
+       
+       /* TODO: make these formatters caching */
+       function format_bib(bib_id) {
+           if (!bib_id) {
+               return "";
+           } else {
+               var result;
+               fieldmapper.standardRequest(
+                   ["open-ils.search",
+                       "open-ils.search.biblio.record.mods_slim.retrieve"], {
+                       "async": false,
+                       "params": [bib_id],
+                       "oncomplete": function(r) {
+                           if (r = openils.Util.readResponse(r)) {
+                               var parts = [];
+                               if (r.title())
+                                   parts.push(r.title());
+                               if (r.author())
+                                   parts.push(r.author());
+                               if (r.author())
+                                   parts.push(r.publisher());
+       
+                               if (!parts.length)
+                                   parts.push(r.tcn());
+       
+                               result = parts.join(" / ");
+                           }
+                       }
+                   }
+               );
+               return "<a href='" + oilsBasePath +
+                   "/serial/list_subscription?record_entry=" + bib_id + "'>" +
+                   result + "</a>";
+           }
+       }
+       
+       function format_date(s) {
+           return s ? openils.Util.timeStamp(s, {"selector": "date"}) : "";
+       }
+       
+       function format_org_unit(aou_id) {
+           return aou_id ? aou.findOrgUnit(aou_id).shortname() : "";
+       }
+       
+       function get_id_and_label(rowIndex, item) {
+           if (!item) return {"id": "", "label": ""};
+           return {
+               "id": this.grid.store.getValue(item, "id"),
+               "label": this.grid.store.getValue(item, "label")
+           };
+       }
+       
+       function format_siss_label(blob) {
+           if (!blob.id) return "";
+           return "<a href='" +
+               oilsBasePath + "/serial/list_item?issuance=" + blob.id +
+               "'>" + (blob.label ? blob.label : "[None]") + "</a>"; /* XXX i18n */
+       }
+       
+       function format_sdist_label(blob) {
+           if (!blob.id) return "";
+           var link = "<a href='" +
+               oilsBasePath + "/serial/list_stream?distribution=" + blob.id +
+               "'>" + (blob.label ? blob.label : "[None]") + "</a>"; /* XXX i18n */
+       
+           var sstr_list = pcrud.search(
+               "sstr",{"distribution":blob.id},{"id_list":true}
+           );
+           count = sstr_list ? sstr_list.length : 0;
+           link += "&nbsp;&nbsp; " + count + " stream(s)";
+           return link;
+       }
+       
+       function append_stream_count(dist_id) {
+           var span = dojo.byId("dist_link_" + dist_id);
+           if (span.childNodes.length) /* textNodes count as childnodes */
+               return;
+           pcrud.search(
+               "sstr", {"distribution": dist_id}, {
+                   "id_list": true,
+                   "oncomplete": function(r) {
+                       var resp = openils.Util.readResponse(r);
+                       var count = resp ? resp.length : 0;
+       
+                       /* XXX i18n */
+                       span.innerHTML = "&nbsp;&nbsp; " + count + " stream(s)";
+                   }
+               }
+           );
+       }
+       
+       function open_batch_receive() {
+           if (!sub) {
+               alert("Let the interface load all the way first.");
+               return;
+           }
+       
+           var url = "/xul/server/serial/batch_receive.xul?docid=" +
+               sub.record_entry() + "&subid=" + sub.id();
+       
+           try {
+               openils.XUL.newTabEasy(url, "Batch Receive"); /* XXX i18n */
+           } catch (E) {
+               location.href = url;
+           }
+       }
+       
+       function toggle_clone_ident_field(dij) {
+           setTimeout(
+               function() {
+                   var disabled = !dij.attr("checked");
+                   clone_ident.attr("disabled", disabled);
+                   if (!disabled) clone_ident.focus();
+               }, 175
+           );
+       }
+       
+       function clone_subscription(form) {
+           if (form.use_ident == "yes") {
+               fieldmapper.standardRequest(
+                   ["open-ils.serial",
+                       "open-ils.serial.biblio.record_entry.by_identifier.atomic"], {
+                       "params": [form.ident, {"id_list": true}],
+                       "async": true,
+                       "oncomplete": function(r) {
+                           r = openils.Util.readResponse(r);
+                           if (!r || !r.length) {
+                               alert("No matches for that indentifier."); /*XXX i18n*/
+                           } else if (r.length != 1) {
+                               alert("Too many matches for that identifier. Use a " +
+                                   "unique identifier."); /* XXX i18n */
+                           } else {
+                               _clone_subscription(r[0]);
+                           }
+                       }
+                   }
+               );
+           } else {
+               _clone_subscription();
+           }
+       }
+       
+       function _clone_subscription(bre_id) {
+           progress_dialog.show(true);
+       
+           fieldmapper.standardRequest(
+               ["open-ils.serial", "open-ils.serial.subscription.clone"], {
+                   "params": [openils.User.authtoken, sub_id, bre_id],
+                   "async": false,
+                   "oncomplete": function(r) {
+                       progress_dialog.hide();
+                       if (!(r = openils.Util.readResponse(r))) {
+                           alert("Error cloning subscription."); /* XXX i18n */
+                       } else {
+                           location.href =
+                               oilsBasePath + "/serial/subscription?id=" + r;
+                       }
+       
+                       /* cloning doesn't clone holdings, so nothing changes at
+                        * OPAC view just because of this, so no need to try
+                        * reload_opac().  */
+                   }
+               }
+           );
+       }
+       
+       openils.Util.addOnLoad(
+           function() {
+               var tab_dispatch = {
+                   "distributions": distributions_tab,
+                   "issuances": issuances_tab
+               };
+       
+               cgi = new openils_CGI();
+               pcrud = new openils_PermaCrud();
+       
+               sub_id = cgi.param("id");
+               load_sub_grid(
+                   sub_id,
+                   (cgi.param("tab") in tab_dispatch) ?
+                       function() {
+                           tab_container.selectChild(
+                               tab_dispatch[cgi.param("tab")]
+                           );
+                       } : null
+               );
+           }
+       );
+       
 
-var pcrud;
-var cgi;
-var sub;
-var sub_id;
-
-function node_by_name(name, ctx) {
-    return dojo.query("[name='" + name + "']", ctx)[0];
-}
-
-/* typing save: add {get,set}Value() to all HTML <select> elements */
-HTMLSelectElement.prototype.getValue = function() {
-    return this.options[this.selectedIndex].value;
-}
-HTMLSelectElement.prototype.setValue = function(s) {
-    for (var i = 0; i < this.options.length; i++) {
-        if (s == this.options[i].value) {
-            this.selectedIndex = i;
-            break;
-        }
-    }
-}
-
-function load_sub_grid(id, oncomplete) {
-    if (!pcrud) return; /* first run, onLoad hasn't fired yet */
-    if (!sub_grid._fresh) {
-        var dist_ids = pcrud.search(
-            "sdist", {"subscription": id}, {"id_list": true}
-        );
-        pcrud.retrieve(
-            "ssub", id, {
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        sub = r;
-                        var data = ssub.toStoreData([r]);
-                        data.items[0].num_dist = dist_ids ? dist_ids.length : 0;
-                        sub_grid.setStore(
-                            new dojo.data.ItemFileReadStore({"data": data})
-                        );
-                        sub_grid._fresh = true;
-                    }
-                },
-                "oncomplete": function() {
-                    if (oncomplete) oncomplete();
-                }
-            }
-        );
-    }
-}
-
-/* TODO: make these formatters caching */
-function format_bib(bib_id) {
-    if (!bib_id) {
-        return "";
-    } else {
-        var result;
-        fieldmapper.standardRequest(
-            ["open-ils.search",
-                "open-ils.search.biblio.record.mods_slim.retrieve"], {
-                "async": false,
-                "params": [bib_id],
-                "oncomplete": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        var parts = [];
-                        if (r.title())
-                            parts.push(r.title());
-                        if (r.author())
-                            parts.push(r.author());
-                        if (r.author())
-                            parts.push(r.publisher());
-
-                        if (!parts.length)
-                            parts.push(r.tcn());
-
-                        result = parts.join(" / ");
-                    }
-                }
-            }
-        );
-        return "<a href='" + oilsBasePath +
-            "/serial/list_subscription?record_entry=" + bib_id + "'>" +
-            result + "</a>";
-    }
-}
-
-function format_date(s) {
-    return s ? openils.Util.timeStamp(s, {"selector": "date"}) : "";
-}
-
-function format_org_unit(aou_id) {
-    return aou_id ? aou.findOrgUnit(aou_id).shortname() : "";
-}
-
-function get_id_and_label(rowIndex, item) {
-    if (!item) return {"id": "", "label": ""};
-    return {
-        "id": this.grid.store.getValue(item, "id"),
-        "label": this.grid.store.getValue(item, "label")
-    };
-}
-
-function format_siss_label(blob) {
-    if (!blob.id) return "";
-    return "<a href='" +
-        oilsBasePath + "/serial/list_item?issuance=" + blob.id +
-        "'>" + (blob.label ? blob.label : "[None]") + "</a>"; /* XXX i18n */
-}
-
-function format_sdist_label(blob) {
-    if (!blob.id) return "";
-    var link = "<a href='" +
-        oilsBasePath + "/serial/list_stream?distribution=" + blob.id +
-        "'>" + (blob.label ? blob.label : "[None]") + "</a>"; /* XXX i18n */
-
-    var sstr_list = pcrud.search(
-        "sstr",{"distribution":blob.id},{"id_list":true}
-    );
-    count = sstr_list ? sstr_list.length : 0;
-    link += "&nbsp;&nbsp; " + count + " stream(s)";
-    return link;
-}
-
-function append_stream_count(dist_id) {
-    var span = dojo.byId("dist_link_" + dist_id);
-    if (span.childNodes.length) /* textNodes count as childnodes */
-        return;
-    pcrud.search(
-        "sstr", {"distribution": dist_id}, {
-            "id_list": true,
-            "oncomplete": function(r) {
-                var resp = openils.Util.readResponse(r);
-                var count = resp ? resp.length : 0;
-
-                /* XXX i18n */
-                span.innerHTML = "&nbsp;&nbsp; " + count + " stream(s)";
-            }
-        }
-    );
-}
-
-function open_batch_receive() {
-    if (!sub) {
-        alert("Let the interface load all the way first.");
-        return;
-    }
-
-    var url = "/xul/server/serial/batch_receive.xul?docid=" +
-        sub.record_entry() + "&subid=" + sub.id();
-
-    try {
-        openils.XUL.newTabEasy(url, "Batch Receive"); /* XXX i18n */
-    } catch (E) {
-        location.href = url;
-    }
-}
-
-function toggle_clone_ident_field(dij) {
-    setTimeout(
-        function() {
-            var disabled = !dij.attr("checked");
-            clone_ident.attr("disabled", disabled);
-            if (!disabled) clone_ident.focus();
-        }, 175
-    );
-}
-
-function clone_subscription(form) {
-    if (form.use_ident == "yes") {
-        fieldmapper.standardRequest(
-            ["open-ils.serial",
-                "open-ils.serial.biblio.record_entry.by_identifier.atomic"], {
-                "params": [form.ident, {"id_list": true}],
-                "async": true,
-                "oncomplete": function(r) {
-                    r = openils.Util.readResponse(r);
-                    if (!r || !r.length) {
-                        alert("No matches for that indentifier."); /*XXX i18n*/
-                    } else if (r.length != 1) {
-                        alert("Too many matches for that identifier. Use a " +
-                            "unique identifier."); /* XXX i18n */
-                    } else {
-                        _clone_subscription(r[0]);
-                    }
-                }
-            }
-        );
-    } else {
-        _clone_subscription();
-    }
-}
-
-function _clone_subscription(bre_id) {
-    progress_dialog.show(true);
-
-    fieldmapper.standardRequest(
-        ["open-ils.serial", "open-ils.serial.subscription.clone"], {
-            "params": [openils.User.authtoken, sub_id, bre_id],
-            "async": false,
-            "oncomplete": function(r) {
-                progress_dialog.hide();
-                if (!(r = openils.Util.readResponse(r))) {
-                    alert("Error cloning subscription."); /* XXX i18n */
-                } else {
-                    location.href =
-                        oilsBasePath + "/serial/subscription?id=" + r;
-                }
-
-                /* cloning doesn't clone holdings, so nothing changes at
-                 * OPAC view just because of this, so no need to try
-                 * reload_opac().  */
-            }
-        }
-    );
-}
-
-openils.Util.addOnLoad(
-    function() {
-        var tab_dispatch = {
-            "distributions": distributions_tab,
-            "issuances": issuances_tab
-        };
-
-        cgi = new openils.CGI();
-        pcrud = new openils.PermaCrud();
-
-        sub_id = cgi.param("id");
-        load_sub_grid(
-            sub_id,
-            (cgi.param("tab") in tab_dispatch) ?
-                function() {
-                    tab_container.selectChild(
-                        tab_dispatch[cgi.param("tab")]
-                    );
-                } : null
-        );
-    }
-);
+});
\ No newline at end of file
index eb4fae4..b2b64de 100644 (file)
-dojo.require("dijit.form.DateTextBox");
+require([
+       "dijit/form/DateTextBox"
+       ],
+function(dijit_form_DateTextBox){
+       
+       function fresh_scap_selector(grid) {
+           /* this really needs to be sync, not async */
+           pcrud.search(
+               "scap", {"subscription": sub_id, "active": "t"}, {
+                   "timeout": 10,
+                   "oncomplete": function(r) {
+                       var data = scap.toStoreData(openils.Util.readResponse(r));
+                       var selector = new dijit.form.FilteringSelect(
+                           {
+                               "store": new dojo.data.ItemFileReadStore({"data":data}),
+                               "searchAttr": "id"
+                           },
+                           dojo.create("span")
+                       );
+                       selector.shove = {
+                           "create": data.items.length ? data.items[0].id : ""
+                       };
+                       dojo.connect(
+                           selector, "onChange", null, function() {
+                               if (this.item) {
+                                   var widget =
+                                       iss_grid.overrideEditWidgets.holding_type;
+                                   widget.attr("value", this.item.type);
+                                   widget.attr("disabled", true);
+                               }
+                           }
+                       );
+       
+                       grid.overrideEditWidgets.caption_and_pattern = selector;
+                       if (grid.overrideEditWidgets.holding_code) {
+                           grid.overrideEditWidgets.holding_code.update_scap_selector(
+                               selector
+                           );
+                       } else {
+                           grid.overrideEditWidgets.holding_code =
+                               new openils.widget.HoldingCode({
+                                   "scap_selector": selector
+                               });
+                           grid.overrideEditWidgets.holding_code.shove = {
+                               "create": "[]"
+                           };
+                           grid.overrideEditWidgets.holding_code.startup();
+                       }
+       
+                       grid.overrideEditWidgets.date_published =
+                           new dijit_form_DateTextBox();
+                       grid.overrideEditWidgets.date_published.shove = {};
+                       grid.overrideEditWidgets.holding_code.date_widget =
+                           grid.overrideEditWidgets.date_published;
+                   }
+               }
+           );
+       }
+       
+       function prepare_prediction_dialog() {
+           if (sub.end_date()) {
+               prediction_dialog_end_date.attr("disabled", false);
+               prediction_dialog_end_date.attr("checked", true);
+           } else {
+               prediction_dialog_end_num.attr("checked", true);
+               prediction_dialog_end_date.attr("disabled", true);
+               prediction_dialog_num_to_predict.focus();
+           }
+           prediction_dialog_submit.attr("disabled", false);
+       }
+       
+       function generate_predictions(fields) {
+           var args = {"ssub_id": sub.id()};
+       
+           if (fields.end_how == "date") {
+               args.end_date = sub.end_date();
+           } else if ((num = Number(fields.num_to_predict)) > 0)  {
+               args.num_to_predict = num;
+           } else {
+               alert("Go with a whole, positive number."); /* XXX i18n */
+               return;
+           }
+       
+           progress_dialog.show(true);
+           try {
+               fieldmapper.standardRequest(
+                   ["open-ils.serial", "open-ils.serial.make_predictions"], {
+                       "params": [openils.User.authtoken, args],
+                       "async": true,
+                       "onresponse": function(r) {
+                           openils.Util.readResponse(r); /* tests for events */
+                       },
+                       "oncomplete": function() {
+                           progress_dialog.hide();
+                           iss_grid.refresh();
+                       }
+                   }
+               );
+           } catch (E) {
+               alert(E);
+               progess_dialog.hide();
+           }
+       }
+       
 
-function fresh_scap_selector(grid) {
-    /* this really needs to be sync, not async */
-    pcrud.search(
-        "scap", {"subscription": sub_id, "active": "t"}, {
-            "timeout": 10,
-            "oncomplete": function(r) {
-                var data = scap.toStoreData(openils.Util.readResponse(r));
-                var selector = new dijit.form.FilteringSelect(
-                    {
-                        "store": new dojo.data.ItemFileReadStore({"data":data}),
-                        "searchAttr": "id"
-                    },
-                    dojo.create("span")
-                );
-                selector.shove = {
-                    "create": data.items.length ? data.items[0].id : ""
-                };
-                dojo.connect(
-                    selector, "onChange", null, function() {
-                        if (this.item) {
-                            var widget =
-                                iss_grid.overrideEditWidgets.holding_type;
-                            widget.attr("value", this.item.type);
-                            widget.attr("disabled", true);
-                        }
-                    }
-                );
-
-                grid.overrideEditWidgets.caption_and_pattern = selector;
-                if (grid.overrideEditWidgets.holding_code) {
-                    grid.overrideEditWidgets.holding_code.update_scap_selector(
-                        selector
-                    );
-                } else {
-                    grid.overrideEditWidgets.holding_code =
-                        new openils.widget.HoldingCode({
-                            "scap_selector": selector
-                        });
-                    grid.overrideEditWidgets.holding_code.shove = {
-                        "create": "[]"
-                    };
-                    grid.overrideEditWidgets.holding_code.startup();
-                }
-
-                grid.overrideEditWidgets.date_published =
-                    new dijit.form.DateTextBox();
-                grid.overrideEditWidgets.date_published.shove = {};
-                grid.overrideEditWidgets.holding_code.date_widget =
-                    grid.overrideEditWidgets.date_published;
-            }
-        }
-    );
-}
-
-function prepare_prediction_dialog() {
-    if (sub.end_date()) {
-        prediction_dialog_end_date.attr("disabled", false);
-        prediction_dialog_end_date.attr("checked", true);
-    } else {
-        prediction_dialog_end_num.attr("checked", true);
-        prediction_dialog_end_date.attr("disabled", true);
-        prediction_dialog_num_to_predict.focus();
-    }
-    prediction_dialog_submit.attr("disabled", false);
-}
-
-function generate_predictions(fields) {
-    var args = {"ssub_id": sub.id()};
-
-    if (fields.end_how == "date") {
-        args.end_date = sub.end_date();
-    } else if ((num = Number(fields.num_to_predict)) > 0)  {
-        args.num_to_predict = num;
-    } else {
-        alert("Go with a whole, positive number."); /* XXX i18n */
-        return;
-    }
-
-    progress_dialog.show(true);
-    try {
-        fieldmapper.standardRequest(
-            ["open-ils.serial", "open-ils.serial.make_predictions"], {
-                "params": [openils.User.authtoken, args],
-                "async": true,
-                "onresponse": function(r) {
-                    openils.Util.readResponse(r); /* tests for events */
-                },
-                "oncomplete": function() {
-                    progress_dialog.hide();
-                    iss_grid.refresh();
-                }
-            }
-        );
-    } catch (E) {
-        alert(E);
-        progess_dialog.hide();
-    }
-}
+});
\ No newline at end of file
index c7ed37c..cd6f94b 100644 (file)
-/* ---------------------------------------------------------------------------
-# Copyright (C) 2008  Georgia Public Library Service
-# Bill Erickson <erickson@esilibrary.com>
-# 
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# --------------------------------------------------------------------------- */
-dojo.require("dojo.parser");
-dojo.require("dojo.io.iframe"); 
-dojo.require("dijit.ProgressBar"); 
-dojo.require("dijit.form.FilteringSelect"); 
-dojo.require("dijit.layout.ContentPane");
-dojo.require("dijit.layout.TabContainer");
-dojo.require("dijit.layout.LayoutContainer");
-dojo.require('dijit.form.Button');
-dojo.require('dijit.form.CheckBox');
-dojo.require('dijit.Toolbar');
-dojo.require('dijit.Tooltip');
-dojo.require('dijit.Menu');
-dojo.require("dijit.Dialog");
-dojo.require("dojo.cookie");
-dojo.require('dojox.grid.DataGrid');
-dojo.require("dojo.data.ItemFileReadStore");
-dojo.require('dojo.date.locale');
-dojo.require('dojo.date.stamp');
-dojo.require("fieldmapper.Fieldmapper");
-dojo.require("fieldmapper.dojoData");
-dojo.require("fieldmapper.OrgUtils");
-dojo.require('openils.CGI');
-dojo.require('openils.User');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('openils.MarcXPathParser');
-dojo.require('openils.widget.GridColumnPicker');
-dojo.require('openils.PermaCrud');
-dojo.require('openils.widget.OrgUnitFilteringSelect');
-dojo.require('openils.widget.AutoGrid');
-dojo.require('openils.widget.AutoFieldWidget');
-dojo.require('openils.widget.ProgressDialog');
-
-
-var globalDivs = [
-    'vl-generic-progress',
-    'vl-generic-progress-with-total',
-    'vl-marc-upload-div',
-    'vl-queue-div',
-    'vl-match-div',
-    'vl-marc-html-div',
-    'vl-queue-select-div',
-    'vl-marc-upload-status-div',
-    'vl-attr-editor-div',
-    'vl-marc-export-div',
-    'vl-profile-editor-div',
-    'vl-item-attr-editor-div',
-    'vl-import-error-div'
-];
-
-var authtoken;
-var VANDELAY_URL = '/vandelay-upload';
-var bibAttrDefs = [];
-var authAttrDefs = [];
-var queuedRecords = [];
-var queuedRecordsMap = {};
-var bibAttrsFetched = false;
-var authAttrsFetched = false;
-var attrDefMap = {}; // maps attr def code names to attr def ids
-var currentType;
-var currentQueueId = null;
-var userCache = {};
-var currentMatchedRecords; // set of loaded matched bib records
-var currentOverlayRecordsMap; // map of import record to overlay record
-var currentOverlayRecordsMapGid; // map of import record to overlay record grid id
-var currentImportRecId; // when analyzing matches, this is the current import record
-var userBibQueues = []; // only non-complete queues
-var userAuthQueues = []; // only non-complete queues
-var allUserBibQueues;
-var allUserAuthQueues;
-var selectableGridRecords;
-var cgi = new openils.CGI();
-var vlQueueGridColumePicker = {};
-var vlBibSources = [];
-var importItemDefs = [];
-var matchSets = {};
-var mergeProfiles = [];
-var copyStatusCache = {};
-var copyLocationCache = {};
-var localeStrings;
-
-/**
-  * Grab initial data
-  */
-function vlInit() {
-
-    dojo.requireLocalization("openils.vandelay", "vandelay");
-    localeStrings = dojo.i18n.getLocalization("openils.vandelay", "vandelay");
-
-    authtoken = openils.User.authtoken;
-    var initNeeded = 8; // how many async responses do we need before we're init'd 
-    var initCount = 0; // how many async reponses we've received
-
-    openils.Util.registerEnterHandler(
-        vlQueueDisplayPage.domNode, function(){retrieveQueuedRecords();});
-    openils.Util.addCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
-
-    function checkInitDone() {
-        initCount++;
-        if(initCount == initNeeded)
-            runStartupCommands();
-    }
-
-    mergeProfiles = new openils.PermaCrud().retrieveAll('vmp');
-    vlUploadMergeProfile.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
-    vlUploadMergeProfile.labelAttr = 'name';
-    vlUploadMergeProfile.searchAttr = 'name';
-    vlUploadMergeProfile.startup();
-
-    vlUploadMergeProfile2.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
-    vlUploadMergeProfile2.labelAttr = 'name';
-    vlUploadMergeProfile2.searchAttr = 'name';
-    vlUploadMergeProfile2.startup();
-
-    vlUploadFtMergeProfile.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
-    vlUploadFtMergeProfile.labelAttr = 'name';
-    vlUploadFtMergeProfile.searchAttr = 'name';
-    vlUploadFtMergeProfile.startup();
-
-    vlUploadFtMergeProfile2.store = new dojo.data.ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
-    vlUploadFtMergeProfile2.labelAttr = 'name';
-    vlUploadFtMergeProfile2.searchAttr = 'name';
-    vlUploadFtMergeProfile2.startup();
-
-
-    // Fetch the bib and authority attribute definitions 
-    vlFetchBibAttrDefs(function () { checkInitDone(); });
-    vlFetchAuthAttrDefs(function () { checkInitDone(); });
-
-    vlRetrieveQueueList('bib', null, 
-        function(list) {
-            allUserBibQueues = list;
-            for(var i = 0; i < allUserBibQueues.length; i++) {
-                if(allUserBibQueues[i].complete() == 'f')
-                    userBibQueues.push(allUserBibQueues[i]);
-            }
-            checkInitDone();
-        }
-    );
-
-    vlRetrieveQueueList('auth', null, 
-        function(list) {
-            allUserAuthQueues = list;
-            for(var i = 0; i < allUserAuthQueues.length; i++) {
-                if(allUserAuthQueues[i].complete() == 'f')
-                    userAuthQueues.push(allUserAuthQueues[i]);
-            }
-            checkInitDone();
-        }
-    );
-
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', 'open-ils.permacrud.search.cbs.atomic'],
-        {   async: true,
-            params: [authtoken, {id:{"!=":null}}, {order_by:{cbs:'id'}}],
-            oncomplete : function(r) {
-                vlBibSources = openils.Util.readResponse(r, false, true);
-                checkInitDone();
-            }
-        }
-    );
-
-    var owner = fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(new openils.User().user.ws_ou()));
-    new openils.PermaCrud().search('viiad', 
-        {owner: owner.map(function(org) { return org.id(); })},
-        {   async: true,
-            oncomplete: function(r) {
-                importItemDefs = openils.Util.readResponse(r);
-                checkInitDone();
-            }
-        }
-    );
-
-    new openils.PermaCrud().search('vms',
-        {owner: owner.map(function(org) { return org.id(); })},
-        {   async: true,
-            oncomplete: function(r) {
-                var sets = openils.Util.readResponse(r);
-                dojo.forEach(sets, 
-                    function(set) {
-                        if(!matchSets[set.mtype()])
-                            matchSets[set.mtype()] = [];
-                        matchSets[set.mtype()].push(set);
-                    }
-                );
-                checkInitDone();
-            }
-        }
-    );
-
-    new openils.PermaCrud().retrieveAll('ccs',
-        {   async: true,
-            oncomplete: function(r) {
-                var stats = openils.Util.readResponse(r);
-                dojo.forEach(stats, function(stat){copyStatusCache[stat.id()] = stat});
-                checkInitDone();
-            }
-        }
-    );
-
-    vlAttrEditorInit();
-    vlExportInit();
-}
-
-
-openils.Util.addOnLoad(vlInit);
-
-
-// fetch the bib and authority attribute definitions
-
-function vlFetchBibAttrDefs(postcomplete) {
-    bibAttrDefs = [];
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', 'open-ils.permacrud.search.vqbrad'],
-        {   async: true,
-            params: [authtoken, {id:{'!=':null}}],
-            onresponse: function(r) {
-                var def = r.recv().content(); 
-                if(e = openils.Event.parse(def[0])) 
-                    return alert(e);
-                bibAttrDefs.push(def);
-            },
-            oncomplete: function() {
-                bibAttrDefs = bibAttrDefs.sort(
-                    function(a, b) {
-                        if(a.id() > b.id()) return 1;
-                        if(a.id() < b.id()) return -1;
-                        return 0;
-                    }
-                );
-                postcomplete();
-            }
-        }
-    );
-}
-
-function vlFetchAuthAttrDefs(postcomplete) {
-    authAttrDefs = [];
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', 'open-ils.permacrud.search.vqarad'],
-        {   async: true,
-            params: [authtoken, {id:{'!=':null}}],
-            onresponse: function(r) {
-                var def = r.recv().content(); 
-                if(e = openils.Event.parse(def[0])) 
-                    return alert(e);
-                authAttrDefs.push(def);
-            },
-            oncomplete: function() {
-                authAttrDefs = authAttrDefs.sort(
-                    function(a, b) {
-                        if(a.id() > b.id()) return 1;
-                        if(a.id() < b.id()) return -1;
-                        return 0;
-                    }
-                );
-                postcomplete();
-            }
-        }
-    );
-}
-
-function vlRetrieveQueueList(type, filter, onload) {
-    type = (type == 'bib') ? type : 'authority';
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.owner.retrieve.atomic'],
-        {   async: true,
-            params: [authtoken, null, filter],
-            oncomplete: function(r) {
-                var list = r.recv().content();
-                if(e = openils.Event.parse(list[0]))
-                    return alert(e);
-                onload(list);
-            }
-        }
-    );
-
-}
-
-function displayGlobalDiv(id) {
-    for(var i = 0; i < globalDivs.length; i++) {
-        try {
-            dojo.style(dojo.byId(globalDivs[i]), 'display', 'none');
-        } catch(e) {
-            alert('please define div ' + globalDivs[i]);
-        }
-    }
-    dojo.style(dojo.byId(id),'display','block');
-
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-marc-export'), 'toolbar_selected');
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-queue-select'), 'toolbar_selected');
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-attr-editor'), 'toolbar_selected');
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-profile-editor'), 'toolbar_selected');
-    openils.Util.removeCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
-
-    if(dojo.byId('vl-match-set-iframe'))
-        dojo.byId('vl-match-set-editor-div').removeChild(dojo.byId('vl-match-set-iframe'));
-
-    switch(id) {
-        case 'vl-marc-export-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-marc-export'), 'toolbar_selected');
-            break;
-        case 'vl-marc-upload-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
-            break;
-        case 'vl-queue-select-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-queue-select'), 'toolbar_selected');
-            break;
-        case 'vl-attr-editor-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-attr-editor'), 'toolbar_selected');
-            break;
-        case 'vl-profile-editor-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-profile-editor'), 'toolbar_selected');
-            break;
-        case 'vl-item-attr-editor-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-import-item-attr-editor'), 'toolbar_selected');
-            break;
-        case 'vl-match-set-editor-div':
-            openils.Util.addCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
-            break;
-    }
-}
-
-function runStartupCommands() {
-    openils.Util.hide(dojo.byId('vl-page-loading'));
-    openils.Util.show(dojo.byId('vl-body-wrapper'));
-    currentQueueId = cgi.param('qid');
-    currentType = cgi.param('qtype');
-    dojo.style('vl-nav-bar', 'visibility', 'visible');
-    if(currentQueueId)
-        return retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-    if (cgi.param('page', 'inspectq'))
-        return displayGlobalDiv('vl-queue-select-div');
-        
-    vlShowUploadForm();
-}
-
-/**
-  * asynchronously upload a file of MARC records
-  */
-function uploadMARC(onload){
-    dojo.byId('vl-upload-status-count').innerHTML = '0';
-    dojo.byId('vl-ses-input').value = authtoken;
-    displayGlobalDiv('vl-marc-upload-status-div');
-    dojo.io.iframe.send({
-        url: VANDELAY_URL,
-        method: "post",
-        handleAs: "html",
-        form: dojo.byId('vl-marc-upload-form'),
-        handle: function(data,ioArgs){
-            var content = data.documentElement.textContent;
-            onload(content);
-        }
-    });
-}      
-
-/**
-  * Creates a new vandelay queue
-  */
-function createQueue(queueName, type, onload, importDefId, matchSet) {
-    var name = (type=='bib') ? 'bib' : 'authority';
-    var method = 'open-ils.vandelay.'+ name +'_queue.create'
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', method],
-        {   async: true,
-            params: [authtoken, queueName, null, name, matchSet, importDefId],
-            oncomplete : function(r) {
-                var queue = r.recv().content();
-                if(e = openils.Event.parse(queue)) 
-                    return alert(e);
-                onload(queue);
-            }
-        }
-    );
-}
-
-/**
-  * Tells vandelay to pull a batch of records from the cache and explode them
-  * out into the vandelay tables
-  */
-function processSpool(key, queueId, type, onload) {
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.'+type+'.process_spool'],
-        {   async: true,
-            params: [authtoken, key, queueId],
-            onresponse : function(r) {
-                var resp = r.recv().content();
-                if(e = openils.Event.parse(resp)) 
-                    return alert(e);
-                dojo.byId('vl-upload-status-count').innerHTML = resp;
-            },
-            oncomplete : function(r) {onload();}
-        }
-    );
-}
-
-function vlExportInit() {
-
-    // queue export
-    var qsel = dojo.byId('vl-queue-export-options');
-    qsel.onchange = function(newVal) {
-        var value = qsel.options[qsel.selectedIndex].value;
-        qsel.selectedIndex = 0;
-        if(!value) return;
-        if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
-        retrieveQueuedRecords(
-            currentType, 
-            currentQueueId, 
-            function(r) { 
-                exportHandler(value, r);
-                displayGlobalDiv('vl-queue-div');
-            },
-            value
-        );
-    }
-
-    // item export
-    var isel = dojo.byId('vl-item-export-options');
-    isel.onchange = function(newVal) {
-        var value = isel.options[isel.selectedIndex].value;
-        isel.selectedIndex = 0;
-        if(!value) return;
-        if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
-
-        displayGlobalDiv('vl-generic-progress');
-        var method = 'open-ils.vandelay.import_item.queue.export.' + value + '.atomic';
-
-        fieldmapper.standardRequest(
-            ['open-ils.vandelay', method],
-            {
-                params : [
-                    authtoken, 
-                    currentQueueId, 
-                    {with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null}
-                ],
-                async : true,
-                oncomplete : function(r) {exportHandler(value, r)}
-            }
-        );
-    }
-}
-
-function exportHandler(type, response) {
-    displayGlobalDiv('vl-import-error-div');
-    try {
-        var content = openils.Util.readResponse(response);
-        if (type=='email') {
-            if (content==1) { alert('Email sent.'); return; }
-            throw(content);
-        }
-        /* handle .atomic versus non-atomic method calls */
-        content = content.constructor == Array
-            ? content[0].template_output().data()
-            : content.template_output().data();
-        switch(type) {
-            case 'print':
-                openils.Util.printHtmlString(content);
-            break;
-            case 'csv':
-                //content = content.replace(/\\t/g,'\t'); // if we really wanted to do .tsv instead
-                openils.XUL.contentToFileSaveDialog(content, null, {
-                    defaultString : 'VandelayExport.csv',
-                    defaultExtension : '.csv',
-                    filterName : 'CSV',
-                    filterExtension : '*.csv',
-                    filterAll : true
-                } );
-            break;
-            default:
-                alert('response = ' + response + '\tcontent:\n' + content);
-        }
-    } catch(E) {
-        alert('Error exporting data: ' + E);
-    }
-}
-
-function retrieveQueuedRecords(type, queueId, onload, doExport) {
-    displayGlobalDiv('vl-generic-progress');
-    queuedRecords = [];
-    queuedRecordsMap = {};
-    currentOverlayRecordsMap = {};
-    currentOverlayRecordsMapGid = {};
-    selectableGridRecords = {};
-
-    if(!type) type = currentType;
-    if(!queueId) queueId = currentQueueId;
-    if(!onload) onload = handleRetrieveRecords;
-
-    var method = 'open-ils.vandelay.'+type+'_queue.records.retrieve';
-
-    if(doExport) method += '.export.' + doExport;
-    if(vlQueueGridShowMatches.checked)
-        method = method.replace('records', 'records.matches');
-
-    method += '.atomic';
-
-    var sel = dojo.byId('vl-queue-display-limit-selector');
-    var limit = parseInt(sel.options[sel.selectedIndex].value);
-    var offset = limit * parseInt(vlQueueDisplayPage.attr('value')-1);
-
-    var params =  [authtoken, queueId, {clear_marc: 1, offset: offset, limit: limit, flesh_import_items:1}];
-    if(vlQueueGridShowNonImport.checked)
-        params[2].non_imported = 1;
-
-    if(vlQueueGridShowImportErrors.checked)
-        params[2].with_import_error = 1;
-
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', method],
-        {   async: true,
-            params: params,
-            oncomplete: function(r){
-                if(doExport) return onload(r);
-                var recs = r.recv().content();
-                if(e = openils.Event.parse(recs[0]))
-                    return alert(e);
-                for(var i = 0; i < recs.length; i++) {
-                    var rec = recs[i];
-                    queuedRecords.push(rec);
-                    queuedRecordsMap[rec.id()] = rec;
-                }
-                onload();
-            }
-        }
-    );
-}
-
-function vlLoadMatchUI(recId) {
-    displayGlobalDiv('vl-generic-progress');
-    var queuedRec = queuedRecordsMap[recId];
-    var matches = queuedRec.matches();
-    var records = [];
-    currentImportRecId = recId;
-    for(var i = 0; i < matches.length; i++)
-        records.push(matches[i].eg_record());
-
-    var retrieve = ['open-ils.search', 'open-ils.search.biblio.record_entry.slim.retrieve'];
-    var params = [records];
-    if(currentType == 'auth') {
-        retrieve = ['open-ils.cat', 'open-ils.cat.authority.record.retrieve'];
-        params = [authtoken, records, {clear_marc:1}];
-    }
-
-    fieldmapper.standardRequest(
-        retrieve,
-        {   async: true,
-            params:params,
-            oncomplete: function(r) {
-                var recs = r.recv().content();
-                if(e = openils.Event.parse(recs))
-                    return alert(e);
-
-                /* ui mangling */
-                displayGlobalDiv('vl-match-div');
-                resetVlMatchGridLayout();
-                currentMatchedRecords = recs;
-                vlMatchGrid.setStructure(vlMatchGridLayout);
-
-                // build the data store of records with match information
-                var dataStore = bre.toStoreData(recs, null, 
-                    {virtualFields:['_id', 'match_score', 'match_quality', 'rec_quality']});
-                dataStore.identifier = '_id';
-
-                var matchSeenMap = {};
-
-                for(var i = 0; i < dataStore.items.length; i++) {
-                    var item = dataStore.items[i];
-                    item._id = i; // just need something unique
-                    for(var j = 0; j < matches.length; j++) {
-                        var match = matches[j];
-                        if(match.eg_record() == item.id && !matchSeenMap[match.id()]) {
-                            if(match.match_score)
-                                item.match_score = match.match_score();
-                            item.match_quality = match.quality();
-                            item.rec_quality = queuedRec.quality();
-                            matchSeenMap[match.id()] = 1;
-                            break;
-                        }
-                    }
-                }
-
-                // now populate the grid
-                vlPopulateMatchGrid(vlMatchGrid, dataStore);
-            }
-        }
-    );
-}
-
-function vlPopulateMatchGrid(grid, data) {
-    var store = new dojo.data.ItemFileReadStore({data:data});
-    grid.setStore(store);
-    grid.update();
-}
-
-function showMe(id) {
-    dojo.style(dojo.byId(id), 'display', 'block');
-}
-function hideMe(id) {
-    dojo.style(dojo.byId(id), 'display', 'none');
-}
-
-
-function vlLoadMARCHtml(recId, inCat, oncomplete) {
-    dijit.byId('vl-marc-html-done-button').onClick = oncomplete;
-    displayGlobalDiv('vl-generic-progress');
-    var api;
-    var params = [recId, 1];
-
-    if(inCat) {
-        hideMe('vl-marc-html-edit-button'); // don't show marc editor button
-        dijit.byId('vl-marc-html-edit-button').onClick = function(){}
-        api = ['open-ils.search', 'open-ils.search.biblio.record.html'];
-        if(currentType == 'auth')
-            api = ['open-ils.search', 'open-ils.search.authority.to_html'];
-    } else {
-        showMe('vl-marc-html-edit-button'); // plug in the marc editor button
-        dijit.byId('vl-marc-html-edit-button').onClick = 
-            function() {vlLoadMarcEditor(currentType, recId, oncomplete);};
-        params = [authtoken, recId];
-        api = ['open-ils.vandelay', 'open-ils.vandelay.queued_bib_record.html'];
-        if(currentType == 'auth')
-            api = ['open-ils.vandelay', 'open-ils.vandelay.queued_authority_record.html'];
-    }
-
-    fieldmapper.standardRequest(
-        api, 
-        {   async: true,
-            params: params,
-            oncomplete: function(r) {
-            displayGlobalDiv('vl-marc-html-div');
-                var html = r.recv().content();
-                dojo.byId('vl-marc-record-html').innerHTML = html;
-            }
-        }
-    );
-}
-
-
-/*
-function getRecMatchesFromAttrCode(rec, attrCode) {
-    var matches = [];
-    var attr = getRecAttrFromCode(rec, attrCode);
-    for(var j = 0; j < rec.matches().length; j++) {
-        var match = rec.matches()[j];
-        if(match.matched_attr() == attr.id()) 
-            matches.push(match);
-    }
-    return matches;
-}
-*/
-
-/*
-function getRecAttrFromMatch(rec, match) {
-    for(var i = 0; i < rec.attributes().length; i++) {
-        var attr = rec.attributes()[i];
-        if(attr.id() == match.matched_attr())
-            return attr;
-    }
-}
-*/
-
-function getRecAttrDefFromAttr(attr, type) {
-    var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
-    for(var i = 0; i < defs.length; i++) {
-        var def = defs[i];
-        if(def.id() == attr.field())
-            return def;
-    }
-}
-
-function getRecAttrFromCode(rec, attrCode) {
-    var defId = attrDefMap[currentType][attrCode];
-    var attrs = rec.attributes();
-    for(var i = 0; i < attrs.length; i++) {
-        var attr = attrs[i];
-        if(attr.field() == defId) 
-            return attr;
-    }
-    return null;
-}
-
-function vlGetViewMatches(rowIdx, item) {
-    if(item) {
-        var id = this.grid.store.getValue(item, 'id');
-        var rec = queuedRecordsMap[id];
-        if(rec.matches().length > 0)
-            return id + ':' + rec.matches().length;
-    }
-    return -1
-}
-
-function vlFormatViewMatches(id) {
-    if(id == -1) return '';
-    var chunks = id.split(':');
-    id = chunks[0];
-    count = chunks[1];
-    return '<a href="javascript:void(0);" onclick="vlLoadMatchUI(' + id + ');">' + this.name + ' (' + count + ')</a>';
-}
-
-function vlGetViewErrors(rowIdx, item) {
-    if(item) {
-        var id = this.grid.store.getValue(item, 'id');
-        var rec = queuedRecordsMap[id];
-        // id:rec_error:item_import_error_count
-        return id + ':' + 
-            (rec.import_error() ? 1 : '') + ':' + 
-            (typeof rec.import_items == 'function'
-                ? rec.import_items().filter(function(i) {return i.import_error()}).length
-                :''
-            );
-    }
-    return -1
-}
-
-function vlFormatViewErrors(chunk) {
-    if(chunk == -1) return '';
-    var id = chunk.split(':')[0];
-    var rec = chunk.split(':')[1];
-    var count = chunk.split(':')[2];
-    var links = '';
-    if(rec) 
-        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Record</a><br/>'; // TODO I18N
-    if(Number(count))
-        links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Items ('+count+')</a>'; // TODO I18N
-    return links;
-}
-
-//var vlItemErrorColumnPicker;
-function vlLoadErrorUI(id) {
-
-    displayGlobalDiv('vl-import-error-div');
-    openils.Util.hide('vl-import-error-grid-all');
-    openils.Util.show('vl-import-error-record');
-
-    var rec = queuedRecordsMap[id];
-
-    dojo.byId('vl-error-id').innerHTML = rec.id();
-    dojo.forEach( // TODO sane authority rec. fields
-        ['title', 'author', 'isbn', 'issn', 'upc'],
-        function(field) {
-            var attr =  getRecAttrFromCode(rec, field);
-            var eid = 'vl-error-' + field;
-            if(attr) {
-                openils.Util.show(dojo.byId(eid).parentNode, 'table-row');
-                dojo.byId(eid).innerHTML = attr.attr_value();
-            } else {
-                openils.Util.hide(dojo.byId(eid).parentNode);
-            }
-        }
-    );
-    var iediv = dojo.byId('vl-error-import-error');
-    var eddiv = dojo.byId('vl-error-error-detail');
-    if(rec.import_error()) {
-        openils.Util.show(iediv.parentNode, 'table-row');
-        openils.Util.show(eddiv.parentNode, 'table-row');
-        iediv.innerHTML = rec.import_error();
-        eddiv.innerHTML = rec.error_detail();
-    } else {
-        openils.Util.hide(iediv.parentNode);
-        openils.Util.hide(eddiv.parentNode);
-    }
-
-    var errorItems = rec.import_items().filter(function(i) {return i.import_error()});
-    if(errorItems.length) {
-        openils.Util.show('vl-import-error-grid-some');
-        storeData = vqbr.toStoreData(errorItems);
-        var store = new dojo.data.ItemFileReadStore({data:storeData});
-        vlImportErrorGrid.setStore(store);
-        vlImportErrorGrid.update();
-    } else {
-        openils.Util.hide('vl-import-error-grid-some');
-    }
-}
-
-function vlLoadErrorUIAll() {
-
-    displayGlobalDiv('vl-import-error-div');
-    openils.Util.hide('vl-import-error-grid-some');
-    openils.Util.hide('vl-import-error-record');
-    openils.Util.show('vl-import-error-grid-all');
-    vlAllImportErrorGrid.resetStore();
-
-    vlImportErrorGrid.displayOffset = 0;
-
-    vlAllImportErrorGrid.dataLoader = function() {
-
-        vlAllImportErrorGrid.showLoadProgressIndicator();
-
-        fieldmapper.standardRequest(
-            ['open-ils.vandelay', 'open-ils.vandelay.import_item.queue.retrieve'],
-            {
-                async : true,
-                params : [
-                    authtoken, currentQueueId, {   
-                        with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null,
-                        offset : vlAllImportErrorGrid.displayOffset,
-                        limit : vlAllImportErrorGrid.displayLimit
-                    }
-                ],
-                onresponse : function(r) {
-                    var item = openils.Util.readResponse(r);
-                    if(!item) return;
-                    vlAllImportErrorGrid.store.newItem(vii.toStoreItem(item));
-                },
-                oncomplete : function() {
-                    vlAllImportErrorGrid.hideLoadProgressIndicator();
-                }
-            }
-        );
-    };
-
-    vlAllImportErrorGrid.dataLoader();
-}
-
-function vlGetOrg(rowIdx, item) {
-    if(!item) return '';
-    var value = this.grid.store.getValue(item, this.field);
-    if(value) return fieldmapper.aou.findOrgUnit(value).shortname();
-    return '';
-}
-
-function vlCopyStatus(rowIdx, item) {
-    if(!item) return '';
-    var value = this.grid.store.getValue(item, this.field);
-    if(value) return copyStatusCache[value].name();
-    return '';
-}
-
-// Note, we don't pre-fetch all copy locations because there could be 
-// a lot of them.  Instead, fetch-and-cache on demand.
-function vlCopyLocation(rowIdx, item) {
-    if(item) {
-        var value = this.grid.store.getValue(item, this.field);
-        if(value) {
-            if(!copyLocationCache[value]) {
-                copyLocationCache[value] = 
-                    new openils.PermaCrud().retrieve('acpl', value);
-            }
-            return copyLocationCache[value].name();
-        }
-    }
-    return '';
-}
-
-function vlFormatViewMatchMARC(id) {
-    return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', true, '+
-        'function(){displayGlobalDiv(\'vl-match-div\');});">' + this.name + '</a>';
-}
-
-function getAttrValue(rowIdx, item) {
-    if(!item) return '';
-    var attrCode = this.field.split('.')[1];
-    var rec = queuedRecordsMap[this.grid.store.getValue(item, 'id')];
-    var attr = getRecAttrFromCode(rec, attrCode);
-    return (attr) ? attr.attr_value() : '';
-}
-
-function vlGetDateTimeField(rowIdx, item) {
-    if(!item) return '';
-    var value = this.grid.store.getValue(item, this.field);
-    if(!value) return '';
-    var date = dojo.date.stamp.fromISOString(value);
-    return dojo.date.locale.format(date, {selector:'date'});
-}
-
-function vlGetCreator(rowIdx, item) {
-    if(!item) return '';
-    var id = this.grid.store.getValue(item, 'creator');
-    if(userCache[id])
-        return userCache[id].usrname();
-    var user = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.retrieve'], [authtoken, id]);
-    if(e = openils.Event.parse(user))
-        return alert(e);
-    userCache[id] = user;
-    return user.usrname();
-}
-
-function vlGetViewMARC(rowIdx, item) {
-    return item && this.grid.store.getValue(item, 'id');
-}
-
-function vlFormatViewMARC(id) {
-    return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', false, '+
-        'function(){displayGlobalDiv(\'vl-queue-div\');});">' + this.name + '</a>';
-}
-
-function vlGetOverlayTargetSelector(rowIdx, item) {
-    if(!item) return;
-    return this.grid.store.getValue(item, '_id') + ':' + this.grid.store.getValue(item, 'id');
-}
-
-function vlFormatOverlayTargetSelector(val) {
-    if(!val) return '';
-    var parts = val.split(':');
-    var _id = parts[0];
-    var id = parts[1];
-    var value = '<input type="checkbox" name="vl-overlay-target-RECID" '+
-        'onclick="vlHandleOverlayTargetSelected(ID, GRIDID);" gridid="GRIDID" match="ID"/>';
-    value = value.replace(/GRIDID/g, _id);
-    value = value.replace(/RECID/g, currentImportRecId);
-    value = value.replace(/ID/g, id);
-    if(_id == currentOverlayRecordsMapGid[currentImportRecId])
-        return value.replace('/>', 'checked="checked"/>');
-    return value;
-}
-
-
-/**
-  * see if the user has enabled overlays for the current match set and, 
-  * if so, map the current import record to the overlay target.
-  */
-function vlHandleOverlayTargetSelected(recId, gridId) {
-    var noneSelected = true;
-    var checkboxes = dojo.query('[name=vl-overlay-target-'+currentImportRecId+']');
-    for(var i = 0; i < checkboxes.length; i++) {
-        var checkbox = checkboxes[i];
-        var matchRecId = checkbox.getAttribute('match');
-        var gid = checkbox.getAttribute('gridid');
-        if(checkbox.checked) {
-            if(matchRecId == recId && gid == gridId) {
-                noneSelected = false;
-                currentOverlayRecordsMap[currentImportRecId] = matchRecId;
-                currentOverlayRecordsMapGid[currentImportRecId] = gid;
-                dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = true;
-                dojo.byId('vl-record-list-selected-' + currentImportRecId).parentNode.className = 'overlay_selected';
-            } else {
-                checkbox.checked = false;
-            }
-        }
-    }
-
-    if(noneSelected) {
-        delete currentOverlayRecordsMap[currentImportRecId];
-        delete currentOverlayRecordsMapGid[currentImportRecId];
-        dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = false;
-        dojo.byId('vl-record-list-selected-' + currentImportRecId).parentNode.className = '';
-    }
-}
-
-var valLastQueueType = null;
-var vlQueueGridLayout = null;
-function buildRecordGrid(type) {
-    displayGlobalDiv('vl-queue-div');
-
-    vlBibQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
-    vlAuthQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
-
-    if(type == 'bib') {
-        openils.Util.show('vl-bib-queue-grid-wrapper');
-        openils.Util.hide('vl-auth-queue-grid-wrapper');
-        vlQueueGrid = vlBibQueueGrid;
-        openils.Util.show('add-to-bucket-action', 'table-row');
-    } else {
-        openils.Util.show('vl-auth-queue-grid-wrapper');
-        openils.Util.hide('vl-bib-queue-grid-wrapper');
-        vlQueueGrid = vlAuthQueueGrid;
-        openils.Util.hide('add-to-bucket-action');
-    }
-
-
-    if(valLastQueueType != type) {
-        valLastQueueType = type;
-        vlQueueGridLayout = vlQueueGrid.attr('structure');
-        var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
-        attrDefMap[type] = {};
-        for(var i = 0; i < defs.length; i++) {
-            var def = defs[i]
-            attrDefMap[type][def.code()] = def.id();
-            var col = {
-                name:def.description(), 
-                field:'attr.' + def.code(),
-                get: getAttrValue,
-                selectableColumn:true
-            };
-            vlQueueGridLayout[0].cells[0].push(col);
-        }
-    }
-
-    dojo.forEach(vlQueueGridLayout[0].cells[0], 
-        function(cell) { 
-            if(cell.field.match(/^\+/)) 
-                cell.nonSelectable=true;
-        }
-    );
-
-    var storeData;
-    if(type == 'bib')
-        storeData = vqbr.toStoreData(queuedRecords);
-    else
-        storeData = vqar.toStoreData(queuedRecords);
-
-    var store = new dojo.data.ItemFileReadStore({data:storeData});
-    vlQueueGrid.setStore(store);
-
-    if(vlQueueGridColumePicker[type]) {
-        vlQueueGrid.update();
-    } else {
-
-        vlQueueGridColumePicker[type] =
-            new openils.widget.GridColumnPicker(
-                authtoken, 'vandelay.queue.'+type, vlQueueGrid, vlQueueGridLayout);
-        vlQueueGridColumePicker[type].load();
-    }
-}
-
-function vlQueueGridPrevPage() {
-    var page = parseInt(vlQueueDisplayPage.getValue());
-    if(page < 2) return;
-    vlQueueDisplayPage.setValue(page - 1);
-    retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-}
-
-function vlQueueGridNextPage() {
-    vlQueueDisplayPage.setValue(parseInt(vlQueueDisplayPage.getValue())+1);
-    retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-}
-
-function vlDeleteQueue(type, queueId, onload) {
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.delete'],
-        {   async: true,
-            params: [authtoken, queueId],
-            oncomplete: function(r) {
-                var resp = r.recv().content();
-                if(e = openils.Event.parse(resp))
-                    return alert(e);
-                onload();
-            }
-        }
-    );
-}
-
-
-function vlQueueGridDrawSelectBox(rowIdx, item) {
-    return item &&  this.grid.store.getValue(item, 'id');
-}
-
-function vlQueueGridFormatSelectBox(id) {
-    var domId = 'vl-record-list-selected-' + id;
-    if (id) { selectableGridRecords[domId] = id; }
-    return "<div><input type='checkbox' id='"+domId+"'/></div>";
-}
-
-function vlSelectAllQueueGridRecords() {
-    for(var id in selectableGridRecords) 
-        dojo.byId(id).checked = true;
-}
-function vlSelectNoQueueGridRecords() {
-    for(var id in selectableGridRecords) 
-        dojo.byId(id).checked = false;
-}
-function vlToggleQueueGridSelect() {
-    if(dojo.byId('vl-queue-grid-row-selector').checked)
-        vlSelectAllQueueGridRecords();
-    else
-        vlSelectNoQueueGridRecords();
-}
-
-var handleRetrieveRecords = function() {
-    buildRecordGrid(currentType);
-    vlFetchQueueSummary(currentQueueId, currentType, 
-        function(summary) {
-            dojo.byId('vl-queue-summary-name').innerHTML = summary.queue.name();
-            dojo.byId('vl-queue-summary-total-count').innerHTML = summary.total +'';
-            dojo.byId('vl-queue-summary-import-count').innerHTML = summary.imported + '';
-            dojo.byId('vl-queue-summary-import-item-count').innerHTML = summary.total_items + '';
-            dojo.byId('vl-queue-summary-import-item-imported-count').innerHTML = summary.total_items_imported + '';
-            dojo.byId('vl-queue-summary-rec-error-count').innerHTML = summary.rec_import_errors + '';
-            dojo.byId('vl-queue-summary-item-error-count').innerHTML = summary.item_import_errors + '';
-           
-            if (dojo.byId('create-bucket-dialog-name')) {
-                dojo.byId('create-bucket-dialog-name').value = summary.queue.name();
-            }
-        }
-    );
-}
-
-function vlFetchQueueSummary(qId, type, onload) {
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.summary.retrieve'],
-        {   async: true,
-            params: [authtoken, qId],
-            oncomplete : function(r) {
-                var summary = r.recv().content();
-                if(e = openils.Event.parse(summary))
-                    return alert(e);
-                return onload(summary);
-            }
-        }
-    );
-}
-
-function handleCreateBucket(args) {
-    var bname = dojo.byId('create-bucket-dialog-name').value;
-    if (!bname) return;
-
-    progressDialog.show(true);
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', 'open-ils.vandelay.bib_queue.to_bucket'],
-        {   async : true,
-            params : [authtoken, currentQueueId, bname],
-            oncomplete : function(r) {
-                progressDialog.hide();
-                setTimeout(function() { 
-                    var resp = openils.Util.readResponse(r);
-                    if (resp.add_count == 0) {
-                        alert(localeStrings.NO_BUCKET_ITEMS);
-                    } else {
-                        alert(
-                            dojo.string.substitute(
-                                localeStrings.BUCKET_CREATE_SUCCESS,
-                                [resp.add_count, bname, resp.item_count]
-                            )
-                        );
-                    }
-                }, 200); // give the dialog a chance to hide
-            }
-        }
-    );
-}
-    
-
-var _importCancelHandler;
-var _importGoHandler;
-function vlHandleQueueItemsAction(action) {
-
-    if(_importCancelHandler) dojo.disconnect(_importCancelHandler);
-
-    _importCancelHandler = dojo.connect(
-        queueItemsImportCancelButton, 
-        'onClick', 
-        function() {
-            queueItemsImportDialog.hide();
-        }
-    );
-
-    if(_importGoHandler)
-        dojo.disconnect(_importGoHandler);
-
-    _importGoHandler = dojo.connect(
-        queueItemsImportGoButton,
-        'onClick', 
-        function() {
-            queueItemsImportDialog.hide();
-
-            // hack to set the widgets the import funcs will be looking at.  Reset them below.
-            vlUploadQueueImportNoMatch.attr('value',  vlUploadQueueImportNoMatch2.attr('value'));
-            vlUploadQueueAutoOverlayExact.attr('value',  vlUploadQueueAutoOverlayExact2.attr('value'));
-            vlUploadQueueAutoOverlay1Match.attr('value',  vlUploadQueueAutoOverlay1Match2.attr('value'));
-            vlUploadMergeProfile.attr('value',  vlUploadMergeProfile2.attr('value'));
-            vlUploadFtMergeProfile.attr('value',  vlUploadFtMergeProfile2.attr('value'));
-            vlUploadQueueAutoOverlayBestMatch.attr('value',  vlUploadQueueAutoOverlayBestMatch2.attr('value'));
-            vlUploadQueueAutoOverlayBestMatchRatio.attr('value',  vlUploadQueueAutoOverlayBestMatchRatio2.attr('value'));
-
-            if(action == 'import') {
-                vlImportSelectedRecords();
-            } else if(action == 'import_all') {
-                vlImportAllRecords();
-            }
-            
-            // reset the widgets to prevent accidental future actions
-            vlUploadQueueImportNoMatch.attr('value',  false);
-            vlUploadQueueImportNoMatch2.attr('value', false);
-            vlUploadQueueAutoOverlayExact.attr('value', false);
-            vlUploadQueueAutoOverlayExact2.attr('value', false);
-            vlUploadQueueAutoOverlay1Match.attr('value', false);
-            vlUploadQueueAutoOverlay1Match2.attr('value', false);
-            vlUploadMergeProfile.attr('value', '');
-            vlUploadMergeProfile2.attr('value', '');
-            vlUploadFtMergeProfile.attr('value', '');
-            vlUploadFtMergeProfile2.attr('value', '');
-            vlUploadQueueAutoOverlayBestMatch.attr('value', false);
-            vlUploadQueueAutoOverlayBestMatch2.attr('value', false);
-            vlUploadQueueAutoOverlayBestMatchRatio.attr('value', '0.0');
-            vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', '0.0');
-        }
-    );
-
-    queueItemsImportDialog.show();
-}
-
-function vlHandleCreateBucket() {
-
-    create-bucket-dialog-name
-}
-    
-
-/* import user-selected records */
-function vlImportSelectedRecords() {
-    var records = [];
-
-    for(var id in selectableGridRecords) {
-        if(dojo.byId(id).checked) {
-            var recId = selectableGridRecords[id];
-            var rec = queuedRecordsMap[recId];
-            if(!rec.import_time()) 
-                records.push(recId);
-        }
-    }
-
-    vlImportRecordQueue(
-        currentType, 
-        currentQueueId, 
-        records,
-        function(){
-            retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-        }
-    );
-}
-
-/* import all (non-imported) queue records */
-function vlImportAllRecords() {
-    vlImportRecordQueue(
-        currentType, 
-        currentQueueId, 
-        null,
-        function(){
-            retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-        }
-    );
-}
-
-/* if recList has values, import only those records */
-function vlImportRecordQueue(type, queueId, recList, onload) {
-    displayGlobalDiv('vl-generic-progress-with-total');
-
-    /* set up options */
-    var options = {overlay_map : currentOverlayRecordsMap};
-
-    if(vlUploadQueueImportNoMatch.checked) {
-        options.import_no_match = true;
-        vlUploadQueueImportNoMatch.checked = false;
-    }
-
-    if(vlUploadQueueAutoOverlayExact.checked) {
-        options.auto_overlay_exact = true;
-        vlUploadQueueAutoOverlayExact.checked = false;
-    }
-
-    if(vlUploadQueueAutoOverlayBestMatch.checked) {
-        options.auto_overlay_best_match = true;
-        vlUploadQueueAutoOverlayBestMatch.checked = false;
-        options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
-    }
-
-    if(vlUploadQueueAutoOverlay1Match.checked) {
-        options.auto_overlay_1match = true;
-        vlUploadQueueAutoOverlay1Match.checked = false;
-        options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
-    }
-
-    var profile = vlUploadMergeProfile.attr('value');
-    if(profile != null && profile != '') {
-        options.merge_profile = profile;
-    }
-
-    var ftprofile = vlUploadFtMergeProfile.attr('value');
-    if(ftprofile != null && ftprofile != '') {
-        options.fall_through_merge_profile = ftprofile;
-    }
-
-
-    /* determine which method we're calling */
-
-    var method = 'open-ils.vandelay.bib_queue.import';
-    if(type == 'auth')
-        method = method.replace('bib', 'auth');
-
-    var params = [authtoken, queueId, options];
-    if(recList) {
-        method = 'open-ils.vandelay.'+currentType+'_record.list.import';
-        params[1] = recList;
-    }
-
-    fieldmapper.standardRequest(
-        ['open-ils.vandelay', method],
-        {   async: true,
-            params: params,
-            onresponse: function(r) {
-                var resp = r.recv().content();
-                if(e = openils.Event.parse(resp))
-                    return alert(e);
-                vlControlledProgressBar.update({maximum:resp.total, progress:resp.progress});
-            },
-            oncomplete: function() {onload();}
-        }
-    );
-}
-
-
-/**
-  * Create queue, upload MARC, process spool, load the newly created queue 
-  */
-function batchUpload() {
-    var queueName = dijit.byId('vl-queue-name').getValue();
-    currentType = dijit.byId('vl-record-type').getValue();
-
-    var handleProcessSpool = function() {
-        if( 
-            vlUploadQueueImportNoMatch.checked || 
-            vlUploadQueueAutoOverlayExact.checked || 
-            vlUploadQueueAutoOverlay1Match.checked ||
-            vlUploadQueueAutoOverlayBestMatch.checked ) {
-
-                vlImportRecordQueue(
-                    currentType, 
-                    currentQueueId, 
-                    null,
-                    function() {
-                        retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-                    }
-                );
-        } else {
-            retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-        }
-    }
-
-    var handleUploadMARC = function(key) {
-        dojo.style(dojo.byId('vl-upload-status-processing'), 'display', 'block');
-        processSpool(key, currentQueueId, currentType, handleProcessSpool);
-    };
-
-    var handleCreateQueue = function(queue) {
-        currentQueueId = queue.id();
-        uploadMARC(handleUploadMARC);
-    };
-    
-    if(vlUploadQueueSelector.getValue() && !queueName) {
-        currentQueueId = vlUploadQueueSelector.getValue();
-        uploadMARC(handleUploadMARC);
-    } else {
-        createQueue(queueName, currentType, handleCreateQueue, 
-            vlUploadQueueHoldingsImportProfile.attr('value'),
-            vlUploadQueueMatchSet.attr('value')
-        );
-    }
-}
-
-
-function vlFleshQueueSelect(selector, type) {
-    var data;
-    if (type == 'bib') {
-        var bibList = allUserBibQueues.filter(
-            function(q) {
-                return (q.queue_type() == 'bib');
-            }
-        );
-        data = vbq.toStoreData(bibList);
-    } else if (type == 'bib-acq') {
-        // ACQ queues are a special type of bib queue
-        var acqList = allUserBibQueues.filter(
-            function(q) {
-                return (q.queue_type() == 'acq');
-            }
-        );
-        data = vbq.toStoreData(acqList);
-    } else {
-        data = vaq.toStoreData(allUserAuthQueues);
-    }
-
-    selector.store = new dojo.data.ItemFileReadStore({data:data});
-    selector.setValue(null);
-    selector.setDisplayedValue('');
-    if(data[0])
-        selector.setValue(data[0].id());
-
-    var qInput = dijit.byId('vl-queue-name');
-
-    var selChange = function(val) {
-        console.log('selector onchange');
-        // user selected a queue from the selector;  clear the input and 
-        // set the item import profile already defined for the queue
-        var queue = allUserBibQueues.filter(function(q) { return (q.id() == val) })[0];
-        if(val) {
-            vlUploadQueueHoldingsImportProfile.attr('value', queue.item_attr_def() || '');
-            vlUploadQueueHoldingsImportProfile.attr('disabled', true);
-            vlUploadQueueMatchSet.attr('value', queue.match_set() || '');
-            vlUploadQueueMatchSet.attr('disabled', true);
-        } else {
-            vlUploadQueueHoldingsImportProfile.attr('value', '');
-            vlUploadQueueHoldingsImportProfile.attr('disabled', false);
-            vlUploadQueueMatchSet.attr('value', '');
-            vlUploadQueueMatchSet.attr('disabled', false);
-        }
-        dojo.disconnect(qInput._onchange);
-        qInput.attr('value', '');
-        qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
-    }
-    
-    var inputChange = function(val) {
-        console.log('qinput onchange');
-        // user entered a new queue name. clear the selector 
-        vlUploadQueueHoldingsImportProfile.attr('disabled', false);
-        vlUploadQueueMatchSet.attr('disabled', false);
-        dojo.disconnect(selector._onchange);
-        selector.attr('value', '');
-        selector._onchange = dojo.connect(selector, 'onChange', selChange);
-    }
-
-    selector._onchange = dojo.connect(selector, 'onChange', selChange);
-    qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
-}
-
-function vlUpdateMatchSetSelector(type) {
-    type = (type.match(/bib/)) ? 'biblio' : 'authority';
-    vlUploadQueueMatchSet.store = 
-        new dojo.data.ItemFileReadStore({data:vms.toStoreData(matchSets[type])});
-}
-
-function vlShowUploadForm() {
-    displayGlobalDiv('vl-marc-upload-div');
-    vlFleshQueueSelect(vlUploadQueueSelector, vlUploadRecordType.getValue());
-    vlUploadSourceSelector.store = 
-        new dojo.data.ItemFileReadStore({data:cbs.toStoreData(vlBibSources, 'source')});
-    vlUploadSourceSelector.setValue(vlBibSources[0].id());
-    vlUploadQueueHoldingsImportProfile.store = 
-        new dojo.data.ItemFileReadStore({data:viiad.toStoreData(importItemDefs)});
-    vlUpdateMatchSetSelector(vlUploadRecordType.getValue());
-
-    // use ratio from the merge profile if it's set
-    dojo.connect(
-        vlUploadMergeProfile, 
-        'onChange',
-        function(val) {
-            if(!val) return;
-            var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
-            if(profile.lwm_ratio() != null)
-               vlUploadQueueAutoOverlayBestMatchRatio.attr('value', profile.lwm_ratio()+''); 
-        }
-    );
-    dojo.connect(
-        vlUploadMergeProfile2, 
-        'onChange',
-        function(val) {
-            if(!val) return;
-            var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
-            if(profile.lwm_ratio() != null)
-               vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', profile.lwm_ratio()+''); 
-        }
-    );
-
-}
-
-function vlShowQueueSelect() {
-    displayGlobalDiv('vl-queue-select-div');
-    vlFleshQueueSelect(vlQueueSelectQueueList, vlQueueSelectType.getValue());
-}
-
-function vlShowMatchSetEditor() {
-    displayGlobalDiv('vl-match-set-editor-div');
-    dojo.byId('vl-match-set-editor-div').appendChild(
-        dojo.create('iframe', {
-            id : 'vl-match-set-iframe',
-            src : oilsBasePath + '/conify/global/vandelay/match_set',
-            style : 'width:100%; height:500px; border:none; margin:0px;'
-        })
-    );
-}
-
-function vlFetchQueueFromForm() {
-    currentType = vlQueueSelectType.attr('value').replace(/-.*/, ''); // trim bib-acq
-    currentQueueId = vlQueueSelectQueueList.getValue();
-    retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
-}
-
-function vlOpenMarcEditWindow(rec, postReloadHTMLHandler) {
-    /*
-        To run in Firefox directly, must set signed.applets.codebase_principal_support
-        to true in about:config
-    */
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-    win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
-
-    var type;
-    if (currentType == 'bib') {
-        type = 'bre';
-    } else {
-        type = 'are';
-    }
-
-    function onsave(r) {
-        // after the record is saved, reload the HTML display
-        var stat = r.recv().content();
-        if(e = openils.Event.parse(stat))
-            return alert(e);
-        alert(dojo.byId('vl-marc-edit-complete-label').innerHTML);
-        win.close();
-        vlLoadMARCHtml(rec.id(), false, postReloadHTMLHandler);
-    }
-
-    win.xulG = {
-        record : {marc : rec.marc(), "rtype": type},
-        save : {
-            label: dojo.byId('vl-marc-edit-save-label').innerHTML,
-            func: function(xmlString) {
-                var method = 'open-ils.permacrud.update.' + rec.classname;
-                rec.marc(xmlString);
-                fieldmapper.standardRequest(
-                    ['open-ils.permacrud', method],
-                    {   async: true,
-                        params: [authtoken, rec],
-                        oncomplete: onsave
-                    }
-                );
-            },
-        },
-        'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
-        'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
-    };
-}
-
-function vlLoadMarcEditor(type, recId, postReloadHTMLHandler) {
-    var method = 'open-ils.permacrud.search.vqbr';
-    if(currentType != 'bib')
-        method = method.replace(/vqbr/,'vqar');
-
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', method],
-        {   async: true, 
-            params: [authtoken, {id : recId}],
-            oncomplete: function(r) {
-                var rec = r.recv().content();
-                if(e = openils.Event.parse(rec))
-                    return alert(e);
-                vlOpenMarcEditWindow(rec, postReloadHTMLHandler);
-            }
-        }
-    );
-}
-
-
-
-//------------------------------------------------------------
-// attribute editors
-
-// attribute-editor global variables
-
-var ATTR_EDITOR_IN_UPDATE_MODE = false;        // true on 'edit', false on 'create'
-var ATTR_EDIT_ID = null;               // id of current 'edit' attribute
-var ATTR_EDIT_GROUP = 'bib';           // bib-attrs or auth-attrs
-
-function vlAttrEditorInit() {
-    // set up tooltips on the edit form
-    connectTooltip('attr-editor-tags'); 
-    connectTooltip('attr-editor-subfields'); 
-}
-
-function vlShowAttrEditor() {
-    displayGlobalDiv('vl-attr-editor-div');
-    loadAttrEditorGrid();
-    idHide('vl-generic-progress');
-}
-
-function setAttrEditorGroup(groupName) {
-    // put us into 'bib'-attr or 'auth'-attr mode.
-    if (ATTR_EDIT_GROUP != groupName) {
-       ATTR_EDIT_GROUP = groupName;
-       loadAttrEditorGrid();
-    }
-}
-
-function onAttrEditorOpen() {
-    // the "bars" have the create/update/cancel/etc. buttons.
-    var create_bar = document.getElementById('attr-editor-create-bar');
-    var update_bar = document.getElementById('attr-editor-update-bar');
-    if (ATTR_EDITOR_IN_UPDATE_MODE) {
-       update_bar.style.display='table-row';
-       create_bar.style.display='none';
-       // hide the dropdown-button
-       idStyle('vl-create-attr-editor-button', 'visibility', 'hidden');
-    } else {
-       dijit.byId('attr-editor-dialog').reset();
-       create_bar.style.display='table-row';
-       update_bar.style.display='none';
-    }
-}
-
-function onAttrEditorClose() {
-    // reset the form to a "create" form. (We may have borrowed it for editing.)
-    ATTR_EDITOR_IN_UPDATE_MODE = false;
-    // show the dropdown-button
-    idStyle('vl-create-attr-editor-button', 'visibility', 'visible');
-}
-
-function loadAttrEditorGrid() {
-    var _data = (ATTR_EDIT_GROUP == 'auth') ? 
-       vqarad.toStoreData(authAttrDefs) : vqbrad.toStoreData(bibAttrDefs) ;
-
-    var store = new dojo.data.ItemFileReadStore({data:_data});
-    attrEditorGrid.setStore(store);
-    attrEditorGrid.onRowDblClick = onAttrEditorClick;
-    attrEditorGrid.update();
-}
-
-function attrGridGetTag(n, item) {
-    // grid helper: return the tags from the row's xpath column.
-    return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).tags;
-}
-
-function attrGridGetSubfield(n, item) {
-    // grid helper: return the subfields from the row's xpath column.
-    return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).subfields;
-}
-
-function onAttrEditorClick() {
-    var row = this.getItem(this.focus.rowIndex);
-    ATTR_EDIT_ID = this.store.getValue(row, 'id');
-    ATTR_EDITOR_IN_UPDATE_MODE = true;
-
-    // populate the popup editor.
-    dijit.byId('attr-editor-code').attr('value', this.store.getValue(row, 'code'));
-    dijit.byId('attr-editor-description').attr('value', this.store.getValue(row, 'description'));
-    var parsed_xpath = xpathParser.parse(this.store.getValue(row, 'xpath'));
-    dijit.byId('attr-editor-tags').attr('value', parsed_xpath.tags);
-    dijit.byId('attr-editor-subfields').attr('value', parsed_xpath.subfields);
-    dijit.byId('attr-editor-xpath').attr('value', this.store.getValue(row, 'xpath'));
-    dijit.byId('attr-editor-remove').attr('value', this.store.getValue(row, 'remove'));
-
-    // set up UI for editing
-    dojo.byId('vl-create-attr-editor-button').click();
-}
-
-function vlSaveAttrDefinition(data) {
-    idHide('vl-attr-editor-div');
-    idShow('vl-generic-progress');
-
-    data.id = ATTR_EDIT_ID;
-
-    // this ought to honour custom xpaths, but overwrite xpaths
-    // derived from tags/subfields.
-    if (data.xpath == '' || looksLikeDerivedXpath(data.xpath)) {
-       var _xpath = tagAndSubFieldsToXpath(data.tag, data.subfield);
-       data.xpath = _xpath;
-    }
-
-    // build up our permacrud params. Key variables here are
-    // "create or update" and "bib or auth".
-
-    var isAuth   = (ATTR_EDIT_GROUP == 'auth');
-    var isCreate = (ATTR_EDIT_ID == null);
-    var rad      = isAuth ? new vqarad() : new vqbrad() ;
-    var method   = 'open-ils.permacrud' + (isCreate ? '.create.' : '.update.') 
-       + (isAuth ? 'vqarad' : 'vqbrad');
-    var _data    = rad.fromStoreItem(data);
-
-    _data.ischanged(1);
-
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', method],
-        {   async: true,
-            params: [authtoken, _data ],
-           onresponse: function(r) { },
-            oncomplete: function(r) {
-               attrEditorFetchAttrDefs(vlShowAttrEditor);
-               ATTR_EDIT_ID = null;
-           },
-           onerror: function(r) {
-               alert('vlSaveAttrDefinition comms error: ' + r);
+require([
+       "dojo/parser",
+       "dojo/io/iframe",
+       "dijit/ProgressBar",
+       "dijit/form/FilteringSelect",
+       "dijit/layout/ContentPane",
+       "dijit/layout/TabContainer",
+       "dijit/layout/LayoutContainer",
+       "dijit/form/Button",
+       "dijit/form/CheckBox",
+       "dijit/Toolbar",
+       "dijit/Tooltip",
+       "dijit/Menu",
+       "dijit/Dialog",
+       "dojo/cookie",
+       "dojox/grid/DataGrid",
+       "dojo/data/ItemFileReadStore",
+       "dojo/date/locale",
+       "dojo/date/stamp",
+       "fieldmapper/Fieldmapper",
+       "fieldmapper/dojoData",
+       "fieldmapper/OrgUtils",
+       "openils/CGI",
+       "openils/User",
+       "openils/Event",
+       "openils/Util",
+       "openils/MarcXPathParser",
+       "openils/widget/GridColumnPicker",
+       "openils/PermaCrud",
+       "openils/widget/OrgUnitFilteringSelect",
+       "openils/widget/AutoGrid",
+       "openils/widget/AutoFieldWidget",
+       "openils/widget/ProgressDialog"
+       ],
+function(dojo_parser,
+       dojo_io_iframe,
+       dijit_ProgressBar,
+       dijit_form_FilteringSelect,
+       dijit_layout_ContentPane,
+       dijit_layout_TabContainer,
+       dijit_layout_LayoutContainer,
+       dijit_form_Button,
+       dijit_form_CheckBox,
+       dijit_Toolbar,
+       dijit_Tooltip,
+       dijit_Menu,
+       dijit_Dialog,
+       dojo_cookie,
+       dojox_grid_DataGrid,
+       dojo_data_ItemFileReadStore,
+       dojo_date_locale,
+       dojo_date_stamp,
+       fieldmapper_Fieldmapper,
+       fieldmapper_dojoData,
+       fieldmapper_OrgUtils,
+       openils_CGI,
+       openils_User,
+       openils_Event,
+       openils_Util,
+       openils_MarcXPathParser,
+       openils_widget_GridColumnPicker,
+       openils_PermaCrud,
+       openils_widget_OrgUnitFilteringSelect,
+       openils_widget_AutoGrid,
+       openils_widget_AutoFieldWidget,
+       openils_widget_ProgressDialog){
+       /* ---------------------------------------------------------------------------
+       # Copyright (C) 2008  Georgia Public Library Service
+       # Bill Erickson <erickson@esilibrary.com>
+       # 
+       # This program is free software; you can redistribute it and/or
+       # modify it under the terms of the GNU General Public License
+       # as published by the Free Software Foundation; either version 2
+       # of the License, or (at your option) any later version.
+       # 
+       # This program is distributed in the hope that it will be useful,
+       # but WITHOUT ANY WARRANTY; without even the implied warranty of
+       # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       # GNU General Public License for more details.
+       # --------------------------------------------------------------------------- */
+       
+       
+       var globalDivs = [
+           'vl-generic-progress',
+           'vl-generic-progress-with-total',
+           'vl-marc-upload-div',
+           'vl-queue-div',
+           'vl-match-div',
+           'vl-marc-html-div',
+           'vl-queue-select-div',
+           'vl-marc-upload-status-div',
+           'vl-attr-editor-div',
+           'vl-marc-export-div',
+           'vl-profile-editor-div',
+           'vl-item-attr-editor-div',
+           'vl-import-error-div'
+       ];
+       
+       var authtoken;
+       var VANDELAY_URL = '/vandelay-upload';
+       var bibAttrDefs = [];
+       var authAttrDefs = [];
+       var queuedRecords = [];
+       var queuedRecordsMap = {};
+       var bibAttrsFetched = false;
+       var authAttrsFetched = false;
+       var attrDefMap = {}; // maps attr def code names to attr def ids
+       var currentType;
+       var currentQueueId = null;
+       var userCache = {};
+       var currentMatchedRecords; // set of loaded matched bib records
+       var currentOverlayRecordsMap; // map of import record to overlay record
+       var currentOverlayRecordsMapGid; // map of import record to overlay record grid id
+       var currentImportRecId; // when analyzing matches, this is the current import record
+       var userBibQueues = []; // only non-complete queues
+       var userAuthQueues = []; // only non-complete queues
+       var allUserBibQueues;
+       var allUserAuthQueues;
+       var selectableGridRecords;
+       var cgi = new openils_CGI();
+       var vlQueueGridColumePicker = {};
+       var vlBibSources = [];
+       var importItemDefs = [];
+       var matchSets = {};
+       var mergeProfiles = [];
+       var copyStatusCache = {};
+       var copyLocationCache = {};
+       var localeStrings;
+       
+       /**
+         * Grab initial data
+         */
+       function vlInit() {
+       
+           dojo.requireLocalization("openils.vandelay", "vandelay");
+           localeStrings = dojo.i18n.getLocalization("openils.vandelay", "vandelay");
+       
+           authtoken = openils_User.authtoken;
+           var initNeeded = 8; // how many async responses do we need before we're init'd 
+           var initCount = 0; // how many async reponses we've received
+       
+           openils_Util.registerEnterHandler(
+               vlQueueDisplayPage.domNode, function(){retrieveQueuedRecords();});
+           openils_Util.addCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
+       
+           function checkInitDone() {
+               initCount++;
+               if(initCount == initNeeded)
+                   runStartupCommands();
            }
-        }
-    );
-}
-
-function attrEditorFetchAttrDefs(callback) {
-    var fn = (ATTR_EDIT_GROUP == 'auth') ? vlFetchAuthAttrDefs : vlFetchBibAttrDefs;
-    return fn(callback);
-}
-
-function vlAttrDelete() {
-    idHide('vl-attr-editor-div');
-    idShow('vl-generic-progress');
-
-    var isAuth = (ATTR_EDIT_GROUP == 'auth');
-    var method = 'open-ils.permacrud.delete.' + (isAuth ? 'vqarad' : 'vqbrad');
-    var rad    = isAuth ? new vqarad() : new vqbrad() ;
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', method],
-        {   async: true,
-           params: [authtoken, rad.fromHash({ id : ATTR_EDIT_ID }), ],
-           oncomplete: function() {
-               dijit.byId('attr-editor-dialog').onCancel(); // close the dialog
-               attrEditorFetchAttrDefs(vlShowAttrEditor);
-               ATTR_EDIT_ID = null;
-           },
-           onerror: function(r) {
-               alert('vlAttrDelete comms error: ' + r);
+       
+           mergeProfiles = new openils_PermaCrud().retrieveAll('vmp');
+           vlUploadMergeProfile.store = new dojo_data_ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+           vlUploadMergeProfile.labelAttr = 'name';
+           vlUploadMergeProfile.searchAttr = 'name';
+           vlUploadMergeProfile.startup();
+       
+           vlUploadMergeProfile2.store = new dojo_data_ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+           vlUploadMergeProfile2.labelAttr = 'name';
+           vlUploadMergeProfile2.searchAttr = 'name';
+           vlUploadMergeProfile2.startup();
+       
+           vlUploadFtMergeProfile.store = new dojo_data_ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+           vlUploadFtMergeProfile.labelAttr = 'name';
+           vlUploadFtMergeProfile.searchAttr = 'name';
+           vlUploadFtMergeProfile.startup();
+       
+           vlUploadFtMergeProfile2.store = new dojo_data_ItemFileReadStore({data:fieldmapper.vmp.toStoreData(mergeProfiles)});
+           vlUploadFtMergeProfile2.labelAttr = 'name';
+           vlUploadFtMergeProfile2.searchAttr = 'name';
+           vlUploadFtMergeProfile2.startup();
+       
+       
+           // Fetch the bib and authority attribute definitions 
+           vlFetchBibAttrDefs(function () { checkInitDone(); });
+           vlFetchAuthAttrDefs(function () { checkInitDone(); });
+       
+           vlRetrieveQueueList('bib', null, 
+               function(list) {
+                   allUserBibQueues = list;
+                   for(var i = 0; i < allUserBibQueues.length; i++) {
+                       if(allUserBibQueues[i].complete() == 'f')
+                           userBibQueues.push(allUserBibQueues[i]);
+                   }
+                   checkInitDone();
+               }
+           );
+       
+           vlRetrieveQueueList('auth', null, 
+               function(list) {
+                   allUserAuthQueues = list;
+                   for(var i = 0; i < allUserAuthQueues.length; i++) {
+                       if(allUserAuthQueues[i].complete() == 'f')
+                           userAuthQueues.push(allUserAuthQueues[i]);
+                   }
+                   checkInitDone();
+               }
+           );
+       
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', 'open-ils.permacrud.search.cbs.atomic'],
+               {   async: true,
+                   params: [authtoken, {id:{"!=":null}}, {order_by:{cbs:'id'}}],
+                   oncomplete : function(r) {
+                       vlBibSources = openils_Util.readResponse(r, false, true);
+                       checkInitDone();
+                   }
+               }
+           );
+       
+           var owner = fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(new openils_User().user.ws_ou()));
+           new openils_PermaCrud().search('viiad', 
+               {owner: owner.map(function(org) { return org.id(); })},
+               {   async: true,
+                   oncomplete: function(r) {
+                       importItemDefs = openils_Util.readResponse(r);
+                       checkInitDone();
+                   }
+               }
+           );
+       
+           new openils_PermaCrud().search('vms',
+               {owner: owner.map(function(org) { return org.id(); })},
+               {   async: true,
+                   oncomplete: function(r) {
+                       var sets = openils_Util.readResponse(r);
+                       dojo.forEach(sets, 
+                           function(set) {
+                               if(!matchSets[set.mtype()])
+                                   matchSets[set.mtype()] = [];
+                               matchSets[set.mtype()].push(set);
+                           }
+                       );
+                       checkInitDone();
+                   }
+               }
+           );
+       
+           new openils_PermaCrud().retrieveAll('ccs',
+               {   async: true,
+                   oncomplete: function(r) {
+                       var stats = openils_Util.readResponse(r);
+                       dojo.forEach(stats, function(stat){copyStatusCache[stat.id()] = stat});
+                       checkInitDone();
+                   }
+               }
+           );
+       
+           vlAttrEditorInit();
+           vlExportInit();
+       }
+       
+       
+       openils_Util.addOnLoad(vlInit);
+       
+       
+       // fetch the bib and authority attribute definitions
+       
+       function vlFetchBibAttrDefs(postcomplete) {
+           bibAttrDefs = [];
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', 'open-ils.permacrud.search.vqbrad'],
+               {   async: true,
+                   params: [authtoken, {id:{'!=':null}}],
+                   onresponse: function(r) {
+                       var def = r.recv().content(); 
+                       if(e = openils_Event.parse(def[0])) 
+                           return alert(e);
+                       bibAttrDefs.push(def);
+                   },
+                   oncomplete: function() {
+                       bibAttrDefs = bibAttrDefs.sort(
+                           function(a, b) {
+                               if(a.id() > b.id()) return 1;
+                               if(a.id() < b.id()) return -1;
+                               return 0;
+                           }
+                       );
+                       postcomplete();
+                   }
+               }
+           );
+       }
+       
+       function vlFetchAuthAttrDefs(postcomplete) {
+           authAttrDefs = [];
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', 'open-ils.permacrud.search.vqarad'],
+               {   async: true,
+                   params: [authtoken, {id:{'!=':null}}],
+                   onresponse: function(r) {
+                       var def = r.recv().content(); 
+                       if(e = openils_Event.parse(def[0])) 
+                           return alert(e);
+                       authAttrDefs.push(def);
+                   },
+                   oncomplete: function() {
+                       authAttrDefs = authAttrDefs.sort(
+                           function(a, b) {
+                               if(a.id() > b.id()) return 1;
+                               if(a.id() < b.id()) return -1;
+                               return 0;
+                           }
+                       );
+                       postcomplete();
+                   }
+               }
+           );
+       }
+       
+       function vlRetrieveQueueList(type, filter, onload) {
+           type = (type == 'bib') ? type : 'authority';
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.owner.retrieve.atomic'],
+               {   async: true,
+                   params: [authtoken, null, filter],
+                   oncomplete: function(r) {
+                       var list = r.recv().content();
+                       if(e = openils_Event.parse(list[0]))
+                           return alert(e);
+                       onload(list);
+                   }
+               }
+           );
+       
+       }
+       
+       function displayGlobalDiv(id) {
+           for(var i = 0; i < globalDivs.length; i++) {
+               try {
+                   dojo.style(dojo.byId(globalDivs[i]), 'display', 'none');
+               } catch(e) {
+                   alert('please define div ' + globalDivs[i]);
+               }
            }
-        }
-    );
-}
-
-// ------------------------------------------------------------
-// utilities for attribute editors
-
-// dom utilities (maybe dojo does these, and these should be replaced)
-
-function idStyle(obId, k, v)   { document.getElementById(obId).style[k] = v;   }
-function idShow(obId)          { idStyle(obId, 'display', 'block');            }
-function idHide(obId)          { idStyle(obId, 'display' , 'none');            }
-
-function connectTooltip(fieldId) {
-    // Given an element id, look up a tooltip element in the doc (same
-    // id with a '-tip' suffix) and associate the two. Maybe dojo has
-    // a better way to do this?
-    var fld = dojo.byId(fieldId);
-    var tip = dojo.byId(fieldId + '-tip');
-    dojo.connect(fld, 'onfocus', function(evt) {
-                    dijit.showTooltip(tip.innerHTML, fld, ['below', 'after']); });
-    dojo.connect(fld, 'onblur', function(evt) { dijit.hideTooltip(fld); });
-}
-
-// xpath utilities
-
-var xpathParser = new openils.MarcXPathParser();
-
-function tagAndSubFieldsToXpath(tags, subfields) {
-    // given tags, and subfields, build up an XPath.
-    try {
-       var parts = {
-           'tags':tags.match(/[\d]+/g), 
-           'subfields':subfields.match(/[a-zA-z]/g) };
-       return xpathParser.compile(parts);
-    } catch (err) {
-       return {'parts':null, 'tags':null, 'error':err};
-    }
-}
-
-function looksLikeDerivedXpath(path) {
-    // Does this path look like it was derived from tags and subfields?
-    var parsed = xpathParser.parse(path);
-    if (parsed.tags == null) 
-       return false;
-    var compiled = xpathParser.compile(parsed);
-    return (path == compiled);
-}
-
-// amazing xpath-util unit-tests
-if (!looksLikeDerivedXpath('//*[@tag="901"]/*[@code="c"]'))    alert('vandelay xpath-utility error');
-if ( looksLikeDerivedXpath('ba-boo-ba-boo!'))                  alert('vandelay xpath-utility error');
-
-
-
-var profileContextOrg
-function vlShowProfileEditor() {
-    displayGlobalDiv('vl-profile-editor-div');
-    buildProfileGrid();
-
-    var connect = function() {
-        dojo.connect(profileContextOrgSelector, 'onChange',
-            function() {
-                profileContextOrg = this.attr('value');
-                pGrid.resetStore();
-                buildProfileGrid();
-            }
-        );
-    };
-
-    new openils.User().buildPermOrgSelector(
-        'ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
-}
-
-function buildProfileGrid() {
-
-    if(profileContextOrg == null)
-        profileContextOrg = openils.User.user.ws_ou();
-
-    pGrid.loadAll( 
-        {order_by : {vmp : 'name'}}, 
-        {owner : fieldmapper.aou.fullPath(profileContextOrg, true)}
-    );
-}
-
-/* --- Import Item Attr Grid --------------- */
-
-var itemAttrContextOrg;
-var itemAttrGridFirstTime = true;
-function vlShowImportItemAttrEditor() {
-    displayGlobalDiv('vl-item-attr-editor-div');
-
-    if (itemAttrGridFirstTime) {
-
-        buildImportItemAttrGrid();
-
-        var connect = function() {
-            dojo.connect(itemAttrContextOrgSelector, 'onChange',
-                function() {
-                    itemAttrContextOrg = this.attr('value');
-                    itemAttrGrid.resetStore();
-                    buildImportItemAttrGrid();
-                }
-            );
-        };
-
-        new openils.User().buildPermOrgSelector(
-            'ADMIN_IMPORT_ITEM_ATTR_DEF', 
-                itemAttrContextOrgSelector, null, connect);
-
-        itemAttrGridFirstTime = false;
-    }
-}
-
-function buildImportItemAttrGrid() {
-
-    if(itemAttrContextOrg == null)
-        itemAttrContextOrg = openils.User.user.ws_ou();
-
-    itemAttrGrid.loadAll( 
-        {order_by : {viiad : 'name'}}, 
-        {owner : fieldmapper.aou.fullPath(itemAttrContextOrg, true)}
-    );
-}
-
+           dojo.style(dojo.byId(id),'display','block');
+       
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-marc-export'), 'toolbar_selected');
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-queue-select'), 'toolbar_selected');
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-attr-editor'), 'toolbar_selected');
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-profile-editor'), 'toolbar_selected');
+           openils_Util.removeCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
+       
+           if(dojo.byId('vl-match-set-iframe'))
+               dojo.byId('vl-match-set-editor-div').removeChild(dojo.byId('vl-match-set-iframe'));
+       
+           switch(id) {
+               case 'vl-marc-export-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-marc-export'), 'toolbar_selected');
+                   break;
+               case 'vl-marc-upload-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-marc-upload'), 'toolbar_selected');
+                   break;
+               case 'vl-queue-select-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-queue-select'), 'toolbar_selected');
+                   break;
+               case 'vl-attr-editor-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-attr-editor'), 'toolbar_selected');
+                   break;
+               case 'vl-profile-editor-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-profile-editor'), 'toolbar_selected');
+                   break;
+               case 'vl-item-attr-editor-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-import-item-attr-editor'), 'toolbar_selected');
+                   break;
+               case 'vl-match-set-editor-div':
+                   openils_Util.addCSSClass(dojo.byId('vl-menu-match-set-editor'), 'toolbar_selected');
+                   break;
+           }
+       }
+       
+       function runStartupCommands() {
+           openils_Util.hide(dojo.byId('vl-page-loading'));
+           openils_Util.show(dojo.byId('vl-body-wrapper'));
+           currentQueueId = cgi.param('qid');
+           currentType = cgi.param('qtype');
+           dojo.style('vl-nav-bar', 'visibility', 'visible');
+           if(currentQueueId)
+               return retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+           if (cgi.param('page', 'inspectq'))
+               return displayGlobalDiv('vl-queue-select-div');
+               
+           vlShowUploadForm();
+       }
+       
+       /**
+         * asynchronously upload a file of MARC records
+         */
+       function uploadMARC(onload){
+           dojo.byId('vl-upload-status-count').innerHTML = '0';
+           dojo.byId('vl-ses-input').value = authtoken;
+           displayGlobalDiv('vl-marc-upload-status-div');
+           dojo_io_iframe.send({
+               url: VANDELAY_URL,
+               method: "post",
+               handleAs: "html",
+               form: dojo.byId('vl-marc-upload-form'),
+               handle: function(data,ioArgs){
+                   var content = data.documentElement.textContent;
+                   onload(content);
+               }
+           });
+       }       
+       
+       /**
+         * Creates a new vandelay queue
+         */
+       function createQueue(queueName, type, onload, importDefId, matchSet) {
+           var name = (type=='bib') ? 'bib' : 'authority';
+           var method = 'open-ils.vandelay.'+ name +'_queue.create'
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', method],
+               {   async: true,
+                   params: [authtoken, queueName, null, name, matchSet, importDefId],
+                   oncomplete : function(r) {
+                       var queue = r.recv().content();
+                       if(e = openils_Event.parse(queue)) 
+                           return alert(e);
+                       onload(queue);
+                   }
+               }
+           );
+       }
+       
+       /**
+         * Tells vandelay to pull a batch of records from the cache and explode them
+         * out into the vandelay tables
+         */
+       function processSpool(key, queueId, type, onload) {
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', 'open-ils.vandelay.'+type+'.process_spool'],
+               {   async: true,
+                   params: [authtoken, key, queueId],
+                   onresponse : function(r) {
+                       var resp = r.recv().content();
+                       if(e = openils_Event.parse(resp)) 
+                           return alert(e);
+                       dojo.byId('vl-upload-status-count').innerHTML = resp;
+                   },
+                   oncomplete : function(r) {onload();}
+               }
+           );
+       }
+       
+       function vlExportInit() {
+       
+           // queue export
+           var qsel = dojo.byId('vl-queue-export-options');
+           qsel.onchange = function(newVal) {
+               var value = qsel.options[qsel.selectedIndex].value;
+               qsel.selectedIndex = 0;
+               if(!value) return;
+               if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
+               retrieveQueuedRecords(
+                   currentType, 
+                   currentQueueId, 
+                   function(r) { 
+                       exportHandler(value, r);
+                       displayGlobalDiv('vl-queue-div');
+                   },
+                   value
+               );
+           }
+       
+           // item export
+           var isel = dojo.byId('vl-item-export-options');
+           isel.onchange = function(newVal) {
+               var value = isel.options[isel.selectedIndex].value;
+               isel.selectedIndex = 0;
+               if(!value) return;
+               if(!confirm('Export as "' + value + '"?')) return; // TODO: i18n
+       
+               displayGlobalDiv('vl-generic-progress');
+               var method = 'open-ils.vandelay.import_item.queue.export.' + value + '.atomic';
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.vandelay', method],
+                   {
+                       params : [
+                           authtoken, 
+                           currentQueueId, 
+                           {with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null}
+                       ],
+                       async : true,
+                       oncomplete : function(r) {exportHandler(value, r)}
+                   }
+               );
+           }
+       }
+       
+       function exportHandler(type, response) {
+           displayGlobalDiv('vl-import-error-div');
+           try {
+               var content = openils_Util.readResponse(response);
+               if (type=='email') {
+                   if (content==1) { alert('Email sent.'); return; }
+                   throw(content);
+               }
+               /* handle .atomic versus non-atomic method calls */
+               content = content.constructor == Array
+                   ? content[0].template_output().data()
+                   : content.template_output().data();
+               switch(type) {
+                   case 'print':
+                       openils_Util.printHtmlString(content);
+                   break;
+                   case 'csv':
+                       //content = content.replace(/\\t/g,'\t'); // if we really wanted to do .tsv instead
+                       openils.XUL.contentToFileSaveDialog(content, null, {
+                           defaultString : 'VandelayExport.csv',
+                           defaultExtension : '.csv',
+                           filterName : 'CSV',
+                           filterExtension : '*.csv',
+                           filterAll : true
+                       } );
+                   break;
+                   default:
+                       alert('response = ' + response + '\tcontent:\n' + content);
+               }
+           } catch(E) {
+               alert('Error exporting data: ' + E);
+           }
+       }
+       
+       function retrieveQueuedRecords(type, queueId, onload, doExport) {
+           displayGlobalDiv('vl-generic-progress');
+           queuedRecords = [];
+           queuedRecordsMap = {};
+           currentOverlayRecordsMap = {};
+           currentOverlayRecordsMapGid = {};
+           selectableGridRecords = {};
+       
+           if(!type) type = currentType;
+           if(!queueId) queueId = currentQueueId;
+           if(!onload) onload = handleRetrieveRecords;
+       
+           var method = 'open-ils.vandelay.'+type+'_queue.records.retrieve';
+       
+           if(doExport) method += '.export.' + doExport;
+           if(vlQueueGridShowMatches.checked)
+               method = method.replace('records', 'records.matches');
+       
+           method += '.atomic';
+       
+           var sel = dojo.byId('vl-queue-display-limit-selector');
+           var limit = parseInt(sel.options[sel.selectedIndex].value);
+           var offset = limit * parseInt(vlQueueDisplayPage.attr('value')-1);
+       
+           var params =  [authtoken, queueId, {clear_marc: 1, offset: offset, limit: limit, flesh_import_items:1}];
+           if(vlQueueGridShowNonImport.checked)
+               params[2].non_imported = 1;
+       
+           if(vlQueueGridShowImportErrors.checked)
+               params[2].with_import_error = 1;
+       
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', method],
+               {   async: true,
+                   params: params,
+                   oncomplete: function(r){
+                       if(doExport) return onload(r);
+                       var recs = r.recv().content();
+                       if(e = openils_Event.parse(recs[0]))
+                           return alert(e);
+                       for(var i = 0; i < recs.length; i++) {
+                           var rec = recs[i];
+                           queuedRecords.push(rec);
+                           queuedRecordsMap[rec.id()] = rec;
+                       }
+                       onload();
+                   }
+               }
+           );
+       }
+       
+       function vlLoadMatchUI(recId) {
+           displayGlobalDiv('vl-generic-progress');
+           var queuedRec = queuedRecordsMap[recId];
+           var matches = queuedRec.matches();
+           var records = [];
+           currentImportRecId = recId;
+           for(var i = 0; i < matches.length; i++)
+               records.push(matches[i].eg_record());
+       
+           var retrieve = ['open-ils.search', 'open-ils.search.biblio.record_entry.slim.retrieve'];
+           var params = [records];
+           if(currentType == 'auth') {
+               retrieve = ['open-ils.cat', 'open-ils.cat.authority.record.retrieve'];
+               params = [authtoken, records, {clear_marc:1}];
+           }
+       
+           fieldmapper.standardRequest(
+               retrieve,
+               {   async: true,
+                   params:params,
+                   oncomplete: function(r) {
+                       var recs = r.recv().content();
+                       if(e = openils_Event.parse(recs))
+                           return alert(e);
+       
+                       /* ui mangling */
+                       displayGlobalDiv('vl-match-div');
+                       resetVlMatchGridLayout();
+                       currentMatchedRecords = recs;
+                       vlMatchGrid.setStructure(vlMatchGridLayout);
+       
+                       // build the data store of records with match information
+                       var dataStore = bre.toStoreData(recs, null, 
+                           {virtualFields:['_id', 'match_score', 'match_quality', 'rec_quality']});
+                       dataStore.identifier = '_id';
+       
+                       var matchSeenMap = {};
+       
+                       for(var i = 0; i < dataStore.items.length; i++) {
+                           var item = dataStore.items[i];
+                           item._id = i; // just need something unique
+                           for(var j = 0; j < matches.length; j++) {
+                               var match = matches[j];
+                               if(match.eg_record() == item.id && !matchSeenMap[match.id()]) {
+                                   if(match.match_score)
+                                       item.match_score = match.match_score();
+                                   item.match_quality = match.quality();
+                                   item.rec_quality = queuedRec.quality();
+                                   matchSeenMap[match.id()] = 1;
+                                   break;
+                               }
+                           }
+                       }
+       
+                       // now populate the grid
+                       vlPopulateMatchGrid(vlMatchGrid, dataStore);
+                   }
+               }
+           );
+       }
+       
+       function vlPopulateMatchGrid(grid, data) {
+           var store = new dojo_data_ItemFileReadStore({data:data});
+           grid.setStore(store);
+           grid.update();
+       }
+       
+       function showMe(id) {
+           dojo.style(dojo.byId(id), 'display', 'block');
+       }
+       function hideMe(id) {
+           dojo.style(dojo.byId(id), 'display', 'none');
+       }
+       
+       
+       function vlLoadMARCHtml(recId, inCat, oncomplete) {
+           dijit.byId('vl-marc-html-done-button').onClick = oncomplete;
+           displayGlobalDiv('vl-generic-progress');
+           var api;
+           var params = [recId, 1];
+       
+           if(inCat) {
+               hideMe('vl-marc-html-edit-button'); // don't show marc editor button
+               dijit.byId('vl-marc-html-edit-button').onClick = function(){}
+               api = ['open-ils.search', 'open-ils.search.biblio.record.html'];
+               if(currentType == 'auth')
+                   api = ['open-ils.search', 'open-ils.search.authority.to_html'];
+           } else {
+               showMe('vl-marc-html-edit-button'); // plug in the marc editor button
+               dijit.byId('vl-marc-html-edit-button').onClick = 
+                   function() {vlLoadMarcEditor(currentType, recId, oncomplete);};
+               params = [authtoken, recId];
+               api = ['open-ils.vandelay', 'open-ils.vandelay.queued_bib_record.html'];
+               if(currentType == 'auth')
+                   api = ['open-ils.vandelay', 'open-ils.vandelay.queued_authority_record.html'];
+           }
+       
+           fieldmapper.standardRequest(
+               api, 
+               {   async: true,
+                   params: params,
+                   oncomplete: function(r) {
+                   displayGlobalDiv('vl-marc-html-div');
+                       var html = r.recv().content();
+                       dojo.byId('vl-marc-record-html').innerHTML = html;
+                   }
+               }
+           );
+       }
+       
+       
+       /*
+       function getRecMatchesFromAttrCode(rec, attrCode) {
+           var matches = [];
+           var attr = getRecAttrFromCode(rec, attrCode);
+           for(var j = 0; j < rec.matches().length; j++) {
+               var match = rec.matches()[j];
+               if(match.matched_attr() == attr.id()) 
+                   matches.push(match);
+           }
+           return matches;
+       }
+       */
+       
+       /*
+       function getRecAttrFromMatch(rec, match) {
+           for(var i = 0; i < rec.attributes().length; i++) {
+               var attr = rec.attributes()[i];
+               if(attr.id() == match.matched_attr())
+                   return attr;
+           }
+       }
+       */
+       
+       function getRecAttrDefFromAttr(attr, type) {
+           var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
+           for(var i = 0; i < defs.length; i++) {
+               var def = defs[i];
+               if(def.id() == attr.field())
+                   return def;
+           }
+       }
+       
+       function getRecAttrFromCode(rec, attrCode) {
+           var defId = attrDefMap[currentType][attrCode];
+           var attrs = rec.attributes();
+           for(var i = 0; i < attrs.length; i++) {
+               var attr = attrs[i];
+               if(attr.field() == defId) 
+                   return attr;
+           }
+           return null;
+       }
+       
+       function vlGetViewMatches(rowIdx, item) {
+           if(item) {
+               var id = this.grid.store.getValue(item, 'id');
+               var rec = queuedRecordsMap[id];
+               if(rec.matches().length > 0)
+                   return id + ':' + rec.matches().length;
+           }
+           return -1
+       }
+       
+       function vlFormatViewMatches(id) {
+           if(id == -1) return '';
+           var chunks = id.split(':');
+           id = chunks[0];
+           count = chunks[1];
+           return '<a href="javascript:void(0);" onclick="vlLoadMatchUI(' + id + ');">' + this.name + ' (' + count + ')</a>';
+       }
+       
+       function vlGetViewErrors(rowIdx, item) {
+           if(item) {
+               var id = this.grid.store.getValue(item, 'id');
+               var rec = queuedRecordsMap[id];
+               // id:rec_error:item_import_error_count
+               return id + ':' + 
+                   (rec.import_error() ? 1 : '') + ':' + 
+                   (typeof rec.import_items == 'function'
+                       ? rec.import_items().filter(function(i) {return i.import_error()}).length
+                       :''
+                   );
+           }
+           return -1
+       }
+       
+       function vlFormatViewErrors(chunk) {
+           if(chunk == -1) return '';
+           var id = chunk.split(':')[0];
+           var rec = chunk.split(':')[1];
+           var count = chunk.split(':')[2];
+           var links = '';
+           if(rec) 
+               links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Record</a><br/>'; // TODO I18N
+           if(Number(count))
+               links += '<a href="javascript:void(0);" onclick="vlLoadErrorUI(' + id + ');">Items ('+count+')</a>'; // TODO I18N
+           return links;
+       }
+       
+       //var vlItemErrorColumnPicker;
+       function vlLoadErrorUI(id) {
+       
+           displayGlobalDiv('vl-import-error-div');
+           openils_Util.hide('vl-import-error-grid-all');
+           openils_Util.show('vl-import-error-record');
+       
+           var rec = queuedRecordsMap[id];
+       
+           dojo.byId('vl-error-id').innerHTML = rec.id();
+           dojo.forEach( // TODO sane authority rec. fields
+               ['title', 'author', 'isbn', 'issn', 'upc'],
+               function(field) {
+                   var attr =  getRecAttrFromCode(rec, field);
+                   var eid = 'vl-error-' + field;
+                   if(attr) {
+                       openils_Util.show(dojo.byId(eid).parentNode, 'table-row');
+                       dojo.byId(eid).innerHTML = attr.attr_value();
+                   } else {
+                       openils_Util.hide(dojo.byId(eid).parentNode);
+                   }
+               }
+           );
+           var iediv = dojo.byId('vl-error-import-error');
+           var eddiv = dojo.byId('vl-error-error-detail');
+           if(rec.import_error()) {
+               openils_Util.show(iediv.parentNode, 'table-row');
+               openils_Util.show(eddiv.parentNode, 'table-row');
+               iediv.innerHTML = rec.import_error();
+               eddiv.innerHTML = rec.error_detail();
+           } else {
+               openils_Util.hide(iediv.parentNode);
+               openils_Util.hide(eddiv.parentNode);
+           }
+       
+           var errorItems = rec.import_items().filter(function(i) {return i.import_error()});
+           if(errorItems.length) {
+               openils_Util.show('vl-import-error-grid-some');
+               storeData = vqbr.toStoreData(errorItems);
+               var store = new dojo_data_ItemFileReadStore({data:storeData});
+               vlImportErrorGrid.setStore(store);
+               vlImportErrorGrid.update();
+           } else {
+               openils_Util.hide('vl-import-error-grid-some');
+           }
+       }
+       
+       function vlLoadErrorUIAll() {
+       
+           displayGlobalDiv('vl-import-error-div');
+           openils_Util.hide('vl-import-error-grid-some');
+           openils_Util.hide('vl-import-error-record');
+           openils_Util.show('vl-import-error-grid-all');
+           vlAllImportErrorGrid.resetStore();
+       
+           vlImportErrorGrid.displayOffset = 0;
+       
+           vlAllImportErrorGrid.dataLoader = function() {
+       
+               vlAllImportErrorGrid.showLoadProgressIndicator();
+       
+               fieldmapper.standardRequest(
+                   ['open-ils.vandelay', 'open-ils.vandelay.import_item.queue.retrieve'],
+                   {
+                       async : true,
+                       params : [
+                           authtoken, currentQueueId, {   
+                               with_import_error: (vlImportItemsShowErrors.checked) ? 1 : null,
+                               offset : vlAllImportErrorGrid.displayOffset,
+                               limit : vlAllImportErrorGrid.displayLimit
+                           }
+                       ],
+                       onresponse : function(r) {
+                           var item = openils_Util.readResponse(r);
+                           if(!item) return;
+                           vlAllImportErrorGrid.store.newItem(vii.toStoreItem(item));
+                       },
+                       oncomplete : function() {
+                           vlAllImportErrorGrid.hideLoadProgressIndicator();
+                       }
+                   }
+               );
+           };
+       
+           vlAllImportErrorGrid.dataLoader();
+       }
+       
+       function vlGetOrg(rowIdx, item) {
+           if(!item) return '';
+           var value = this.grid.store.getValue(item, this.field);
+           if(value) return fieldmapper.aou.findOrgUnit(value).shortname();
+           return '';
+       }
+       
+       function vlCopyStatus(rowIdx, item) {
+           if(!item) return '';
+           var value = this.grid.store.getValue(item, this.field);
+           if(value) return copyStatusCache[value].name();
+           return '';
+       }
+       
+       // Note, we don't pre-fetch all copy locations because there could be 
+       // a lot of them.  Instead, fetch-and-cache on demand.
+       function vlCopyLocation(rowIdx, item) {
+           if(item) {
+               var value = this.grid.store.getValue(item, this.field);
+               if(value) {
+                   if(!copyLocationCache[value]) {
+                       copyLocationCache[value] = 
+                           new openils_PermaCrud().retrieve('acpl', value);
+                   }
+                   return copyLocationCache[value].name();
+               }
+           }
+           return '';
+       }
+       
+       function vlFormatViewMatchMARC(id) {
+           return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', true, '+
+               'function(){displayGlobalDiv(\'vl-match-div\');});">' + this.name + '</a>';
+       }
+       
+       function getAttrValue(rowIdx, item) {
+           if(!item) return '';
+           var attrCode = this.field.split('.')[1];
+           var rec = queuedRecordsMap[this.grid.store.getValue(item, 'id')];
+           var attr = getRecAttrFromCode(rec, attrCode);
+           return (attr) ? attr.attr_value() : '';
+       }
+       
+       function vlGetDateTimeField(rowIdx, item) {
+           if(!item) return '';
+           var value = this.grid.store.getValue(item, this.field);
+           if(!value) return '';
+           var date = dojo_date_stamp.fromISOString(value);
+           return dojo_date_locale.format(date, {selector:'date'});
+       }
+       
+       function vlGetCreator(rowIdx, item) {
+           if(!item) return '';
+           var id = this.grid.store.getValue(item, 'creator');
+           if(userCache[id])
+               return userCache[id].usrname();
+           var user = fieldmapper.standardRequest(
+               ['open-ils.actor', 'open-ils.actor.user.retrieve'], [authtoken, id]);
+           if(e = openils_Event.parse(user))
+               return alert(e);
+           userCache[id] = user;
+           return user.usrname();
+       }
+       
+       function vlGetViewMARC(rowIdx, item) {
+           return item && this.grid.store.getValue(item, 'id');
+       }
+       
+       function vlFormatViewMARC(id) {
+           return '<a href="javascript:void(0);" onclick="vlLoadMARCHtml(' + id + ', false, '+
+               'function(){displayGlobalDiv(\'vl-queue-div\');});">' + this.name + '</a>';
+       }
+       
+       function vlGetOverlayTargetSelector(rowIdx, item) {
+           if(!item) return;
+           return this.grid.store.getValue(item, '_id') + ':' + this.grid.store.getValue(item, 'id');
+       }
+       
+       function vlFormatOverlayTargetSelector(val) {
+           if(!val) return '';
+           var parts = val.split(':');
+           var _id = parts[0];
+           var id = parts[1];
+           var value = '<input type="checkbox" name="vl-overlay-target-RECID" '+
+               'onclick="vlHandleOverlayTargetSelected(ID, GRIDID);" gridid="GRIDID" match="ID"/>';
+           value = value.replace(/GRIDID/g, _id);
+           value = value.replace(/RECID/g, currentImportRecId);
+           value = value.replace(/ID/g, id);
+           if(_id == currentOverlayRecordsMapGid[currentImportRecId])
+               return value.replace('/>', 'checked="checked"/>');
+           return value;
+       }
+       
+       
+       /**
+         * see if the user has enabled overlays for the current match set and, 
+         * if so, map the current import record to the overlay target.
+         */
+       function vlHandleOverlayTargetSelected(recId, gridId) {
+           var noneSelected = true;
+           var checkboxes = dojo.query('[name=vl-overlay-target-'+currentImportRecId+']');
+           for(var i = 0; i < checkboxes.length; i++) {
+               var checkbox = checkboxes[i];
+               var matchRecId = checkbox.getAttribute('match');
+               var gid = checkbox.getAttribute('gridid');
+               if(checkbox.checked) {
+                   if(matchRecId == recId && gid == gridId) {
+                       noneSelected = false;
+                       currentOverlayRecordsMap[currentImportRecId] = matchRecId;
+                       currentOverlayRecordsMapGid[currentImportRecId] = gid;
+                       dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = true;
+                       dojo.byId('vl-record-list-selected-' + currentImportRecId).parentNode.className = 'overlay_selected';
+                   } else {
+                       checkbox.checked = false;
+                   }
+               }
+           }
+       
+           if(noneSelected) {
+               delete currentOverlayRecordsMap[currentImportRecId];
+               delete currentOverlayRecordsMapGid[currentImportRecId];
+               dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = false;
+               dojo.byId('vl-record-list-selected-' + currentImportRecId).parentNode.className = '';
+           }
+       }
+       
+       var valLastQueueType = null;
+       var vlQueueGridLayout = null;
+       function buildRecordGrid(type) {
+           displayGlobalDiv('vl-queue-div');
+       
+           vlBibQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
+           vlAuthQueueGrid.canSort = function(col){ if(Math.abs(col) == 1) { return false; } else { return true; } }; 
+       
+           if(type == 'bib') {
+               openils_Util.show('vl-bib-queue-grid-wrapper');
+               openils_Util.hide('vl-auth-queue-grid-wrapper');
+               vlQueueGrid = vlBibQueueGrid;
+               openils_Util.show('add-to-bucket-action', 'table-row');
+           } else {
+               openils_Util.show('vl-auth-queue-grid-wrapper');
+               openils_Util.hide('vl-bib-queue-grid-wrapper');
+               vlQueueGrid = vlAuthQueueGrid;
+               openils_Util.hide('add-to-bucket-action');
+           }
+       
+       
+           if(valLastQueueType != type) {
+               valLastQueueType = type;
+               vlQueueGridLayout = vlQueueGrid.attr('structure');
+               var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
+               attrDefMap[type] = {};
+               for(var i = 0; i < defs.length; i++) {
+                   var def = defs[i]
+                   attrDefMap[type][def.code()] = def.id();
+                   var col = {
+                       name:def.description(), 
+                       field:'attr.' + def.code(),
+                       get: getAttrValue,
+                       selectableColumn:true
+                   };
+                   vlQueueGridLayout[0].cells[0].push(col);
+               }
+           }
+       
+           dojo.forEach(vlQueueGridLayout[0].cells[0], 
+               function(cell) { 
+                   if(cell.field.match(/^\+/)) 
+                       cell.nonSelectable=true;
+               }
+           );
+       
+           var storeData;
+           if(type == 'bib')
+               storeData = vqbr.toStoreData(queuedRecords);
+           else
+               storeData = vqar.toStoreData(queuedRecords);
+       
+           var store = new dojo_data_ItemFileReadStore({data:storeData});
+           vlQueueGrid.setStore(store);
+       
+           if(vlQueueGridColumePicker[type]) {
+               vlQueueGrid.update();
+           } else {
+       
+               vlQueueGridColumePicker[type] =
+                   new openils_widget_GridColumnPicker(
+                       authtoken, 'vandelay.queue.'+type, vlQueueGrid, vlQueueGridLayout);
+               vlQueueGridColumePicker[type].load();
+           }
+       }
+       
+       function vlQueueGridPrevPage() {
+           var page = parseInt(vlQueueDisplayPage.getValue());
+           if(page < 2) return;
+           vlQueueDisplayPage.setValue(page - 1);
+           retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+       }
+       
+       function vlQueueGridNextPage() {
+           vlQueueDisplayPage.setValue(parseInt(vlQueueDisplayPage.getValue())+1);
+           retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+       }
+       
+       function vlDeleteQueue(type, queueId, onload) {
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.delete'],
+               {   async: true,
+                   params: [authtoken, queueId],
+                   oncomplete: function(r) {
+                       var resp = r.recv().content();
+                       if(e = openils_Event.parse(resp))
+                           return alert(e);
+                       onload();
+                   }
+               }
+           );
+       }
+       
+       
+       function vlQueueGridDrawSelectBox(rowIdx, item) {
+           return item &&  this.grid.store.getValue(item, 'id');
+       }
+       
+       function vlQueueGridFormatSelectBox(id) {
+           var domId = 'vl-record-list-selected-' + id;
+           if (id) { selectableGridRecords[domId] = id; }
+           return "<div><input type='checkbox' id='"+domId+"'/></div>";
+       }
+       
+       function vlSelectAllQueueGridRecords() {
+           for(var id in selectableGridRecords) 
+               dojo.byId(id).checked = true;
+       }
+       function vlSelectNoQueueGridRecords() {
+           for(var id in selectableGridRecords) 
+               dojo.byId(id).checked = false;
+       }
+       function vlToggleQueueGridSelect() {
+           if(dojo.byId('vl-queue-grid-row-selector').checked)
+               vlSelectAllQueueGridRecords();
+           else
+               vlSelectNoQueueGridRecords();
+       }
+       
+       var handleRetrieveRecords = function() {
+           buildRecordGrid(currentType);
+           vlFetchQueueSummary(currentQueueId, currentType, 
+               function(summary) {
+                   dojo.byId('vl-queue-summary-name').innerHTML = summary.queue.name();
+                   dojo.byId('vl-queue-summary-total-count').innerHTML = summary.total +'';
+                   dojo.byId('vl-queue-summary-import-count').innerHTML = summary.imported + '';
+                   dojo.byId('vl-queue-summary-import-item-count').innerHTML = summary.total_items + '';
+                   dojo.byId('vl-queue-summary-import-item-imported-count').innerHTML = summary.total_items_imported + '';
+                   dojo.byId('vl-queue-summary-rec-error-count').innerHTML = summary.rec_import_errors + '';
+                   dojo.byId('vl-queue-summary-item-error-count').innerHTML = summary.item_import_errors + '';
+                  
+                   if (dojo.byId('create-bucket-dialog-name')) {
+                       dojo.byId('create-bucket-dialog-name').value = summary.queue.name();
+                   }
+               }
+           );
+       }
+       
+       function vlFetchQueueSummary(qId, type, onload) {
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.summary.retrieve'],
+               {   async: true,
+                   params: [authtoken, qId],
+                   oncomplete : function(r) {
+                       var summary = r.recv().content();
+                       if(e = openils_Event.parse(summary))
+                           return alert(e);
+                       return onload(summary);
+                   }
+               }
+           );
+       }
+       
+       function handleCreateBucket(args) {
+           var bname = dojo.byId('create-bucket-dialog-name').value;
+           if (!bname) return;
+       
+           progressDialog.show(true);
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', 'open-ils.vandelay.bib_queue.to_bucket'],
+               {   async : true,
+                   params : [authtoken, currentQueueId, bname],
+                   oncomplete : function(r) {
+                       progressDialog.hide();
+                       setTimeout(function() { 
+                           var resp = openils_Util.readResponse(r);
+                           if (resp.add_count == 0) {
+                               alert(localeStrings.NO_BUCKET_ITEMS);
+                           } else {
+                               alert(
+                                   dojo.string.substitute(
+                                       localeStrings.BUCKET_CREATE_SUCCESS,
+                                       [resp.add_count, bname, resp.item_count]
+                                   )
+                               );
+                           }
+                       }, 200); // give the dialog a chance to hide
+                   }
+               }
+           );
+       }
+           
+       
+       var _importCancelHandler;
+       var _importGoHandler;
+       function vlHandleQueueItemsAction(action) {
+       
+           if(_importCancelHandler) dojo.disconnect(_importCancelHandler);
+       
+           _importCancelHandler = dojo.connect(
+               queueItemsImportCancelButton, 
+               'onClick', 
+               function() {
+                   queueItemsImportDialog.hide();
+               }
+           );
+       
+           if(_importGoHandler)
+               dojo.disconnect(_importGoHandler);
+       
+           _importGoHandler = dojo.connect(
+               queueItemsImportGoButton,
+               'onClick', 
+               function() {
+                   queueItemsImportDialog.hide();
+       
+                   // hack to set the widgets the import funcs will be looking at.  Reset them below.
+                   vlUploadQueueImportNoMatch.attr('value',  vlUploadQueueImportNoMatch2.attr('value'));
+                   vlUploadQueueAutoOverlayExact.attr('value',  vlUploadQueueAutoOverlayExact2.attr('value'));
+                   vlUploadQueueAutoOverlay1Match.attr('value',  vlUploadQueueAutoOverlay1Match2.attr('value'));
+                   vlUploadMergeProfile.attr('value',  vlUploadMergeProfile2.attr('value'));
+                   vlUploadFtMergeProfile.attr('value',  vlUploadFtMergeProfile2.attr('value'));
+                   vlUploadQueueAutoOverlayBestMatch.attr('value',  vlUploadQueueAutoOverlayBestMatch2.attr('value'));
+                   vlUploadQueueAutoOverlayBestMatchRatio.attr('value',  vlUploadQueueAutoOverlayBestMatchRatio2.attr('value'));
+       
+                   if(action == 'import') {
+                       vlImportSelectedRecords();
+                   } else if(action == 'import_all') {
+                       vlImportAllRecords();
+                   }
+                   
+                   // reset the widgets to prevent accidental future actions
+                   vlUploadQueueImportNoMatch.attr('value',  false);
+                   vlUploadQueueImportNoMatch2.attr('value', false);
+                   vlUploadQueueAutoOverlayExact.attr('value', false);
+                   vlUploadQueueAutoOverlayExact2.attr('value', false);
+                   vlUploadQueueAutoOverlay1Match.attr('value', false);
+                   vlUploadQueueAutoOverlay1Match2.attr('value', false);
+                   vlUploadMergeProfile.attr('value', '');
+                   vlUploadMergeProfile2.attr('value', '');
+                   vlUploadFtMergeProfile.attr('value', '');
+                   vlUploadFtMergeProfile2.attr('value', '');
+                   vlUploadQueueAutoOverlayBestMatch.attr('value', false);
+                   vlUploadQueueAutoOverlayBestMatch2.attr('value', false);
+                   vlUploadQueueAutoOverlayBestMatchRatio.attr('value', '0.0');
+                   vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', '0.0');
+               }
+           );
+       
+           queueItemsImportDialog.show();
+       }
+       
+       function vlHandleCreateBucket() {
+       
+           create-bucket-dialog-name
+       }
+           
+       
+       /* import user-selected records */
+       function vlImportSelectedRecords() {
+           var records = [];
+       
+           for(var id in selectableGridRecords) {
+               if(dojo.byId(id).checked) {
+                   var recId = selectableGridRecords[id];
+                   var rec = queuedRecordsMap[recId];
+                   if(!rec.import_time()) 
+                       records.push(recId);
+               }
+           }
+       
+           vlImportRecordQueue(
+               currentType, 
+               currentQueueId, 
+               records,
+               function(){
+                   retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+               }
+           );
+       }
+       
+       /* import all (non-imported) queue records */
+       function vlImportAllRecords() {
+           vlImportRecordQueue(
+               currentType, 
+               currentQueueId, 
+               null,
+               function(){
+                   retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+               }
+           );
+       }
+       
+       /* if recList has values, import only those records */
+       function vlImportRecordQueue(type, queueId, recList, onload) {
+           displayGlobalDiv('vl-generic-progress-with-total');
+       
+           /* set up options */
+           var options = {overlay_map : currentOverlayRecordsMap};
+       
+           if(vlUploadQueueImportNoMatch.checked) {
+               options.import_no_match = true;
+               vlUploadQueueImportNoMatch.checked = false;
+           }
+       
+           if(vlUploadQueueAutoOverlayExact.checked) {
+               options.auto_overlay_exact = true;
+               vlUploadQueueAutoOverlayExact.checked = false;
+           }
+       
+           if(vlUploadQueueAutoOverlayBestMatch.checked) {
+               options.auto_overlay_best_match = true;
+               vlUploadQueueAutoOverlayBestMatch.checked = false;
+               options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
+           }
+       
+           if(vlUploadQueueAutoOverlay1Match.checked) {
+               options.auto_overlay_1match = true;
+               vlUploadQueueAutoOverlay1Match.checked = false;
+               options.match_quality_ratio = vlUploadQueueAutoOverlayBestMatchRatio.attr('value');
+           }
+       
+           var profile = vlUploadMergeProfile.attr('value');
+           if(profile != null && profile != '') {
+               options.merge_profile = profile;
+           }
+       
+           var ftprofile = vlUploadFtMergeProfile.attr('value');
+           if(ftprofile != null && ftprofile != '') {
+               options.fall_through_merge_profile = ftprofile;
+           }
+       
+       
+           /* determine which method we're calling */
+       
+           var method = 'open-ils.vandelay.bib_queue.import';
+           if(type == 'auth')
+               method = method.replace('bib', 'auth');
+       
+           var params = [authtoken, queueId, options];
+           if(recList) {
+               method = 'open-ils.vandelay.'+currentType+'_record.list.import';
+               params[1] = recList;
+           }
+       
+           fieldmapper.standardRequest(
+               ['open-ils.vandelay', method],
+               {   async: true,
+                   params: params,
+                   onresponse: function(r) {
+                       var resp = r.recv().content();
+                       if(e = openils_Event.parse(resp))
+                           return alert(e);
+                       vlControlledProgressBar.update({maximum:resp.total, progress:resp.progress});
+                   },
+                   oncomplete: function() {onload();}
+               }
+           );
+       }
+       
+       
+       /**
+         * Create queue, upload MARC, process spool, load the newly created queue 
+         */
+       function batchUpload() {
+           var queueName = dijit.byId('vl-queue-name').getValue();
+           currentType = dijit.byId('vl-record-type').getValue();
+       
+           var handleProcessSpool = function() {
+               if( 
+                   vlUploadQueueImportNoMatch.checked || 
+                   vlUploadQueueAutoOverlayExact.checked || 
+                   vlUploadQueueAutoOverlay1Match.checked ||
+                   vlUploadQueueAutoOverlayBestMatch.checked ) {
+       
+                       vlImportRecordQueue(
+                           currentType, 
+                           currentQueueId, 
+                           null,
+                           function() {
+                               retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+                           }
+                       );
+               } else {
+                   retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+               }
+           }
+       
+           var handleUploadMARC = function(key) {
+               dojo.style(dojo.byId('vl-upload-status-processing'), 'display', 'block');
+               processSpool(key, currentQueueId, currentType, handleProcessSpool);
+           };
+       
+           var handleCreateQueue = function(queue) {
+               currentQueueId = queue.id();
+               uploadMARC(handleUploadMARC);
+           };
+           
+           if(vlUploadQueueSelector.getValue() && !queueName) {
+               currentQueueId = vlUploadQueueSelector.getValue();
+               uploadMARC(handleUploadMARC);
+           } else {
+               createQueue(queueName, currentType, handleCreateQueue, 
+                   vlUploadQueueHoldingsImportProfile.attr('value'),
+                   vlUploadQueueMatchSet.attr('value')
+               );
+           }
+       }
+       
+       
+       function vlFleshQueueSelect(selector, type) {
+           var data;
+           if (type == 'bib') {
+               var bibList = allUserBibQueues.filter(
+                   function(q) {
+                       return (q.queue_type() == 'bib');
+                   }
+               );
+               data = vbq.toStoreData(bibList);
+           } else if (type == 'bib-acq') {
+               // ACQ queues are a special type of bib queue
+               var acqList = allUserBibQueues.filter(
+                   function(q) {
+                       return (q.queue_type() == 'acq');
+                   }
+               );
+               data = vbq.toStoreData(acqList);
+           } else {
+               data = vaq.toStoreData(allUserAuthQueues);
+           }
+       
+           selector.store = new dojo_data_ItemFileReadStore({data:data});
+           selector.setValue(null);
+           selector.setDisplayedValue('');
+           if(data[0])
+               selector.setValue(data[0].id());
+       
+           var qInput = dijit.byId('vl-queue-name');
+       
+           var selChange = function(val) {
+               console.log('selector onchange');
+               // user selected a queue from the selector;  clear the input and 
+               // set the item import profile already defined for the queue
+               var queue = allUserBibQueues.filter(function(q) { return (q.id() == val) })[0];
+               if(val) {
+                   vlUploadQueueHoldingsImportProfile.attr('value', queue.item_attr_def() || '');
+                   vlUploadQueueHoldingsImportProfile.attr('disabled', true);
+                   vlUploadQueueMatchSet.attr('value', queue.match_set() || '');
+                   vlUploadQueueMatchSet.attr('disabled', true);
+               } else {
+                   vlUploadQueueHoldingsImportProfile.attr('value', '');
+                   vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+                   vlUploadQueueMatchSet.attr('value', '');
+                   vlUploadQueueMatchSet.attr('disabled', false);
+               }
+               dojo.disconnect(qInput._onchange);
+               qInput.attr('value', '');
+               qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
+           }
+           
+           var inputChange = function(val) {
+               console.log('qinput onchange');
+               // user entered a new queue name. clear the selector 
+               vlUploadQueueHoldingsImportProfile.attr('disabled', false);
+               vlUploadQueueMatchSet.attr('disabled', false);
+               dojo.disconnect(selector._onchange);
+               selector.attr('value', '');
+               selector._onchange = dojo.connect(selector, 'onChange', selChange);
+           }
+       
+           selector._onchange = dojo.connect(selector, 'onChange', selChange);
+           qInput._onchange = dojo.connect(qInput, 'onChange', inputChange);
+       }
+       
+       function vlUpdateMatchSetSelector(type) {
+           type = (type.match(/bib/)) ? 'biblio' : 'authority';
+           vlUploadQueueMatchSet.store = 
+               new dojo_data_ItemFileReadStore({data:vms.toStoreData(matchSets[type])});
+       }
+       
+       function vlShowUploadForm() {
+           displayGlobalDiv('vl-marc-upload-div');
+           vlFleshQueueSelect(vlUploadQueueSelector, vlUploadRecordType.getValue());
+           vlUploadSourceSelector.store = 
+               new dojo_data_ItemFileReadStore({data:cbs.toStoreData(vlBibSources, 'source')});
+           vlUploadSourceSelector.setValue(vlBibSources[0].id());
+           vlUploadQueueHoldingsImportProfile.store = 
+               new dojo_data_ItemFileReadStore({data:viiad.toStoreData(importItemDefs)});
+           vlUpdateMatchSetSelector(vlUploadRecordType.getValue());
+       
+           // use ratio from the merge profile if it's set
+           dojo.connect(
+               vlUploadMergeProfile, 
+               'onChange',
+               function(val) {
+                   if(!val) return;
+                   var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
+                   if(profile.lwm_ratio() != null)
+                      vlUploadQueueAutoOverlayBestMatchRatio.attr('value', profile.lwm_ratio()+''); 
+               }
+           );
+           dojo.connect(
+               vlUploadMergeProfile2, 
+               'onChange',
+               function(val) {
+                   if(!val) return;
+                   var profile = mergeProfiles.filter(function(p) { return (p.id() == val); })[0];
+                   if(profile.lwm_ratio() != null)
+                      vlUploadQueueAutoOverlayBestMatchRatio2.attr('value', profile.lwm_ratio()+''); 
+               }
+           );
+       
+       }
+       
+       function vlShowQueueSelect() {
+           displayGlobalDiv('vl-queue-select-div');
+           vlFleshQueueSelect(vlQueueSelectQueueList, vlQueueSelectType.getValue());
+       }
+       
+       function vlShowMatchSetEditor() {
+           displayGlobalDiv('vl-match-set-editor-div');
+           dojo.byId('vl-match-set-editor-div').appendChild(
+               dojo.create('iframe', {
+                   id : 'vl-match-set-iframe',
+                   src : oilsBasePath + '/conify/global/vandelay/match_set',
+                   style : 'width:100%; height:500px; border:none; margin:0px;'
+               })
+           );
+       }
+       
+       function vlFetchQueueFromForm() {
+           currentType = vlQueueSelectType.attr('value').replace(/-.*/, ''); // trim bib-acq
+           currentQueueId = vlQueueSelectQueueList.getValue();
+           retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
+       }
+       
+       function vlOpenMarcEditWindow(rec, postReloadHTMLHandler) {
+           /*
+               To run in Firefox directly, must set signed.applets.codebase_principal_support
+               to true in about:config
+           */
+           netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+           win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
+       
+           var type;
+           if (currentType == 'bib') {
+               type = 'bre';
+           } else {
+               type = 'are';
+           }
+       
+           function onsave(r) {
+               // after the record is saved, reload the HTML display
+               var stat = r.recv().content();
+               if(e = openils_Event.parse(stat))
+                   return alert(e);
+               alert(dojo.byId('vl-marc-edit-complete-label').innerHTML);
+               win.close();
+               vlLoadMARCHtml(rec.id(), false, postReloadHTMLHandler);
+           }
+       
+           win.xulG = {
+               record : {marc : rec.marc(), "rtype": type},
+               save : {
+                   label: dojo.byId('vl-marc-edit-save-label').innerHTML,
+                   func: function(xmlString) {
+                       var method = 'open-ils.permacrud.update.' + rec.classname;
+                       rec.marc(xmlString);
+                       fieldmapper.standardRequest(
+                           ['open-ils.permacrud', method],
+                           {   async: true,
+                               params: [authtoken, rec],
+                               oncomplete: onsave
+                           }
+                       );
+                   },
+               },
+               'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
+               'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
+           };
+       }
+       
+       function vlLoadMarcEditor(type, recId, postReloadHTMLHandler) {
+           var method = 'open-ils.permacrud.search.vqbr';
+           if(currentType != 'bib')
+               method = method.replace(/vqbr/,'vqar');
+       
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', method],
+               {   async: true, 
+                   params: [authtoken, {id : recId}],
+                   oncomplete: function(r) {
+                       var rec = r.recv().content();
+                       if(e = openils_Event.parse(rec))
+                           return alert(e);
+                       vlOpenMarcEditWindow(rec, postReloadHTMLHandler);
+                   }
+               }
+           );
+       }
+       
+       
+       
+       //------------------------------------------------------------
+       // attribute editors
+       
+       // attribute-editor global variables
+       
+       var ATTR_EDITOR_IN_UPDATE_MODE = false; // true on 'edit', false on 'create'
+       var ATTR_EDIT_ID = null;                // id of current 'edit' attribute
+       var ATTR_EDIT_GROUP = 'bib';            // bib-attrs or auth-attrs
+       
+       function vlAttrEditorInit() {
+           // set up tooltips on the edit form
+           connectTooltip('attr-editor-tags'); 
+           connectTooltip('attr-editor-subfields'); 
+       }
+       
+       function vlShowAttrEditor() {
+           displayGlobalDiv('vl-attr-editor-div');
+           loadAttrEditorGrid();
+           idHide('vl-generic-progress');
+       }
+       
+       function setAttrEditorGroup(groupName) {
+           // put us into 'bib'-attr or 'auth'-attr mode.
+           if (ATTR_EDIT_GROUP != groupName) {
+               ATTR_EDIT_GROUP = groupName;
+               loadAttrEditorGrid();
+           }
+       }
+       
+       function onAttrEditorOpen() {
+           // the "bars" have the create/update/cancel/etc. buttons.
+           var create_bar = document.getElementById('attr-editor-create-bar');
+           var update_bar = document.getElementById('attr-editor-update-bar');
+           if (ATTR_EDITOR_IN_UPDATE_MODE) {
+               update_bar.style.display='table-row';
+               create_bar.style.display='none';
+               // hide the dropdown-button
+               idStyle('vl-create-attr-editor-button', 'visibility', 'hidden');
+           } else {
+               dijit.byId('attr-editor-dialog').reset();
+               create_bar.style.display='table-row';
+               update_bar.style.display='none';
+           }
+       }
+       
+       function onAttrEditorClose() {
+           // reset the form to a "create" form. (We may have borrowed it for editing.)
+           ATTR_EDITOR_IN_UPDATE_MODE = false;
+           // show the dropdown-button
+           idStyle('vl-create-attr-editor-button', 'visibility', 'visible');
+       }
+       
+       function loadAttrEditorGrid() {
+           var _data = (ATTR_EDIT_GROUP == 'auth') ? 
+               vqarad.toStoreData(authAttrDefs) : vqbrad.toStoreData(bibAttrDefs) ;
+       
+           var store = new dojo_data_ItemFileReadStore({data:_data});
+           attrEditorGrid.setStore(store);
+           attrEditorGrid.onRowDblClick = onAttrEditorClick;
+           attrEditorGrid.update();
+       }
+       
+       function attrGridGetTag(n, item) {
+           // grid helper: return the tags from the row's xpath column.
+           return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).tags;
+       }
+       
+       function attrGridGetSubfield(n, item) {
+           // grid helper: return the subfields from the row's xpath column.
+           return item && xpathParser.parse(this.grid.store.getValue(item, 'xpath')).subfields;
+       }
+       
+       function onAttrEditorClick() {
+           var row = this.getItem(this.focus.rowIndex);
+           ATTR_EDIT_ID = this.store.getValue(row, 'id');
+           ATTR_EDITOR_IN_UPDATE_MODE = true;
+       
+           // populate the popup editor.
+           dijit.byId('attr-editor-code').attr('value', this.store.getValue(row, 'code'));
+           dijit.byId('attr-editor-description').attr('value', this.store.getValue(row, 'description'));
+           var parsed_xpath = xpathParser.parse(this.store.getValue(row, 'xpath'));
+           dijit.byId('attr-editor-tags').attr('value', parsed_xpath.tags);
+           dijit.byId('attr-editor-subfields').attr('value', parsed_xpath.subfields);
+           dijit.byId('attr-editor-xpath').attr('value', this.store.getValue(row, 'xpath'));
+           dijit.byId('attr-editor-remove').attr('value', this.store.getValue(row, 'remove'));
+       
+           // set up UI for editing
+           dojo.byId('vl-create-attr-editor-button').click();
+       }
+       
+       function vlSaveAttrDefinition(data) {
+           idHide('vl-attr-editor-div');
+           idShow('vl-generic-progress');
+       
+           data.id = ATTR_EDIT_ID;
+       
+           // this ought to honour custom xpaths, but overwrite xpaths
+           // derived from tags/subfields.
+           if (data.xpath == '' || looksLikeDerivedXpath(data.xpath)) {
+               var _xpath = tagAndSubFieldsToXpath(data.tag, data.subfield);
+               data.xpath = _xpath;
+           }
+       
+           // build up our permacrud params. Key variables here are
+           // "create or update" and "bib or auth".
+       
+           var isAuth   = (ATTR_EDIT_GROUP == 'auth');
+           var isCreate = (ATTR_EDIT_ID == null);
+           var rad      = isAuth ? new vqarad() : new vqbrad() ;
+           var method   = 'open-ils.permacrud' + (isCreate ? '.create.' : '.update.') 
+               + (isAuth ? 'vqarad' : 'vqbrad');
+           var _data    = rad.fromStoreItem(data);
+       
+           _data.ischanged(1);
+       
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', method],
+               {   async: true,
+                   params: [authtoken, _data ],
+                   onresponse: function(r) { },
+                   oncomplete: function(r) {
+                       attrEditorFetchAttrDefs(vlShowAttrEditor);
+                       ATTR_EDIT_ID = null;
+                   },
+                   onerror: function(r) {
+                       alert('vlSaveAttrDefinition comms error: ' + r);
+                   }
+               }
+           );
+       }
+       
+       function attrEditorFetchAttrDefs(callback) {
+           var fn = (ATTR_EDIT_GROUP == 'auth') ? vlFetchAuthAttrDefs : vlFetchBibAttrDefs;
+           return fn(callback);
+       }
+       
+       function vlAttrDelete() {
+           idHide('vl-attr-editor-div');
+           idShow('vl-generic-progress');
+       
+           var isAuth = (ATTR_EDIT_GROUP == 'auth');
+           var method = 'open-ils.permacrud.delete.' + (isAuth ? 'vqarad' : 'vqbrad');
+           var rad    = isAuth ? new vqarad() : new vqbrad() ;
+           fieldmapper.standardRequest(
+               ['open-ils.permacrud', method],
+               {   async: true,
+                   params: [authtoken, rad.fromHash({ id : ATTR_EDIT_ID }), ],
+                   oncomplete: function() {
+                       dijit.byId('attr-editor-dialog').onCancel(); // close the dialog
+                       attrEditorFetchAttrDefs(vlShowAttrEditor);
+                       ATTR_EDIT_ID = null;
+                   },
+                   onerror: function(r) {
+                       alert('vlAttrDelete comms error: ' + r);
+                   }
+               }
+           );
+       }
+       
+       // ------------------------------------------------------------
+       // utilities for attribute editors
+       
+       // dom utilities (maybe dojo does these, and these should be replaced)
+       
+       function idStyle(obId, k, v)    { document.getElementById(obId).style[k] = v;   }
+       function idShow(obId)           { idStyle(obId, 'display', 'block');            }
+       function idHide(obId)           { idStyle(obId, 'display' , 'none');            }
+       
+       function connectTooltip(fieldId) {
+           // Given an element id, look up a tooltip element in the doc (same
+           // id with a '-tip' suffix) and associate the two. Maybe dojo has
+           // a better way to do this?
+           var fld = dojo.byId(fieldId);
+           var tip = dojo.byId(fieldId + '-tip');
+           dojo.connect(fld, 'onfocus', function(evt) {
+                            dijit.showTooltip(tip.innerHTML, fld, ['below', 'after']); });
+           dojo.connect(fld, 'onblur', function(evt) { dijit.hideTooltip(fld); });
+       }
+       
+       // xpath utilities
+       
+       var xpathParser = new openils_MarcXPathParser();
+       
+       function tagAndSubFieldsToXpath(tags, subfields) {
+           // given tags, and subfields, build up an XPath.
+           try {
+               var parts = {
+                   'tags':tags.match(/[\d]+/g), 
+                   'subfields':subfields.match(/[a-zA-z]/g) };
+               return xpathParser.compile(parts);
+           } catch (err) {
+               return {'parts':null, 'tags':null, 'error':err};
+           }
+       }
+       
+       function looksLikeDerivedXpath(path) {
+           // Does this path look like it was derived from tags and subfields?
+           var parsed = xpathParser.parse(path);
+           if (parsed.tags == null) 
+               return false;
+           var compiled = xpathParser.compile(parsed);
+           return (path == compiled);
+       }
+       
+       // amazing xpath-util unit-tests
+       if (!looksLikeDerivedXpath('//*[@tag="901"]/*[@code="c"]'))     alert('vandelay xpath-utility error');
+       if ( looksLikeDerivedXpath('ba-boo-ba-boo!'))                   alert('vandelay xpath-utility error');
+       
+       
+       
+       var profileContextOrg
+       function vlShowProfileEditor() {
+           displayGlobalDiv('vl-profile-editor-div');
+           buildProfileGrid();
+       
+           var connect = function() {
+               dojo.connect(profileContextOrgSelector, 'onChange',
+                   function() {
+                       profileContextOrg = this.attr('value');
+                       pGrid.resetStore();
+                       buildProfileGrid();
+                   }
+               );
+           };
+       
+           new openils_User().buildPermOrgSelector(
+               'ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
+       }
+       
+       function buildProfileGrid() {
+       
+           if(profileContextOrg == null)
+               profileContextOrg = openils_User.user.ws_ou();
+       
+           pGrid.loadAll( 
+               {order_by : {vmp : 'name'}}, 
+               {owner : fieldmapper.aou.fullPath(profileContextOrg, true)}
+           );
+       }
+       
+       /* --- Import Item Attr Grid --------------- */
+       
+       var itemAttrContextOrg;
+       var itemAttrGridFirstTime = true;
+       function vlShowImportItemAttrEditor() {
+           displayGlobalDiv('vl-item-attr-editor-div');
+       
+           if (itemAttrGridFirstTime) {
+       
+               buildImportItemAttrGrid();
+       
+               var connect = function() {
+                   dojo.connect(itemAttrContextOrgSelector, 'onChange',
+                       function() {
+                           itemAttrContextOrg = this.attr('value');
+                           itemAttrGrid.resetStore();
+                           buildImportItemAttrGrid();
+                       }
+                   );
+               };
+       
+               new openils_User().buildPermOrgSelector(
+                   'ADMIN_IMPORT_ITEM_ATTR_DEF', 
+                       itemAttrContextOrgSelector, null, connect);
+       
+               itemAttrGridFirstTime = false;
+           }
+       }
+       
+       function buildImportItemAttrGrid() {
+       
+           if(itemAttrContextOrg == null)
+               itemAttrContextOrg = openils_User.user.ws_ou();
+       
+           itemAttrGrid.loadAll( 
+               {order_by : {viiad : 'name'}}, 
+               {owner : fieldmapper.aou.fullPath(itemAttrContextOrg, true)}
+           );
+       }
+       
+       
+
+});
\ No newline at end of file