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

Evergreen Git git at git.evergreen-ils.org
Thu Feb 21 13:05:57 EST 2019


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  fed895c05164efb1c01b1516b3bc806477fc587f (commit)
       via  71e0eacf2ebb8d0691b99d943e43008769e7fe63 (commit)
       via  b4de131879229f2aefd3ee84c6d4e3f3962657a0 (commit)
       via  1e248098368da44b2b6c9a2eef30faef67a9356f (commit)
       via  a76e3313a76ef2c548590baba9b9def0768ca3dd (commit)
       via  c7783ded8da83b035f2c0a007609f101f75fc600 (commit)
       via  a80f25685d9f7617434c800adb62c20031ed8508 (commit)
       via  2da42a5b540c67e3421f06629e34a71c1d958072 (commit)
       via  920b10a2834ee41b2d2a61eba4eee8f6a3bbcba4 (commit)
       via  06a7f93c29b90f3e3a63c30cce8156f413a23124 (commit)
       via  f1c754c49211f82ca82773e0d578b31768c37f40 (commit)
       via  c33bc5438d3383666aaddb43b190c5da5ea8b2b4 (commit)
       via  a4411d6aedfc2134b85af40f9580375a06ffc1b6 (commit)
      from  c85d6a55a5edc1b37e76366f8849326a586996da (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 fed895c05164efb1c01b1516b3bc806477fc587f
Author: Chris Sharp <csharp at georgialibraries.org>
Date:   Thu Feb 21 13:05:48 2019 -0500

    LP#1787479: Stamping upgrade script
    
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index eb484ceab5..8c5113f7a4 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -92,7 +92,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1152', :eg_version); -- berick/dbwells
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1153', :eg_version); -- abowling/kmlussier/csharp
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels b/Open-ILS/src/sql/Pg/upgrade/1153.data.update_item_label_org_unit_labels.sql
similarity index 98%
rename from Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels
rename to Open-ILS/src/sql/Pg/upgrade/1153.data.update_item_label_org_unit_labels.sql
index dbb59f74b5..9cfee9b6ec 100644
--- a/Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels
+++ b/Open-ILS/src/sql/Pg/upgrade/1153.data.update_item_label_org_unit_labels.sql
@@ -1,6 +1,6 @@
 BEGIN;
 
---SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('1153', :eg_version);
 
 UPDATE config.org_unit_setting_type
 SET label = oils_i18n_gettext(

commit 71e0eacf2ebb8d0691b99d943e43008769e7fe63
Author: Adam Bowling <abowling at emeralddata.net>
Date:   Tue Feb 12 23:15:08 2019 -0500

    LP#1787479 Final fixes for print label customizations. Corrects duplication of pcrud objects
    in previous submission and adds CSS to item label template.
    
    Signed-off-by: Adam Bowling <abowling at emeralddata.net>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 87814c1834..47ccde682b 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -2,12 +2,8 @@
 <style>
   /* TODO: move me */
 
-  body {
-
-  }
-
   table.page-break {
-  page-break-before: always;
+      page-break-before: always;
   }
 
 </style>
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
index d8a877303e..46730ac00b 100644
--- a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
+++ b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
@@ -95,6 +95,10 @@ HTML itself.
     /* This pulls from the "Item Print Label - Inline CSS" setting */
     {{settings['webstaff.cat.label.inline_css']||''}}
 
+    .print+.print {
+        page-break-before: always;
+    }
+
 </style>
 
 <!-- Here we are implementing our combined label as 2-column HTML <table>, with
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index e24520b792..255368931f 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -33,8 +33,6 @@ function (egCore) {
     service.flesh = {
         flesh: 3,
         flesh_fields: {
-            acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect'],
-            acn: ['record', 'prefix', 'suffix'],
             acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect', 'circ_lib'],
             acn: ['record', 'prefix', 'suffix', 'owning_lib'],
             bre: ['simple_record', 'creator', 'editor']

commit b4de131879229f2aefd3ee84c6d4e3f3962657a0
Author: Adam Bowling <abowling at emeralddata.net>
Date:   Tue Jan 29 16:31:43 2019 -0500

    LP#1787479: Fixes print label template customization saving for local storage
    
    updates and incorporates added template fields from the patch
    user/gmcharlt/lp1726568_label_owning_lib.
    
    Signed-off-by: Adam Bowling <abowling at emeralddata.net>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 14a60d9090..87814c1834 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="Windows-1252"?>
 <style>
   /* TODO: move me */
 
@@ -270,5 +271,4 @@ label. Use pocket label left margin to identify how much space to provide betwee
           context="preview_scope"></div>
     </div>
     <!-- col -->
-</div>
-
+</div>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
index cf14d6465d..d8a877303e 100644
--- a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
+++ b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="Windows-1252"?>
 <!--
 Template for printing item labels (spine, pocket, etc.).
 
@@ -20,7 +21,7 @@ HTML itself.
 -->
 <style>
     /* The .spine here defines a CSS "class", which in this case, is used by our
-       spine label */
+       left label */
     .spine {
 
         /* this pulls from the "Item Print Label Font Family" setting, but will
@@ -35,24 +36,24 @@ HTML itself.
            default to 'normal' */
         font-weight: {{settings['webstaff.cat.label.font.weight'] || 'normal'}};
 
-        /* this pulls from the "Item Print Label - Height for Spine Label"
+        /* this pulls from the "Item Print Label - Height for Left Label"
            setting, but will default to '1.25in' */
         height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
         min-height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
         max-height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
 
-        /* this pulls from the "Item Print Label - Width for Spine Label"
+        /* this pulls from the "Item Print Label - Width for Left Label"
            setting, but will default to '1in' */
         width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
         min-width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
         max-width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
 
-        /* this pulls from the "Item Print Label - Left Margin for Spine Label"
+        /* this pulls from the "Item Print Label - Left Margin for Left Label"
            setting, but will default to '0in' */
         margin-left: {{settings['webstaff.cat.label.left_label.left_margin'] || '0in'}};
     }
 
-    /* This CSS class is used by our pocket label */
+    /* This CSS class is used by our right label */
     .pocket {
 
         /* this pulls from the "Item Print Label Font Family" setting, but will
@@ -67,19 +68,19 @@ HTML itself.
            default to 'normal' */
         font-weight: {{settings['webstaff.cat.label.font.weight'] || 'normal'}};
 
-        /* this pulls from the "Item Print Label - Height for Spine Label"
+        /* this pulls from the "Item Print Label - Height for Left Label"
            setting, but will default to '1.25in' */
         height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
         min-height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
         max-height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
 
-        /* this pulls from the "Item Print Label - Width for Spine Label"
+        /* this pulls from the "Item Print Label - Width for Left Label"
            setting, but will default to '2.625in' */
         width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
         min-width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
         max-width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
 
-        /* this pulls from the "Item Print Label - Left Margin for Pocket Label"
+        /* this pulls from the "Item Print Label - Left Margin for Left Label"
            setting, but will default to '0in' */
         margin-left: {{settings['webstaff.cat.label.right_label.left_margin'] || '0in'}};
 
@@ -120,6 +121,7 @@ HTML itself.
                 copy['call_number.label_class']
                 copy['call_number.label_sortkey']
                 copy['call_number.notes']
+                copy['call_number.owning_lib']
                 copy['call_number.owning_lib.id']
                 copy['call_number.owning_lib.shortname']
                 copy['call_number.owning_lib.name']
@@ -172,6 +174,7 @@ HTML itself.
                 copy['call_number.uri_maps']
                 copy['call_number.uris']
                 copy['circ_as_type']
+                copy['circ_lib']
                 copy['circ_lib.id']
                 copy['circ_lib.shortname']
                 copy['circ_lib.name']
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index 9907985aab..e24520b792 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -35,6 +35,8 @@ function (egCore) {
         flesh_fields: {
             acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect'],
             acn: ['record', 'prefix', 'suffix'],
+            acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect', 'circ_lib'],
+            acn: ['record', 'prefix', 'suffix', 'owning_lib'],
             bre: ['simple_record', 'creator', 'editor']
         },
         select: {
@@ -123,7 +125,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
             selected: "spine-pocket"
         },
         page: {
-            column_class: ["spine"],
+            column_class: ["spine", "pocket"],
             dimensions: {
                 columns: 2,
                 rows: 1
@@ -164,7 +166,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                 $scope.preview_scope = {
                     'copies': []
                     , 'settings': {}
-                    , 'toolbox_settings': toolbox_settings
+                    , 'toolbox_settings': JSON.parse(JSON.stringify(toolbox_settings))
                     , 'get_cn_for': function (copy) {
                         var key = $scope.rendered_cn_key_by_copy_id[copy.id];
                         if (key) {
@@ -216,7 +218,6 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         return !angular.isNumber(toolbox_settings.page.dimensions.rows) || !angular.isNumber(toolbox_settings.page.start_position.row) ? false : (toolbox_settings.page.start_position.row <= toolbox_settings.page.dimensions.rows);
                     }
                 };
-
                 $scope.record_details = {};
                 $scope.org_unit_settings = {};
 
@@ -256,6 +257,11 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                                 }
                             }
                         });
+                        egCore.hatch.getItem('cat.printlabels.last_toolbox_settings').then(function (last_toolbox_settings) {
+                            if (last_toolbox_settings) {
+                                $scope.preview_scope.toolbox_settings = JSON.parse(JSON.stringify(last_toolbox_settings));
+                            }
+                        });
                     })
                 );
 
@@ -289,13 +295,6 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         egCore.print.fleshPrintScope($scope.preview_scope);
                         $scope.template_changed(); // load the default
                         $scope.rebuild_cn_set();
-                        if ($scope.preview_scope.toolbox_settings && $scope.template_name && $scope.print.template_content) {
-                            var re = /eg\_plt/i;
-                            if (re.test($scope.print.template_content)) {
-                                $scope.applyTemplate($scope.template_name);
-                                $scope.redraw_label_table();
-                            }
-                        }
                     });
 
                 });
@@ -307,6 +306,12 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
 
     }
 
+    $scope.checkForToolboxCustomizations = function (tText, redraw) {
+        var re = /eg\_plt\_(\d+)/;
+        redraw ? $scope.redraw_label_table() : false;
+        return re.test(tText);
+    }
+
     $scope.fetchTemplates = function (set_default) {
         return egCore.hatch.getItem('cat.printlabels.templates').then(function (t) {
             if (t) {
@@ -325,22 +330,17 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
     $scope.fetchTemplates(true);
 
     $scope.applyTemplate = function (n) {
-        if (n) {
-            if ($scope.templates[n]) {
-                $scope.print.cn_template_content = $scope.templates[n].cn_content;
-                $scope.print.template_content = $scope.templates[n].content;
-                $scope.print.template_context = $scope.templates[n].context;
-                for (var s in $scope.templates[n].settings) {
-                    $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
-                }
-                if ($scope.templates[n].toolbox_settings) {
-                    $scope.preview_scope.toolbox_settings = $scope.templates[n].toolbox_settings;
-                    $scope.create_print_label_table();
-                }
-                egCore.hatch.setItem('cat.printlabels.default_template', n);
-                $scope.save_locally();
-            }
+        $scope.print.cn_template_content = $scope.templates[n].cn_content;
+        $scope.print.template_content = $scope.templates[n].content;
+        $scope.print.template_context = $scope.templates[n].context;
+        for (var s in $scope.templates[n].settings) {
+            $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
+        }
+        if ($scope.templates[n].toolbox_settings) {
+            $scope.preview_scope.toolbox_settings = JSON.parse(JSON.stringify($scope.templates[n].toolbox_settings));
         }
+        egCore.hatch.setItem('cat.printlabels.default_template', n);
+        $scope.save_locally();
     }
 
     $scope.deleteTemplate = function (n) {
@@ -361,6 +361,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
 
     $scope.saveTemplate = function (n) {
         if (n) {
+
             $scope.templates[n] = {
                 content: $scope.print.template_content
                 , context: $scope.print.template_context
@@ -401,6 +402,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
         .then(
             function (html) {
                 $scope.print.template_content = html;
+                $scope.checkForToolboxCustomizations(html, true);
             },
             function () {
                 $scope.print.template_content = '';
@@ -423,9 +425,15 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
         );
         egCore.hatch.getItem('cat.printlabels.last_settings').then(function (s) {
             if (s) {
-                $scope.preview_scope.settings = s;
+                $scope.preview_scope.settings = JSON.parse(JSON.stringify(s));
             }
         });
+        egCore.hatch.getItem('cat.printlabels.last_toolbox_settings').then(function (t) {
+            if (t) {
+                $scope.preview_scope.toolbox_settings = JSON.parse(JSON.stringify(t));
+            }
+        });
+
     }
 
     $scope.reset_to_default = function () {
@@ -439,10 +447,12 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
             'item_label_cn'
         );
         egCore.hatch.removeItem('cat.printlabels.last_settings');
+        egCore.hatch.removeItem('cat.printlabels.last_toolbox_settings');
         for (s in $scope.preview_scope.settings) {
             $scope.preview_scope.settings[s] = undefined;
         }
-        $scope.preview_scope.settings = {};
+        $scope.preview_scope.toolbox_settings = JSON.parse(JSON.stringify(toolbox_settings));
+
         egCore.org.settings($scope.org_unit_setting_list).then(function (res) {
             $scope.preview_scope.settings = res;
         });
@@ -463,7 +473,8 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
             'item_label_cn',
             $scope.print.cn_template_content
         );
-        egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);
+        egCore.hatch.setItem('cat.printlabels.last_settings', JSON.parse(JSON.stringify($scope.preview_scope.settings)));
+        egCore.hatch.setItem('cat.printlabels.last_toolbox_settings', JSON.parse(JSON.stringify($scope.preview_scope.toolbox_settings)));
     }
 
     $scope.imported_print_templates = { data: '' };
@@ -476,9 +487,11 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         content: el.content
                         , context: el.context
                         , cn_content: el.cn_content
-                        , settings: el.settings
-                        , toolbox_settings: el.toolbox_settings
+                        , settings: JSON.parse(JSON.stringify(el.settings))
                     };
+                    if (el.toolbox_settings) {
+                        $scope.templates[k].toolbox_settings = JSON.parse(JSON.stringify(el.toolbox_settings));
+                    }
                 });
                 $scope.saveTemplate();
                 $scope.template_changed(); // refresh
@@ -512,50 +525,39 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
         });
     }
 
