From a4411d6aedfc2134b85af40f9580375a06ffc1b6 Mon Sep 17 00:00:00 2001 From: Adam Bowling Date: Wed, 17 Oct 2018 16:15:44 -0400 Subject: [PATCH] LP#1787479: Custom label printing toolbox Signed-off-by: Adam Bowling Signed-off-by: Chris Sharp Conflicts: Open-ILS/src/templates/staff/cat/printlabels/t_view.tt2 --- .../staff/cat/printlabels/t_view.tt2 | 382 +++++++++---- .../ui/default/staff/cat/printlabels/app.js | 529 +++++++++++++----- 2 files changed, 664 insertions(+), 247 deletions(-) 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 @@ -
-
- [% l('Print Item Labels') %] -
-
+

[% l('Print Item Labels') %]

-
-
-
- [% l('Template') %] - -
-
-
- -
-
-
- [% l('Printer') %] - +
+
+
+
+ [% l('Template') %] +
+
+ +
+
+ +
+
+ [% l('Printer') %] +
+
+ +
-
-
@@ -53,14 +105,14 @@ - -
+
-
@@ -69,92 +121,196 @@
-
- -
-
-
-

- [% l('Call Number Preview') %] -

-
-

+

-
[% l('Changes here will wipe out manual changes in the Call Numbers tab.') %]
- -
-
+ +
  • + + [% l('Call Numbers') %] + +
  • +
  • + + [% l('Settings') %] + +
  • +
  • + + [% l('Label Template') %] + +
  • + +
    +
    +
    +

    + [% l('Call Number Preview') %] +

    +
    -
    -
    -
    -

    - [% l('Formatted Call Numbers') %] -

    -
    [% l('Manual adjustments may be made here. These do not get saved with templates.') %]
    -
    - +
    +
    +
    -
    -
    -
    [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]
    -
    -
    -
    -
    +
    +

    + [% l('Formatted Call Numbers') %] +

    +
    + + [% l('Manual adjustments may be made here. These do not get saved with templates.') %]
    +
    +
    +
    +
    - -
    -
    -
    - [% l( - "Unable to load template '[_1]'. The web server returned an error.", - '{{print.template_name}}') - %] +
    +
    + + *All settings will be saved with templates + +
    + +
    + + [% l('These settings do get saved with templates and will override corresponding Library Settings.') %]
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    -
    - +
    +
    + [% l( + "Unable to load template '[_1]'. The web server returned an error.", + '{{print.template_name}}') + %] +
    +
    + +
    -
    -
    -

    - [% l('Label Preview') %] -

    -
    -
    +
    +

    + [% l('Label Preview') %] +

    +
    +
    +
    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 = " 0 && toolbox_settings.feed_option.selected === 'sheet' ? ' page-break' : ''}}\" ng-init=\"parentIndex = $index\" ng-repeat=\"row in label_output_copies\">\n"; + table += "\n"; + table += "\n" + table += "\n"; + table += "
    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 += "
    \n";
    +            table += "{{col.c ? get_cn_for(col.c) : ''}}";
    +            table += "
    \n"; + table += "
     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 += "
    \n"; + table += "
    "; + var comments = html.match(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g); + html = html.replace(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g, ""); + var style = html.match(/\]*\>(?:(?!\<\/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 = "
    \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 : ""; +} -- 2.43.2