[open-ils-commits] [GIT] Evergreen ILS branch master updated. 5757ab093599b0f98b178dc879032f555fc8f878

Evergreen Git git at git.evergreen-ils.org
Sat Feb 18 13:33:08 EST 2017


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  5757ab093599b0f98b178dc879032f555fc8f878 (commit)
       via  e0e18273d37e5a9d98b5a77e8d8afd968617803e (commit)
       via  577361730ac72106362b483bbfa00dcf3997e0a4 (commit)
       via  0bd63e583da386fdaa53216a38a237547d36337e (commit)
       via  cacd801e4374d8fe8d044712f7cc09ef242d8123 (commit)
       via  fd7f9e90703ac6795db69018dd718561d5ebae45 (commit)
       via  96bbfc81dae88e1976ff805eea15339d22c0d5b6 (commit)
       via  e14402e2cc9255da8a2ee26d3d6c22f8e8771866 (commit)
       via  7486798554516caa0c88ccca358d44670a7705f3 (commit)
       via  ce6e8b4d974a16a72a4b5a6d8e9bbd7da9f77953 (commit)
       via  3454c1a4cbf7d4d69ac5783684266945fcbbc0a2 (commit)
       via  56d96cdebc0f1adb628c63f33e9e9b62708f5c59 (commit)
       via  144739828769a8baa533bc671932c8b97e5c6470 (commit)
       via  64d4167d8f04afef189d35667c17745d2e9e785c (commit)
       via  5e071c58b2b9f755e9631b53e64833a0c0071a9f (commit)
       via  56688c5f13ea9a7a6420d49c8b6d3f5fa9d54ac6 (commit)
       via  0e54e117d4fc7ac2de2a991a60a161d32fc58e0d (commit)
       via  05b039549ec0ad8f9796dda4166ca3ac42029b30 (commit)
       via  21320546e3efa0d2edb4a329cc2f345861679f37 (commit)
       via  9c0c85565836d854bd0177760ac51859399e7768 (commit)
       via  ba80da67aec5b5f6c4a3b725bd56937d10621b9b (commit)
       via  055bf28964ef99d3eacd8889a4f30974fb914e26 (commit)
      from  fe3a9d0b9d9486e0ec68dd73c457357b44ddd75e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5757ab093599b0f98b178dc879032f555fc8f878
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Mon Feb 13 11:49:58 2017 -0500

    webstaff: Book Items Now from Item Status
    
    We can't do what we did with Make Items Bookable, consolidating the items into
    one call and invoking a single interface; we get "Can't book multiple resource
    types at once".  For now, we disable the 'Book Item Now' menu entry if multiple
    items are selected.
    
    Signed-off-by: Jason Etheridge <jason at esilibrary.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
index f152ac8..9755360 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_list.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
@@ -12,7 +12,8 @@
   <eg-grid-action handler="make_copies_bookable"
     label="[% l('Make Items Bookable') %]"></eg-grid-action>
   <eg-grid-action handler="book_copies_now"
-    label="[% l('Book Items Now') %]"></eg-grid-action>
+    disabled="need_one_selected"
+    label="[% l('Book Item Now') %]"></eg-grid-action>
   <eg-grid-action handler="requestItems"
     label="[% l('Request Items') %]"></eg-grid-action>
   <eg-grid-action handler="attach_to_peer_bib"
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index d873abe..5468daf 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -331,6 +331,12 @@ function($scope , $q , $routeParams , $location , $timeout , $window , egCore ,
         });
     }
 
+    $scope.need_one_selected = function() {
+        var items = $scope.gridControls.selectedItems();
+        if (items.length == 1) return false;
+        return true;
+    };
+
     $scope.make_copies_bookable = function() {
 
         var copies_by_record = {};
@@ -389,6 +395,73 @@ function($scope , $q , $routeParams , $location , $timeout , $window , egCore ,
         });
     }
 
+    $scope.book_copies_now = function() {
+        var copies_by_record = {};
+        var record_list = [];
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function (item) {
+                var record_id = item['call_number.record.id'];
+                if (typeof copies_by_record[ record_id ] == 'undefined') {
+                    copies_by_record[ record_id ] = [];
+                    record_list.push( record_id );
+                }
+                copies_by_record[ record_id ].push(item.id);
+            }
+        );
+
+        var promises = [];
+        var combined_brt = [];
+        var combined_brsrc = [];
+        angular.forEach(record_list, function(record_id) {
+            promises.push(
+                egCore.net.request(
+                    'open-ils.booking',
+                    'open-ils.booking.resources.create_from_copies',
+                    egCore.auth.token(),
+                    copies_by_record[record_id]
+                ).then(function(results) {
+                    if (results && results['brt']) {
+                        combined_brt = combined_brt.concat(results['brt']);
+                    }
+                    if (results && results['brsrc']) {
+                        combined_brsrc = combined_brsrc.concat(results['brsrc']);
+                    }
+                })
+            );
+        });
+
+        $q.all(promises).then(function() {
+            if (combined_brt.length > 0 || combined_brsrc.length > 0) {
+                $uibModal.open({
+                    template: '<eg-embed-frame url="booking_admin_url" handlers="funcs"></eg-embed-frame>',
+                    animation: true,
+                    size: 'md',
+                    controller:
+                           ['$scope','$location','egCore','$uibModalInstance',
+                    function($scope , $location , egCore , $uibModalInstance) {
+
+                        $scope.funcs = {
+                            ses : egCore.auth.token(),
+                            bresv_interface_opts : {
+                                booking_results : {
+                                     brt : combined_brt
+                                    ,brsrc : combined_brsrc
+                                }
+                            }
+                        }
+
+                        var booking_path = '/eg/booking/reservation';
+
+                        $scope.booking_admin_url =
+                            $location.absUrl().replace(/\/eg\/staff.*/, booking_path);
+
+                    }]
+                });
+            }
+        });
+    }
+
     $scope.requestItems = function() {
         var copy_list = gatherSelectedHoldingsIds();
         if (copy_list.length == 0) return;

commit e0e18273d37e5a9d98b5a77e8d8afd968617803e
Author: Jason Etheridge <jason at esilibrary.com>
Date:   Mon Feb 13 11:21:43 2017 -0500

    webstaff: toward booking from Item Status
    
    This patch also offers an improvement from the XUL version,
    as batches involving multiple bibs are now correctly handled.
    
    Signed-off-by: Jason Etheridge <jason at esilibrary.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
index 087c4e0..f152ac8 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_list.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
@@ -9,6 +9,10 @@
 
   <eg-grid-action handler="add_copies_to_bucket"
     label="[% l('Add Items to Bucket') %]"></eg-grid-action>
+  <eg-grid-action handler="make_copies_bookable"
+    label="[% l('Make Items Bookable') %]"></eg-grid-action>
+  <eg-grid-action handler="book_copies_now"
+    label="[% l('Book Items Now') %]"></eg-grid-action>
   <eg-grid-action handler="requestItems"
     label="[% l('Request Items') %]"></eg-grid-action>
   <eg-grid-action handler="attach_to_peer_bib"
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index 33cc22c..d873abe 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -331,6 +331,64 @@ function($scope , $q , $routeParams , $location , $timeout , $window , egCore ,
         });
     }
 
+    $scope.make_copies_bookable = function() {
+
+        var copies_by_record = {};
+        var record_list = [];
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function (item) {
+                var record_id = item['call_number.record.id'];
+                if (typeof copies_by_record[ record_id ] == 'undefined') {
+                    copies_by_record[ record_id ] = [];
+                    record_list.push( record_id );
+                }
+                copies_by_record[ record_id ].push(item.id);
+            }
+        );
+
+        var promises = [];
+        var combined_results = [];
+        angular.forEach(record_list, function(record_id) {
+            promises.push(
+                egCore.net.request(
+                    'open-ils.booking',
+                    'open-ils.booking.resources.create_from_copies',
+                    egCore.auth.token(),
+                    copies_by_record[record_id]
+                ).then(function(results) {
+                    if (results && results['brsrc']) {
+                        combined_results = combined_results.concat(results['brsrc']);
+                    }
+                })
+            );
+        });
+
+        $q.all(promises).then(function() {
+            if (combined_results.length > 0) {
+                $uibModal.open({
+                    template: '<eg-embed-frame url="booking_admin_url" handlers="funcs"></eg-embed-frame>',
+                    animation: true,
+                    size: 'md',
+                    controller:
+                           ['$scope','$location','egCore','$uibModalInstance',
+                    function($scope , $location , egCore , $uibModalInstance) {
+
+                        $scope.funcs = {
+                            ses : egCore.auth.token(),
+                            resultant_brsrc : combined_results.map(function(o) { return o[0]; })
+                        }
+
+                        var booking_path = '/eg/conify/global/booking/resource';
+
+                        $scope.booking_admin_url =
+                            $location.absUrl().replace(/\/eg\/staff.*/, booking_path);
+                    }]
+                });
+            }
+        });
+    }
+
     $scope.requestItems = function() {
         var copy_list = gatherSelectedHoldingsIds();
         if (copy_list.length == 0) return;

commit 577361730ac72106362b483bbfa00dcf3997e0a4
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Feb 13 10:44:07 2017 -0500

    tweak Booking administration pagse
    
    - remove admin/booking/conify/* routes in favor of
      admin/server/booking/* routes that already existed;
      as part of this, remove redudant admin EmbedBookingCtl
      controller
    - alphabetize the entries on the Booking Administration splash
      page
    - remove Booking links from the Server Administration splash
      page
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/admin/booking/t_splash.tt2 b/Open-ILS/src/templates/staff/admin/booking/t_splash.tt2
index fcd79e8..f51ea00 100644
--- a/Open-ILS/src/templates/staff/admin/booking/t_splash.tt2
+++ b/Open-ILS/src/templates/staff/admin/booking/t_splash.tt2
@@ -9,11 +9,11 @@
 
 [%
     interfaces = [
-     [ l('Resource Attribute Maps'), "./admin/booking/conify/resource_attr_map" ]
-    ,[ l('Resource Attribute Values'), "./admin/booking/conify/resource_attr_value" ]
-    ,[ l('Resource Attributes'), "./admin/booking/conify/resource_attr" ]
-    ,[ l('Resource Types'), "./admin/booking/conify/resource_type" ]
-    ,[ l('Resources'), "./admin/booking/conify/resource" ]
+     [ l('Resources'), "./admin/server/booking/resource" ]
+    ,[ l('Resource Attribute Maps'), "./admin/server/booking/resource_attr_map" ]
+    ,[ l('Resource Attribute Values'), "./admin/server/booking/resource_attr_value" ]
+    ,[ l('Resource Attributes'), "./admin/server/booking/resource_attr" ]
+    ,[ l('Resource Types'), "./admin/server/booking/resource_type" ]
    ];
 
    USE table(interfaces, cols=3);
diff --git a/Open-ILS/src/templates/staff/admin/server/t_splash.tt2 b/Open-ILS/src/templates/staff/admin/server/t_splash.tt2
index 1932120..5d8bf43 100644
--- a/Open-ILS/src/templates/staff/admin/server/t_splash.tt2
+++ b/Open-ILS/src/templates/staff/admin/server/t_splash.tt2
@@ -17,11 +17,6 @@
     ,[ l('Authority Thesauri'), "./admin/server/cat/authority/thesaurus" ]
     ,[ l('Best-Hold Selection Sort Order'), "./admin/server/config/best_hold_order" ]
     ,[ l('Billing Types'), "./admin/server/config/billing_type" ]
-    ,[ l('Booking Resource Attribute Maps'), "./admin/server/booking/resource_attr_map" ]
-    ,[ l('Booking Resource Attribute Values'), "./admin/server/booking/resource_attr_value" ]
-    ,[ l('Booking Resource Attributes'), "./admin/server/booking/resource_attr" ]
-    ,[ l('Booking Resource Types'), "./admin/server/booking/resource_type" ]
-    ,[ l('Booking Resources'), "./admin/server/booking/resource" ]
     ,[ l('Call Number Prefixes'), "./admin/server/config/acn_prefix" ]
     ,[ l('Call Number Suffixes'), "./admin/server/config/acn_suffix" ]
     ,[ l('Circulation Duration Rules'), "./admin/server/config/rule_circ_duration" ]
diff --git a/Open-ILS/web/js/ui/default/staff/admin/booking/app.js b/Open-ILS/web/js/ui/default/staff/admin/booking/app.js
index 6f12e09..46f54d8 100644
--- a/Open-ILS/web/js/ui/default/staff/admin/booking/app.js
+++ b/Open-ILS/web/js/ui/default/staff/admin/booking/app.js
@@ -8,53 +8,9 @@ angular.module('egBookingAdmin',
     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); 
     var resolver = {delay : function(egStartup) {return egStartup.go()}};
 
-    var eframe_template = 
-        '<eg-embed-frame url="booking_admin_url" handlers="funcs"></eg-embed-frame>';
-
-    $routeProvider.when('/admin/booking/:noun/:verb/:extra?', {
-        template: eframe_template,
-        controller: 'EmbedBookingCtl',
-        resolve : resolver
-    });
-
     // default page 
     $routeProvider.otherwise({
         templateUrl : './admin/booking/t_splash',
         resolve : resolver
     });
 }])
-
-.controller('EmbedBookingCtl',
-       ['$scope','$routeParams','$location','egCore',
-function($scope , $routeParams , $location , egCore) {
-
-    $scope.funcs = {
-        ses : egCore.auth.token(),
-    }
-
-    var booking_path = '/eg/';
-
-    if ($routeParams.noun == 'conify') {
-        booking_path += 'conify/global/booking/' + $routeParams.verb
-            + (typeof $routeParams.extra != 'undefined'
-                ? '/' + $routeParams.extra
-                : '')
-            + location.search;
-    } else {
-        booking_path += 'booking/'
-            + $routeParams.noun + '/' + $routeParams.verb
-            + (typeof $routeParams.extra != 'undefined'
-                ? '/' + $routeParams.extra
-                : '')
-            + location.search;
-    }
-
-    // embed URL must include protocol/domain or it will be loaded via
-    // push-state, resulting in an infinitely nested pages.
-    $scope.booking_admin_url =
-        $location.absUrl().replace(/\/eg\/staff.*/, booking_path);
-
-    console.log('Loading Admin Booking URL: ' + $scope.booking_admin_url);
-
-}])
-