-    $scope.create_print_label_table = function () {
+    $scope.redraw_label_table = function () {
         if ($scope.print_label_form.$valid && $scope.print.template_content && $scope.preview_scope) {
             $scope.preview_scope.label_output_copies = labelOutputRowsFilter($scope.preview_scope.copies, $scope.preview_scope.toolbox_settings);
+            var d = new Date().getTime().toString();
             var html = $scope.print.template_content;
-            var d = new Date(); //Added to table ID with 'eg_plt_' to cause $complie on $scope.print.template_content to fire due to template content change.
-            var table = "<table id=\"eg_plt_" + d.getTime().toString() + "_{{$index}}\" eg-print-label-table style=\"border-collapse: collapse; border: 0 solid transparent; border-spacing: 0; margin: {{$index === 0 ? toolbox_settings.page.margins.top.size : 0}} 0 0 0;\" class=\"custom-label-table{{$index % toolbox_settings.page.dimensions.rows === 0 && $index > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n";
-            table += "<tr>\n";
-            table += "<td style=\"border: 0 solid transparent; padding: {{parentIndex % toolbox_settings.page.dimensions.rows === 0 && toolbox_settings.feed_option.selected === 'sheet' && parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : 0}} 0 0 {{$index === 0 ? toolbox_settings.page.margins.left.size : col.styl ? col.styl : toolbox_settings.page.space_between_labels.horizontal.size}};\" ng-repeat=\"col in row.columns\">\n";
-            table += "<pre class=\"{{col.cls}}\" style=\"border: none; margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'spine'\">\n";
-            table += "{{col.c ? get_cn_for(col.c) : ''}}";
-            table += "</pre>\n";
-            table += "<pre class=\"{{col.cls}}{{parentIndex % toolbox_settings.page.dimensions.rows === 0 && parentIndex > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" style=\"border: none;  margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'pocket'\">\n";
-            table += "{{col.c ? col.c.barcode : ''}}\n";
-            table += "{{col.c ? col.c['call_number.label'] : ''}}\n";
-            table += "{{col.c ? get_bib_for(col.c).author : ''}}\n";
-            table += "{{col.c ? (get_bib_for(col.c).title | wrap:28:'once':'  ') : ''}}\n";
-            table += "</pre>\n";
-            table += "</td>\n"
-            table += "</tr>\n";
-            table += "</table>";
-            var comments = html.match(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g);
-            html = html.replace(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g, "");
-            var style = html.match(/\<style[^\>]*\>(?:(?!\<\/style\>)(?:.|\s))*\<\/style\>\s*/gi);
-            var output = (style ? style.join("\n") : "") + (comments ? comments.join("\n") : "") + table;
-            output = output.replace(/\n+/, "\n");
-            $scope.print.template_content = output;
-            $scope.save_locally();
-        }
-    }
-
-    $scope.redraw_label_table = function () {
-        var d = new Date(); //Added to table ID with 'eg_plt_' to cause $complie on $scope.print.template_content to fire due to template content change.
-        var table = "<table id=\"eg_plt_" + d.getTime().toString() + "\"\></table>\n";
-        $scope.print.template_content += table;
-        $scope.create_print_label_table();
-    }
-
-    $scope.$watch('preview_scope.toolbox_settings.page.dimensions.columns',
-        function (newVal, oldVal) {
-            if (newVal && newVal != oldVal && $scope.preview_scope) {
-                $scope.redraw_label_table();
+            if ($scope.checkForToolboxCustomizations(html)) {
+                html = html.replace(/eg\_plt\_\d+/, "eg_plt_" + d);
+                $scope.print.template_content = html;
+            } else {
+                var table = "<table id=\"eg_plt_" + d + "_{{$index}}\" eg-print-label-table style=\"border-collapse: collapse; border: 0 solid transparent; border-spacing: 0; margin: {{$index === 0 ? toolbox_settings.page.margins.top.size : 0}} 0 0 0;\" class=\"custom-label-table{{$index % toolbox_settings.page.dimensions.rows === 0 && $index > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n";
+                table += "<tr>\n";
+                table += "<td style=\"border: 0 solid transparent; padding: {{parentIndex % toolbox_settings.page.dimensions.rows === 0 && toolbox_settings.feed_option.selected === 'sheet' && parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : 0}} 0 0 {{$index === 0 ? toolbox_settings.page.margins.left.size : col.styl ? col.styl : toolbox_settings.page.space_between_labels.horizontal.size}};\" ng-repeat=\"col in row.columns\">\n";
+                table += "<pre class=\"{{col.cls}}\" style=\"border: none; margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'spine'\">\n";
+                table += "{{col.c ? get_cn_for(col.c) : ''}}";
+                table += "</pre>\n";
+                table += "<pre class=\"{{col.cls}}{{parentIndex % toolbox_settings.page.dimensions.rows === 0 && parentIndex > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" style=\"border: none;  margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'pocket'\">\n";
+                table += "{{col.c ? col.c.barcode : ''}}\n";
+                table += "{{col.c ? col.c['call_number.label'] : ''}}\n";
+                table += "{{col.c ? get_bib_for(col.c).author : ''}}\n";
+                table += "{{col.c ? (get_bib_for(col.c).title | wrap:28:'once':'  ') : ''}}\n";
+                table += "</pre>\n";
+                table += "</td>\n"
+                table += "</tr>\n";
+                table += "</table>";
+                var comments = html.match(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g);
+                html = html.replace(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g, '');
+                var style = html.match(/\<style[^\>]*\>(?:(?!\<\/style\>)(?:.|\s))*\<\/style\>\s*/gi);
+                var output = (comments ? comments.join("\n") : "") + (style ? style.join("\n") : "") + table;
+                output = output.replace(/\n+/, "\n");
+                $scope.print.template_content = output;
             }
         }
-    );
+    }
 
     $scope.$watch('print.cn_template_content', function (newVal, oldVal) {
         if (newVal && newVal != oldVal) {
@@ -575,14 +577,14 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
         }
     });
 
-    $scope.$watchGroup(['preview_scope.toolbox_settings.page.margins.top.size', 'preview_scope.toolbox_settings.page.margins.left.size', 'preview_scope.toolbox_settings.page.dimensions.rows', 'preview_scope.toolbox_settings.page.space_between_labels.horizontal.size', 'preview_scope.toolbox_settings.page.space_between_labels.vertical.size', 'preview_scope.toolbox_settings.page.start_position.row', 'preview_scope.toolbox_settings.page.start_position.column', 'preview_scope.toolbox_settings.page.label.gap.size'], function (newVal, oldVal) {
+    $scope.$watchGroup(['preview_scope.toolbox_settings.page.margins.top.size', 'preview_scope.toolbox_settings.page.margins.left.size', 'preview_scope.toolbox_settings.page.dimensions.rows', 'preview_scope.toolbox_settings.page.dimensions.columns', 'preview_scope.toolbox_settings.page.space_between_labels.horizontal.size', 'preview_scope.toolbox_settings.page.space_between_labels.vertical.size', 'preview_scope.toolbox_settings.page.start_position.row', 'preview_scope.toolbox_settings.page.start_position.column', 'preview_scope.toolbox_settings.page.label.gap.size'], function (newVal, oldVal) {
         if (newVal && newVal != oldVal && $scope.preview_scope.label_output_copies) {
             $scope.redraw_label_table();
         }
     });
 
     $scope.$watch("preview_scope.toolbox_settings.mode.selected", function (newVal, oldVal) {
-        if (newVal && newVal != oldVal) {
+        if (newVal && newVal != oldVal && $scope.preview_scope) {
             var ts_p = $scope.preview_scope.toolbox_settings.page;
             if (ts_p.label.set.size === 1) {
                 if (newVal === "spine-pocket") {
@@ -605,7 +607,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
     });
 
     $scope.$watch("preview_scope.toolbox_settings.page.label.set.size", function (newVal, oldVal) {
-        if (newVal && newVal != oldVal) {
+        if (newVal && newVal != oldVal && oldVal) {
             var ts_p = $scope.preview_scope.toolbox_settings.page;
             if (angular.isNumber(newVal)) {
                 while (ts_p.column_class.length > ts_p.label.set.size) {
@@ -615,7 +617,24 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                     ts_p.column_class.push("spine");
                 }
             }
-            $scope.redraw_label_table();
+        }
+    });
+
+    $scope.$watch('print.cn_template_content', function (newVal, oldVal) {
+        if (newVal && newVal != oldVal) {
+            $scope.rebuild_cn_set();
+        }
+    });
+
+    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function (newVal, oldVal) {
+        if (newVal && newVal != oldVal) {
+            $scope.rebuild_cn_set();
+        }
+    });
+
+    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function (newVal, oldVal) {
+        if (newVal && newVal != oldVal) {
+            $scope.rebuild_cn_set();
         }
     });
 
@@ -916,4 +935,4 @@ function getPrintLabelOutputClass(index, settings) {
 
 function getPrintLabelStyle(index, settings) {
     return index > 0 && (index % settings.page.label.set.size === 0) ? settings.page.label.gap.size : "";
-}
+}
\ No newline at end of file

commit 1e248098368da44b2b6c9a2eef30faef67a9356f
Author: Adam Bowling <abowling at emeralddata.net>
Date:   Thu Jan 10 02:05:50 2019 -0500

    LP#1787479: Adds customization for multipage print label printing and fixes the issue
    with saving templates after the change to DB saving from local storage.
    
    Signed-off-by: Adam Bowling <abowling at emeralddata.net>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index 1e83afa994..9907985aab 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -216,6 +216,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         return !angular.isNumber(toolbox_settings.page.dimensions.rows) || !angular.isNumber(toolbox_settings.page.start_position.row) ? false : (toolbox_settings.page.start_position.row <= toolbox_settings.page.dimensions.rows);
                     }
                 };
+
                 $scope.record_details = {};
                 $scope.org_unit_settings = {};
 
@@ -324,18 +325,22 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
     $scope.fetchTemplates(true);
 
     $scope.applyTemplate = function (n) {
-        $scope.print.cn_template_content = $scope.templates[n].cn_content;
-        $scope.print.template_content = $scope.templates[n].content;
-        $scope.print.template_context = $scope.templates[n].context;
-        for (var s in $scope.templates[n].settings) {
-            $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
-        }
-        if ($scope.templates[n].toolbox_settings) {
-            $scope.preview_scope.toolbox_settings = $scope.templates[n].toolbox_settings;
-            $scope.create_print_label_table();
+        if (n) {
+            if ($scope.templates[n]) {
+                $scope.print.cn_template_content = $scope.templates[n].cn_content;
+                $scope.print.template_content = $scope.templates[n].content;
+                $scope.print.template_context = $scope.templates[n].context;
+                for (var s in $scope.templates[n].settings) {
+                    $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
+                }
+                if ($scope.templates[n].toolbox_settings) {
+                    $scope.preview_scope.toolbox_settings = $scope.templates[n].toolbox_settings;
+                    $scope.create_print_label_table();
+                }
+                egCore.hatch.setItem('cat.printlabels.default_template', n);
+                $scope.save_locally();
+            }
         }
-        egCore.hatch.setItem('cat.printlabels.default_template', n);
-        $scope.save_locally();
     }
 
     $scope.deleteTemplate = function (n) {
@@ -356,13 +361,12 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
 
     $scope.saveTemplate = function (n) {
         if (n) {
-
             $scope.templates[n] = {
                 content: $scope.print.template_content
                 , context: $scope.print.template_context
                 , cn_content: $scope.print.cn_template_content
-                , settings: $scope.preview_scope.settings
-                , toolbox_settings: $scope.preview_scope.toolbox_settings
+                , settings: JSON.parse(JSON.stringify($scope.preview_scope.settings))
+                , toolbox_settings: JSON.parse(JSON.stringify($scope.preview_scope.toolbox_settings))
             };
             $scope.template_name_list = Object.keys($scope.templates);
 
@@ -513,7 +517,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
             $scope.preview_scope.label_output_copies = labelOutputRowsFilter($scope.preview_scope.copies, $scope.preview_scope.toolbox_settings);
             var html = $scope.print.template_content;
             var d = new Date(); //Added to table ID with 'eg_plt_' to cause $complie on $scope.print.template_content to fire due to template content change.
-            var table = "<table id=\"eg_plt_" + d.getTime().toString() + "_{{$index}}\" eg-print-label-table style=\"border-collapse: collapse; border: 0 solid transparent; border-spacing: 0; margin: {{$index === 0 ?toolbox_settings.page.margins.top.size : 0}} 0 0 0;\" class=\"custom-label-table{{$index % toolbox_settings.page.dimensions.rows === 0 && $index > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n";
+            var table = "<table id=\"eg_plt_" + d.getTime().toString() + "_{{$index}}\" eg-print-label-table style=\"border-collapse: collapse; border: 0 solid transparent; border-spacing: 0; margin: {{$index === 0 ? toolbox_settings.page.margins.top.size : 0}} 0 0 0;\" class=\"custom-label-table{{$index % toolbox_settings.page.dimensions.rows === 0 && $index > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n";
             table += "<tr>\n";
             table += "<td style=\"border: 0 solid transparent; padding: {{parentIndex % toolbox_settings.page.dimensions.rows === 0 && toolbox_settings.feed_option.selected === 'sheet' && parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : 0}} 0 0 {{$index === 0 ? toolbox_settings.page.margins.left.size : col.styl ? col.styl : toolbox_settings.page.space_between_labels.horizontal.size}};\" ng-repeat=\"col in row.columns\">\n";
             table += "<pre class=\"{{col.cls}}\" style=\"border: none; margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'spine'\">\n";

commit a76e3313a76ef2c548590baba9b9def0768ca3dd
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Fri Nov 23 09:07:26 2018 -0500

    LP#1787479: Relocate t_view.tt2 file
    
    Accidentally saved tt2 changes in last commit to the wrong directory. Fixing
    it in this commit.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 059d88f7ba..14a60d9090 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -147,14 +147,45 @@
                                     </li>
                                 </ul>
                               </div>
-                              <div class="eg-print-label-section">
-                                <h4>[% l('Print Option') %]</h4>
+                             <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                <p><strong>[% l('Starting Position on Label Grid') %]</strong>
+                                <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
+                                <ul>
+                                    <li>
+                                        <label>[% l('Row') %]:</label>
+                                        <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
+                                    </li>
+                                    <li>
+                                        <label>[% l('Column') %]:</label>
+                                        <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
+                                    </li>
+                                </ul>
+                                </div>
+                              </div>
+                              <div class="eg-print-label-group">
+                                <div class="eg-print-label-section">
+                                <h4>[% l('Label Set Configuration') %]
+                                <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
+section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
                                 <ul>
                                     <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
                                         <label>{{x.label}}:</label>
                                         <input name="label-output-mode" ng-model="preview_scope.toolbox_settings.mode.selected" type="radio" value="{{x.value}}" />
                                     </li>
                                 </ul>
+                                </div>
+                                <div class="eg-print-label-section" ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                                <label>[% l('Number of Labels in Set') %]:</label>
+                                <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
+                                <ul>
+                                    <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
+                                        <label>[% l('Column') %] {{i + 1}}</label>
+                                        <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
+                                            <option value="spine">[% l('Spine Label') %]</option>
+                                            <option value="pocket">[% l('Pocket Label') %]</option>
+                                        </select>
+                                    </li>
+                                </ul>
                               </div>
                             </div>
                             <div class="eg-print-label-group">
@@ -184,51 +215,18 @@
                                     </li>
                                 </ul>
                               </div>
-                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                              <div class="eg-print-label-section">
                                 <p><label>[% l('Gap Between Rows') %]</label>
                                 <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                                 <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
                                </div>
                                <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                <p><label>[% l('Gap Between Columns') %]</label>
+                               <p><label>[% l('Gap Between') %] <span ng-if="preview_scope.toolbox_settings.page.label.set.size > 1">[% l(' Label Sets') %]</span><span ng-if="preview_scope.toolbox_settings.page.label.set.size === 1">[% l(' Columns') %]</span></label>
                                 <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
 label. Use pocket label left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                               <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
                               </div>
-                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                <p><strong>[% l('Starting Position on Label Grid') %]</strong>
-                                <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
-                                <ul>
-                                    <li>
-                                        <label>[% l('Row') %]:</label>
-                                        <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
-                                    </li>
-                                    <li>
-                                        <label>[% l('Column') %]:</label>
-                                        <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
-                                    </li>
-                                </ul>
-                                </div>
-                              </div> 
-                              <div class="eg-print-label-group" class="eg-print-label-section" ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.page.dimensions.columns > 1">
-                                <div>
-                                <h4>[% l('Label Set Configuration') %]
-                                <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
-section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
-                                <label>[% l('Number of Labels in Set') %]:</label>
-                              <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
-                                <ul>
-                                    <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
-                                        <label>[% l('Column') %] {{i + 1}}</label>
-                                        <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
-                                            <option value="spine">[% l('Spine Label') %]</option>
-                                            <option value="pocket">[% l('Pocket Label') %]</option>
-                                        </select>
-                                    </li>
-                                </ul>
-                              <br />
-                              </div>
-                            </div>                          
+                            </div> 
                         </div>                
                     </form>
                     <div class="eg-print-label-group">
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2 b/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2
deleted file mode 100644
index 14a60d9090..0000000000
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2
+++ /dev/null
@@ -1,274 +0,0 @@
-<style>
-  /* TODO: move me */
-
-  body {
-
-  }
-
-  table.page-break {
-  page-break-before: always;
-  }
-
-</style>
-
-<div class="container-fluid" style="text-align:center">
-    <div class="alert alert-info alert-less-pad strong-text-2">
-        [% l('Print Item Labels') %]
-    </div>
-</div>
-<div class="row">
-    <div class="col-md-3">
-       <div class="input-group">
-            <span class="input-group-addon">[% l('Template') %]</span>
-            <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
-       </div>
-    </div>
-    <div class="col-md-1">
-        <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
-    </div>
-    <div class="col-md-2">
-       <div class="input-group">
-            <span class="input-group-addon">[% l('Printer') %]</span>
-            <select class="form-control" ng-model="print.template_context">
-                <option value="default">[% l('Default') %]</option>
-                <option value="receipt">[% l('Receipt') %]</option>
-                <option value="label">[% l('Label') %]</option>
-                <option value="mail">[% l('Mail') %]</option>
-                <option value="offline">[% l('Offline') %]</option>
-            </select>
-        </div>
-    </div>
-    <div class="col-md-2">
-        <div class="btn-group">
-            <button class="btn btn-default" ng-click="saveTemplate(template_name)">[% l('Save') %]</button>
-            <button class="btn btn-default" ng-click="deleteTemplate(template_name)">[% l('Delete') %]</button>
-        </div>
-    </div>
-    <div class="col-md-3">
-        <div class="btn-group">
-            <span class="btn btn-default btn-file">
-                [% l('Import') %]
-                <input type="file" eg-file-reader container="imported_templates.data">
-            </span>
-            <label class="btn btn-default"
-                eg-json-exporter container="templates"
-                default-file-name="'[% l('exported_label_templates.json') %]'">
-                [% l('Export') %]
-            </label>
-            <label class="btn btn-default" ng-click="reset_to_default()">
-                [% l('Default') %]</button>
-            </div>
-    </div>
-    <div class="col-md-1 pull-right">
-        <button class="btn btn-default" ng-click="print_labels()">[% l('Print') %]</button>
-    </div>
-</div>
-
-<hr/>
-
-<div class="row">
-    <div class="col-md-5">
-        <ul class="nav nav-tabs">
-            <li ng-class="{active : current_tab == 'cn_template'}">
-                <a ng-click="set_tab('cn_template')">
-                    [% l('Call Number Template') %]
-                </a>
-            </li>
-            <li ng-class="{active : current_tab == 'call_numbers'}">
-                <a ng-click="set_tab('call_numbers')">
-                    [% l('Call Numbers') %]
-                </a>
-            </li>
-            <li ng-class="{active : current_tab == 'settings'}">
-                <a ng-click="set_tab('settings')">
-                    [% l('Settings') %]
-                </a>
-            </li>
-            <li ng-class="{active : current_tab == 'template'}">
-                <a ng-click="set_tab('template')">
-                    [% l('Label Template') %]
-                </a>
-            </li>
-        </ul>
-        <div class="tab-content">
-            <div class="tab-pane active">
-                <div ng-show="current_tab == 'cn_template'">
-                    <h4>
-                        [% l('Call Number Preview') %]
-                    </h4>
-                    <div eg-print-template-output ng-show="true"
-                        content="print.cn_template_content"
-                        context="{ copy : preview_scope.copies[0], get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
-                    <h4>
-                        [% l('Call Number Template') %]
-                    </h4>
-                    <div>
-                        <span>
-                            [% l('Changes here will wipe out manual changes in the Call Numbers tab.') %]<br/>
-                        </span>
-                    </div>
-                    <textarea ng-model="print.cn_template_content" class="print-template-text">
-                    </textarea>
-                    <div ng-repeat="copy in preview_scope.copies">
-                        <div id="cn_for_copy_{{copy.id}}" eg-print-template-output ng-show="false"
-                            content="print.cn_template_content"
-                            context="{ copy : copy, get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
-                    </div>
-                </div>
-                <div ng-show="current_tab == 'call_numbers'">
-                    <h4>
-                        [% l('Formatted Call Numbers') %]
-                    </h4>
-                    <div>
-                        <span>
-                            [% l('Manual adjustments may be made here. These do not get saved with templates.') %]<br/>
-                        </span>
-                    </div>
-                    <div ng-repeat="cn in rendered_call_number_set">
-                        <textarea ng-model="cn.value" class="cn-template-text">
-                        </textarea>
-                    </div>
-                </div>
-                <div ng-show="current_tab == 'settings'">
-                    <div>
-                        <span>
-                          <b>[% l('*All settings will be saved with templates') %]</b>
-                        </span>
-                    </div>
-                    <form id="print_label_form" name="print_label_form">
-                        <div class="print-label-toolbox">
-                          <div class="eg-print-label-group">
-                            <div class="eg-print-label-section">
-                                <h4>[% l('Print Feed Option') %]</h4>
-                                <ul>
-                                    <li ng-repeat="x in preview_scope.toolbox_settings.feed_option.options">
-                                        <label>{{x.label}}:</label>
-                                        <input name="label-feed-option" ng-model="preview_scope.toolbox_settings.feed_option.selected" ng-change="redraw_label_table()" type="radio" value="{{x.value}}" />
-                                    </li>
-                                </ul>
-                              </div>
-                             <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                <p><strong>[% l('Starting Position on Label Grid') %]</strong>
-                                <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
-                                <ul>
-                                    <li>
-                                        <label>[% l('Row') %]:</label>
-                                        <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
-                                    </li>
-                                    <li>
-                                        <label>[% l('Column') %]:</label>
-                                        <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
-                                    </li>
-                                </ul>
-                                </div>
-                              </div>
-                              <div class="eg-print-label-group">
-                                <div class="eg-print-label-section">
-                                <h4>[% l('Label Set Configuration') %]
-                                <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
-section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
-                                <ul>
-                                    <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
-                                        <label>{{x.label}}:</label>
-                                        <input name="label-output-mode" ng-model="preview_scope.toolbox_settings.mode.selected" type="radio" value="{{x.value}}" />
-                                    </li>
-                                </ul>
-                                </div>
-                                <div class="eg-print-label-section" ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.page.dimensions.columns > 1">
-                                <label>[% l('Number of Labels in Set') %]:</label>
-                                <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
-                                <ul>
-                                    <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
-                                        <label>[% l('Column') %] {{i + 1}}</label>
-                                        <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
-                                            <option value="spine">[% l('Spine Label') %]</option>
-                                            <option value="pocket">[% l('Pocket Label') %]</option>
-                                        </select>
-                                    </li>
-                                </ul>
-                              </div>
-                            </div>
-                            <div class="eg-print-label-group">
-                            <h4>[% l('Page Settings') %]</h4>
-                              <div class="eg-print-label-section">
-                                <p><strong>[% l('Page Margins') %]</strong>
-                                <eg-help-popover help-text="[% l('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
-                                <ul>
-                                    <li ng-repeat="(x, y) in preview_scope.toolbox_settings.page.margins">
-                                        <label>{{y.label}}:</label>
-                                        <input type="text" eg-print-label-valid-css ng-model="preview_scope.toolbox_settings.page.margins[x].size" required />
-                                    </li>
-                                </ul>
-                              </div>
-                              <div class="eg-print-label-section">
-                                <p><strong>[% l('Print Grid Size') %]</strong>
-                                        <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'"><eg-help-popover help-text="[% l('Please specify the number of columns and rows on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></span>
-                                         <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'continuous'"><eg-help-popover help-text="[% l('Please specify the number of columns on the print medium. The column count should include each label on your medium. For example, if your medium has a spine and pocket label, the count is two.') %]"></span></p>
-                                <ul>
-                                    <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                        <label>[% l('Rows') %]:</label>
-                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.rows" ng-min="1" required />
-                                    </li>
-                                    <li>
-                                        <label>[% l('Columns') %]:</label>
-                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.columns" ng-min="1" required />
-                                    </li>
-                                </ul>
-                              </div>
-                              <div class="eg-print-label-section">
-                                <p><label>[% l('Gap Between Rows') %]</label>
-                                <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
-                                <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
-                               </div>
-                               <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                               <p><label>[% l('Gap Between') %] <span ng-if="preview_scope.toolbox_settings.page.label.set.size > 1">[% l(' Label Sets') %]</span><span ng-if="preview_scope.toolbox_settings.page.label.set.size === 1">[% l(' Columns') %]</span></label>
-                                <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
-label. Use pocket label left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
-                              <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
-                              </div>
-                            </div> 
-                        </div>                
-                    </form>
-                    <div class="eg-print-label-group">
-                      <div>
-                       <h4>Font & Label Settings</h4>
-                        <span>
-                            [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]<br/>
-                        </span>
-                      </div>
-                      <div ng-repeat="s in org_unit_settings">
-                        <div class="row" style="margin-top: 5mm;">
-                        <div class="col-md-6"><label>{{s.label}} <eg-help-popover help-text="{{s.description}}"></label></div>
-                            <div class="col-md-6">
-                                <input type="text" ng-model="preview_scope.settings[s.name]"></input>
-                            </div>
-                         </div>
-                      </div>
-                     </div>
-                </div>
-                <div ng-show="current_tab == 'template'">
-                    <div ng-if="print.load_failed" class="alert alert-danger">
-                        [% l(
-                        "Unable to load template '[_1]'.  The web server returned an error.",
-                        '{{print.template_name}}')
-                        %]
-                    </div>
-                    <div>
-                        <textarea ng-model="print.template_content" class="print-template-text">
-                        </textarea>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-    <div class="col-md-7">
-        <h3>
-            [% l('Label Preview') %]
-        </h3>
-        <div eg-print-template-output
-          content="print.template_content"
-          context="preview_scope"></div>
-    </div>
-    <!-- col -->
-</div>
-

commit c7783ded8da83b035f2c0a007609f101f75fc600
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Sat Nov 17 13:56:12 2018 -0500

    LP#1787479: More layout changes
    
    Based on cataloger feedback, we 1) Moved the Label Set Configuration higher
    grouped with the spine and spine/pocket options because all of these options
    are related 2) Moved the starting label option higher because this is the
    one setting that will probably be adjusted on each use of the interface and
    3) Made the Gap Between Columns setting label conditional on whether the
    user us printing to a sheet with label sets or not.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2 b/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2
new file mode 100644
index 0000000000..14a60d9090
--- /dev/null
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/t_view.tt2
@@ -0,0 +1,274 @@
+<style>
+  /* TODO: move me */
+
+  body {
+
+  }
+
+  table.page-break {
+  page-break-before: always;
+  }
+
+</style>
+
+<div class="container-fluid" style="text-align:center">
+    <div class="alert alert-info alert-less-pad strong-text-2">
+        [% l('Print Item Labels') %]
+    </div>
+</div>
+<div class="row">
+    <div class="col-md-3">
+       <div class="input-group">
+            <span class="input-group-addon">[% l('Template') %]</span>
+            <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
+       </div>
+    </div>
+    <div class="col-md-1">
+        <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
+    </div>
+    <div class="col-md-2">
+       <div class="input-group">
+            <span class="input-group-addon">[% l('Printer') %]</span>
+            <select class="form-control" ng-model="print.template_context">
+                <option value="default">[% l('Default') %]</option>
+                <option value="receipt">[% l('Receipt') %]</option>
+                <option value="label">[% l('Label') %]</option>
+                <option value="mail">[% l('Mail') %]</option>
+                <option value="offline">[% l('Offline') %]</option>
+            </select>
+        </div>
+    </div>
+    <div class="col-md-2">
+        <div class="btn-group">
+            <button class="btn btn-default" ng-click="saveTemplate(template_name)">[% l('Save') %]</button>
+            <button class="btn btn-default" ng-click="deleteTemplate(template_name)">[% l('Delete') %]</button>
+        </div>
+    </div>
+    <div class="col-md-3">
+        <div class="btn-group">
+            <span class="btn btn-default btn-file">
+                [% l('Import') %]
+                <input type="file" eg-file-reader container="imported_templates.data">
+            </span>
+            <label class="btn btn-default"
+                eg-json-exporter container="templates"
+                default-file-name="'[% l('exported_label_templates.json') %]'">
+                [% l('Export') %]
+            </label>
+            <label class="btn btn-default" ng-click="reset_to_default()">
+                [% l('Default') %]</button>
+            </div>
+    </div>
+    <div class="col-md-1 pull-right">
+        <button class="btn btn-default" ng-click="print_labels()">[% l('Print') %]</button>
+    </div>
+</div>
+
+<hr/>
+
+<div class="row">
+    <div class="col-md-5">
+        <ul class="nav nav-tabs">
+            <li ng-class="{active : current_tab == 'cn_template'}">
+                <a ng-click="set_tab('cn_template')">
+                    [% l('Call Number Template') %]
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'call_numbers'}">
+                <a ng-click="set_tab('call_numbers')">
+                    [% l('Call Numbers') %]
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'settings'}">
+                <a ng-click="set_tab('settings')">
+                    [% l('Settings') %]
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'template'}">
+                <a ng-click="set_tab('template')">
+                    [% l('Label Template') %]
+                </a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <div ng-show="current_tab == 'cn_template'">
+                    <h4>
+                        [% l('Call Number Preview') %]
+                    </h4>
+                    <div eg-print-template-output ng-show="true"
+                        content="print.cn_template_content"
+                        context="{ copy : preview_scope.copies[0], get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
+                    <h4>
+                        [% l('Call Number Template') %]
+                    </h4>
+                    <div>
+                        <span>
+                            [% l('Changes here will wipe out manual changes in the Call Numbers tab.') %]<br/>
+                        </span>
+                    </div>
+                    <textarea ng-model="print.cn_template_content" class="print-template-text">
+                    </textarea>
+                    <div ng-repeat="copy in preview_scope.copies">
+                        <div id="cn_for_copy_{{copy.id}}" eg-print-template-output ng-show="false"
+                            content="print.cn_template_content"
+                            context="{ copy : copy, get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
+                    </div>
+                </div>
+                <div ng-show="current_tab == 'call_numbers'">
+                    <h4>
+                        [% l('Formatted Call Numbers') %]
+                    </h4>
+                    <div>
+                        <span>
+                            [% l('Manual adjustments may be made here. These do not get saved with templates.') %]<br/>
+                        </span>
+                    </div>
+                    <div ng-repeat="cn in rendered_call_number_set">
+                        <textarea ng-model="cn.value" class="cn-template-text">
+                        </textarea>
+                    </div>
+                </div>
+                <div ng-show="current_tab == 'settings'">
+                    <div>
+                        <span>
+                          <b>[% l('*All settings will be saved with templates') %]</b>
+                        </span>
+                    </div>
+                    <form id="print_label_form" name="print_label_form">
+                        <div class="print-label-toolbox">
+                          <div class="eg-print-label-group">
+                            <div class="eg-print-label-section">
+                                <h4>[% l('Print Feed Option') %]</h4>
+                                <ul>
+                                    <li ng-repeat="x in preview_scope.toolbox_settings.feed_option.options">
+                                        <label>{{x.label}}:</label>
+                                        <input name="label-feed-option" ng-model="preview_scope.toolbox_settings.feed_option.selected" ng-change="redraw_label_table()" type="radio" value="{{x.value}}" />
+                                    </li>
+                                </ul>
+                              </div>
+                             <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                <p><strong>[% l('Starting Position on Label Grid') %]</strong>
+                                <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
+                                <ul>
+                                    <li>
+                                        <label>[% l('Row') %]:</label>
+                                        <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
+                                    </li>
+                                    <li>
+                                        <label>[% l('Column') %]:</label>
+                                        <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
+                                    </li>
+                                </ul>
+                                </div>
+                              </div>
+                              <div class="eg-print-label-group">
+                                <div class="eg-print-label-section">
+                                <h4>[% l('Label Set Configuration') %]
+                                <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
+section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
+                                <ul>
+                                    <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
+                                        <label>{{x.label}}:</label>
+                                        <input name="label-output-mode" ng-model="preview_scope.toolbox_settings.mode.selected" type="radio" value="{{x.value}}" />
+                                    </li>
+                                </ul>
+                                </div>
+                                <div class="eg-print-label-section" ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                                <label>[% l('Number of Labels in Set') %]:</label>
+                                <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
+                                <ul>
+                                    <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
+                                        <label>[% l('Column') %] {{i + 1}}</label>
+                                        <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
+                                            <option value="spine">[% l('Spine Label') %]</option>
+                                            <option value="pocket">[% l('Pocket Label') %]</option>
+                                        </select>
+                                    </li>
+                                </ul>
+                              </div>
+                            </div>
+                            <div class="eg-print-label-group">
+                            <h4>[% l('Page Settings') %]</h4>
+                              <div class="eg-print-label-section">
+                                <p><strong>[% l('Page Margins') %]</strong>
+                                <eg-help-popover help-text="[% l('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                                <ul>
+                                    <li ng-repeat="(x, y) in preview_scope.toolbox_settings.page.margins">
+                                        <label>{{y.label}}:</label>
+                                        <input type="text" eg-print-label-valid-css ng-model="preview_scope.toolbox_settings.page.margins[x].size" required />
+                                    </li>
+                                </ul>
+                              </div>
+                              <div class="eg-print-label-section">
+                                <p><strong>[% l('Print Grid Size') %]</strong>
+                                        <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'"><eg-help-popover help-text="[% l('Please specify the number of columns and rows on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></span>
+                                         <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'continuous'"><eg-help-popover help-text="[% l('Please specify the number of columns on the print medium. The column count should include each label on your medium. For example, if your medium has a spine and pocket label, the count is two.') %]"></span></p>
+                                <ul>
+                                    <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                        <label>[% l('Rows') %]:</label>
+                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.rows" ng-min="1" required />
+                                    </li>
+                                    <li>
+                                        <label>[% l('Columns') %]:</label>
+                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.columns" ng-min="1" required />
+                                    </li>
+                                </ul>
+                              </div>
+                              <div class="eg-print-label-section">
+                                <p><label>[% l('Gap Between Rows') %]</label>
+                                <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                                <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
+                               </div>
+                               <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                               <p><label>[% l('Gap Between') %] <span ng-if="preview_scope.toolbox_settings.page.label.set.size > 1">[% l(' Label Sets') %]</span><span ng-if="preview_scope.toolbox_settings.page.label.set.size === 1">[% l(' Columns') %]</span></label>
+                                <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
+label. Use pocket label left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                              <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
+                              </div>
+                            </div> 
+                        </div>                
+                    </form>
+                    <div class="eg-print-label-group">
+                      <div>
+                       <h4>Font & Label Settings</h4>
+                        <span>
+                            [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]<br/>
+                        </span>
+                      </div>
+                      <div ng-repeat="s in org_unit_settings">
+                        <div class="row" style="margin-top: 5mm;">
+                        <div class="col-md-6"><label>{{s.label}} <eg-help-popover help-text="{{s.description}}"></label></div>
+                            <div class="col-md-6">
+                                <input type="text" ng-model="preview_scope.settings[s.name]"></input>
+                            </div>
+                         </div>
+                      </div>
+                     </div>
+                </div>
+                <div ng-show="current_tab == 'template'">
+                    <div ng-if="print.load_failed" class="alert alert-danger">
+                        [% l(
+                        "Unable to load template '[_1]'.  The web server returned an error.",
+                        '{{print.template_name}}')
+                        %]
+                    </div>
+                    <div>
+                        <textarea ng-model="print.template_content" class="print-template-text">
+                        </textarea>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="col-md-7">
+        <h3>
+            [% l('Label Preview') %]
+        </h3>
+        <div eg-print-template-output
+          content="print.template_content"
+          context="preview_scope"></div>
+    </div>
+    <!-- col -->
+</div>
+

commit a80f25685d9f7617434c800adb62c20031ed8508
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Wed Nov 14 12:49:16 2018 -0500

    LP#1787479: Make the display of some fields conditional on the Print Feed Option
    
    Ideally, we would remove the entire Page Settings section for those who are
    printing to a continuous feed printer, but it carries the risk of users
    not being able to see a field with a value that is affecting the layout. However, there are several fields within that section that don't affect the
    continuous feed layout and can be hidden in continuous feed mode. I also
    adjusted one help tip to display different information for continuous and
    sheet feed printing.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index c67bf86b32..059d88f7ba 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -171,7 +171,8 @@
                               </div>
                               <div class="eg-print-label-section">
                                 <p><strong>[% l('Print Grid Size') %]</strong>
-                                <eg-help-popover help-text="[% l('Please specify the number of columns and rows (if applicable) on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></p>
+                                        <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'"><eg-help-popover help-text="[% l('Please specify the number of columns and rows on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></span>
+                                         <span ng-if="preview_scope.toolbox_settings.feed_option.selected === 'continuous'"><eg-help-popover help-text="[% l('Please specify the number of columns on the print medium. The column count should include each label on your medium. For example, if your medium has a spine and pocket label, the count is two.') %]"></span></p>
                                 <ul>
                                     <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
                                         <label>[% l('Rows') %]:</label>
@@ -183,22 +184,22 @@
                                     </li>
                                 </ul>
                               </div>
-                              <div class="eg-print-label-section">
+                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
                                 <p><label>[% l('Gap Between Rows') %]</label>
                                 <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                                 <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
                                </div>
-                               <div class="eg-print-label-section">
+                               <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
                                 <p><label>[% l('Gap Between Columns') %]</label>
                                 <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
 label. Use pocket label left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                               <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
                               </div>
-                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
                                 <p><strong>[% l('Starting Position on Label Grid') %]</strong>
                                 <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
                                 <ul>
-                                    <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                    <li>
                                         <label>[% l('Row') %]:</label>
                                         <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
                                     </li>
@@ -209,14 +210,14 @@ label. Use pocket label left margin to identify how much space to provide betwee
                                 </ul>
                                 </div>
                               </div> 
-                              <div class="eg-print-label-group">
-                                <div class="eg-print-label-section">
+                              <div class="eg-print-label-group" class="eg-print-label-section" ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                                <div>
                                 <h4>[% l('Label Set Configuration') %]
                                 <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
 section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
                                 <label>[% l('Number of Labels in Set') %]:</label>
                               <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
-                                <ul ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.mode.selected === 'spine-pocket'">
+                                <ul>
                                     <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
                                         <label>[% l('Column') %] {{i + 1}}</label>
                                         <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index f798f4c4a4..1e83afa994 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -117,8 +117,8 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
         },
         mode: {
             options: [
-                { label: "Label 1 Only", value: "spine-only" },
-                { label: "Labels 1 & 2", value: "spine-pocket" }
+                { label: "Spine Label", value: "spine-only" },
+                { label: "Pocket Label", value: "spine-pocket" }
             ],
             selected: "spine-pocket"
         },

commit 2da42a5b540c67e3421f06629e34a71c1d958072
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Tue Nov 13 21:07:19 2018 -0500

    LP#1787479: Make label names more descriptive
    
    As previously discussed on the cataloging list and the LP bug, the previous
    label names of 'left' and 'right' no longer make sense since staff can
    control the placement of these labels. However, label 1 and label 2 are too
    generic. We therefore are going back to spine and pocket labels.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index 5047e77f5e..80db67122d 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -17064,13 +17064,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.left_margin'
-        ,'Item Print Label - Left Margin for Left Label'
+        ,'Item Print Label - Left Margin for Spine Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.left_margin'
-        ,'Set the default left margin for the leftmost item print Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default left margin for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
@@ -17080,13 +17080,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.left_margin'
-        ,'Item Print Label - Left Margin for Right Label'
+        ,'Item Print Label - Left Margin for Pocket Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.left_margin'
-        ,'Set the default left margin for the rightmost item print label (or in other words, the desired space between the two labels). Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default left margin for the item print Pocket Label (or in other words, the desired space between the two labels). Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
@@ -17096,13 +17096,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.height'
-        ,'Item Print Label - Height for Left Label'
+        ,'Item Print Label - Height for Spine Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.height'
-        ,'Set the default height for the leftmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default height for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
@@ -17112,13 +17112,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.width'
-        ,'Item Print Label - Width for Left Label'
+        ,'Item Print Label - Width for Spine Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.left_label.width'
-        ,'Set the default width for the leftmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default width for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
@@ -17128,13 +17128,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.height'
-        ,'Item Print Label - Height for Right Label'
+        ,'Item Print Label - Height for Pocket Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.height'
-        ,'Set the default height for the rightmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default height for the item print Pocket Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
@@ -17144,13 +17144,13 @@ INSERT into config.org_unit_setting_type (
     ,'cat'
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.width'
-        ,'Item Print Label - Width for Right Label'
+        ,'Item Print Label - Width for Pocket Label'
         ,'coust'
         ,'label'
     )
     ,oils_i18n_gettext(
          'webstaff.cat.label.right_label.width'
-        ,'Set the default width for the rightmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'Set the default width for the item print Pocket Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
         ,'coust'
         ,'description'
     )
diff --git a/Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels b/Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels
new file mode 100644
index 0000000000..dbb59f74b5
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/xxxx.data.update_item_label_org_unit_labels
@@ -0,0 +1,96 @@
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.left_margin'
+        ,'Item Print Label - Left Margin for Spine Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.left_margin'
+        ,'Set the default left margin for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.left_label.left_margin';
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.left_margin'
+        ,'Item Print Label - Left Margin for Pocket Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.left_margin'
+        ,'Set the default left margin for the item print Pocket Label (or in other words, the desired space between the two labels). Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.right_label.left_margin';
+
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.height'
+        ,'Item Print Label - Height for Spine Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.height'
+        ,'Set the default height for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.left_label.height';
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.width'
+        ,'Item Print Label - Width for Spine Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.left_label.width'
+        ,'Set the default width for the item print Spine Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.left_label.width';
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.height'
+        ,'Item Print Label - Height for Pocket Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.height'
+        ,'Set the default height for the item print Pocket Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.right_label.height';
+
+UPDATE config.org_unit_setting_type
+SET label = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.width'
+        ,'Item Print Label - Width for Pocket Label'
+        ,'coust'
+        ,'label'
+    ),
+     description = oils_i18n_gettext(
+         'webstaff.cat.label.right_label.width'
+        ,'Set the default width for the item print Pocket Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
+        ,'coust'
+        ,'description'
+    )
+WHERE NAME = 'webstaff.cat.label.right_label.width';
+
+COMMIT;
diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 6d2e5ea30c..c67bf86b32 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -191,7 +191,7 @@
                                <div class="eg-print-label-section">
                                 <p><label>[% l('Gap Between Columns') %]</label>
                                 <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
-label. Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+label. Use pocket label left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                               <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
                               </div>
                               <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
@@ -220,8 +220,8 @@ section, identify the number of labels contained in each set, which columns shou
                                     <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
                                         <label>[% l('Column') %] {{i + 1}}</label>
                                         <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
-                                            <option value="spine">[% l('Label 1') %]</option>
-                                            <option value="pocket">[% l('Label 2') %]</option>
+                                            <option value="spine">[% l('Spine Label') %]</option>
+                                            <option value="pocket">[% l('Pocket Label') %]</option>
                                         </select>
                                     </li>
                                 </ul>
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
index 3fdd2fc8df..cf14d6465d 100644
--- a/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
+++ b/Open-ILS/src/templates/staff/share/print_templates/t_item_label.tt2
@@ -20,7 +20,7 @@ HTML itself.
 -->
 <style>
     /* The .spine here defines a CSS "class", which in this case, is used by our
-       left label */
+       spine label */
     .spine {
 
         /* this pulls from the "Item Print Label Font Family" setting, but will
@@ -35,24 +35,24 @@ HTML itself.
            default to 'normal' */
         font-weight: {{settings['webstaff.cat.label.font.weight'] || 'normal'}};
 
-        /* this pulls from the "Item Print Label - Height for Left Label"
+        /* this pulls from the "Item Print Label - Height for Spine Label"
            setting, but will default to '1.25in' */
         height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
         min-height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
         max-height: {{settings['webstaff.cat.label.left_label.height'] || '1.25in'}};
 
-        /* this pulls from the "Item Print Label - Width for Left Label"
+        /* this pulls from the "Item Print Label - Width for Spine Label"
            setting, but will default to '1in' */
         width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
         min-width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
         max-width: {{settings['webstaff.cat.label.left_label.width'] || '1in'}};
 
-        /* this pulls from the "Item Print Label - Left Margin for Left Label"
+        /* this pulls from the "Item Print Label - Left Margin for Spine Label"
            setting, but will default to '0in' */
         margin-left: {{settings['webstaff.cat.label.left_label.left_margin'] || '0in'}};
     }
 