commit 0bd63e583da386fdaa53216a38a237547d36337e
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Thu Feb 9 10:48:20 2017 -0500

    webstaff: implement Edit MARC Order Record
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/acq/index.tt2 b/Open-ILS/src/templates/staff/acq/index.tt2
index b3a17d1..060c4b3 100644
--- a/Open-ILS/src/templates/staff/acq/index.tt2
+++ b/Open-ILS/src/templates/staff/acq/index.tt2
@@ -12,6 +12,11 @@
 <!--<script src="[% ctx.media_prefix %]/js/ui/default/staff/reporter/services/template.js"></script>-->
 <!--[% INCLUDE 'staff/reporter/share/report_strings.tt2' %]-->
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/acq/app.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/marcrecord.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/record.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/tagtable.js"></script>
+[% INCLUDE 'staff/cat/share/marcedit_strings.tt2' %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/marcedit.js"></script>
 <link rel="stylesheet" href="[% ctx.base_path %]/staff/css/acq.css" />
 [% END %]
 
diff --git a/Open-ILS/src/templates/staff/acq/t_edit_marc_order_record.tt2 b/Open-ILS/src/templates/staff/acq/t_edit_marc_order_record.tt2
new file mode 100644
index 0000000..1deeb5b
--- /dev/null
+++ b/Open-ILS/src/templates/staff/acq/t_edit_marc_order_record.tt2
@@ -0,0 +1,16 @@
+<div>
+  <div class="modal-header">
+    <button type="button" class="close"
+      ng-click="cancel()" aria-hidden="true">×</button>
+    <h4 class="modal-title">[% l('Edit MARC Order Record') %]</h4>
+  </div>
+  <div class="modal-body">
+    <eg-marc-edit-record dirty-flag="dirty_flag" marc-xml="args.marc_xml"
+                         in-place-mode="true" record-type="bre" save-label="[% l('Modify') %]" />
+  </div>
+  <div class="modal-footer">
+    <input type="submit" ng-click="ok(args)"
+        class="btn btn-primary" value="[% l('Use Edits') %]"/>
+    <button class="btn btn-warning" ng-click="cancel()">[% l('Cancel') %]</button>
+  </div>
+</div>
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 8957853..44e7b97 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -3506,15 +3506,19 @@ function AcqLiTable() {
 
     this.editOrderMarc = function(li) {
 
+        var self = this;
+        if(window.IAMBROWSER) {
+            xulG.edit_marc_order_record(li, function(li) { self.drawInfo(li.id()) });
+            return;
+        }
+
         /*  To run in Firefox directly, must set signed.applets.codebase_principal_support
             to true in about:config */
-
         if(openils.XUL.isXUL()) {
             win = window.open('/xul/' + openils.XUL.buildId() + '/server/cat/marcedit.xul','','chrome');
         } else {
             win = window.open('/xul/server/cat/marcedit.xul','','chrome'); 
         }
-        var self = this;
         win.xulG = {
             record : {marc : li.marc(), "rtype": "bre"},
             save : {
diff --git a/Open-ILS/web/js/ui/default/staff/acq/app.js b/Open-ILS/web/js/ui/default/staff/acq/app.js
index 6f302e8..c9ce2f0 100644
--- a/Open-ILS/web/js/ui/default/staff/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/acq/app.js
@@ -1,5 +1,5 @@
 angular.module('egAcquisitions',
-    ['ngRoute', 'ui.bootstrap', 'egCoreMod','egUiMod'])
+    ['ngRoute', 'ui.bootstrap', 'egCoreMod','egUiMod','egMarcMod'])
 
 .config(['$routeProvider','$locationProvider','$compileProvider', 
  function($routeProvider , $locationProvider , $compileProvider) {
@@ -31,8 +31,8 @@ angular.module('egAcquisitions',
 }])
 
 .controller('EmbedAcqCtl', 
-       ['$scope','$routeParams','$location','$window','$timeout','egCore',
-function($scope , $routeParams , $location , $window , $timeout , egCore) {
+       ['$scope','$routeParams','$location','$window','$timeout','egCore','$uibModal',
+function($scope , $routeParams , $location , $window , $timeout , egCore , $uibModal) {
 
     var relay_url = function(url) {
         if (url.match(/\/eg\/acq/)) {
@@ -67,10 +67,39 @@ function($scope , $routeParams , $location , $window , $timeout , egCore) {
         });
     }
 
+    var edit_marc_order_record = function(li, callback) {
+        var args = {
+            'marc_xml' : li.marc()
+        };
+        $uibModal.open({
+            templateUrl: './acq/t_edit_marc_order_record',
+            size: 'lg',
+            controller:
+                ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
+                $scope.focusMe = true;
+                $scope.args = args;
+                $scope.dirty_flag = false;
+                $scope.ok = function(args) { $uibModalInstance.close(args) }
+                $scope.cancel = function () { $uibModalInstance.dismiss() }
+            }]
+        }).result.then(function (args) {
+            li.marc(args.marc_xml);
+            egCore.net.request(
+                'open-ils.acq',
+                'open-ils.acq.lineitem.update',
+                egCore.auth.token(),
+                li
+            ).then(function() {
+                callback(li);
+            });
+        });
+    }
+
     $scope.funcs = {
         ses : egCore.auth.token(),
         relay_url : relay_url,
-        volume_item_creator : volume_item_creator
+        volume_item_creator : volume_item_creator,
+        edit_marc_order_record : edit_marc_order_record
     }
 
     var acq_path = '/eg/acq/' + 

commit cacd801e4374d8fe8d044712f7cc09ef242d8123
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 17:26:15 2017 -0500

    webstaff: implement Update Barcodes line item action
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/acq/app.js b/Open-ILS/web/js/ui/default/staff/acq/app.js
index 431ab4d..6f302e8 100644
--- a/Open-ILS/web/js/ui/default/staff/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/acq/app.js
@@ -46,9 +46,31 @@ function($scope , $routeParams , $location , $window , $timeout , egCore) {
         }
     }
 
+    // minimal version sufficient to update copy barcodes
+    var volume_item_creator = function(params) {
+        egCore.net.request(
+            'open-ils.actor',
+            'open-ils.actor.anon_cache.set_value',
+            null, 'edit-these-copies', {
+                copies: params.existing_copies.map(function(acp) { return acp.id(); }),
+                raw: [],
+                hide_vols : false,
+                hide_copies : false
+            }
+        ).then(function(key) {
+            if (key) {
+                var url = egCore.env.basePath + 'cat/volcopy/' + key;
+                $timeout(function() { $window.open(url, '_blank') });
+            } else {
+                alert('Could not create anonymous cache key!');
+            }
+        });
+    }
+
     $scope.funcs = {
         ses : egCore.auth.token(),
-        relay_url : relay_url
+        relay_url : relay_url,
+        volume_item_creator : volume_item_creator
     }
 
     var acq_path = '/eg/acq/' + 

commit fd7f9e90703ac6795db69018dd718561d5ebae45
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 17:04:07 2017 -0500

    webstaff: rework line item sub-actions menu
    
    Since Chrome doesn't generate click events for the act of
    selecting an <option>, change to dispatching line item
    sub-actions based on change events on the <select>.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index b616d90..4d4773d 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -204,11 +204,11 @@
                     </td>
                     <td>
                         <select name='actions'>
-                            <option name='action_none'>[% l('-- Actions --') %]</option>
-                            <option name='action_update_barcodes'>[% l('Update Barcodes') %]</option>
-                            <option name='action_holdings_maint'>[% l('Holdings Maint.') %]</option>
-                            <option name='action_manage_claims'>[% l('Claims') %]</option>
-                            <option name='action_view_history'>[% l('View History') %]</option>
+                            <option name='action_none' value='action_none'>[% l('-- Actions --') %]</option>
+                            <option name='action_update_barcodes' value='action_update_barcodes'>[% l('Update Barcodes') %]</option>
+                            <option name='action_holdings_maint' value='action_holdings_maint'>[% l('Holdings Maint.') %]</option>
+                            <option name='action_manage_claims' value='action_manage_claims'>[% l('Claims') %]</option>
+                            <option name='action_view_history' value='action_view_history'>[% l('View History') %]</option>
                         </select>
                     </td>
                     <td>
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 7f34f55..8957853 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -922,7 +922,6 @@ function AcqLiTable() {
         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) {
@@ -1224,13 +1223,26 @@ function AcqLiTable() {
     this.updateLiState = function(li, row) {
         if (!row) row = this._findLiRow(li);
 
+        nodeByName("actions", row).onchange = function() {
+            switch(this.options[this.selectedIndex].value) {
+                case 'action_update_barcodes':
+                    self.showRealCopyEditUI(li);
+                    nodeByName("action_none", row).selected = true;
+                    break;
+                case 'action_holdings_maint':
+                    (self.generateMakeRecTab( li.eg_bib_id(), 'copy_browser', row ))();
+                    break;
+                case 'action_manage_claims':
+                    self.fetchClaimInfo(li.id(), true, function(full) { self.claimDialog.show(full) }, row);
+                    break;
+                case 'action_view_history':
+                    location.href = oilsBasePath + '/acq/lineitem/history/' + li.id();
+                    break;
+            }
+        };
         var actUpdateBarcodes = nodeByName("action_update_barcodes", row);
         var actHoldingsMaint = nodeByName("action_holdings_maint", row);
 
-        // always allow access to LI history
-        nodeByName('action_view_history', row).onclick = 
-            function() { location.href = oilsBasePath + '/acq/lineitem/history/' + li.id(); };
-
         /* 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());
@@ -1262,13 +1274,7 @@ function AcqLiTable() {
                 (lids && !lids.filter(function(lid) { return lid.eg_copy_id() })[0] )) {
 
             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 );
         }
 
         var state_cell = nodeByName("li_state_" + li.state(), row);

commit 96bbfc81dae88e1976ff805eea15339d22c0d5b6
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 15:56:34 2017 -0500

    webstaff: implement 'Export Single Attribute List'
    
    This bundles in FileSaver.js, an MIT-licensed library
    that implements support for the HTML5 saveAs() routine.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index dc3434e..b616d90 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -2,6 +2,7 @@
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/common/vlagent.js'> </script>
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/common/li_table.js'> </script>
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/financial/claim_voucher.js'> </script>
+<script type="text/javascript" src='[% ctx.media_prefix %]/js/file-saver/FileSaver.min.js'> </script>
 <div id='acq-lit-table-container'>
 
     <div> <!-- Container for actions selector and paging controls.
diff --git a/Open-ILS/web/js/file-saver/FileSaver.js b/Open-ILS/web/js/file-saver/FileSaver.js
new file mode 100644
index 0000000..fb71494
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/FileSaver.js
@@ -0,0 +1,188 @@
+/* FileSaver.js
+ * A saveAs() FileSaver implementation.
+ * 1.3.2
+ * 2016-06-16 18:25:19
+ *
+ * By Eli Grey, http://eligrey.com
+ * License: MIT
+ *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
+ */
+
+/*global self */
+/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
+
+var saveAs = saveAs || (function(view) {
+	"use strict";
+	// IE <10 is explicitly unsupported
+	if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
+		return;
+	}
+	var
+		  doc = view.document
+		  // only get URL when necessary in case Blob.js hasn't overridden it yet
+		, get_URL = function() {
+			return view.URL || view.webkitURL || view;
+		}
+		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
+		, can_use_save_link = "download" in save_link
+		, click = function(node) {
+			var event = new MouseEvent("click");
+			node.dispatchEvent(event);
+		}
+		, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
+		, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
+		, throw_outside = function(ex) {
+			(view.setImmediate || view.setTimeout)(function() {
+				throw ex;
+			}, 0);
+		}
+		, force_saveable_type = "application/octet-stream"
+		// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
+		, arbitrary_revoke_timeout = 1000 * 40 // in ms
+		, revoke = function(file) {
+			var revoker = function() {
+				if (typeof file === "string") { // file is an object URL
+					get_URL().revokeObjectURL(file);
+				} else { // file is a File
+					file.remove();
+				}
+			};
+			setTimeout(revoker, arbitrary_revoke_timeout);
+		}
+		, dispatch = function(filesaver, event_types, event) {
+			event_types = [].concat(event_types);
+			var i = event_types.length;
+			while (i--) {
+				var listener = filesaver["on" + event_types[i]];
+				if (typeof listener === "function") {
+					try {
+						listener.call(filesaver, event || filesaver);
+					} catch (ex) {
+						throw_outside(ex);
+					}
+				}
+			}
+		}
+		, auto_bom = function(blob) {
+			// prepend BOM for UTF-8 XML and text/* types (including HTML)
+			// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
+			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
+				return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
+			}
+			return blob;
+		}
+		, FileSaver = function(blob, name, no_auto_bom) {
+			if (!no_auto_bom) {
+				blob = auto_bom(blob);
+			}
+			// First try a.download, then web filesystem, then object URLs
+			var
+				  filesaver = this
+				, type = blob.type
+				, force = type === force_saveable_type
+				, object_url
+				, dispatch_all = function() {
+					dispatch(filesaver, "writestart progress write writeend".split(" "));
+				}
+				// on any filesys errors revert to saving with object URLs
+				, fs_error = function() {
+					if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
+						// Safari doesn't allow downloading of blob urls
+						var reader = new FileReader();
+						reader.onloadend = function() {
+							var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
+							var popup = view.open(url, '_blank');
+							if(!popup) view.location.href = url;
+							url=undefined; // release reference before dispatching
+							filesaver.readyState = filesaver.DONE;
+							dispatch_all();
+						};
+						reader.readAsDataURL(blob);
+						filesaver.readyState = filesaver.INIT;
+						return;
+					}
+					// don't create more object URLs than needed
+					if (!object_url) {
+						object_url = get_URL().createObjectURL(blob);
+					}
+					if (force) {
+						view.location.href = object_url;
+					} else {
+						var opened = view.open(object_url, "_blank");
+						if (!opened) {
+							// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
+							view.location.href = object_url;
+						}
+					}
+					filesaver.readyState = filesaver.DONE;
+					dispatch_all();
+					revoke(object_url);
+				}
+			;
+			filesaver.readyState = filesaver.INIT;
+
+			if (can_use_save_link) {
+				object_url = get_URL().createObjectURL(blob);
+				setTimeout(function() {
+					save_link.href = object_url;
+					save_link.download = name;
+					click(save_link);
+					dispatch_all();
+					revoke(object_url);
+					filesaver.readyState = filesaver.DONE;
+				});
+				return;
+			}
+
+			fs_error();
+		}
+		, FS_proto = FileSaver.prototype
+		, saveAs = function(blob, name, no_auto_bom) {
+			return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
+		}
+	;
+	// IE 10+ (native saveAs)
+	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
+		return function(blob, name, no_auto_bom) {
+			name = name || blob.name || "download";
+
+			if (!no_auto_bom) {
+				blob = auto_bom(blob);
+			}
+			return navigator.msSaveOrOpenBlob(blob, name);
+		};
+	}
+
+	FS_proto.abort = function(){};
+	FS_proto.readyState = FS_proto.INIT = 0;
+	FS_proto.WRITING = 1;
+	FS_proto.DONE = 2;
+
+	FS_proto.error =
+	FS_proto.onwritestart =
+	FS_proto.onprogress =
+	FS_proto.onwrite =
+	FS_proto.onabort =
+	FS_proto.onerror =
+	FS_proto.onwriteend =
+		null;
+
+	return saveAs;
+}(
+	   typeof self !== "undefined" && self
+	|| typeof window !== "undefined" && window
+	|| this.content
+));
+// `self` is undefined in Firefox for Android content script context
+// while `this` is nsIContentFrameMessageManager
+// with an attribute `content` that corresponds to the window
+
+if (typeof module !== "undefined" && module.exports) {
+  module.exports.saveAs = saveAs;
+} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
+  define("FileSaver.js", function() {
+    return saveAs;
+  });
+}
diff --git a/Open-ILS/web/js/file-saver/FileSaver.min.js b/Open-ILS/web/js/file-saver/FileSaver.min.js
new file mode 100644
index 0000000..9a1e397
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/FileSaver.min.js
@@ -0,0 +1,2 @@
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
+var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=functio
 n(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w
 .DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})}
diff --git a/Open-ILS/web/js/file-saver/LICENSE.md b/Open-ILS/web/js/file-saver/LICENSE.md
new file mode 100644
index 0000000..32ef3ca
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/LICENSE.md
@@ -0,0 +1,11 @@
+The MIT License
+
+Copyright © 2016 [Eli Grey][1].
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  [1]: http://eligrey.com
diff --git a/Open-ILS/web/js/file-saver/README.md b/Open-ILS/web/js/file-saver/README.md
new file mode 100644
index 0000000..4cb9293
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/README.md
@@ -0,0 +1,135 @@
+If you need to save really large files bigger then the blob's size limitation or don't have 
+enough RAM, then have a look at the more advanced [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js)
+that can save data directly to the hard drive asynchronously with the power of the new streams API. That will have
+support for progress, cancelation and knowing when it's done writing
+
+FileSaver.js
+============
+
+FileSaver.js implements the `saveAs()` FileSaver interface in browsers that do
+not natively support it. There is a [FileSaver.js demo][1] that demonstrates saving
+various media types.
+
+FileSaver.js is the solution to saving files on the client-side, and is perfect for
+webapps that need to generate files, or for saving sensitive information that shouldn't be
+sent to an external server.
+
+Looking for `canvas.toBlob()` for saving canvases? Check out
+[canvas-toBlob.js][2] for a cross-browser implementation.
+
+Supported browsers
+------------------
+
+| Browser        | Constructs as | Filenames    | Max Blob Size | Dependencies |
+| -------------- | ------------- | ------------ | ------------- | ------------ |
+| Firefox 20+    | Blob          | Yes          | 800 MiB       | None         |
+| Firefox < 20   | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |
+| Chrome         | Blob          | Yes          | [500 MiB][3]  | None         |
+| Chrome for Android | Blob      | Yes          | [500 MiB][3]  | None         |
+| Edge           | Blob          | Yes          | ?             | None         |
+| IE 10+         | Blob          | Yes          | 600 MiB       | None         |
+| Opera 15+      | Blob          | Yes          | 500 MiB       | None         |
+| Opera < 15     | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |
+| Safari 6.1+*   | Blob          | No           | ?             | None         |
+| Safari < 6     | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |
+
+Feature detection is possible:
+
+```js
+try {
+    var isFileSaverSupported = !!new Blob;
+} catch (e) {}
+```
+
+### IE < 10
+
+It is possible to save text files in IE < 10 without Flash-based polyfills.
+See [ChenWenBrian and koffsyrup's `saveTextAs()`](https://github.com/koffsyrup/FileSaver.js#examples) for more details.
+
+### Safari 6.1+
+
+Blobs may be opened instead of saved sometimes—you may have to direct your Safari users to manually
+press <kbd>⌘</kbd>+<kbd>S</kbd> to save the file after it is opened. Using the `application/octet-stream` MIME type to force downloads [can cause issues in Safari](https://github.com/eligrey/FileSaver.js/issues/12#issuecomment-47247096).
+
+### iOS
+
+saveAs must be run within a user interaction event such as onTouchDown or onClick; setTimeout will prevent saveAs from triggering. Due to restrictions in iOS saveAs opens in a new window instead of downloading, if you want this fixed please [tell Apple](https://bugs.webkit.org/show_bug.cgi?id=102914) how this bug is affecting you.
+
+Syntax
+------
+
+```js
+FileSaver saveAs(Blob/File data, optional DOMString filename, optional Boolean disableAutoBOM)
+```
+
+Pass `true` for `disableAutoBOM` if you don't want FileSaver.js to automatically provide Unicode text encoding hints (see: [byte order mark](https://en.wikipedia.org/wiki/Byte_order_mark)).
+
+Examples
+--------
+
+### Saving text
+
+```js
+var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
+saveAs(blob, "hello world.txt");
+```
+
+The standard W3C File API [`Blob`][4] interface is not available in all browsers.
+[Blob.js][5] is a cross-browser `Blob` implementation that solves this.
+
+### Saving a canvas
+
+```js
+var canvas = document.getElementById("my-canvas"), ctx = canvas.getContext("2d");
+// draw to canvas...
+canvas.toBlob(function(blob) {
+    saveAs(blob, "pretty image.png");
+});
+```
+
+Note: The standard HTML5 `canvas.toBlob()` method is not available in all browsers.
+[canvas-toBlob.js][6] is a cross-browser `canvas.toBlob()` that polyfills this.
+
+### Saving File
+
+You can save a File constructor without specifying a filename. The
+File itself already contains a name, There is a hand full of ways to get a file
+instance (from storage, file input, new constructor)
+But if you still want to change the name, then you can change it in the 2nd argument
+
+```js
+var file = new File(["Hello, world!"], "hello world.txt", {type: "text/plain;charset=utf-8"});
+saveAs(file);
+```
+
+
+
+![Tracking image](https://in.getclicky.com/212712ns.gif)
+
+  [1]: http://eligrey.com/demos/FileSaver.js/
+  [2]: https://github.com/eligrey/canvas-toBlob.js
+  [3]: https://code.google.com/p/chromium/issues/detail?id=375297
+  [4]: https://developer.mozilla.org/en-US/docs/DOM/Blob
+  [5]: https://github.com/eligrey/Blob.js
+  [6]: https://github.com/eligrey/canvas-toBlob.js
+
+Contributing
+------------
+
+The `FileSaver.js` distribution file is compiled with Uglify.js like so:
+
+```bash
+uglifyjs FileSaver.js --mangle --comments /@source/ > FileSaver.min.js
+# or simply:
+npm run build
+```
+
+Please make sure you build a production version before submitting a pull request.
+
+Installation
+------------------
+
+```bash
+npm install file-saver --save
+bower install file-saver
+```
diff --git a/Open-ILS/web/js/file-saver/bower.json b/Open-ILS/web/js/file-saver/bower.json
new file mode 100644
index 0000000..6428c78
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/bower.json
@@ -0,0 +1,21 @@
+{
+  "name": "file-saver",
+  "main": "FileSaver.js",
+  "version": "1.3.3",
+  "homepage": "https://github.com/eligrey/FileSaver.js",
+  "authors": [
+    "Eli Grey <me at eligrey.com>"
+  ],
+  "description": "An HTML5 saveAs() FileSaver implementation",
+  "keywords": [
+    "filesaver",
+    "saveas",
+    "blob"
+  ],
+  "license": "MIT",
+  "ignore": [
+      "*",
+      "!FileSaver.*js",
+      "!LICENSE.md"
+  ]
+}
diff --git a/Open-ILS/web/js/file-saver/demo/demo.css b/Open-ILS/web/js/file-saver/demo/demo.css
new file mode 100644
index 0000000..de4a78d
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/demo/demo.css
@@ -0,0 +1,50 @@
+html {
+	background-color: #DDD;
+}
+body {
+    -webkit-box-sizing: content-box;
+    -moz-box-sizing: content-box;
+    box-sizing: content-box;
+	width: 900px;
+	margin: 0 auto;
+	font-family: Verdana, Helvetica, Arial, sans-serif;
+	-webkit-box-shadow: 0 0 10px 2px rgba(0, 0, 0, .5);
+	-moz-box-shadow: 0 0 10px 2px rgba(0, 0, 0, .5);
+	box-shadow: 0 0 10px 2px rgba(0, 0, 0, .5);
+	padding: 7px 25px 70px;
+	background-color: #FFF;
+}
+h1, h2, h3, h4, h5, h6 {
+	font-family: Georgia, "Times New Roman", serif;
+}
+h2, form {
+	text-align: center;
+}
+form {
+	margin-top: 5px;
+}
+.input {
+	width: 500px;
+	height: 300px;
+	margin: 0 auto;
+	display: block;
+}
+section {
+	margin-top: 40px;
+}
+#canvas {
+	cursor: crosshair;
+}
+#canvas, #html {
+	border: 1px solid #000;
+}
+.filename {
+	text-align: right;
+}
+#html {
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+	overflow: auto;
+	padding: 1em;
+}
diff --git a/Open-ILS/web/js/file-saver/demo/demo.js b/Open-ILS/web/js/file-saver/demo/demo.js
new file mode 100755
index 0000000..2b3dc34
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/demo/demo.js
@@ -0,0 +1,216 @@
+/*! FileSaver.js demo script
+ *  2016-05-26
+ *
+ *  By Eli Grey, http://eligrey.com
+ *  License: MIT
+ *    See LICENSE.md
+ */
+
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/demo/demo.js */
+
+/*jshint laxbreak: true, laxcomma: true, smarttabs: true*/
+/*global saveAs, self*/
+
+(function(view) {
+"use strict";
+// The canvas drawing portion of the demo is based off the demo at
+// http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/
+var
+	  document = view.document
+	, $ = function(id) {
+		return document.getElementById(id);
+	}
+	, session = view.sessionStorage
+	// only get URL when necessary in case Blob.js hasn't defined it yet
+	, get_blob = function() {
+		return view.Blob;
+	}
+
+	, canvas = $("canvas")
+	, canvas_options_form = $("canvas-options")
+	, canvas_filename = $("canvas-filename")
+	, canvas_clear_button = $("canvas-clear")
+
+	, text = $("text")
+	, text_options_form = $("text-options")
+	, text_filename = $("text-filename")
+
+	, html = $("html")
+	, html_options_form = $("html-options")
+	, html_filename = $("html-filename")
+
+	, ctx = canvas.getContext("2d")
+	, drawing = false
+	, x_points = session.x_points || []
+	, y_points = session.y_points || []
+	, drag_points = session.drag_points || []
+	, add_point = function(x, y, dragging) {
+		x_points.push(x);
+		y_points.push(y);
+		drag_points.push(dragging);
+	}
+	, draw = function(){
+		canvas.width = canvas.width;
+		ctx.lineWidth = 6;
+		ctx.lineJoin = "round";
+		ctx.strokeStyle = "#000000";
+		var
+			  i = 0
+			, len = x_points.length
+		;
+		for(; i < len; i++) {
+			ctx.beginPath();
+			if (i && drag_points[i]) {
+				ctx.moveTo(x_points[i-1], y_points[i-1]);
+			} else {
+				ctx.moveTo(x_points[i]-1, y_points[i]);
+			}
+			ctx.lineTo(x_points[i], y_points[i]);
+			ctx.closePath();
+			ctx.stroke();
+		}
+	}
+	, stop_drawing = function() {
+		drawing = false;
+	}
+
+	// Title guesser and document creator available at https://gist.github.com/1059648
+	, guess_title = function(doc) {
+		var
+			  h = "h6 h5 h4 h3 h2 h1".split(" ")
+			, i = h.length
+			, headers
+			, header_text
+		;
+		while (i--) {
+			headers = doc.getElementsByTagName(h[i]);
+			for (var j = 0, len = headers.length; j < len; j++) {
+				header_text = headers[j].textContent.trim();
+				if (header_text) {
+					return header_text;
+				}
+			}
+		}
+	}
+	, doc_impl = document.implementation
+	, create_html_doc = function(html) {
+		var
+			  dt = doc_impl.createDocumentType('html', null, null)
+			, doc = doc_impl.createDocument("http://www.w3.org/1999/xhtml", "html", dt)
+			, doc_el = doc.documentElement
+			, head = doc_el.appendChild(doc.createElement("head"))
+			, charset_meta = head.appendChild(doc.createElement("meta"))
+			, title = head.appendChild(doc.createElement("title"))
+			, body = doc_el.appendChild(doc.createElement("body"))
+			, i = 0
+			, len = html.childNodes.length
+		;
+		charset_meta.setAttribute("charset", html.ownerDocument.characterSet);
+		for (; i < len; i++) {
+			body.appendChild(doc.importNode(html.childNodes.item(i), true));
+		}
+		var title_text = guess_title(doc);
+		if (title_text) {
+			title.appendChild(doc.createTextNode(title_text));
+		}
+		return doc;
+	}
+;
+canvas.width = 500;
+canvas.height = 300;
+
+  if (typeof x_points === "string") {
+	x_points = JSON.parse(x_points);
+} if (typeof y_points === "string") {
+	y_points = JSON.parse(y_points);
+} if (typeof drag_points === "string") {
+	drag_points = JSON.parse(drag_points);
+} if (session.canvas_filename) {
+	canvas_filename.value = session.canvas_filename;
+} if (session.text) {
+	text.value = session.text;
+} if (session.text_filename) {
+	text_filename.value = session.text_filename;
+} if (session.html) {
+	html.innerHTML = session.html;
+} if (session.html_filename) {
+	html_filename.value = session.html_filename;
+}
+
+drawing = true;
+draw();
+drawing = false;
+
+canvas_clear_button.addEventListener("click", function() {
+	canvas.width = canvas.width;
+	x_points.length =
+	y_points.length =
+	drag_points.length =
+		0;
+}, false);
+canvas.addEventListener("mousedown", function(event) {
+	event.preventDefault();
+	drawing = true;
+	add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, false);
+	draw();
+}, false);
+canvas.addEventListener("mousemove", function(event) {
+	if (drawing) {
+		add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, true);
+		draw();
+	}
+}, false);
+canvas.addEventListener("mouseup", stop_drawing, false);
+canvas.addEventListener("mouseout", stop_drawing, false);
+
+canvas_options_form.addEventListener("submit", function(event) {
+	event.preventDefault();
+	canvas.toBlobHD(function(blob) {
+		saveAs(
+			  blob
+			, (canvas_filename.value || canvas_filename.placeholder) + ".png"
+		);
+	}, "image/png");
+}, false);
+
+text_options_form.addEventListener("submit", function(event) {
+	event.preventDefault();
+	var BB = get_blob();
+	saveAs(
+		  new BB(
+			  [text.value || text.placeholder]
+			, {type: "text/plain;charset=" + document.characterSet}
+		)
+		, (text_filename.value || text_filename.placeholder) + ".txt"
+	);
+}, false);
+
+html_options_form.addEventListener("submit", function(event) {
+	event.preventDefault();
+	var
+		  BB = get_blob()
+		, xml_serializer = new XMLSerializer()
+		, doc = create_html_doc(html)
+	;
+	saveAs(
+		  new BB(
+			  [xml_serializer.serializeToString(doc)]
+			, {type: "text/plain;charset=" + document.characterSet}
+		)
+		, (html_filename.value || html_filename.placeholder) + ".xhtml"
+	);
+}, false);
+
+view.addEventListener("unload", function() {
+	session.x_points = JSON.stringify(x_points);
+	session.y_points = JSON.stringify(y_points);
+	session.drag_points = JSON.stringify(drag_points);
+	session.canvas_filename = canvas_filename.value;
+
+	session.text = text.value;
+	session.text_filename = text_filename.value;
+
+	session.html = html.innerHTML;
+	session.html_filename = html_filename.value;
+}, false);
+}(self));
diff --git a/Open-ILS/web/js/file-saver/demo/demo.min.js b/Open-ILS/web/js/file-saver/demo/demo.min.js
new file mode 100755
index 0000000..d30333c
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/demo/demo.min.js
@@ -0,0 +1,2 @@
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/demo/demo.js */
+(function(n){"use strict";var s=n.document,g=function(A){return s.getElementById(A)},b=n.sessionStorage,x=function(){return n.Blob},f=g("canvas"),r=g("canvas-options"),y=g("canvas-filename"),p=g("canvas-clear"),q=g("text"),t=g("text-options"),h=g("text-filename"),m=g("html"),e=g("html-options"),i=g("html-filename"),u=f.getContext("2d"),z=false,a=b.x_points||[],o=b.y_points||[],d=b.drag_points||[],j=function(A,C,B){a.push(A);o.push(C);d.push(B)},l=function(){f.width=f.width;u.lineWidth=6;u.lineJoin="round";u.strokeStyle="#000000";var B=0,A=a.length;for(;B<A;B++){u.beginPath();if(B&&d[B]){u.moveTo(a[B-1],o[B-1])}else{u.moveTo(a[B]-1,o[B])}u.lineTo(a[B],o[B]);u.closePath();u.stroke()}},c=function(){z=false},w=function(E){var D="h6 h5 h4 h3 h2 h1".split(" "),C=D.length,F,G;while(C--){F=E.getElementsByTagName(D[C]);for(var B=0,A=F.length;B<A;B++){G=F[B].textContent.trim();if(G){return G}}}},v=s.implementation,k=function(D){var B=v.createDocumentType("html",null,null),J=v.createDo
 cument("http://www.w3.org/1999/xhtml","html",B),A=J.documentElement,H=A.appendChild(J.createElement("head")),K=H.appendChild(J.createElement("meta")),I=H.appendChild(J.createElement("title")),E=A.appendChild(J.createElement("body")),C=0,G=D.childNodes.length;K.setAttribute("charset",D.ownerDocument.characterSet);for(;C<G;C++){E.appendChild(J.importNode(D.childNodes.item(C),true))}var F=w(J);if(F){I.appendChild(J.createTextNode(F))}return J};f.width=500;f.height=300;if(typeof a==="string"){a=JSON.parse(a)}if(typeof o==="string"){o=JSON.parse(o)}if(typeof d==="string"){d=JSON.parse(d)}if(b.canvas_filename){y.value=b.canvas_filename}if(b.text){q.value=b.text}if(b.text_filename){h.value=b.text_filename}if(b.html){m.innerHTML=b.html}if(b.html_filename){i.value=b.html_filename}z=true;l();z=false;p.addEventListener("click",function(){f.width=f.width;a.length=o.length=d.length=0},false);f.addEventListener("mousedown",function(A){A.preventDefault();z=true;j(A.pageX-f.offsetLeft,A.pag
 eY-f.offsetTop,false);l()},false);f.addEventListener("mousemove",function(A){if(z){j(A.pageX-f.offsetLeft,A.pageY-f.offsetTop,true);l()}},false);f.addEventListener("mouseup",c,false);f.addEventListener("mouseout",c,false);r.addEventListener("submit",function(A){A.preventDefault();f.toBlobHD(function(B){saveAs(B,(y.value||y.placeholder)+".png")},"image/png")},false);t.addEventListener("submit",function(A){A.preventDefault();var B=x();saveAs(new B([q.value||q.placeholder],{type:"text/plain;charset="+s.characterSet}),(h.value||h.placeholder)+".txt")},false);e.addEventListener("submit",function(B){B.preventDefault();var D=x(),A=new XMLSerializer,C=k(m);saveAs(new D([A.serializeToString(C)],{type:"text/plain;charset="+s.characterSet}),(i.value||i.placeholder)+".xhtml")},false);n.addEventListener("unload",function(){b.x_points=JSON.stringify(a);b.y_points=JSON.stringify(o);b.drag_points=JSON.stringify(d);b.canvas_filename=y.value;b.text=q.value;b.text_filename=h.value;b.html=m.inn
 erHTML;b.html_filename=i.value},false)}(self));
\ No newline at end of file
diff --git a/Open-ILS/web/js/file-saver/demo/index.xhtml b/Open-ILS/web/js/file-saver/demo/index.xhtml
new file mode 100644
index 0000000..7a60efd
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/demo/index.xhtml
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US-x-Hixie">
+<head>
+    <meta charset="utf-8"/>
+    <title>FileSaver.js demo</title>
+    <link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/eligrey/FileSaver.js/702cd2e820b680f88a0f299e33085c196806fc52/demo/demo.css"/>
+</head>
+<body>
+    <h1><a href="https://github.com/eligrey/FileSaver.js">FileSaver.js</a> demo</h1>
+    <p>
+        The following examples demonstrate how it is possible to generate and save any type of data right in the browser using the <code>saveAs()</code> FileSaver interface, without contacting any servers.
+    </p>
+    <section id="image-demo">
+        <h2>Saving an image</h2>
+        <canvas class="input" id="canvas" width="500" height="300"/>
+        <form id="canvas-options">
+            <label>Filename: <input type="text" class="filename" id="canvas-filename" placeholder="doodle"/>.png</label>
+            <input type="submit" value="Save"/>
+            <input type="button" id="canvas-clear" value="Clear"/>
+        </form>
+    </section>
+    <section id="text-demo">
+        <h2>Saving text</h2>
+        <textarea class="input" id="text" placeholder="Once upon a time..."/>
+        <form id="text-options">
+            <label>Filename: <input type="text" class="filename" id="text-filename" placeholder="a plain document"/>.txt</label>
+            <input type="submit" value="Save"/>
+        </form>
+    </section>
+    <section id="html-demo">
+        <h2>Saving rich text</h2>
+        <div class="input" id="html" contenteditable="">
+            <h3>Some example rich text</h3>
+            <ul>
+                <li><del>Plain</del> <ins>Boring</ins> text.</li>
+                <li><em>Emphasized text!</em></li>
+                <li><strong>Strong text!</strong></li>
+                <li>
+                    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="70" height="70">
+                        <circle cx="35" cy="35" r="35" fill="red"/>
+                        <text x="10" y="40">image</text>
+                    </svg>
+                </li>
+                <li><a href="https://github.com/eligrey/FileSaver.js">A link.</a></li>
+            </ul>
+        </div>
+        <form id="html-options">
+            <label>Filename: <input type="text" class="filename" id="html-filename" placeholder="a rich document"/>.xhtml</label>
+            <input type="submit" value="Save"/>
+        </form>
+    </section>
+    <script async="" src="https://cdn.rawgit.com/eligrey/Blob.js/0cef2746414269b16834878a8abc52eb9d53e6bd/Blob.js"/>
+    <script async="" src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"/>
+    <script async="" src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"/>
+    <script async="" src="https://cdn.rawgit.com/eligrey/FileSaver.js/597b6cd0207ce408a6d34890b5b2826b13450714/demo/demo.js"/>
+</body>
+</html>
diff --git a/Open-ILS/web/js/file-saver/package.json b/Open-ILS/web/js/file-saver/package.json
new file mode 100644
index 0000000..0593fa4
--- /dev/null
+++ b/Open-ILS/web/js/file-saver/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "file-saver",
+  "version": "1.3.3",
+  "description": "An HTML5 saveAs() FileSaver implementation",
+  "main": "FileSaver.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 0",
+    "build": "uglifyjs FileSaver.js --mangle --comments /@source/ > FileSaver.min.js"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/eligrey/FileSaver.js"
+  },
+  "keywords": [
+    "filesaver",
+    "saveas",
+    "blob"
+  ],
+  "author": {
+    "name": "Eli Grey",
+    "email": "me at eligrey.com"
+  },
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/eligrey/FileSaver.js/issues"
+  },
+  "homepage": "https://github.com/eligrey/FileSaver.js#readme",
+  "devDependencies": {
+    "uglify-js": "^2.6.2"
+  },
+  "readme": "If you need to save really large files bigger then the blob's size limitation or don't have \nenough RAM, then have a look at the more advanced [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js)\nthat can save data directly to the hard drive asynchronously with the power of the new streams API. That will have\nsupport for progress, cancelation and knowing when it's done writing\n\nFileSaver.js\n============\n\nFileSaver.js implements the `saveAs()` FileSaver interface in browsers that do\nnot natively support it. There is a [FileSaver.js demo][1] that demonstrates saving\nvarious media types.\n\nFileSaver.js is the solution to saving files on the client-side, and is perfect for\nwebapps that need to generate files, or for saving sensitive information that shouldn't be\nsent to an external server.\n\nLooking for `canvas.toBlob()` for saving canvases? Check out\n[canvas-toBlob.js][2] for a cross-browser implementation.\n\nSupported browsers\n--------
 ----------\n\n| Browser        | Constructs as | Filenames    | Max Blob Size | Dependencies |\n| -------------- | ------------- | ------------ | ------------- | ------------ |\n| Firefox 20+    | Blob          | Yes          | 800 MiB       | None         |\n| Firefox < 20   | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |\n| Chrome         | Blob          | Yes          | [500 MiB][3]  | None         |\n| Chrome for Android | Blob      | Yes          | [500 MiB][3]  | None         |\n| Edge           | Blob          | Yes          | ?             | None         |\n| IE 10+         | Blob          | Yes          | 600 MiB       | None         |\n| Opera 15+      | Blob          | Yes          | 500 MiB       | None         |\n| Opera < 15     | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |\n| Safari 6.1+*   | Blob          | No           | ?             | None         |\n| Safar
 i < 6     | data: URI     | No           | n/a           | [Blob.js](https://github.com/eligrey/Blob.js) |\n\nFeature detection is possible:\n\n```js\ntry {\n    var isFileSaverSupported = !!new Blob;\n} catch (e) {}\n```\n\n### IE < 10\n\nIt is possible to save text files in IE < 10 without Flash-based polyfills.\nSee [ChenWenBrian and koffsyrup's `saveTextAs()`](https://github.com/koffsyrup/FileSaver.js#examples) for more details.\n\n### Safari 6.1+\n\nBlobs may be opened instead of saved sometimes—you may have to direct your Safari users to manually\npress <kbd>⌘</kbd>+<kbd>S</kbd> to save the file after it is opened. Using the `application/octet-stream` MIME type to force downloads [can cause issues in Safari](https://github.com/eligrey/FileSaver.js/issues/12#issuecomment-47247096).\n\n### iOS\n\nsaveAs must be run within a user interaction event such as onTouchDown or onClick; setTimeout will prevent saveAs from triggering. Due to restrictions in iOS saveAs opens in
  a new window instead of downloading, if you want this fixed please [tell Apple](https://bugs.webkit.org/show_bug.cgi?id=102914) how this bug is affecting you.\n\nSyntax\n------\n\n```js\nFileSaver saveAs(Blob/File data, optional DOMString filename, optional Boolean disableAutoBOM)\n```\n\nPass `true` for `disableAutoBOM` if you don't want FileSaver.js to automatically provide Unicode text encoding hints (see: [byte order mark](https://en.wikipedia.org/wiki/Byte_order_mark)).\n\nExamples\n--------\n\n### Saving text\n\n```js\nvar blob = new Blob([\"Hello, world!\"], {type: \"text/plain;charset=utf-8\"});\nsaveAs(blob, \"hello world.txt\");\n```\n\nThe standard W3C File API [`Blob`][4] interface is not available in all browsers.\n[Blob.js][5] is a cross-browser `Blob` implementation that solves this.\n\n### Saving a canvas\n\n```js\nvar canvas = document.getElementById(\"my-canvas\"), ctx = canvas.getContext(\"2d\");\n// draw to canvas...\ncanvas.toBlob(function(blob) {\n    
 saveAs(blob, \"pretty image.png\");\n});\n```\n\nNote: The standard HTML5 `canvas.toBlob()` method is not available in all browsers.\n[canvas-toBlob.js][6] is a cross-browser `canvas.toBlob()` that polyfills this.\n\n### Saving File\n\nYou can save a File constructor without specifying a filename. The\nFile itself already contains a name, There is a hand full of ways to get a file\ninstance (from storage, file input, new constructor)\nBut if you still want to change the name, then you can change it in the 2nd argument\n\n```js\nvar file = new File([\"Hello, world!\"], \"hello world.txt\", {type: \"text/plain;charset=utf-8\"});\nsaveAs(file);\n```\n\n\n\n![Tracking image](https://in.getclicky.com/212712ns.gif)\n\n  [1]: http://eligrey.com/demos/FileSaver.js/\n  [2]: https://github.com/eligrey/canvas-toBlob.js\n  [3]: https://code.google.com/p/chromium/issues/detail?id=375297\n  [4]: https://developer.mozilla.org/en-US/docs/DOM/Blob\n  [5]: https://github.com/eligrey/Blob.js\n
   [6]: https://github.com/eligrey/canvas-toBlob.js\n\nContributing\n------------\n\nThe `FileSaver.js` distribution file is compiled with Uglify.js like so:\n\n```bash\nuglifyjs FileSaver.js --mangle --comments /@source/ > FileSaver.min.js\n# or simply:\nnpm run build\n```\n\nPlease make sure you build a production version before submitting a pull request.\n\nInstallation\n------------------\n\n```bash\nnpm install file-saver --save\nbower install file-saver\n```\n",
+  "readmeFilename": "README.md",
+  "_id": "file-saver at 1.3.3",
+  "_shasum": "cdd4c44d3aa264eac2f68ec165bc791c34af1232",
+  "_from": "file-saver@",
+  "_resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz"
+}
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 7c1c311..7f34f55 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -3122,10 +3122,15 @@ function AcqLiTable() {
                 }
             }
             try {
-                openils.XUL.contentToFileSaveDialog(
-                    value_list.join("\n"),
-                    localeStrings.EXPORT_SAVE_DIALOG_TITLE
-                );
+                if (window.IAMBROWSER) {
+                    var blob = new Blob(value_list, {type: "text/plain;charset=utf-8"});
+                    saveAs(blob, "export_attr_list.txt");
+                } else {
+                    openils.XUL.contentToFileSaveDialog(
+                        value_list.join("\n"),
+                        localeStrings.EXPORT_SAVE_DIALOG_TITLE
+                    );
+                }
             } catch (E) {
                 alert(E);
             }

commit e14402e2cc9255da8a2ee26d3d6c22f8e8771866
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 15:21:26 2017 -0500

    webstaff: fix CSV-parsing of catalog record ID lists
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
index 47d23d1..886d4be 100644
--- a/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
+++ b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
@@ -95,7 +95,7 @@ if (!dojo._hasResource["openils.widget.XULTermLoader"]) {
                         var reader = new FileReader();
                         reader.onloadend = function(evt) {
                             data = self[
-                                this.parseCSV ? "parseAsCSV" : "parseUnimaginatively"
+                                self.parseCSV ? "parseAsCSV" : "parseUnimaginatively"
                             ](evt.target.result);
                             updateTermList();
                         };

commit 7486798554516caa0c88ccca358d44670a7705f3
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 14:46:31 2017 -0500

    webstaff: get Load Catalog Record IDs working
    
    Provide an alternative using FileReader to the XPCOM-based
    client-side file reading that this feature uses under XUL.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
index db49e08..47d23d1 100644
--- a/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
+++ b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js
@@ -35,13 +35,24 @@ if (!dojo._hasResource["openils.widget.XULTermLoader"]) {
                     "span", {"innerHTML": this.terms.length},
                     this.labelNode, "first"
                 );
-                this.buttonNode = dojo.create(
-                    "button", {
-                        "innerHTML": this._.BUTTON_TEXT,
-                        "onclick": function() { self.loadTerms(); }
-                    },
-                    this.domNode, "last"
-                );
+                if (window.parent.IEMBEDXUL) {
+                    this.buttonNode = dojo.create(
+                        "input", {
+                            "type" : "file",
+                            "innerHTML": this._.BUTTON_TEXT,
+                            "onchange": function(evt) { self.loadTerms(evt); }
+                        },
+                        this.domNode, "last"
+                    );
+                } else {
+                    this.buttonNode = dojo.create(
+                        "button", {
+                            "innerHTML": this._.BUTTON_TEXT,
+                            "onclick": function() { self.loadTerms(); }
+                        },
+                        this.domNode, "last"
+                    );
+                }
 
                 if (this.args.parentNode)
                     dojo.place(this.domNode, this.args.parentNode, "last");
@@ -57,31 +68,49 @@ if (!dojo._hasResource["openils.widget.XULTermLoader"]) {
             "focus": function() {
                 this.buttonNode.focus();
             },
-            "loadTerms": function() {
+            "loadTerms": function(evt) {
                 try {
                     if (this.terms.length >= this.args.termLimit) {
                         alert(this._.TERM_LIMIT);
                         return;
                     }
-                    var data = this[
-                        this.parseCSV ? "parseAsCSV" : "parseUnimaginatively"
-                    ](
-                        openils.XUL.contentFromFileOpenDialog(
-                            this._.CHOOSE_FILE, this.args.fileSizeLimit
-                        )
-                    );
+                    var data;
+                    var self = this;
 
-                    if (data.length + this.terms.length >=
-                        this.args.termLimit) {
-                        alert(this._.TERM_LIMIT_SOME);
-                        var can = this.args.termLimit - this.terms.length;
-                        if (can > 0)
-                            this.terms = this.terms.concat(data.slice(0, can));
+                    function updateTermList() {
+                        if (data.length + self.terms.length >=
+                            self.args.termLimit) {
+                            alert(self._.TERM_LIMIT_SOME);
+                            var can = self.args.termLimit - self.terms.length;
+                            if (can > 0)
+                                self.terms = self.terms.concat(data.slice(0, can));
+                        } else {
+                            self.terms = self.terms.concat(data);
+                        }
+                        self.attr("value", self.terms);
+                        self.updateCount();
+                    }
+
+                    if (evt && window.IAMBROWSER) {
+                        var reader = new FileReader();
+                        reader.onloadend = function(evt) {
+                            data = self[
+                                this.parseCSV ? "parseAsCSV" : "parseUnimaginatively"
+                            ](evt.target.result);
+                            updateTermList();
+                        };
+                        reader.readAsText(evt.target.files[0]);
                     } else {
-                        this.terms = this.terms.concat(data);
+                        data = this[
+                            this.parseCSV ? "parseAsCSV" : "parseUnimaginatively"
+                        ](
+                            openils.XUL.contentFromFileOpenDialog(
+                                this._.CHOOSE_FILE, this.args.fileSizeLimit
+                            )
+                        );
+                        updateTermList();
                     }
-                    this.attr("value", this.terms);
-                    this.updateCount();
+
                 } catch(E) {
                     alert(E);
                 }
diff --git a/Open-ILS/web/js/ui/default/staff/services/eframe.js b/Open-ILS/web/js/ui/default/staff/services/eframe.js
index 7fdbd0a..b9b4dd7 100644
--- a/Open-ILS/web/js/ui/default/staff/services/eframe.js
+++ b/Open-ILS/web/js/ui/default/staff/services/eframe.js
@@ -33,6 +33,10 @@ angular.module('egCoreMod')
         link: function (scope, element, attrs) {
             scope.autoresize = 'autoresize' in attrs;
             scope.showIframe = true;
+            // well, I *might* embed XUL; in any event, this gives a way
+            // for things like Dojo widgets to detect whether they are
+            // running in an eframe before the frame load has finished.
+            window.IEMBEDXUL = true;
             element.find('iframe').on(
                 'load',
                 function() {scope.egEmbedFrameLoader(this)}

commit ce6e8b4d974a16a72a4b5a6d8e9bbd7da9f77953
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Wed Feb 8 13:21:35 2017 -0500

    webstaff: fix highlighting of line item
    
    Tweak the layout of the line item table so that returning
    to it from the copy view causes only the border of the
    line item information box to be highlighted, not both the
    border and a random segment of the contents.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index 7cb762f..dc3434e 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -159,7 +159,7 @@
                                     </td>
                                 </tr>
                                 <tr>
-                                    <td colspan='0'>
+                                    <td colspan='8'>
                                         <table><tr>
                                             <td>[% l('Order Identifier') %]</td>
                                             <td>

commit 3454c1a4cbf7d4d69ac5783684266945fcbbc0a2
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Tue Feb 7 16:15:08 2017 -0500

    webstaff: avoid showing 'null' as source label when displaying line item
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 879ba81..7c1c311 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -710,7 +710,8 @@ function AcqLiTable() {
         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()));
+        if (li.source_label() !== null)
+            dojo.query('[name=source_label]', row)[0].appendChild(document.createTextNode(li.source_label()));
 
         // so we can scroll to it later
         dojo.query('[name=bib-info-cell]', row)[0].id = 'li-title-ref-' + li.id();

commit 56d96cdebc0f1adb628c63f33e9e9b62708f5c59
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Tue Feb 7 15:45:35 2017 -0500

    webstaff: using egEmbedFrame escape opens new window at frame's current location
    
    ... as opposed to whatever URL was originally embedded.
    
    For example, if you doing a PO search, then open the frame in a
    new window, the results of the search are displayed in that window
    rather than an empty search form.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/services/eframe.js b/Open-ILS/web/js/ui/default/staff/services/eframe.js
index e87b0ba..7fdbd0a 100644
--- a/Open-ILS/web/js/ui/default/staff/services/eframe.js
+++ b/Open-ILS/web/js/ui/default/staff/services/eframe.js
@@ -276,7 +276,7 @@ angular.module('egCoreMod')
             // open a new tab with the embedded URL
             $scope.escapeEmbed = function() {
                 $scope.showIframe = false;
-                $window.open($scope.url, '_blank').focus();
+                $window.open($scope.iframe.contentWindow.location, '_blank').focus();
             }
             $scope.restoreEmbed = function() {
                 $scope.showIframe = true;

commit 144739828769a8baa533bc671932c8b97e5c6470
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Tue Feb 7 12:56:43 2017 -0500

    webstaff: add view/place orders action to holdings grid
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
index bd618ac..4a5181e 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
@@ -37,6 +37,8 @@
       label="[% l('Add Items to Bucket') %]"></eg-grid-action>
     <eg-grid-action handler="requestItems"
       label="[% l('Request Items') %]"></eg-grid-action>
+    <eg-grid-action handler="view_place_orders"
+      label="[% l('View/Place Orders') %]"></eg-grid-action>
     <eg-grid-action handler="attach_to_peer_bib"
       label="[% l('Link as Conjoined to Previously Marked Bib Record') %]"></eg-grid-action>
     <eg-grid-action handler="markLibAsVolTarget"
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index 0a1c01c..3ab19cc 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -703,6 +703,12 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         });
     }
 
+    $scope.view_place_orders = function() {
+        if (!$scope.record_id) return;
+        var url = egCore.env.basePath + 'acq/legacy/lineitem/related/' + $scope.record_id + '?target=bib';
+        $timeout(function() { $window.open(url, '_blank') });
+    }
+
     $scope.replaceBarcodes = function() {
         var copy_list = gatherSelectedRawCopies();
         if (copy_list.length == 0) return;

commit 64d4167d8f04afef189d35667c17745d2e9e785c
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Tue Feb 7 12:32:05 2017 -0500

    distinguish between XUL and Firefox when excluding XUL code from webstaff
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/dojo/openils/XUL.js b/Open-ILS/web/js/dojo/openils/XUL.js
index 459681f..47021d2 100644
--- a/Open-ILS/web/js/dojo/openils/XUL.js
+++ b/Open-ILS/web/js/dojo/openils/XUL.js
@@ -4,11 +4,16 @@ if(!dojo._hasResource["openils.XUL"]) {
     dojo.declare('openils.XUL', null, {});
 
     openils.XUL.Component_copy;
-    try {
-        openils.XUL.Component_copy = Components;
-    } catch (e) {
-        openils.XUL.Component_copy = null;
-    };
+    if (!window.IAMBROWSER) {
+        // looks like Firefox also exposes 'Components', so its
+        // existence is not sufficient check of XUL-ness
+        try {
+            if (Components.classes)
+                openils.XUL.Component_copy = Components;
+        } catch (e) {
+            openils.XUL.Component_copy = null;
+        };
+    }
 
     openils.XUL.isXUL = function() {
         if(location.protocol == 'chrome:' || location.protocol == 'oils:') return true;

commit 5e071c58b2b9f755e9631b53e64833a0c0071a9f
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Feb 6 17:23:34 2017 -0500

    webstaff: more intra-acq linking fixes
    
    - fix typo from previous patch
    - implement linking out to queues and invoices from
      line item table
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 818a576..879ba81 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -765,10 +765,13 @@ function AcqLiTable() {
                         openils.Util.show(nodeByName('queue', row), 'inline');
                         var link = nodeByName("queue_link", row);
                         link.onclick = function() { 
+                            var url = oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + qrec.queue()
                             // open a new tab to the vandelay queue for this record
-                            openils.XUL.newTabEasy(
-                                oilsBasePath + '/vandelay/vandelay?qtype=bib&qid=' + qrec.queue()
-                            );
+                            if (window.IAMBROWSER) {
+                                xulG.relay_url(url);
+                            } else {
+                                openils.XUL.newTabEasy(url);
+                            }
                         }
                     }
                 }
@@ -1236,10 +1239,13 @@ function AcqLiTable() {
             openils.Util.show(nodeByName("invoices_span", row), "inline");
             var link = nodeByName("invoices_link", row);
             link.onclick = function() {
-                openils.XUL.newTabEasy(
-                    oilsBasePath + "/acq/search/unified?so=" +
-                    base64Encode({"jub":[{"id": li.id()}]}) + "&rt=invoice"
-                );
+                var url = oilsBasePath + "/acq/search/unified?so=" +
+                          base64Encode({"jub":[{"id": li.id()}]}) + "&rt=invoice"
+                if (window.IAMBROWSER) {
+                    xulG.relay_url(url);
+                } else {
+                    openils.XUL.newTabEasy(url);
+                }
                 return false;
             };
         }
diff --git a/Open-ILS/web/js/ui/default/staff/acq/app.js b/Open-ILS/web/js/ui/default/staff/acq/app.js
index b9e5b0f..431ab4d 100644
--- a/Open-ILS/web/js/ui/default/staff/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/acq/app.js
@@ -53,7 +53,7 @@ function($scope , $routeParams , $location , $window , $timeout , egCore) {
 
     var acq_path = '/eg/acq/' + 
         $routeParams.noun + '/' + $routeParams.verb +
-        ((typeof $routeParams.record != 'undefined') ? '/' + $routeParams.record : '')
+        ((typeof $routeParams.record != 'undefined') ? '/' + $routeParams.record : '') +
         location.search;
 
     $scope.min_height = 2000; // give lots of space to start

commit 56688c5f13ea9a7a6420d49c8b6d3f5fa9d54ac6
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Feb 6 16:42:35 2017 -0500

    improve linking out of acquisitions upload page
    
    - cat/catalog/vandelay route no longer drops
      query parameters, permitting direct linking to import
      queues (e.g., /eg/staff/cat/catalog/vandelay?qtype=bib&qid=367)
    - acq/legacy/:noun/:verb/:record now a valid acq route
    - purchase order MARC loading now generates usable links
      back to PO, selection list, and/or import queue
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/acq/picklist/upload.js b/Open-ILS/web/js/ui/default/acq/picklist/upload.js
index df11b0f..6abdc69 100644
--- a/Open-ILS/web/js/ui/default/acq/picklist/upload.js
+++ b/Open-ILS/web/js/ui/default/acq/picklist/upload.js
@@ -177,7 +177,11 @@ function acqHandlePostUpload(key, plId) {
                                 link.setAttribute('href', url); 
                             } else {
                                 link.setAttribute('href', 'javascript:;'); // for linky-ness
-                                link.onclick = function() { openils.XUL.newTabEasy(url, null, null, true) };
+                                if (window.IAMBROWSER) {
+                                    link.onclick = function() { xulG.relay_url(url) };
+                                } else {
+                                    link.onclick = function() { openils.XUL.newTabEasy(url, null, null, true) };
+                                }
                             }
                         }
                             
diff --git a/Open-ILS/web/js/ui/default/staff/acq/app.js b/Open-ILS/web/js/ui/default/staff/acq/app.js
index b100c4f..b9e5b0f 100644
--- a/Open-ILS/web/js/ui/default/staff/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/acq/app.js
@@ -17,6 +17,12 @@ angular.module('egAcquisitions',
         resolve : resolver
     });
 
+    $routeProvider.when('/acq/legacy/:noun/:verb/:record', {
+        template: eframe_template,
+        controller: 'EmbedAcqCtl',
+        resolve : resolver
+    });
+
     // default page 
     $routeProvider.otherwise({
         templateUrl : './t_splash',
@@ -25,15 +31,30 @@ angular.module('egAcquisitions',
 }])
 
 .controller('EmbedAcqCtl', 
-       ['$scope','$routeParams','$location','egCore',
-function($scope , $routeParams , $location , egCore) {
+       ['$scope','$routeParams','$location','$window','$timeout','egCore',
+function($scope , $routeParams , $location , $window , $timeout , egCore) {
+
+    var relay_url = function(url) {
+        if (url.match(/\/eg\/acq/)) {
+            var munged_url = egCore.env.basePath + 
+                url.replace(/^.*?\/eg\/acq\//, "acq/legacy/");
+            $timeout(function() { $window.open(munged_url, '_blank') });
+        } else if (url.match(/\/eg\/vandelay/)) {
+            var munged_url = egCore.env.basePath + 
+                url.replace(/^.*?\/eg\/vandelay\/vandelay/, "cat/catalog/vandelay");
+            $timeout(function() { $window.open(munged_url, '_blank') });
+        }
+    }
 
     $scope.funcs = {
         ses : egCore.auth.token(),
+        relay_url : relay_url
     }
 
     var acq_path = '/eg/acq/' + 
-        $routeParams.noun + '/' + $routeParams.verb + location.search;
+        $routeParams.noun + '/' + $routeParams.verb +
+        ((typeof $routeParams.record != 'undefined') ? '/' + $routeParams.record : '')
+        location.search;
 
     $scope.min_height = 2000; // give lots of space to start
 
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index 3b48469..0a1c01c 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -1542,7 +1542,7 @@ function($scope , $location) {
 .controller('VandelayCtrl',
        ['$scope','$location',
 function($scope , $location) {
-    $scope.vandelay_url = $location.absUrl().replace(/\/staff.*/, '/vandelay/vandelay');
+    $scope.vandelay_url = $location.absUrl().replace(/\/staff\/cat\/catalog\/vandelay/, '/vandelay/vandelay');
 }])
 
 .controller('ManageAuthoritiesCtrl',

commit 0e54e117d4fc7ac2de2a991a60a161d32fc58e0d
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Feb 6 12:53:41 2017 -0500

    tweak embedding of Dojo acq admin pages
    
    - turn on the "open in new window" button
    - set minimum height of eframes to 2000px
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/admin/acq/app.js b/Open-ILS/web/js/ui/default/staff/admin/acq/app.js
index 245725e..14dbe84 100644
--- a/Open-ILS/web/js/ui/default/staff/admin/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/admin/acq/app.js
@@ -9,7 +9,7 @@ angular.module('egAcqAdmin',
     var resolver = {delay : function(egStartup) {return egStartup.go()}};
 
     var eframe_template = 
-        '<eg-embed-frame url="acq_admin_url" handlers="funcs"></eg-embed-frame>';
+        '<eg-embed-frame allow-escape="true" min-height="min_height" url="acq_admin_url" handlers="funcs"></eg-embed-frame>';
 
     $routeProvider.when('/admin/acq/:noun/:verb/:extra?', {
         template: eframe_template,
@@ -49,6 +49,8 @@ function($scope , $routeParams , $location , egCore) {
             + location.search;
     }
 
+    $scope.min_height = 2000; // give lots of space to start
+
     // embed URL must include protocol/domain or it will be loaded via
     // push-state, resulting in an infinitely nested pages.
     $scope.acq_admin_url =

commit 05b039549ec0ad8f9796dda4166ca3ac42029b30
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Feb 6 12:39:17 2017 -0500

    tweak embedding of Dojo acquisitions pages
    
    - turn on the "open in new window" button
    - set minimum height of eframes to 2000px
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/acq/app.js b/Open-ILS/web/js/ui/default/staff/acq/app.js
index d5fd847..b100c4f 100644
--- a/Open-ILS/web/js/ui/default/staff/acq/app.js
+++ b/Open-ILS/web/js/ui/default/staff/acq/app.js
@@ -9,7 +9,7 @@ angular.module('egAcquisitions',
     var resolver = {delay : function(egStartup) {return egStartup.go()}};
 
     var eframe_template = 
-        '<eg-embed-frame url="acq_url" handlers="funcs"></eg-embed-frame>';
+        '<eg-embed-frame allow-escape="true" min-height="min_height" url="acq_url" handlers="funcs"></eg-embed-frame>';
 
     $routeProvider.when('/acq/legacy/:noun/:verb', {
         template: eframe_template,
@@ -35,6 +35,8 @@ function($scope , $routeParams , $location , egCore) {
     var acq_path = '/eg/acq/' + 
         $routeParams.noun + '/' + $routeParams.verb + location.search;
 
+    $scope.min_height = 2000; // give lots of space to start
+
     // embed URL must include protocol/domain or it will be loaded via
     // push-state, resulting in an infinitely nested pages.
     $scope.acq_url = 

commit 21320546e3efa0d2edb4a329cc2f345861679f37
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue Jan 17 11:46:50 2017 -0500

    webstaff: Restrict spinner use to positive numbers
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
index 2f423de..92c2201 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
@@ -328,7 +328,7 @@ function(egCore , $q) {
                     '<div class="label label-danger" ng-if="duplicate_barcode">{{duplicate_barcode_string}}</div>'+
                     '<div class="label label-danger" ng-if="empty_barcode">{{empty_barcode_string}}</div>'+
                 '</div>'+
-                '<div class="col-xs-3"><input class="form-control" type="number" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
+                '<div class="col-xs-3"><input class="form-control" type="number" min="1" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
                 '<div class="col-xs-4"><eg-basic-combo-box eg-disabled="record == 0" list="parts" selected="part"></eg-basic-combo-box></div>'+
             '</div>',
 

commit 9c0c85565836d854bd0177760ac51859399e7768
Author: Mike Rylander <mrylander at gmail.com>
Date:   Mon Jan 16 16:21:31 2017 -0500

    webstaff: improve truthiness test of bre.deleted in marc editor
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2 b/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
index fd2e133..d7ed391 100644
--- a/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
+++ b/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
@@ -42,8 +42,8 @@
           <button class="btn btn-primary" ng-click="saveRecord()">{{ saveLabel || "[% l('Save') %]"}}</button>
         </span>
         <span class="btn-group">
-          <button ng-hide="brandNewRecord || embedded || Record().deleted()" class="btn btn-default" ng-click="deleteRecord()">[% l('Delete') %]</button>
-          <button ng-if="!brandNewRecord && Record().deleted()" class="btn btn-default" ng-click="undeleteRecord()">[% l('Undelete') %]</button>
+          <button ng-hide="brandNewRecord || embedded || Record().deleted() == 't'" class="btn btn-default" ng-click="deleteRecord()">[% l('Delete') %]</button>
+          <button ng-hide="brandNewRecord || Record().deleted() != 't'" class="btn btn-default" ng-click="undeleteRecord()">[% l('Undelete') %]</button>
         </span>
         <span class="btn-group">
           <button class="btn btn-default" ng-click="showHelp = !showHelp">[% l('Help') %]</button>

commit ba80da67aec5b5f6c4a3b725bd56937d10621b9b
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue Jan 10 12:38:27 2017 -0500

    webstaff: colspan=0 does not render how we want in chrome -- change that for lineitem tables
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index 40546c8..7cb762f 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -174,7 +174,7 @@
                                     </td>
                                 </tr>
                                 <tr>
-                                    <td colspan='0'>
+                                    <td colspan='8'>
                                         <span name="liid"># </span> | 
                                         <span name="li_existing_count">0</span> 
                                         <span name="catalog" class='hidden'> | <a title='[% l('Show In Catalog') %]' name="catalog_link" href="javascript:void(0);">[% l('&#x279F; catalog') %]</a></span> 
@@ -223,7 +223,7 @@
                     <td><input type='text' size='8' name='price'/></td>
                 </tr>
                 <tr id='acq-inline-copies-row' class='acq-inline-copies-row'>
-                    <td colspan='0'>
+                    <td colspan='8'>
                         <table class='acq-li-inline-copies-table'>
                             <thead>
                                 <tr>

commit 055bf28964ef99d3eacd8889a4f30974fb914e26
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue Jan 10 12:10:58 2017 -0500

    webstaff: Protect some ACQ interfaces from XUL-ish calls
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>

diff --git a/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js b/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
index 6ef5126..95ca98a 100644
--- a/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
+++ b/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
@@ -11,7 +11,7 @@ function InvoiceLinkDialogManager(which, target) {
             var join = (idx == 0) ? '?' : '&';
             path += join + "attach_" + self.which + "=" + id;
         });
-        if (openils.XUL.isXUL()) {
+        if (openils.XUL.isXUL() && !window.IAMBROWSER) {
             openils.XUL.newTabEasy(
                 path,
                 /* tab title */ dojo.string.substitute(
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 7a34720..818a576 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -3158,7 +3158,7 @@ function AcqLiTable() {
         }
         var path = oilsBasePath + '/acq/invoice/view?create=1';
         dojo.forEach(liIds, function(li, idx) { path += '&attach_li=' + li });
-        if (openils.XUL.isXUL())
+        if (openils.XUL.isXUL() && !window.IAMBROWSER)
             openils.XUL.newTabEasy(path, localeStrings.NEW_INVOICE, null, true);
         else
             location.href = path;
diff --git a/Open-ILS/web/js/ui/default/acq/invoice/view.js b/Open-ILS/web/js/ui/default/acq/invoice/view.js
index 4fbc16a..9ac5e25 100644
--- a/Open-ILS/web/js/ui/default/acq/invoice/view.js
+++ b/Open-ILS/web/js/ui/default/acq/invoice/view.js
@@ -275,7 +275,7 @@ function performSearch(pageDir, clearFirst) {
 
     var searchObject = termManager.buildSearchObject();
 
-    if (openils.XUL.isXUL()) {
+    if (openils.XUL.isXUL() && !window.IAMBROWSER) {
 
         cookieSvc.setCookieString(cookieUriSSL, null, 
             "invs=" + base64Encode(searchObject) + ';max-age=2592000', null);
@@ -373,7 +373,7 @@ function renderUnifiedSearch() {
 
         var searchObject, searchConjunction;
 
-        if (openils.XUL.isXUL()) {
+        if (openils.XUL.isXUL() && !window.IAMBROWSER) {
     
             if (!cookieSvc) {
 

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/src/templates/acq/common/li_table.tt2     |   17 +-
 Open-ILS/src/templates/staff/acq/index.tt2         |    5 +
 .../staff/acq/t_edit_marc_order_record.tt2         |   16 ++
 .../src/templates/staff/admin/booking/t_splash.tt2 |   10 +-
 .../src/templates/staff/admin/server/t_splash.tt2  |    5 -
 .../src/templates/staff/cat/catalog/t_holdings.tt2 |    2 +
 Open-ILS/src/templates/staff/cat/item/t_list.tt2   |    5 +
 .../src/templates/staff/cat/share/t_marcedit.tt2   |    4 +-
 Open-ILS/web/js/dojo/openils/XUL.js                |   15 +-
 .../web/js/dojo/openils/widget/XULTermLoader.js    |   77 +++++---
 Open-ILS/web/js/file-saver/FileSaver.js            |  188 +++++++++++++++++
 Open-ILS/web/js/file-saver/FileSaver.min.js        |    2 +
 Open-ILS/web/js/file-saver/LICENSE.md              |   11 +
 Open-ILS/web/js/file-saver/README.md               |  135 ++++++++++++
 Open-ILS/web/js/file-saver/bower.json              |   21 ++
 Open-ILS/web/js/file-saver/demo/demo.css           |   50 +++++
 Open-ILS/web/js/file-saver/demo/demo.js            |  216 ++++++++++++++++++++
 Open-ILS/web/js/file-saver/demo/demo.min.js        |    2 +
 Open-ILS/web/js/file-saver/demo/index.xhtml        |   57 +++++
 Open-ILS/web/js/file-saver/package.json            |   37 ++++
 .../web/js/ui/default/acq/common/inv_dialog.js     |    2 +-
 Open-ILS/web/js/ui/default/acq/common/li_table.js  |   74 +++++---
 Open-ILS/web/js/ui/default/acq/invoice/view.js     |    4 +-
 Open-ILS/web/js/ui/default/acq/picklist/upload.js  |    6 +-
 Open-ILS/web/js/ui/default/staff/acq/app.js        |   84 +++++++-
 Open-ILS/web/js/ui/default/staff/admin/acq/app.js  |    4 +-
 .../web/js/ui/default/staff/admin/booking/app.js   |   44 ----
 .../web/js/ui/default/staff/cat/catalog/app.js     |    8 +-
 Open-ILS/web/js/ui/default/staff/cat/item/app.js   |  131 ++++++++++++
 .../web/js/ui/default/staff/cat/volcopy/app.js     |    2 +-
 .../web/js/ui/default/staff/services/eframe.js     |    6 +-
 31 files changed, 1108 insertions(+), 132 deletions(-)
 create mode 100644 Open-ILS/src/templates/staff/acq/t_edit_marc_order_record.tt2
 create mode 100644 Open-ILS/web/js/file-saver/FileSaver.js
 create mode 100644 Open-ILS/web/js/file-saver/FileSaver.min.js
 create mode 100644 Open-ILS/web/js/file-saver/LICENSE.md
 create mode 100644 Open-ILS/web/js/file-saver/README.md
 create mode 100644 Open-ILS/web/js/file-saver/bower.json
 create mode 100644 Open-ILS/web/js/file-saver/demo/demo.css
 create mode 100755 Open-ILS/web/js/file-saver/demo/demo.js
 create mode 100755 Open-ILS/web/js/file-saver/demo/demo.min.js
 create mode 100644 Open-ILS/web/js/file-saver/demo/index.xhtml
 create mode 100644 Open-ILS/web/js/file-saver/package.json


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list