-    /* This CSS class is used by our right label */
+    /* This CSS class is used by our pocket label */
     .pocket {
 
         /* this pulls from the "Item Print Label Font Family" setting, but will
@@ -67,19 +67,19 @@ HTML itself.
            default to 'normal' */
         font-weight: {{settings['webstaff.cat.label.font.weight'] || 'normal'}};
 
-        /* this pulls from the "Item Print Label - Height for Left Label"
+        /* this pulls from the "Item Print Label - Height for Spine Label"
            setting, but will default to '1.25in' */
         height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
         min-height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
         max-height: {{settings['webstaff.cat.label.right_label.height'] || '1.25in'}};
 
-        /* this pulls from the "Item Print Label - Width for Left Label"
+        /* this pulls from the "Item Print Label - Width for Spine Label"
            setting, but will default to '2.625in' */
         width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
         min-width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
         max-width: {{settings['webstaff.cat.label.right_label.width'] || '2.625in'}};
 
-        /* this pulls from the "Item Print Label - Left Margin for Left Label"
+        /* this pulls from the "Item Print Label - Left Margin for Pocket Label"
            setting, but will default to '0in' */
         margin-left: {{settings['webstaff.cat.label.right_label.left_margin'] || '0in'}};
 

commit 920b10a2834ee41b2d2a61eba4eee8f6a3bbcba4
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Tue Nov 13 12:27:02 2018 -0500

    LP#1787479: Some CSS and other formatting changes
    
    Moved most of the CSS to cat.css.tt2; removed most borders since they aren't
    needed as much without the full settings descriptions on the page; added some
    more help text and removed Windows carriage returns.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index b059f8e9ac..6d2e5ea30c 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -9,59 +9,6 @@
   page-break-before: always;
   }
 
-  div.print-label-toolbox input[type=number], div.print-label-toolbox input[type=text] {
-  border: 1px solid #999;
-  border-radius: 3px;
-  margin-right: 12px;
-  width: 56px;
-  }
-
-  div.print-label-toolbox div.eg-print-label-section {
-  border-bottom: 1px solid #DED;
-  display: block;
-  margin: 0 0 10px 0;
-  padding: 0 0 10px 0;
-  }
-
-  div.print-label-toolbox input.ng-invalid {
-  background-color: #FFFF00;
-  color: #FF0000;
-  }
-
-  div.print-label-toolbox label {
-  padding-right: 4px;
-  }
-
-  div.print-label-toolbox ul {
-  display: block;
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-  }
-
-  div.print-label-toolbox ul li {
-  display: inline-block;
-  padding: 0 0 0 6px;
-  }
-
-  div.print-label-toolbox ul li:first-child {
-  display: inline-block;
-  padding: 0 0 0 0;
-  }
-
-  table.custom-label-table td {
-  vertical-align: top;
-  }
-
-  .print-template-text {
-  height: 36em;
-  width: 100%;
-  }
-
-  .cn-template-text {
-  height: 12em;
-  width: 100%;
-  }
 </style>
 
 <div class="container-fluid" style="text-align:center">
@@ -190,6 +137,7 @@
                     </div>
                     <form id="print_label_form" name="print_label_form">
                         <div class="print-label-toolbox">
+                          <div class="eg-print-label-group">
                             <div class="eg-print-label-section">
                                 <h4>[% l('Print Feed Option') %]</h4>
                                 <ul>
@@ -198,8 +146,8 @@
                                         <input name="label-feed-option" ng-model="preview_scope.toolbox_settings.feed_option.selected" ng-change="redraw_label_table()" type="radio" value="{{x.value}}" />
                                     </li>
                                 </ul>
-                            </div>
-                            <div class="eg-print-label-section">
+                              </div>
+                              <div class="eg-print-label-section">
                                 <h4>[% l('Print Option') %]</h4>
                                 <ul>
                                     <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
@@ -207,9 +155,11 @@
                                         <input name="label-output-mode" ng-model="preview_scope.toolbox_settings.mode.selected" type="radio" value="{{x.value}}" />
                                     </li>
                                 </ul>
+                              </div>
                             </div>
+                            <div class="eg-print-label-group">
                             <h4>[% l('Page Settings') %]</h4>
-                            <div class="eg-print-label-section">
+                              <div class="eg-print-label-section">
                                 <p><strong>[% l('Page Margins') %]</strong>
                                 <eg-help-popover help-text="[% l('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                                 <ul>
@@ -218,8 +168,8 @@
                                         <input type="text" eg-print-label-valid-css ng-model="preview_scope.toolbox_settings.page.margins[x].size" required />
                                     </li>
                                 </ul>
-                            </div>
-                            <div class="eg-print-label-section">
+                              </div>
+                              <div class="eg-print-label-section">
                                 <p><strong>[% l('Print Grid Size') %]</strong>
                                 <eg-help-popover help-text="[% l('Please specify the number of columns and rows (if applicable) on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></p>
                                 <ul>
@@ -232,16 +182,19 @@
                                         <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.columns" ng-min="1" required />
                                     </li>
                                 </ul>
-                            </div>
-                            <div class="eg-print-label-section">
+                              </div>
+                              <div class="eg-print-label-section">
                                 <p><label>[% l('Gap Between Rows') %]</label>
                                 <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                                 <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
-                                <p><label>[% l('Gap Between Columns of a Label Set') %]</label>
-                                <eg-help-popover help-text="[% l('Gap between a label set (i.e. gap between each spine/pocket combo). Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                               </div>
+                               <div class="eg-print-label-section">
+                                <p><label>[% l('Gap Between Columns') %]</label>
+                                <eg-help-popover help-text="[% l('Gap between each label set (spine/pocket combo) or, when using only one type of label, between each
+label. Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                               <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
-                            </div>
-                            <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                              </div>
+                              <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
                                 <p><strong>[% l('Starting Position on Label Grid') %]</strong>
                                 <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
                                 <ul>
@@ -254,13 +207,15 @@
                                         <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
                                     </li>
                                 </ul>
-                            </div>
-                            <div class="eg-print-label-section">
-                                <h4>[% l('Label Set Configuration') %]</h4>
+                                </div>
+                              </div> 
+                              <div class="eg-print-label-group">
+                                <div class="eg-print-label-section">
+                                <h4>[% l('Label Set Configuration') %]
+                                <eg-help-popover help-text="[% l('A label set is a group of labels that should print data from the same bibliographic record. In this
+section, identify the number of labels contained in each set, which columns should print spine label data, and which columns should print pocket label data.') %]"></h4>
                                 <label>[% l('Number of Labels in Set') %]:</label>
                               <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
-                              <br />
-                              <br />
                                 <ul ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.mode.selected === 'spine-pocket'">
                                     <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
                                         <label>[% l('Column') %] {{i + 1}}</label>
@@ -271,22 +226,26 @@
                                     </li>
                                 </ul>
                               <br />
+                              </div>
                             </div>                          
                         </div>                
                     </form>
-                    <div>
+                    <div class="eg-print-label-group">
+                      <div>
+                       <h4>Font & Label Settings</h4>
                         <span>
                             [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]<br/>
                         </span>
-                    </div>
-                    <div ng-repeat="s in org_unit_settings">
+                      </div>
+                      <div ng-repeat="s in org_unit_settings">
                         <div class="row" style="margin-top: 5mm;">
                         <div class="col-md-6"><label>{{s.label}} <eg-help-popover help-text="{{s.description}}"></label></div>
                             <div class="col-md-6">
                                 <input type="text" ng-model="preview_scope.settings[s.name]"></input>
                             </div>
-                        </div>
-                    </div>
+                         </div>
+                      </div>
+                     </div>
                 </div>
                 <div ng-show="current_tab == 'template'">
                     <div ng-if="print.load_failed" class="alert alert-danger">
diff --git a/Open-ILS/src/templates/staff/css/cat.css.tt2 b/Open-ILS/src/templates/staff/css/cat.css.tt2
index 9af6bd73b8..ccb2847d78 100644
--- a/Open-ILS/src/templates/staff/css/cat.css.tt2
+++ b/Open-ILS/src/templates/staff/css/cat.css.tt2
@@ -198,3 +198,67 @@ grid[name="-none-"] * label { color: black; }
     white-space: nowrap;
 }
 
+/* Print Item Templates Styles */
+
+div.print-label-toolbox input[type=number], div.print-label-toolbox input[type=text] {
+  border: 1px solid #999;
+  border-radius: 3px;
+  margin-right: 12px;
+  width: 56px;
+  }
+
+div.print-label-toolbox div.eg-print-label-section {
+  display: block;
+  margin: 0 0 10px 0;
+  }
+
+div.eg-print-label-group {
+  border-bottom: 1px solid #DED;
+  margin: 0 0 10px 0;
+  padding: 0 0 10px 0;
+  }
+
+.eg-print-label-section p {
+  margin: 0 0 5px 0;
+  }
+
+div.print-label-toolbox input.ng-invalid {
+  background-color: #FFFF00;
+  color: #FF0000;
+  }
+
+div.print-label-toolbox label {
+  padding-right: 4px;
+  }
+
+div.print-label-toolbox ul {
+  display: block;
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+  }
+
+div.print-label-toolbox ul li {
+  display: inline-block;
+  padding: 0 0 0 6px;
+  }
+
+div.print-label-toolbox ul li:first-child {
+  display: inline-block;
+  padding: 0 0 0 0;
+  }
+
+table.custom-label-table td {
+  vertical-align: top;
+  }
+
+.print-template-text {
+   height: 36em;
+   width: 100%;
+   }
+
+.cn-template-text {
+  height: 12em;
+  width: 100%;
+  }
+
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index cf8e15919c..f798f4c4a4 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -290,9 +290,9 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         $scope.rebuild_cn_set();
                         if ($scope.preview_scope.toolbox_settings && $scope.template_name && $scope.print.template_content) {
                             var re = /eg\_plt/i;
-                            if (re.test($scope.print.template_content)) {
-                                $scope.applyTemplate($scope.template_name);
-                                $scope.redraw_label_table();
+                            if (re.test($scope.print.template_content)) {
+                                $scope.applyTemplate($scope.template_name);
+                                $scope.redraw_label_table();
                             }
                         }
                     });

commit 06a7f93c29b90f3e3a63c30cce8156f413a23124
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Mon Nov 12 13:04:02 2018 -0500

    LP#1787479: Use help tips for settings descriptions
    
    We are now using popup help tips for descriptions on this page. Adding these
    help tips for the new settings added through this branch.
    
    Also changes strings in this interface to translatable text.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 56871ae04f..b059f8e9ac 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -185,13 +185,13 @@
                 <div ng-show="current_tab == 'settings'">
                     <div>
                         <span>
-                          <b>*All settings will be saved with templates</b>
+                          <b>[% l('*All settings will be saved with templates') %]</b>
                         </span>
                     </div>
                     <form id="print_label_form" name="print_label_form">
                         <div class="print-label-toolbox">
                             <div class="eg-print-label-section">
-                                <h4>Print Feed Option</h4>
+                                <h4>[% l('Print Feed Option') %]</h4>
                                 <ul>
                                     <li ng-repeat="x in preview_scope.toolbox_settings.feed_option.options">
                                         <label>{{x.label}}:</label>
@@ -200,7 +200,7 @@
                                 </ul>
                             </div>
                             <div class="eg-print-label-section">
-                                <h4>Print Option</h4>
+                                <h4>[% l('Print Option') %]</h4>
                                 <ul>
                                     <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
                                         <label>{{x.label}}:</label>
@@ -208,10 +208,10 @@
                                     </li>
                                 </ul>
                             </div>
-                            <h3>Page Settings</h3>
+                            <h4>[% l('Page Settings') %]</h4>
                             <div class="eg-print-label-section">
-                                <h4>Page Margins</h4>
-                                <p>Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p>
+                                <p><strong>[% l('Page Margins') %]</strong>
+                                <eg-help-popover help-text="[% l('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
                                 <ul>
                                     <li ng-repeat="(x, y) in preview_scope.toolbox_settings.page.margins">
                                         <label>{{y.label}}:</label>
@@ -220,52 +220,53 @@
                                 </ul>
                             </div>
                             <div class="eg-print-label-section">
-                                <h4>Print Grid Size</h4>
-                                <p>Please specify the number of columns and rows (if applicable) on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.</p>
+                                <p><strong>[% l('Print Grid Size') %]</strong>
+                                <eg-help-popover help-text="[% l('Please specify the number of columns and rows (if applicable) on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.') %]"></p>
                                 <ul>
                                     <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                        <label>Rows:</label>
+                                        <label>[% l('Rows') %]:</label>
                                         <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.rows" ng-min="1" required />
                                     </li>
                                     <li>
-                                        <label>Columns:</label>
+                                        <label>[% l('Columns') %]:</label>
                                         <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.columns" ng-min="1" required />
                                     </li>
                                 </ul>
                             </div>
                             <div class="eg-print-label-section">
-                                <label>Gap Between Rows</label> <input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required />
-                                <p>Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p><br />
-                                <label>Gap Between Columns of a Label Set</label>
-                              <input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required />
-                                <p>Gap between a label “set” (i.e. gap between each spine/pocket combo). Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p>
+                                <p><label>[% l('Gap Between Rows') %]</label>
+                                <eg-help-popover help-text="[%l ('Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                                <p><input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required /></p>
+                                <p><label>[% l('Gap Between Columns of a Label Set') %]</label>
+                                <eg-help-popover help-text="[% l('Gap between a label set (i.e. gap between each spine/pocket combo). Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)') %]"></p>
+                              <p><input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required /></p>
                             </div>
                             <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
-                                <h4>Starting Position on Label Grid</h4>
-                                <p>Specify the starting <span ng-if="preview_scope.toolbox_settings.feed_option.selected == 'sheet'">row and </span>column where labels should begin printing. Values must not exceed the respective <span ng-if="preview_scope.toolbox_settings.feed_option.selected == 'sheet'">row and </span>column size specified in "Print Grid Size"</p>
+                                <p><strong>[% l('Starting Position on Label Grid') %]</strong>
+                                <eg-help-popover help-text="[% l('Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"') %]"></p>
                                 <ul>
                                     <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
-                                        <label>Row:</label>
+                                        <label>[% l('Row') %]:</label>
                                         <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
                                     </li>
                                     <li>
-                                        <label>Column:</label>
+                                        <label>[% l('Column') %]:</label>
                                         <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
                                     </li>
                                 </ul>
                             </div>
                             <div class="eg-print-label-section">
-                                <h4>Label Set Configuration</h4>
-                                <label>Number of Labels in Set:</label>
+                                <h4>[% l('Label Set Configuration') %]</h4>
+                                <label>[% l('Number of Labels in Set') %]:</label>
                               <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
                               <br />
                               <br />
                                 <ul ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.mode.selected === 'spine-pocket'">
                                     <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
-                                        <label>Col. {{i + 1}}</label>
+                                        <label>[% l('Column') %] {{i + 1}}</label>
                                         <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
-                                            <option value="spine">Label 1</option>
-                                            <option value="pocket">Label 2</option>
+                                            <option value="spine">[% l('Label 1') %]</option>
+                                            <option value="pocket">[% l('Label 2') %]</option>
                                         </select>
                                     </li>
                                 </ul>

commit f1c754c49211f82ca82773e0d578b31768c37f40
Author: Kathy Lussier <klussier at masslnc.org>
Date:   Mon Nov 12 09:42:50 2018 -0500

    LP#1787479: Restore spacing fixes
    
    Restore spacing fixes introduced by Garry Collum that were overwritten with
    the original branch.
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index ba1590f0bc..56871ae04f 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -64,32 +64,31 @@
   }
 </style>
 
-<h2>[% l('Print Item Labels') %]</h2>
-
-<div class="row bg-info">
-    <div class="col-md-6">
-        <div class="row">
-            <div class="col-md-1">
-                <span class="h4">[% l('Template') %]</span>
-            </div>
-            <div class="col-md-5">
-                <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
-            </div>
-            <div class="col-md-1">
-                <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
-            </div>
-            <div class="col-md-1">
-                <span class="h4">[% l('Printer') %]</span>
-            </div>
-            <div class="col-md-4">
-                <select class="form-control" ng-model="print.template_context">
-                    <option value="default">[% l('Default') %]</option>
-                    <option value="receipt">[% l('Receipt') %]</option>
-                    <option value="label">[% l('Label') %]</option>
-                    <option value="mail">[% l('Mail') %]</option>
-                    <option value="offline">[% l('Offline') %]</option>
-                </select>
-            </div>
+<div class="container-fluid" style="text-align:center">
+    <div class="alert alert-info alert-less-pad strong-text-2">
+        [% l('Print Item Labels') %]
+    </div>
+</div>
+<div class="row">
+    <div class="col-md-3">
+       <div class="input-group">
+            <span class="input-group-addon">[% l('Template') %]</span>
+            <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
+       </div>
+    </div>
+    <div class="col-md-1">
+        <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
+    </div>
+    <div class="col-md-2">
+       <div class="input-group">
+            <span class="input-group-addon">[% l('Printer') %]</span>
+            <select class="form-control" ng-model="print.template_context">
+                <option value="default">[% l('Default') %]</option>
+                <option value="receipt">[% l('Receipt') %]</option>
+                <option value="label">[% l('Label') %]</option>
+                <option value="mail">[% l('Mail') %]</option>
+                <option value="offline">[% l('Offline') %]</option>
+            </select>
         </div>
     </div>
     <div class="col-md-2">

commit c33bc5438d3383666aaddb43b190c5da5ea8b2b4
Author: Adam Bowling <abowling at emeralddata.net>
Date:   Fri Nov 9 00:35:25 2018 -0500

    Modified Evergreen custom print label toolbox with save modifications.
    
    Signed-off-by: Adam Bowling <abowling at emeralddata.net>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index 880eaed230..ba1590f0bc 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -243,7 +243,7 @@
                             </div>
                             <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
                                 <h4>Starting Position on Label Grid</h4>
-                                <p>Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"</p>
+                                <p>Specify the starting <span ng-if="preview_scope.toolbox_settings.feed_option.selected == 'sheet'">row and </span>column where labels should begin printing. Values must not exceed the respective <span ng-if="preview_scope.toolbox_settings.feed_option.selected == 'sheet'">row and </span>column size specified in "Print Grid Size"</p>
                                 <ul>
                                     <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
                                         <label>Row:</label>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index 9d006b8e3e..cf8e15919c 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -288,6 +288,13 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
                         egCore.print.fleshPrintScope($scope.preview_scope);
                         $scope.template_changed(); // load the default
                         $scope.rebuild_cn_set();
+                        if ($scope.preview_scope.toolbox_settings && $scope.template_name && $scope.print.template_content) {
+                            var re = /eg\_plt/i;
+                            if (re.test($scope.print.template_content)) {
+                                $scope.applyTemplate($scope.template_name);
+                                $scope.redraw_label_table();
+                            }
+                        }
                     });
 
                 });
@@ -527,6 +534,7 @@ function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet,
             var output = (style ? style.join("\n") : "") + (comments ? comments.join("\n") : "") + table;
             output = output.replace(/\n+/, "\n");
             $scope.print.template_content = output;
+            $scope.save_locally();
         }
     }
 

commit a4411d6aedfc2134b85af40f9580375a06ffc1b6
Author: Adam Bowling <abowling at emeralddata.net>
Date:   Wed Oct 17 16:15:44 2018 -0400

    LP#1787479: Custom label printing toolbox
    
    Signed-off-by: Adam Bowling <abowling at emeralddata.net>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>
    
    Conflicts:
            Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2

diff --git a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
index fc5dfed3fd..880eaed230 100644
--- a/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
+++ b/Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2
@@ -1,51 +1,103 @@
 <style>
   /* TODO: move me */
+
+  body {
+
+  }
+
+  table.page-break {
+  page-break-before: always;
+  }
+
+  div.print-label-toolbox input[type=number], div.print-label-toolbox input[type=text] {
+  border: 1px solid #999;
+  border-radius: 3px;
+  margin-right: 12px;
+  width: 56px;
+  }
+
+  div.print-label-toolbox div.eg-print-label-section {
+  border-bottom: 1px solid #DED;
+  display: block;
+  margin: 0 0 10px 0;
+  padding: 0 0 10px 0;
+  }
+
+  div.print-label-toolbox input.ng-invalid {
+  background-color: #FFFF00;
+  color: #FF0000;
+  }
+
+  div.print-label-toolbox label {
+  padding-right: 4px;
+  }
+
+  div.print-label-toolbox ul {
+  display: block;
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+  }
+
+  div.print-label-toolbox ul li {
+  display: inline-block;
+  padding: 0 0 0 6px;
+  }
+
+  div.print-label-toolbox ul li:first-child {
+  display: inline-block;
+  padding: 0 0 0 0;
+  }
+
+  table.custom-label-table td {
+  vertical-align: top;
+  }
+
   .print-template-text {
-    height: 36em;
-    width: 100%;
+  height: 36em;
+  width: 100%;
   }
+
   .cn-template-text {
-    height: 12em;
-    width: 100%;
+  height: 12em;
+  width: 100%;
   }
 </style>
 
-<div class="container-fluid" style="text-align:center">
-    <div class="alert alert-info alert-less-pad strong-text-2">
-        [% l('Print Item Labels') %]
-    </div>
-</div>
+<h2>[% l('Print Item Labels') %]</h2>
 
-<div class="row">
-    <div class="col-md-3">
-       <div class="input-group">
-            <span class="input-group-addon">[% l('Template') %]</span>
-            <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
-       </div>
-    </div>
-    <div class="col-md-1">
-        <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
-    </div>
-    <div class="col-md-2">
-       <div class="input-group">
-            <span class="input-group-addon">[% l('Printer') %]</span>
-            <select class="form-control" ng-model="print.template_context">
-                <option value="default">[% l('Default') %]</option>
-                <option value="receipt">[% l('Receipt') %]</option>
-                <option value="label">[% l('Label') %]</option>
-                <option value="mail">[% l('Mail') %]</option>
-                <option value="offline">[% l('Offline') %]</option>
-            </select>
+<div class="row bg-info">
+    <div class="col-md-6">
+        <div class="row">
+            <div class="col-md-1">
+                <span class="h4">[% l('Template') %]</span>
+            </div>
+            <div class="col-md-5">
+                <eg-basic-combo-box list="template_name_list" selected="template_name"></eg-basic-combo-box>
+            </div>
+            <div class="col-md-1">
+                <button class="btn btn-default" ng-click="applyTemplate(template_name)">[% l('Apply') %]</button>
+            </div>
+            <div class="col-md-1">
+                <span class="h4">[% l('Printer') %]</span>
+            </div>
+            <div class="col-md-4">
+                <select class="form-control" ng-model="print.template_context">
+                    <option value="default">[% l('Default') %]</option>
+                    <option value="receipt">[% l('Receipt') %]</option>
+                    <option value="label">[% l('Label') %]</option>
+                    <option value="mail">[% l('Mail') %]</option>
+                    <option value="offline">[% l('Offline') %]</option>
+                </select>
+            </div>
         </div>
     </div>
-
     <div class="col-md-2">
         <div class="btn-group">
             <button class="btn btn-default" ng-click="saveTemplate(template_name)">[% l('Save') %]</button>
             <button class="btn btn-default" ng-click="deleteTemplate(template_name)">[% l('Delete') %]</button>
         </div>
     </div>
-
     <div class="col-md-3">
         <div class="btn-group">
             <span class="btn btn-default btn-file">
@@ -53,14 +105,14 @@
                 <input type="file" eg-file-reader container="imported_templates.data">
             </span>
             <label class="btn btn-default"
-               eg-json-exporter container="templates"
-               default-file-name="'[% l('exported_label_templates.json') %]'">
-               [% l('Export') %]
+                eg-json-exporter container="templates"
+                default-file-name="'[% l('exported_label_templates.json') %]'">
+                [% l('Export') %]
             </label>
-            <label class="btn btn-default" ng-click="reset_to_default()">[% l('Default') %]</label>
-         </div>
+            <label class="btn btn-default" ng-click="reset_to_default()">
+                [% l('Default') %]</button>
+            </div>
     </div>
-
     <div class="col-md-1 pull-right">
         <button class="btn btn-default" ng-click="print_labels()">[% l('Print') %]</button>
     </div>
@@ -69,92 +121,196 @@
 <hr/>
 
 <div class="row">
-  <div class="col-md-5">
-    <ul class="nav nav-tabs">
-        <li ng-class="{active : current_tab == 'cn_template'}">
-            <a ng-click="set_tab('cn_template')">
-                [% l('Call Number Template') %]
-            </a>
-        </li>
-        <li ng-class="{active : current_tab == 'call_numbers'}">
-            <a ng-click="set_tab('call_numbers')">
-                [% l('Call Numbers') %]
-            </a>
-        </li>
-        <li ng-class="{active : current_tab == 'settings'}">
-            <a ng-click="set_tab('settings')">
-                [% l('Settings') %]
-            </a>
-        </li>
-        <li ng-class="{active : current_tab == 'template'}">
-            <a ng-click="set_tab('template')">
-                [% l('Label Template') %]
-            </a>
-        </li>
-    </ul>
-    <div class="tab-content">
-        <div class="tab-pane active">
-            <div ng-show="current_tab == 'cn_template'">
-                <h4>
-                    [% l('Call Number Preview') %]
-                </h4>
-                <div eg-print-template-output ng-show="true"
-                    content="print.cn_template_content"
-                    context="{ copy : preview_scope.copies[0], get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
-                <h4>
+    <div class="col-md-5">
+        <ul class="nav nav-tabs">
+            <li ng-class="{active : current_tab == 'cn_template'}">
+                <a ng-click="set_tab('cn_template')">
                     [% l('Call Number Template') %]
-                </h4>
-                <div><span>[% l('Changes here will wipe out manual changes in the Call Numbers tab.') %]<br/></span></div>
-                <textarea ng-model="print.cn_template_content" class="print-template-text">
-                </textarea>
-                <div ng-repeat="copy in preview_scope.copies">
-                    <div id="cn_for_copy_{{copy.id}}" eg-print-template-output ng-show="false"
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'call_numbers'}">
+                <a ng-click="set_tab('call_numbers')">
+                    [% l('Call Numbers') %]
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'settings'}">
+                <a ng-click="set_tab('settings')">
+                    [% l('Settings') %]
+                </a>
+            </li>
+            <li ng-class="{active : current_tab == 'template'}">
+                <a ng-click="set_tab('template')">
+                    [% l('Label Template') %]
+                </a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <div ng-show="current_tab == 'cn_template'">
+                    <h4>
+                        [% l('Call Number Preview') %]
+                    </h4>
+                    <div eg-print-template-output ng-show="true"
                         content="print.cn_template_content"
-                        context="{ copy : copy, get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
-                </div>
-            </div>
-            <div ng-show="current_tab == 'call_numbers'">
-                <h4>
-                    [% l('Formatted Call Numbers') %]
-                </h4>
-                <div><span>[% l('Manual adjustments may be made here. These do not get saved with templates.') %]<br/></span></div>
-                <div ng-repeat="cn in rendered_call_number_set">
-                    <textarea ng-model="cn.value" class="cn-template-text">
+                        context="{ copy : preview_scope.copies[0], get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
+                    <h4>
+                        [% l('Call Number Template') %]
+                    </h4>
+                    <div>
+                        <span>
+                            [% l('Changes here will wipe out manual changes in the Call Numbers tab.') %]<br/>
+                        </span>
+                    </div>
+                    <textarea ng-model="print.cn_template_content" class="print-template-text">
                     </textarea>
+                    <div ng-repeat="copy in preview_scope.copies">
+                        <div id="cn_for_copy_{{copy.id}}" eg-print-template-output ng-show="false"
+                            content="print.cn_template_content"
+                            context="{ copy : copy, get_cn_and_location_prefix : preview_scope.get_cn_and_location_prefix, get_cn_and_location_suffix : preview_scope.get_cn_and_location_suffix, settings : preview_scope.settings }"></div>
+                    </div>
                 </div>
-            </div>
-            <div ng-show="current_tab == 'settings'">
-                <div><span>[% l('These settings do get saved with templates and will override corresponding Library Settings.') %]<br/></span></div>
-                <div ng-repeat="s in org_unit_settings">
-                    <div class="row" style="margin-top: 5mm;">
-                        <div class="col-md-6"><label>{{s.label}} <eg-help-popover help-text="{{s.description}}"></label></div>
-                        <div class="col-md-6"><input type="text" ng-model="preview_scope.settings[s.name]"></input></div>
+                <div ng-show="current_tab == 'call_numbers'">
+                    <h4>
+                        [% l('Formatted Call Numbers') %]
+                    </h4>
+                    <div>
+                        <span>
+                            [% l('Manual adjustments may be made here. These do not get saved with templates.') %]<br/>
+                        </span>
+                    </div>
+                    <div ng-repeat="cn in rendered_call_number_set">
+                        <textarea ng-model="cn.value" class="cn-template-text">
+                        </textarea>
                     </div>
                 </div>
-
-            </div>
-            <div ng-show="current_tab == 'template'">
-                <div ng-if="print.load_failed" class="alert alert-danger">
-                  [% l(
-                    "Unable to load template '[_1]'.  The web server returned an error.",
-                    '{{print.template_name}}')
-                  %]
+                <div ng-show="current_tab == 'settings'">
+                    <div>
+                        <span>
+                          <b>*All settings will be saved with templates</b>
+                        </span>
+                    </div>
+                    <form id="print_label_form" name="print_label_form">
+                        <div class="print-label-toolbox">
+                            <div class="eg-print-label-section">
+                                <h4>Print Feed Option</h4>
+                                <ul>
+                                    <li ng-repeat="x in preview_scope.toolbox_settings.feed_option.options">
+                                        <label>{{x.label}}:</label>
+                                        <input name="label-feed-option" ng-model="preview_scope.toolbox_settings.feed_option.selected" ng-change="redraw_label_table()" type="radio" value="{{x.value}}" />
+                                    </li>
+                                </ul>
+                            </div>
+                            <div class="eg-print-label-section">
+                                <h4>Print Option</h4>
+                                <ul>
+                                    <li ng-repeat="x in preview_scope.toolbox_settings.mode.options">
+                                        <label>{{x.label}}:</label>
+                                        <input name="label-output-mode" ng-model="preview_scope.toolbox_settings.mode.selected" type="radio" value="{{x.value}}" />
+                                    </li>
+                                </ul>
+                            </div>
+                            <h3>Page Settings</h3>
+                            <div class="eg-print-label-section">
+                                <h4>Page Margins</h4>
+                                <p>Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p>
+                                <ul>
+                                    <li ng-repeat="(x, y) in preview_scope.toolbox_settings.page.margins">
+                                        <label>{{y.label}}:</label>
+                                        <input type="text" eg-print-label-valid-css ng-model="preview_scope.toolbox_settings.page.margins[x].size" required />
+                                    </li>
+                                </ul>
+                            </div>
+                            <div class="eg-print-label-section">
+                                <h4>Print Grid Size</h4>
+                                <p>Please specify the number of columns and rows (if applicable) on the print medium. This setting is only used to control page layouts and breaks. It will not limit the number of rows printed, which is determined by the number of selected copies. The column count should include each label in a row. For example, if you have a row with two sets of labels that include one spine and one pocket, the count is four.</p>
+                                <ul>
+                                    <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                        <label>Rows:</label>
+                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.rows" ng-min="1" required />
+                                    </li>
+                                    <li>
+                                        <label>Columns:</label>
+                                        <input type="number" ng-model="preview_scope.toolbox_settings.page.dimensions.columns" ng-min="1" required />
+                                    </li>
+                                </ul>
+                            </div>
+                            <div class="eg-print-label-section">
+                                <label>Gap Between Rows</label> <input type="text" ng-model="preview_scope.toolbox_settings.page.space_between_labels.vertical.size" ng-min="0" eg-print-label-valid-css required />
+                                <p>Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p><br />
+                                <label>Gap Between Columns of a Label Set</label>
+                              <input type="text" ng-model="preview_scope.toolbox_settings.page.label.gap.size" ng-min="0" eg-print-label-valid-css required />
+                                <p>Gap between a label “set” (i.e. gap between each spine/pocket combo). Use label 2 left margin to identify how much space to provide between labels within one set. Must be in format "0.25in", "2.5cm", "1.5in", etc. (units of measurement must be valid CSS)</p>
+                            </div>
+                            <div class="eg-print-label-section" ng-if="preview_scope.toolbox_settings.page.dimensions.rows > 1 || preview_scope.toolbox_settings.page.dimensions.columns > 1">
+                                <h4>Starting Position on Label Grid</h4>
+                                <p>Specify the starting row and column where labels should begin printing. Values must not exceed the respective row and column size specified in "Print Grid Size"</p>
+                                <ul>
+                                    <li ng-if="preview_scope.toolbox_settings.feed_option.selected === 'sheet'">
+                                        <label>Row:</label>
+                                        <input type="number" eg-print-label-valid-int eg-print-label-row-bounds ng-model="preview_scope.toolbox_settings.page.start_position.row" ng-min="1" required />
+                                    </li>
+                                    <li>
+                                        <label>Column:</label>
+                                        <input type="number" eg-print-label-valid-int="" eg-print-label-column-bounds ng-model="preview_scope.toolbox_settings.page.start_position.column" ng-min="1"  required />
+                                    </li>
+                                </ul>
+                            </div>
+                            <div class="eg-print-label-section">
+                                <h4>Label Set Configuration</h4>
+                                <label>Number of Labels in Set:</label>
+                              <input type="number" eg-print-label-valid-int="" ng-min="1" ng-model="preview_scope.toolbox_settings.page.label.set.size" />
+                              <br />
+                              <br />
+                                <ul ng-if="preview_scope.valid_print_label_start_column() && preview_scope.valid_print_label_start_row() && preview_scope.toolbox_settings.mode.selected === 'spine-pocket'">
+                                    <li ng-repeat="i in preview_scope.toolbox_settings.page.label.set.size | columnRowRange">
+                                        <label>Col. {{i + 1}}</label>
+                                        <select id="eg_print_label_column_spec_{{i}}" ng-model="preview_scope.toolbox_settings.page.column_class[i]" ng-change="redraw_label_table()">
+                                            <option value="spine">Label 1</option>
+                                            <option value="pocket">Label 2</option>
+                                        </select>
+                                    </li>
+                                </ul>
+                              <br />
+                            </div>                          
+                        </div>                
+                    </form>
+                    <div>
+                        <span>
+                            [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]<br/>
+                        </span>
+                    </div>
+                    <div ng-repeat="s in org_unit_settings">
+                        <div class="row" style="margin-top: 5mm;">
+                        <div class="col-md-6"><label>{{s.label}} <eg-help-popover help-text="{{s.description}}"></label></div>
+                            <div class="col-md-6">
+                                <input type="text" ng-model="preview_scope.settings[s.name]"></input>
+                            </div>
+                        </div>
+                    </div>
                 </div>
-                <div>
-                  <textarea ng-model="print.template_content" class="print-template-text">
-                  </textarea>
+                <div ng-show="current_tab == 'template'">
+                    <div ng-if="print.load_failed" class="alert alert-danger">
+                        [% l(
+                        "Unable to load template '[_1]'.  The web server returned an error.",
+                        '{{print.template_name}}')
+                        %]
+                    </div>
+                    <div>
+                        <textarea ng-model="print.template_content" class="print-template-text">
+                        </textarea>
+                    </div>
                 </div>
             </div>
         </div>
     </div>
-  </div>
-  <div class="col-md-7">
-    <h3>
-        [% l('Label Preview') %]
-    </h3>
-    <div eg-print-template-output
-      content="print.template_content"
-      context="preview_scope"></div>
-  </div> <!-- col -->
+    <div class="col-md-7">
+        <h3>
+            [% l('Label Preview') %]
+        </h3>
+        <div eg-print-template-output
+          content="print.template_content"
+          context="preview_scope"></div>
+    </div>
+    <!-- col -->
 </div>
 
diff --git a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
index 28b78d1bde..9d006b8e3e 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/printlabels/app.js
@@ -5,69 +5,69 @@
 angular.module('egPrintLabels',
     ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
 
-.config(function($routeProvider, $locationProvider, $compileProvider) {
+.config(function ($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
-	
+
     var resolver = {
-        delay : ['egStartup', function(egStartup) { return egStartup.go(); }]
+        delay: ['egStartup', function (egStartup) { return egStartup.go(); }]
     };
 
     $routeProvider.when('/cat/printlabels/:dataKey', {
         templateUrl: './cat/printlabels/t_view',
         controller: 'LabelCtrl',
-        resolve : resolver
+        resolve: resolver
     });
 
 })
 
-.factory('itemSvc', 
+.factory('itemSvc',
        ['egCore',
-function(egCore) {
+function (egCore) {
 
     var service = {
-        copies : [], // copy barcode search results
-        index : 0 // search grid index
+        copies: [], // copy barcode search results
+        index: 0 // search grid index
     };
 
-    service.flesh = {   
-        flesh : 3, 
-        flesh_fields : {
-            acp : ['call_number','location','status','location','floating','circ_modifier','age_protect','circ_lib'],
-            acn : ['record','prefix','suffix','owning_lib'],
-            bre : ['simple_record','creator','editor']
+    service.flesh = {
+        flesh: 3,
+        flesh_fields: {
+            acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect'],
+            acn: ['record', 'prefix', 'suffix'],
+            bre: ['simple_record', 'creator', 'editor']
         },
-        select : { 
+        select: {
             // avoid fleshing MARC on the bre
             // note: don't add simple_record.. not sure why
-            bre : ['id','tcn_value','creator','editor'],
-        } 
+            bre: ['id', 'tcn_value', 'creator', 'editor'],
+        }
     }
 
     // resolved with the last received copy
-    service.fetch = function(barcode, id, noListDupes) {
+    service.fetch = function (barcode, id, noListDupes) {
         var promise;
 
         if (barcode) {
-            promise = egCore.pcrud.search('acp', 
-                {barcode : barcode, deleted : 'f'}, service.flesh);
+            promise = egCore.pcrud.search('acp',
+                { barcode: barcode, deleted: 'f' }, service.flesh);
         } else {
             promise = egCore.pcrud.retrieve('acp', id, service.flesh);
         }
 
         var lastRes;
         return promise.then(
-            function() {return lastRes},
+            function () { return lastRes },
             null, // error
 
             // notify reads the stream of copies, one at a time.
-            function(copy) {
+            function (copy) {
 
                 var flatCopy;
                 if (noListDupes) {
                     // use the existing copy if possible
                     flatCopy = service.copies.filter(
-                        function(c) {return c.id == copy.id()})[0];
+                        function (c) { return c.id == copy.id() })[0];
                 }
 
                 if (!flatCopy) {
@@ -77,8 +77,8 @@ function(egCore) {
                 }
 
                 return lastRes = {
-                    copy : copy, 
-                    index : flatCopy.index
+                    copy: copy,
+                    index: flatCopy.index
                 }
             }
         );
@@ -90,19 +90,66 @@ function(egCore) {
 /**
  * Label controller!
  */
-.controller('LabelCtrl', 
-       ['$scope','$q','$window','$routeParams','$location','$timeout','egCore','egNet','ngToast','itemSvc',
-function($scope , $q , $window , $routeParams , $location , $timeout , egCore , egNet , ngToast , itemSvc ) {
+.controller('LabelCtrl',
+       ['$scope', '$q', '$window', '$routeParams', '$location', '$timeout', 'egCore', 'egNet', 'ngToast', 'itemSvc', 'labelOutputRowsFilter',
+function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet, ngToast, itemSvc, labelOutputRowsFilter) {
 
     var dataKey = $routeParams.dataKey;
     console.debug('dataKey: ' + dataKey);
 
     $scope.print = {
-        template_name : 'item_label',
-        template_output : '',
-        template_context : 'default'
+        template_name: 'item_label',
+        template_output: '',
+        template_context: 'default'
     };
 
+    var toolbox_settings = {
+        feed_option: {
+            options: [
+                { label: "Continuous", value: "continuous" },
+                { label: "Sheet", value: "sheet" },
+            ],
+            selected: "continuous"
+        },
+        label_set: {
+            margin_between: 0,
+            size: 1
+        },
+        mode: {
+            options: [
+                { label: "Label 1 Only", value: "spine-only" },
+                { label: "Labels 1 & 2", value: "spine-pocket" }
+            ],
+            selected: "spine-pocket"
+        },
+        page: {
+            column_class: ["spine"],
+            dimensions: {
+                columns: 2,
+                rows: 1
+            },
+            label: {
+                gap: {
+                    size: 0
+                },
+                set: {
+                    size: 2
+                }
+            },
+            margins: {
+                top: { size: 0, label: "Top" },
+                left: { size: 0, label: "Left" },
+            },
+            space_between_labels: {
+                horizontal: { size: 0, label: "Horizontal" },
+                vertical: { size: 0, label: "Vertical" }
+            },
+            start_position: {
+                column: 1,
+                row: 1
+            }
+        }
+    };
 
     if (dataKey && dataKey.length > 0) {
 
@@ -115,9 +162,10 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
             if (data) {
 
                 $scope.preview_scope = {
-                     'copies' : []
-                    ,'settings' : {}
-                    ,'get_cn_for' : function(copy) {
+                    'copies': []
+                    , 'settings': {}
+                    , 'toolbox_settings': toolbox_settings
+                    , 'get_cn_for': function (copy) {
                         var key = $scope.rendered_cn_key_by_copy_id[copy.id];
                         if (key) {
                             var manual_cn = $scope.rendered_call_number_set[key];
@@ -130,22 +178,22 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                             return '...';
                         }
                     }
-                    ,'get_bib_for' : function(copy) {
+                    , 'get_bib_for': function (copy) {
                         return $scope.record_details[copy['call_number.record.id']];
                     }
-                    ,'get_cn_prefix' : function(copy) {
+                    , 'get_cn_prefix': function (copy) {
                         return copy['call_number.prefix.label'];
                     }
-                    ,'get_cn_suffix' : function(copy) {
+                    , 'get_cn_suffix': function (copy) {
                         return copy['call_number.suffix.label'];
                     }
-                    ,'get_location_prefix' : function(copy) {
+                    , 'get_location_prefix': function (copy) {
                         return copy['location.label_prefix'];
                     }
-                    ,'get_location_suffix' : function(copy) {
+                    , 'get_location_suffix': function (copy) {
                         return copy['location.label_suffix'];
                     }
-                    ,'get_cn_and_location_prefix' : function(copy,separator) {
+                    , 'get_cn_and_location_prefix': function (copy, separator) {
                         var acpl_prefix = copy['location.label_prefix'] || '';
                         var cn_prefix = copy['call_number.prefix.label'] || '';
                         var prefix = acpl_prefix + ' ' + cn_prefix;
@@ -153,7 +201,7 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                         if (separator && prefix != '') { prefix += separator; }
                         return prefix;
                     }
-                    ,'get_cn_and_location_suffix' : function(copy,separator) {
+                    , 'get_cn_and_location_suffix': function (copy, separator) {
                         var acpl_suffix = copy['location.label_suffix'] || '';
                         var cn_suffix = copy['call_number.suffix.label'] || '';
                         var suffix = cn_suffix + ' ' + acpl_suffix;
@@ -161,6 +209,12 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                         if (separator && suffix != '') { suffix = separator + suffix; }
                         return suffix;
                     }
+                    , 'valid_print_label_start_column': function () {
+                        return !angular.isNumber(toolbox_settings.page.dimensions.columns) || !angular.isNumber(toolbox_settings.page.start_position.column) ? false : (toolbox_settings.page.start_position.column <= toolbox_settings.page.dimensions.columns);
+                    }
+                    , 'valid_print_label_start_row': function () {
+                        return !angular.isNumber(toolbox_settings.page.dimensions.rows) || !angular.isNumber(toolbox_settings.page.start_position.row) ? false : (toolbox_settings.page.start_position.row <= toolbox_settings.page.dimensions.rows);
+                    }
                 };
                 $scope.record_details = {};
                 $scope.org_unit_settings = {};
@@ -168,33 +222,33 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                 var promises = [];
                 $scope.org_unit_setting_list = [
                      'webstaff.cat.label.font.family'
-                    ,'webstaff.cat.label.font.size'
-                    ,'webstaff.cat.label.font.weight'
-                    ,'webstaff.cat.label.inline_css'
-                    ,'webstaff.cat.label.left_label.height'
-                    ,'webstaff.cat.label.left_label.left_margin'
-                    ,'webstaff.cat.label.left_label.width'
-                    ,'webstaff.cat.label.right_label.height'
-                    ,'webstaff.cat.label.right_label.left_margin'
-                    ,'webstaff.cat.label.right_label.width'
-                    ,'webstaff.cat.label.call_number_wrap_filter_height'
-                    ,'webstaff.cat.label.call_number_wrap_filter_width'
+                    , 'webstaff.cat.label.font.size'
+                    , 'webstaff.cat.label.font.weight'
+                    , 'webstaff.cat.label.inline_css'
+                    , 'webstaff.cat.label.left_label.height'
+                    , 'webstaff.cat.label.left_label.left_margin'
+                    , 'webstaff.cat.label.left_label.width'
+                    , 'webstaff.cat.label.right_label.height'
+                    , 'webstaff.cat.label.right_label.left_margin'
+                    , 'webstaff.cat.label.right_label.width'
+                    , 'webstaff.cat.label.call_number_wrap_filter_height'
+                    , 'webstaff.cat.label.call_number_wrap_filter_width'
                 ];
 
                 promises.push(
-                    egCore.pcrud.search('coust',{name:$scope.org_unit_setting_list}).then(
+                    egCore.pcrud.search('coust', { name: $scope.org_unit_setting_list }).then(
                          null
-                        ,null
-                        ,function(yaous) {
+                        , null
+                        , function (yaous) {
                             $scope.org_unit_settings[yaous.name()] = egCore.idl.toHash(yaous, true);
                         }
                     )
                 );
 
                 promises.push(
-                    egCore.org.settings($scope.org_unit_setting_list).then(function(res) {
+                    egCore.org.settings($scope.org_unit_setting_list).then(function (res) {
                         $scope.preview_scope.settings = res;
-                        egCore.hatch.getItem('cat.printlabels.last_settings').then(function(last_settings) {
+                        egCore.hatch.getItem('cat.printlabels.last_settings').then(function (last_settings) {
                             if (last_settings) {
                                 for (s in last_settings) {
                                     $scope.preview_scope.settings[s] = last_settings[s];
@@ -204,20 +258,20 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                     })
                 );
 
-                angular.forEach(data.copies, function(copy) {
+                angular.forEach(data.copies, function (copy) {
                     promises.push(
-                        itemSvc.fetch(null,copy).then(function(res) {
+                        itemSvc.fetch(null, copy).then(function (res) {
                             var flat_copy = egCore.idl.toHash(res.copy, true);
                             $scope.preview_scope.copies.push(flat_copy);
-                            $scope.record_details[ flat_copy['call_number.record.id'] ] = 1;
+                            $scope.record_details[flat_copy['call_number.record.id']] = 1;
                         })
                     )
                 });
 
-                $q.all(promises).then(function() {
+                $q.all(promises).then(function () {
 
                     var promises2 = [];
-                    angular.forEach($scope.record_details, function(el,k,obj) {
+                    angular.forEach($scope.record_details, function (el, k, obj) {
                         promises2.push(
                             egNet.request(
                                 'open-ils.search',
@@ -229,7 +283,7 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                         );
                     });
 
-                    $q.all(promises2).then(function() {
+                    $q.all(promises2).then(function () {
                         // today, staff, current_location, etc.
                         egCore.print.fleshPrintScope($scope.preview_scope);
                         $scope.template_changed(); // load the default
@@ -246,13 +300,13 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
     }
 
     $scope.fetchTemplates = function (set_default) {
-        return egCore.hatch.getItem('cat.printlabels.templates').then(function(t) {
+        return egCore.hatch.getItem('cat.printlabels.templates').then(function (t) {
             if (t) {
                 $scope.templates = t;
                 $scope.template_name_list = Object.keys(t);
                 if (set_default) {
-                    egCore.hatch.getItem('cat.printlabels.default_template').then(function(d) {
-                        if ($scope.template_name_list.indexOf(d,0) > -1) {
+                    egCore.hatch.getItem('cat.printlabels.default_template').then(function (d) {
+                        if ($scope.template_name_list.indexOf(d, 0) > -1) {
                             $scope.template_name = d;
                         }
                     });
@@ -269,6 +323,10 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
         for (var s in $scope.templates[n].settings) {
             $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
         }
+        if ($scope.templates[n].toolbox_settings) {
+            $scope.preview_scope.toolbox_settings = $scope.templates[n].toolbox_settings;
+            $scope.create_print_label_table();
+        }
         egCore.hatch.setItem('cat.printlabels.default_template', n);
         $scope.save_locally();
     }
@@ -281,7 +339,7 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
             egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
             $scope.fetchTemplates();
             ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_DELETE);
-            egCore.hatch.getItem('cat.printlabels.default_template').then(function(d) {
+            egCore.hatch.getItem('cat.printlabels.default_template').then(function (d) {
                 if (d && d == n) {
                     egCore.hatch.removeItem('cat.printlabels.default_template');
                 }
@@ -293,10 +351,11 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
         if (n) {
 
             $scope.templates[n] = {
-                 content : $scope.print.template_content
-                ,context : $scope.print.template_context
-                ,cn_content : $scope.print.cn_template_content
-                ,settings : $scope.preview_scope.settings
+                content: $scope.print.template_content
+                , context: $scope.print.template_context
+                , cn_content: $scope.print.cn_template_content
+                , settings: $scope.preview_scope.settings
+                , toolbox_settings: $scope.preview_scope.toolbox_settings
             };
             $scope.template_name_list = Object.keys($scope.templates);
 
@@ -313,53 +372,52 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
     }
 
     $scope.templates = {};
-    $scope.imported_templates = { data : '' };
+    $scope.imported_templates = { data: '' };
     $scope.template_name = '';
     $scope.template_name_list = [];
 
-    $scope.print_labels = function() {
+    $scope.print_labels = function () {
         return egCore.print.print({
-            context : $scope.print.template_context,
-            template : $scope.print.template_name,
-            scope : $scope.preview_scope,
+            context: $scope.print.template_context,
+            template: $scope.print.template_name,
+            scope: $scope.preview_scope,
         });
     }
 
-    $scope.template_changed = function() {
+    $scope.template_changed = function () {
         $scope.print.load_failed = false;
         egCore.print.getPrintTemplate('item_label')
         .then(
-            function(html) { 
+            function (html) {
                 $scope.print.template_content = html;
             },
-            function() {
+            function () {
                 $scope.print.template_content = '';
                 $scope.print.load_failed = true;
             }
         );
         egCore.print.getPrintTemplateContext('item_label')
-        .then(function(template_context) {
+        .then(function (template_context) {
             $scope.print.template_context = template_context;
         });
         egCore.print.getPrintTemplate('item_label_cn')
         .then(
-            function(html) {
+            function (html) {
                 $scope.print.cn_template_content = html;
             },
-            function() {
+            function () {
                 $scope.print.cn_template_content = '';
                 $scope.print.load_failed = true;
             }
         );
-        egCore.hatch.getItem('cat.printlabels.last_settings').then(function(s) {
+        egCore.hatch.getItem('cat.printlabels.last_settings').then(function (s) {
             if (s) {
                 $scope.preview_scope.settings = s;
             }
         });
-
     }
 
-    $scope.reset_to_default = function() {
+    $scope.reset_to_default = function () {
         egCore.print.removePrintTemplate(
             'item_label'
         );
@@ -374,14 +432,14 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
             $scope.preview_scope.settings[s] = undefined;
         }
         $scope.preview_scope.settings = {};
-        egCore.org.settings($scope.org_unit_setting_list).then(function(res) {
+        egCore.org.settings($scope.org_unit_setting_list).then(function (res) {
             $scope.preview_scope.settings = res;
         });
 
         $scope.template_changed();
     }
 
-    $scope.save_locally = function() {
+    $scope.save_locally = function () {
         egCore.print.storePrintTemplate(
             'item_label',
             $scope.print.template_content
@@ -397,17 +455,18 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
         egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);
     }
 
-    $scope.imported_print_templates = { data : '' };
-    $scope.$watch('imported_templates.data', function(newVal, oldVal) {
+    $scope.imported_print_templates = { data: '' };
+    $scope.$watch('imported_templates.data', function (newVal, oldVal) {
         if (newVal && newVal != oldVal) {
             try {
                 var data = JSON.parse(newVal);
-                angular.forEach(data, function(el,k) {
+                angular.forEach(data, function (el, k) {
                     $scope.templates[k] = {
-                         content : el.content
-                        ,context : el.context
-                        ,cn_content : el.cn_content
-                        ,settings : el.settings
+                        content: el.content
+                        , context: el.context
+                        , cn_content: el.cn_content
+                        , settings: el.settings
+                        , toolbox_settings: el.toolbox_settings
                     };
                 });
                 $scope.saveTemplate();
@@ -421,18 +480,18 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
 
     $scope.rendered_call_number_set = {};
     $scope.rendered_cn_key_by_copy_id = {};
-    $scope.rebuild_cn_set = function() {
-        $timeout(function(){
+    $scope.rebuild_cn_set = function () {
+        $timeout(function () {
             $scope.rendered_call_number_set = {};
             $scope.rendered_cn_key_by_copy_id = {};
             for (var i = 0; i < $scope.preview_scope.copies.length; i++) {
                 var copy = $scope.preview_scope.copies[i];
-                var rendered_cn = document.getElementById('cn_for_copy_'+copy.id);
+                var rendered_cn = document.getElementById('cn_for_copy_' + copy.id);
                 if (rendered_cn && rendered_cn.textContent) {
                     var key = rendered_cn.textContent;
                     if (typeof $scope.rendered_call_number_set[key] == 'undefined') {
                         $scope.rendered_call_number_set[key] = {
-                            value : key
+                            value: key
                         };
                     }
                     $scope.rendered_cn_key_by_copy_id[copy.id] = key;
@@ -442,46 +501,191 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
         });
     }
 
-    $scope.$watch('print.cn_template_content', function(newVal, oldVal) {
+    $scope.create_print_label_table = function () {
+        if ($scope.print_label_form.$valid && $scope.print.template_content && $scope.preview_scope) {
+            $scope.preview_scope.label_output_copies = labelOutputRowsFilter($scope.preview_scope.copies, $scope.preview_scope.toolbox_settings);
+            var html = $scope.print.template_content;
+            var d = new Date(); //Added to table ID with 'eg_plt_' to cause $complie on $scope.print.template_content to fire due to template content change.
+            var table = "<table id=\"eg_plt_" + d.getTime().toString() + "_{{$index}}\" eg-print-label-table style=\"border-collapse: collapse; border: 0 solid transparent; border-spacing: 0; margin: {{$index === 0 ?toolbox_settings.page.margins.top.size : 0}} 0 0 0;\" class=\"custom-label-table{{$index % toolbox_settings.page.dimensions.rows === 0 && $index > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n";
+            table += "<tr>\n";
+            table += "<td style=\"border: 0 solid transparent; padding: {{parentIndex % toolbox_settings.page.dimensions.rows === 0 && toolbox_settings.feed_option.selected === 'sheet' && parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : parentIndex > 0 ? toolbox_settings.page.space_between_labels.vertical.size : 0}} 0 0 {{$index === 0 ? toolbox_settings.page.margins.left.size : col.styl ? col.styl : toolbox_settings.page.space_between_labels.horizontal.size}};\" ng-repeat=\"col in row.columns\">\n";
+            table += "<pre class=\"{{col.cls}}\" style=\"border: none; margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'spine'\">\n";
+            table += "{{col.c ? get_cn_for(col.c) : ''}}";
+            table += "</pre>\n";
+            table += "<pre class=\"{{col.cls}}{{parentIndex % toolbox_settings.page.dimensions.rows === 0 && parentIndex > 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" style=\"border: none;  margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'pocket'\">\n";
+            table += "{{col.c ? col.c.barcode : ''}}\n";
+            table += "{{col.c ? col.c['call_number.label'] : ''}}\n";
+            table += "{{col.c ? get_bib_for(col.c).author : ''}}\n";
+            table += "{{col.c ? (get_bib_for(col.c).title | wrap:28:'once':'  ') : ''}}\n";
+            table += "</pre>\n";
+            table += "</td>\n"
+            table += "</tr>\n";
+            table += "</table>";
+            var comments = html.match(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g);
+            html = html.replace(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g, "");
+            var style = html.match(/\<style[^\>]*\>(?:(?!\<\/style\>)(?:.|\s))*\<\/style\>\s*/gi);
+            var output = (style ? style.join("\n") : "") + (comments ? comments.join("\n") : "") + table;
+            output = output.replace(/\n+/, "\n");
+            $scope.print.template_content = output;
+        }
+    }
+
+    $scope.redraw_label_table = function () {
+        var d = new Date(); //Added to table ID with 'eg_plt_' to cause $complie on $scope.print.template_content to fire due to template content change.
+        var table = "<table id=\"eg_plt_" + d.getTime().toString() + "\"\></table>\n";
+        $scope.print.template_content += table;
+        $scope.create_print_label_table();
+    }
+
+    $scope.$watch('preview_scope.toolbox_settings.page.dimensions.columns',
+        function (newVal, oldVal) {
+            if (newVal && newVal != oldVal && $scope.preview_scope) {
+                $scope.redraw_label_table();
+            }
+        }
+    );
+
+    $scope.$watch('print.cn_template_content', function (newVal, oldVal) {
         if (newVal && newVal != oldVal) {
             $scope.rebuild_cn_set();
         }
     });
 
-    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function(newVal, oldVal) {
+    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function (newVal, oldVal) {
         if (newVal && newVal != oldVal) {
             $scope.rebuild_cn_set();
         }
     });
 
-    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function(newVal, oldVal) {
+    $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function (newVal, oldVal) {
         if (newVal && newVal != oldVal) {
             $scope.rebuild_cn_set();
         }
     });
 
+    $scope.$watchGroup(['preview_scope.toolbox_settings.page.margins.top.size', 'preview_scope.toolbox_settings.page.margins.left.size', 'preview_scope.toolbox_settings.page.dimensions.rows', 'preview_scope.toolbox_settings.page.space_between_labels.horizontal.size', 'preview_scope.toolbox_settings.page.space_between_labels.vertical.size', 'preview_scope.toolbox_settings.page.start_position.row', 'preview_scope.toolbox_settings.page.start_position.column', 'preview_scope.toolbox_settings.page.label.gap.size'], function (newVal, oldVal) {
+        if (newVal && newVal != oldVal && $scope.preview_scope.label_output_copies) {
+            $scope.redraw_label_table();
+        }
+    });
+
+    $scope.$watch("preview_scope.toolbox_settings.mode.selected", function (newVal, oldVal) {
+        if (newVal && newVal != oldVal) {
+            var ts_p = $scope.preview_scope.toolbox_settings.page;
+            if (ts_p.label.set.size === 1) {
+                if (newVal === "spine-pocket") {
+                    ts_p.column_class = ["spine", "pocket"];
+                    ts_p.label.set.size = 2;
+                } else {
+                    ts_p.column_class = ["spine"];
+                }
+            } else {
+                if (newVal === "spine-only") {
+                    for (var i = 0; i < ts_p.label.set.size; i++) {
+                        ts_p.column_class[i] = "spine";
+                    }
+                } else {
+                    ts_p.label.set.size === 2 ? ts_p.column_class = ["spine", "pocket"] : false;
+                }
+            }
+            $scope.redraw_label_table();
+        }
+    });
+
+    $scope.$watch("preview_scope.toolbox_settings.page.label.set.size", function (newVal, oldVal) {
+        if (newVal && newVal != oldVal) {
+            var ts_p = $scope.preview_scope.toolbox_settings.page;
+            if (angular.isNumber(newVal)) {
+                while (ts_p.column_class.length > ts_p.label.set.size) {
+                    ts_p.column_class.splice((ts_p.column_class.length - 1), 1);
+                }
+                while (ts_p.column_class.length < ts_p.label.set.size) {
+                    ts_p.column_class.push("spine");
+                }
+            }
+            $scope.redraw_label_table();
+        }
+    });
+
     $scope.current_tab = 'call_numbers';
-    $scope.set_tab = function(tab) {
+    $scope.set_tab = function (tab) {
         $scope.current_tab = tab;
     }
 
 }])
 
-// 
-.directive('egPrintTemplateOutput', ['$compile',function($compile) {
-    return function(scope, element, attrs) {
+.directive("egPrintLabelColumnBounds", function () {
+    return {
+        link: function (scope, element, attr, ctrl) {
+            function withinBounds(v) {
+                scope.$watch("preview_scope.toolbox_settings.page.dimensions.columns", function (newVal, oldVal) {
+                    ctrl.$setValidity("egWithinPrintColumnBounds", scope.preview_scope.valid_print_label_start_column())
+                });
+                return v;
+            }
+            ctrl.$parsers.push(withinBounds);
+            ctrl.$formatters.push(withinBounds);
+        },
+        require: "ngModel"
+    }
+})
+
+.directive("egPrintLabelRowBounds", function () {
+    return {
+        link: function (scope, element, attr, ctrl) {
+            function withinBounds(v) {
+                scope.$watch("preview_scope.toolbox_settings.page.dimensions.rows", function (newVal, oldVal) {
+                    ctrl.$setValidity("egWithinPrintRowBounds", scope.preview_scope.valid_print_label_start_row());
+                });
+                return v;
+            }
+            ctrl.$parsers.push(withinBounds);
+            ctrl.$formatters.push(withinBounds);
+        },
+        require: "ngModel"
+    }
+})
+
+.directive("egPrintLabelValidCss", function () {
+    return {
+        require: "ngModel",
+        link: function (scope, element, attr, ctrl) {
+            function floatValidation(v) {
+                ctrl.$setValidity("isFloat", v.toString().match(/^\-*(?:^0$|(?:\d+)(?:\.\d{1,})*([a-z]{2}))$/) ? true : false);
+                return v;
+            }
+            ctrl.$parsers.push(floatValidation);
+        }
+    }
+})
+
+.directive("egPrintLabelValidInt", function () {
+    return {
+        require: "ngModel",
+        link: function (scope, element, attr, ctrl) {
+            function intValidation(v) {
+                ctrl.$setValidity("isInteger", v.toString().match(/^\d+$/));
+                return v;
+            }
+            ctrl.$parsers.push(intValidation);
+        }
+    }
+})
+
+.directive('egPrintTemplateOutput', ['$compile', function ($compile) {
+    return function (scope, element, attrs) {
         scope.$watch(
-            function(scope) {
+            function (scope) {
                 return scope.$eval(attrs.content);
             },
-            function(value) {
+            function (value) {
                 // create an isolate scope and copy the print context
                 // data into the new scope.
                 // TODO: see also print security concerns in egHatch
                 var result = element.html(value);
                 var context = scope.$eval(attrs.context);
                 var print_scope = scope.$new(true);
-                angular.forEach(context, function(val, key) {
+                angular.forEach(context, function (val, key) {
                     print_scope[key] = val;
                 })
                 $compile(element.contents())(print_scope);
@@ -490,8 +694,8 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
     };
 }])
 
-.filter('cn_wrap', function() {
-    return function(input, w, h, wrap_type) {
+.filter('cn_wrap', function () {
+    return function (input, w, h, wrap_type) {
         var names;
         var prefix = input[0];
         var callnum = input[1];
@@ -506,11 +710,11 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                on the spine label: subclass letters, subclass numbers, cutter numbers, trailing stuff (date) */
             var patt1 = /^([A-Z]{1,3})\s*(\d+(?:\.\d+)?)\s*(\.[A-Z]\d*)\s*([A-Z]\d*)?\s*(\d\d\d\d(?:-\d\d\d\d)?)?\s*(.*)$/i;
             var result = callnum.match(patt1);
-            if (result) { 
-                callnum = result.slice(1).join('\t');  
+            if (result) {
+                callnum = result.slice(1).join('\t');
             } else {
                 callnum = callnum.split(/\s+/).join('\t');
-            } 
+            }
 
             /* If result is null, leave callnum alone. Can't parse this malformed call num */
         } else {
@@ -527,17 +731,17 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
         /* At this point, the call number pieces are separated by tab characters.  This allows
         *  some space-containing constructs like "v. 1" to appear on one line
         */
-        callnum = callnum.replace(/\t\t/g,'\t');  /* Squeeze out empties */ 
+        callnum = callnum.replace(/\t\t/g, '\t');  /* Squeeze out empties */
         names = callnum.split('\t');
         var j = 0; var tb = [];
         while (j < h) {
-            
+
             /* spine */
             if (j < w) {
 
                 var name = names.shift();
                 if (name) {
-                    name = String( name );
+                    name = String(name);
 
                     /* if the name is greater than the label width... */
                     if (name.length > w) {
@@ -548,20 +752,20 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                             if (name.match(/^\./)) sname[0] = '.' + sname[0];
                             for (var k = 1; k < sname.length; k++) sname[k] = '.' + sname[k];
                             /* and put all but the first one back into the names array */
-                            names = sname.slice(1).concat( names );
+                            names = sname.slice(1).concat(names);
                             /* if the name fragment is still greater than the label width... */
                             if (sname[0].length > w) {
                                 /* then just truncate and throw the rest back into the names array */
-                                tb[j] = sname[0].substr(0,w);
-                                names = [ sname[0].substr(w) ].concat( names );
+                                tb[j] = sname[0].substr(0, w);
+                                names = [sname[0].substr(w)].concat(names);
                             } else {
                                 /* otherwise we're set */
                                 tb[j] = sname[0];
                             }
                         } else {
                             /* if we can't split on periods, then just truncate and throw the rest back into the names array */
-                            tb[j] = name.substr(0,w);
-                            names = [ name.substr(w) ].concat( names );
+                            tb[j] = name.substr(0, w);
+                            names = [name.substr(w)].concat(names);
                         }
                     } else {
                         /* otherwise we're set */
@@ -575,8 +779,58 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
     }
 })
 
-.filter('wrap', function() {
-    return function(input, w, wrap_type, indent) {
+.filter("columnRowRange", function () {
+    return function (i) {
+        var res = [];
+        for (var j = 0; j < i; j++) {
+            res.push(j);
+        }
+        return res;
+    }
+})
+
+//Accepts $scope.preview_scope.copies and $scope.preview_scope.toolbox_settings as its parameters.
+.filter("labelOutputRows", function () {
+    return function (copies, settings) {
+        var cols = [], rows = [];
+        for (var j = 0; j < (settings.page.start_position.row - 1) ; j++) {
+            cols = [];
+            for (var k = 0; k < settings.page.dimensions.columns; k++) {
+                cols.push({ c: null, index: k, cls: getPrintLabelOutputClass(k, settings), styl: getPrintLabelStyle(k, settings) });
+            }
+            rows.push({ columns: cols });
+        }
+        cols = [];
+        for (var j = 0; j < (settings.page.start_position.column - 1) ; j++) {
+            cols.push({ c: null, index: j, cls: getPrintLabelOutputClass(j, settings), styl: getPrintLabelStyle(j, settings) });
+        }
+        var m = cols.length;
+        for (var j = 0; j < copies.length; j++) {
+            for (var n = 0; n < settings.page.label.set.size; n++) {
+                if (m < settings.page.dimensions.columns) {
+                    cols.push({ c: copies[j], index: cols.length, cls: getPrintLabelOutputClass(m, settings), styl: getPrintLabelStyle(m, settings) });
+                    m += 1;
+                }
+                if (m === settings.page.dimensions.columns) {
+                    m = 0;
+                    rows.push({ columns: cols });
+                    cols = [];
+                    n = settings.page.label.set.size;
+                }
+            }
+        }
+        cols.length > 0 ? rows.push({ columns: cols }) : false;
+        if (rows.length > 0) {
+            while ((rows[(rows.length - 1)].columns.length) < settings.page.dimensions.columns) {
+                rows[(rows.length - 1)].columns.push({ c: null, index: rows[(rows.length - 1)].columns.length, cls: getPrintLabelOutputClass(rows[(rows.length - 1)].columns.length, settings), styl: getPrintLabelStyle(rows[(rows.length - 1)].columns.length, settings) });
+            }
+        }
+        return rows;
+    }
+})
+
+.filter('wrap', function () {
+    return function (input, w, wrap_type, indent) {
         var output;
 
         if (!w) return input;
@@ -589,17 +843,17 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                 if_cant_wrap_then_truncate,
                 idx
         ) {
-            if (idx>10) {
+            if (idx > 10) {
                 console.log('possible infinite recursion, aborting');
                 return '';
             }
             if (String(text).length <= length) {
                 return text;
             } else {
-                var truncated_text = String(text).substr(0,length);
+                var truncated_text = String(text).substr(0, length);
                 var pivot_pos = truncated_text.lastIndexOf(' ');
-                var left_chunk = text.substr(0,pivot_pos).replace(/\s*$/,'');
-                var right_chunk = String(text).substr(pivot_pos+1);
+                var left_chunk = text.substr(0, pivot_pos).replace(/\s*$/, '');
+                var right_chunk = String(text).substr(pivot_pos + 1);
 
                 var wrapped_line;
                 if (left_chunk.length == 0) {
@@ -621,7 +875,7 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
                                     length,
                                     false,
                                     if_cant_wrap_then_truncate,
-                                    idx+1)
+                                    idx + 1)
                                 : right_chunk
                             )
                         )
@@ -631,16 +885,23 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
             }
         }
 
-        switch(wrap_type) {
+        switch (wrap_type) {
             case 'once':
-                output = wrap_on_space(input,w,true,false,0);
-            break;
+                output = wrap_on_space(input, w, true, false, 0);
+                break;
             default:
-                output = wrap_on_space(input,w,false,false,0);
-            break;
+                output = wrap_on_space(input, w, false, false, 0);
+                break;
         }
 
         return output;
     }
-})
+});
+
+function getPrintLabelOutputClass(index, settings) {
+    return settings.page.column_class[index % settings.page.label.set.size];
+}
 
+function getPrintLabelStyle(index, settings) {
+    return index > 0 && (index % settings.page.label.set.size === 0) ? settings.page.label.gap.size : "";
+}

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

Summary of changes:
 Open-ILS/src/sql/Pg/002.schema.config.sql          |   2 +-
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |  24 +-
 ...1153.data.update_item_label_org_unit_labels.sql |  96 ++++
 .../src/templates/staff/cat/printlabels/t_view.tt2 | 298 +++++++----
 Open-ILS/src/templates/staff/css/cat.css.tt2       |  64 +++
 .../staff/share/print_templates/t_item_label.tt2   |   7 +
 .../web/js/ui/default/staff/cat/printlabels/app.js | 562 ++++++++++++++++-----
 7 files changed, 810 insertions(+), 243 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/1153.data.update_item_label_org_unit_labels.sql


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list