-/**\r
- * Vol/Copy Editor\r
- */\r
-\r
-angular.module('egPrintLabels',\r
- ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])\r
-\r
-.config(function ($routeProvider, $locationProvider, $compileProvider) {\r
- $locationProvider.html5Mode(true);\r
- $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export\r
-\r
- var resolver = {\r
- delay: ['egStartup', function (egStartup) { return egStartup.go(); }]\r
- };\r
-\r
- $routeProvider.when('/cat/printlabels/:dataKey', {\r
- templateUrl: './cat/printlabels/t_view',\r
- controller: 'LabelCtrl',\r
- resolve: resolver\r
- });\r
-\r
-})\r
-\r
-.factory('itemSvc',\r
- ['egCore',\r
-function (egCore) {\r
-\r
- var service = {\r
- copies: [], // copy barcode search results\r
- index: 0 // search grid index\r
- };\r
-\r
- service.flesh = {\r
- flesh: 3,\r
- flesh_fields: {\r
- acp: ['call_number', 'location', 'status', 'location', 'floating', 'circ_modifier', 'age_protect'],\r
- acn: ['record', 'prefix', 'suffix'],\r
- bre: ['simple_record', 'creator', 'editor']\r
- },\r
- select: {\r
- // avoid fleshing MARC on the bre\r
- // note: don't add simple_record.. not sure why\r
- bre: ['id', 'tcn_value', 'creator', 'editor'],\r
- }\r
- }\r
-\r
- // resolved with the last received copy\r
- service.fetch = function (barcode, id, noListDupes) {\r
- var promise;\r
-\r
- if (barcode) {\r
- promise = egCore.pcrud.search('acp',\r
- { barcode: barcode, deleted: 'f' }, service.flesh);\r
- } else {\r
- promise = egCore.pcrud.retrieve('acp', id, service.flesh);\r
- }\r
-\r
- var lastRes;\r
- return promise.then(\r
- function () { return lastRes },\r
- null, // error\r
-\r
- // notify reads the stream of copies, one at a time.\r
- function (copy) {\r
-\r
- var flatCopy;\r
- if (noListDupes) {\r
- // use the existing copy if possible\r
- flatCopy = service.copies.filter(\r
- function (c) { return c.id == copy.id() })[0];\r
- }\r
-\r
- if (!flatCopy) {\r
- flatCopy = egCore.idl.toHash(copy, true);\r
- flatCopy.index = service.index++;\r
- service.copies.unshift(flatCopy);\r
- }\r
-\r
- return lastRes = {\r
- copy: copy,\r
- index: flatCopy.index\r
- }\r
- }\r
- );\r
- }\r
-\r
- return service;\r
-}])\r
-\r
-/**\r
- * Label controller!\r
- */\r
-.controller('LabelCtrl',\r
- ['$scope', '$q', '$window', '$routeParams', '$location', '$timeout', 'egCore', 'egNet', 'ngToast', 'itemSvc', 'labelOutputRowsFilter',\r
-function ($scope, $q, $window, $routeParams, $location, $timeout, egCore, egNet, ngToast, itemSvc, labelOutputRowsFilter) {\r
-\r
- var dataKey = $routeParams.dataKey;\r
- console.debug('dataKey: ' + dataKey);\r
-\r
- $scope.print = {\r
- template_name: 'item_label',\r
- template_output: '',\r
- template_context: 'default'\r
- };\r
-\r
- var toolbox_settings = {\r
- feed_option: {\r
- options: [\r
- { label: "Continuous", value: "continuous" },\r
- { label: "Sheet", value: "sheet" },\r
- ],\r
- selected: "continuous"\r
- },\r
- label_set: {\r
- margin_between: 0,\r
- size: 1\r
- },\r
- mode: {\r
- options: [\r
- { label: "Label 1 Only", value: "spine-only" },\r
- { label: "Labels 1 & 2", value: "spine-pocket" }\r
- ],\r
- selected: "spine-pocket"\r
- },\r
- page: {\r
- column_class: ["spine"],\r
- dimensions: {\r
- columns: 2,\r
- rows: 1\r
- },\r
- label: {\r
- gap: {\r
- size: 0\r
- },\r
- set: {\r
- size: 2\r
- }\r
- },\r
- margins: {\r
- top: { size: 0, label: "Top" },\r
- left: { size: 0, label: "Left" },\r
- },\r
- space_between_labels: {\r
- horizontal: { size: 0, label: "Horizontal" },\r
- vertical: { size: 0, label: "Vertical" }\r
- },\r
- start_position: {\r
- column: 1,\r
- row: 1\r
- }\r
- }\r
- };\r
-\r
- if (dataKey && dataKey.length > 0) {\r
-\r
- egNet.request(\r
- 'open-ils.actor',\r
- 'open-ils.actor.anon_cache.get_value',\r
- dataKey, 'print-labels-these-copies'\r
- ).then(function (data) {\r
-\r
- if (data) {\r
-\r
- $scope.preview_scope = {\r
- 'copies': []\r
- , 'settings': {}\r
- , 'toolbox_settings': toolbox_settings\r
- , 'get_cn_for': function (copy) {\r
- var key = $scope.rendered_cn_key_by_copy_id[copy.id];\r
- if (key) {\r
- var manual_cn = $scope.rendered_call_number_set[key];\r
- if (manual_cn && manual_cn.value) {\r
- return manual_cn.value;\r
- } else {\r
- return '..';\r
- }\r
- } else {\r
- return '...';\r
- }\r
- }\r
- , 'get_bib_for': function (copy) {\r
- return $scope.record_details[copy['call_number.record.id']];\r
- }\r
- , 'get_cn_prefix': function (copy) {\r
- return copy['call_number.prefix.label'];\r
- }\r
- , 'get_cn_suffix': function (copy) {\r
- return copy['call_number.suffix.label'];\r
- }\r
- , 'get_location_prefix': function (copy) {\r
- return copy['location.label_prefix'];\r
- }\r
- , 'get_location_suffix': function (copy) {\r
- return copy['location.label_suffix'];\r
- }\r
- , 'get_cn_and_location_prefix': function (copy, separator) {\r
- var acpl_prefix = copy['location.label_prefix'] || '';\r
- var cn_prefix = copy['call_number.prefix.label'] || '';\r
- var prefix = acpl_prefix + ' ' + cn_prefix;\r
- prefix = prefix.trim();\r
- if (separator && prefix != '') { prefix += separator; }\r
- return prefix;\r
- }\r
- , 'get_cn_and_location_suffix': function (copy, separator) {\r
- var acpl_suffix = copy['location.label_suffix'] || '';\r
- var cn_suffix = copy['call_number.suffix.label'] || '';\r
- var suffix = cn_suffix + ' ' + acpl_suffix;\r
- suffix = suffix.trim();\r
- if (separator && suffix != '') { suffix = separator + suffix; }\r
- return suffix;\r
- }\r
- , 'valid_print_label_start_column': function () {\r
- 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);\r
- }\r
- , 'valid_print_label_start_row': function () {\r
- 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);\r
- }\r
- };\r
- $scope.record_details = {};\r
- $scope.org_unit_settings = {};\r
-\r
- var promises = [];\r
- $scope.org_unit_setting_list = [\r
- 'webstaff.cat.label.font.family'\r
- , 'webstaff.cat.label.font.size'\r
- , 'webstaff.cat.label.font.weight'\r
- , 'webstaff.cat.label.inline_css'\r
- , 'webstaff.cat.label.left_label.height'\r
- , 'webstaff.cat.label.left_label.left_margin'\r
- , 'webstaff.cat.label.left_label.width'\r
- , 'webstaff.cat.label.right_label.height'\r
- , 'webstaff.cat.label.right_label.left_margin'\r
- , 'webstaff.cat.label.right_label.width'\r
- , 'webstaff.cat.label.call_number_wrap_filter_height'\r
- , 'webstaff.cat.label.call_number_wrap_filter_width'\r
- ];\r
-\r
- promises.push(\r
- egCore.pcrud.search('coust', { name: $scope.org_unit_setting_list }).then(\r
- null\r
- , null\r
- , function (yaous) {\r
- $scope.org_unit_settings[yaous.name()] = egCore.idl.toHash(yaous, true);\r
- }\r
- )\r
- );\r
-\r
- promises.push(\r
- egCore.org.settings($scope.org_unit_setting_list).then(function (res) {\r
- $scope.preview_scope.settings = res;\r
- egCore.hatch.getItem('cat.printlabels.last_settings').then(function (last_settings) {\r
- if (last_settings) {\r
- for (s in last_settings) {\r
- $scope.preview_scope.settings[s] = last_settings[s];\r
- }\r
- }\r
- });\r
- })\r
- );\r
-\r
- angular.forEach(data.copies, function (copy) {\r
- promises.push(\r
- itemSvc.fetch(null, copy).then(function (res) {\r
- var flat_copy = egCore.idl.toHash(res.copy, true);\r
- $scope.preview_scope.copies.push(flat_copy);\r
- $scope.record_details[flat_copy['call_number.record.id']] = 1;\r
- })\r
- )\r
- });\r
-\r
- $q.all(promises).then(function () {\r
-\r
- var promises2 = [];\r
- angular.forEach($scope.record_details, function (el, k, obj) {\r
- promises2.push(\r
- egNet.request(\r
- 'open-ils.search',\r
- 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',\r
- k\r
- ).then(function (data) {\r
- obj[k] = egCore.idl.toHash(data, true);\r
- })\r
- );\r
- });\r
-\r
- $q.all(promises2).then(function () {\r
- // today, staff, current_location, etc.\r
- egCore.print.fleshPrintScope($scope.preview_scope);\r
- $scope.template_changed(); // load the default\r
- $scope.rebuild_cn_set();\r
- });\r
-\r
- });\r
- } else {\r
- ngToast.danger(egCore.strings.KEY_EXPIRED);\r
- }\r
-\r
- });\r
-\r
- }\r
-\r
- $scope.fetchTemplates = function (set_default) {\r
- return egCore.hatch.getItem('cat.printlabels.templates').then(function (t) {\r
- if (t) {\r
- $scope.templates = t;\r
- $scope.template_name_list = Object.keys(t);\r
- if (set_default) {\r
- egCore.hatch.getItem('cat.printlabels.default_template').then(function (d) {\r
- if ($scope.template_name_list.indexOf(d, 0) > -1) {\r
- $scope.template_name = d;\r
- }\r
- });\r
- }\r
- }\r
- });\r
- }\r
- $scope.fetchTemplates(true);\r
-\r
- $scope.applyTemplate = function (n) {\r
- $scope.print.cn_template_content = $scope.templates[n].cn_content;\r
- $scope.print.template_content = $scope.templates[n].content;\r
- $scope.print.template_context = $scope.templates[n].context;\r
- for (var s in $scope.templates[n].settings) {\r
- $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];\r
- }\r
- if ($scope.templates[n].toolbox_settings) {\r
- $scope.preview_scope.toolbox_settings = $scope.templates[n].toolbox_settings;\r
- $scope.create_print_label_table();\r
- }\r
- egCore.hatch.setItem('cat.printlabels.default_template', n);\r
- $scope.save_locally();\r
- }\r
-\r
- $scope.deleteTemplate = function (n) {\r
- if (n) {\r
- delete $scope.templates[n]\r
- $scope.template_name_list = Object.keys($scope.templates);\r
- $scope.template_name = '';\r
- egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);\r
- $scope.fetchTemplates();\r
- ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_DELETE);\r
- egCore.hatch.getItem('cat.printlabels.default_template').then(function (d) {\r
- if (d && d == n) {\r
- egCore.hatch.removeItem('cat.printlabels.default_template');\r
- }\r
- });\r
- }\r
- }\r
-\r
- $scope.saveTemplate = function (n) {\r
- if (n) {\r
-\r
- $scope.templates[n] = {\r
- content: $scope.print.template_content\r
- , context: $scope.print.template_context\r
- , cn_content: $scope.print.cn_template_content\r
- , settings: $scope.preview_scope.settings\r
- , toolbox_settings: $scope.preview_scope.toolbox_settings\r
- };\r
- $scope.template_name_list = Object.keys($scope.templates);\r
-\r
- egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);\r
- $scope.fetchTemplates();\r
-\r
- $scope.dirty = false;\r
- } else {\r
- // save all templates, as we might do after an import\r
- egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);\r
- $scope.fetchTemplates();\r
- }\r
- ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_SAVE);\r
- }\r
-\r
- $scope.templates = {};\r
- $scope.imported_templates = { data: '' };\r
- $scope.template_name = '';\r
- $scope.template_name_list = [];\r
-\r
- $scope.print_labels = function () {\r
- return egCore.print.print({\r
- context: $scope.print.template_context,\r
- template: $scope.print.template_name,\r
- scope: $scope.preview_scope,\r
- });\r
- }\r
-\r
- $scope.template_changed = function () {\r
- $scope.print.load_failed = false;\r
- egCore.print.getPrintTemplate('item_label')\r
- .then(\r
- function (html) {\r
- $scope.print.template_content = html;\r
- },\r
- function () {\r
- $scope.print.template_content = '';\r
- $scope.print.load_failed = true;\r
- }\r
- );\r
- egCore.print.getPrintTemplateContext('item_label')\r
- .then(function (template_context) {\r
- $scope.print.template_context = template_context;\r
- });\r
- egCore.print.getPrintTemplate('item_label_cn')\r
- .then(\r
- function (html) {\r
- $scope.print.cn_template_content = html;\r
- },\r
- function () {\r
- $scope.print.cn_template_content = '';\r
- $scope.print.load_failed = true;\r
- }\r
- );\r
- egCore.hatch.getItem('cat.printlabels.last_settings').then(function (s) {\r
- if (s) {\r
- $scope.preview_scope.settings = s;\r
- }\r
- });\r
- }\r
-\r
- $scope.reset_to_default = function () {\r
- egCore.print.removePrintTemplate(\r
- 'item_label'\r
- );\r
- egCore.print.removePrintTemplateContext(\r
- 'item_label'\r
- );\r
- egCore.print.removePrintTemplate(\r
- 'item_label_cn'\r
- );\r
- egCore.hatch.removeItem('cat.printlabels.last_settings');\r
- for (s in $scope.preview_scope.settings) {\r
- $scope.preview_scope.settings[s] = undefined;\r
- }\r
- $scope.preview_scope.settings = {};\r
- egCore.org.settings($scope.org_unit_setting_list).then(function (res) {\r
- $scope.preview_scope.settings = res;\r
- });\r
-\r
- $scope.template_changed();\r
- }\r
-\r
- $scope.save_locally = function () {\r
- egCore.print.storePrintTemplate(\r
- 'item_label',\r
- $scope.print.template_content\r
- );\r
- egCore.print.storePrintTemplateContext(\r
- 'item_label',\r
- $scope.print.template_context\r
- );\r
- egCore.print.storePrintTemplate(\r
- 'item_label_cn',\r
- $scope.print.cn_template_content\r
- );\r
- egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);\r
- }\r
-\r
- $scope.imported_print_templates = { data: '' };\r
- $scope.$watch('imported_templates.data', function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- try {\r
- var data = JSON.parse(newVal);\r
- angular.forEach(data, function (el, k) {\r
- $scope.templates[k] = {\r
- content: el.content\r
- , context: el.context\r
- , cn_content: el.cn_content\r
- , settings: el.settings\r
- , toolbox_settings: el.toolbox_settings\r
- };\r
- });\r
- $scope.saveTemplate();\r
- $scope.template_changed(); // refresh\r
- ngToast.create(egCore.strings.PRINT_TEMPLATES_SUCCESS_IMPORT);\r
- } catch (E) {\r
- ngToast.warning(egCore.strings.PRINT_TEMPLATES_FAIL_IMPORT);\r
- }\r
- }\r
- });\r
-\r
- $scope.rendered_call_number_set = {};\r
- $scope.rendered_cn_key_by_copy_id = {};\r
- $scope.rebuild_cn_set = function () {\r
- $timeout(function () {\r
- $scope.rendered_call_number_set = {};\r
- $scope.rendered_cn_key_by_copy_id = {};\r
- for (var i = 0; i < $scope.preview_scope.copies.length; i++) {\r
- var copy = $scope.preview_scope.copies[i];\r
- var rendered_cn = document.getElementById('cn_for_copy_' + copy.id);\r
- if (rendered_cn && rendered_cn.textContent) {\r
- var key = rendered_cn.textContent;\r
- if (typeof $scope.rendered_call_number_set[key] == 'undefined') {\r
- $scope.rendered_call_number_set[key] = {\r
- value: key\r
- };\r
- }\r
- $scope.rendered_cn_key_by_copy_id[copy.id] = key;\r
- }\r
- }\r
- $scope.preview_scope.tickle = Date() + ' ' + Math.random();\r
- });\r
- }\r
-\r
- $scope.create_print_label_table = function () {\r
- if ($scope.print_label_form.$valid && $scope.print.template_content && $scope.preview_scope) {\r
- $scope.preview_scope.label_output_copies = labelOutputRowsFilter($scope.preview_scope.copies, $scope.preview_scope.toolbox_settings);\r
- var html = $scope.print.template_content;\r
- 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.\r
- 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";\r
- table += "<tr>\n";\r
- 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";\r
- table += "<pre class=\"{{col.cls}}\" style=\"border: none; margin-bottom: 0; margin-top: 0; overflow: hidden;\" ng-if=\"col.cls === 'spine'\">\n";\r
- table += "{{col.c ? get_cn_for(col.c) : ''}}";\r
- table += "</pre>\n";\r
- 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";\r
- table += "{{col.c ? col.c.barcode : ''}}\n";\r
- table += "{{col.c ? col.c['call_number.label'] : ''}}\n";\r
- table += "{{col.c ? get_bib_for(col.c).author : ''}}\n";\r
- table += "{{col.c ? (get_bib_for(col.c).title | wrap:28:'once':' ') : ''}}\n";\r
- table += "</pre>\n";\r
- table += "</td>\n"\r
- table += "</tr>\n";\r
- table += "</table>";\r
- var comments = html.match(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g);\r
- html = html.replace(/\<\!\-\-(?:(?!\-\-\>)(?:.|\s))*\-\-\>\s*/g, "");\r
- var style = html.match(/\<style[^\>]*\>(?:(?!\<\/style\>)(?:.|\s))*\<\/style\>\s*/gi);\r
- var output = (style ? style.join("\n") : "") + (comments ? comments.join("\n") : "") + table;\r
- output = output.replace(/\n+/, "\n");\r
- $scope.print.template_content = output;\r
- }\r
- }\r
-\r
- $scope.redraw_label_table = function () {\r
- 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.\r
- var table = "<table id=\"eg_plt_" + d.getTime().toString() + "\"\></table>\n";\r
- $scope.print.template_content += table;\r
- $scope.create_print_label_table();\r
- }\r
-\r
- $scope.$watch('preview_scope.toolbox_settings.page.dimensions.columns',\r
- function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal && $scope.preview_scope) {\r
- $scope.redraw_label_table();\r
- }\r
- }\r
- );\r
-\r
- $scope.$watch('print.cn_template_content', function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- $scope.rebuild_cn_set();\r
- }\r
- });\r
-\r
- $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- $scope.rebuild_cn_set();\r
- }\r
- });\r
-\r
- $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- $scope.rebuild_cn_set();\r
- }\r
- });\r
-\r
- $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) {\r
- if (newVal && newVal != oldVal && $scope.preview_scope.label_output_copies) {\r
- $scope.redraw_label_table();\r
- }\r
- });\r
-\r
- $scope.$watch("preview_scope.toolbox_settings.mode.selected", function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- var ts_p = $scope.preview_scope.toolbox_settings.page;\r
- if (ts_p.label.set.size === 1) {\r
- if (newVal === "spine-pocket") {\r
- ts_p.column_class = ["spine", "pocket"];\r
- ts_p.label.set.size = 2;\r
- } else {\r
- ts_p.column_class = ["spine"];\r
- }\r
- } else {\r
- if (newVal === "spine-only") {\r
- for (var i = 0; i < ts_p.label.set.size; i++) {\r
- ts_p.column_class[i] = "spine";\r
- }\r
- } else {\r
- ts_p.label.set.size === 2 ? ts_p.column_class = ["spine", "pocket"] : false;\r
- }\r
- }\r
- $scope.redraw_label_table();\r
- }\r
- });\r
-\r
- $scope.$watch("preview_scope.toolbox_settings.page.label.set.size", function (newVal, oldVal) {\r
- if (newVal && newVal != oldVal) {\r
- var ts_p = $scope.preview_scope.toolbox_settings.page;\r
- if (angular.isNumber(newVal)) {\r
- while (ts_p.column_class.length > ts_p.label.set.size) {\r
- ts_p.column_class.splice((ts_p.column_class.length - 1), 1);\r
- }\r
- while (ts_p.column_class.length < ts_p.label.set.size) {\r
- ts_p.column_class.push("spine");\r
- }\r
- }\r
- $scope.redraw_label_table();\r
- }\r
- });\r
-\r
- $scope.current_tab = 'call_numbers';\r
- $scope.set_tab = function (tab) {\r
- $scope.current_tab = tab;\r
- }\r
-\r
-}])\r
-\r
-.directive("egPrintLabelColumnBounds", function () {\r
- return {\r
- link: function (scope, element, attr, ctrl) {\r
- function withinBounds(v) {\r
- scope.$watch("preview_scope.toolbox_settings.page.dimensions.columns", function (newVal, oldVal) {\r
- ctrl.$setValidity("egWithinPrintColumnBounds", scope.preview_scope.valid_print_label_start_column())\r
- });\r
- return v;\r
- }\r
- ctrl.$parsers.push(withinBounds);\r
- ctrl.$formatters.push(withinBounds);\r
- },\r
- require: "ngModel"\r
- }\r
-})\r
-\r
-.directive("egPrintLabelRowBounds", function () {\r
- return {\r
- link: function (scope, element, attr, ctrl) {\r
- function withinBounds(v) {\r
- scope.$watch("preview_scope.toolbox_settings.page.dimensions.rows", function (newVal, oldVal) {\r
- ctrl.$setValidity("egWithinPrintRowBounds", scope.preview_scope.valid_print_label_start_row());\r
- });\r
- return v;\r
- }\r
- ctrl.$parsers.push(withinBounds);\r
- ctrl.$formatters.push(withinBounds);\r
- },\r
- require: "ngModel"\r
- }\r
-})\r
-\r
-.directive("egPrintLabelValidCss", function () {\r
- return {\r
- require: "ngModel",\r
- link: function (scope, element, attr, ctrl) {\r
- function floatValidation(v) {\r
- ctrl.$setValidity("isFloat", v.toString().match(/^\-*(?:^0$|(?:\d+)(?:\.\d{1,})*([a-z]{2}))$/) ? true : false);\r
- return v;\r
- }\r
- ctrl.$parsers.push(floatValidation);\r
- }\r
- }\r
-})\r
-\r
-.directive("egPrintLabelValidInt", function () {\r
- return {\r
- require: "ngModel",\r
- link: function (scope, element, attr, ctrl) {\r
- function intValidation(v) {\r
- ctrl.$setValidity("isInteger", v.toString().match(/^\d+$/));\r
- return v;\r
- }\r
- ctrl.$parsers.push(intValidation);\r
- }\r
- }\r
-})\r
-\r
-.directive('egPrintTemplateOutput', ['$compile', function ($compile) {\r
- return function (scope, element, attrs) {\r
- scope.$watch(\r
- function (scope) {\r
- return scope.$eval(attrs.content);\r
- },\r
- function (value) {\r
- // create an isolate scope and copy the print context\r
- // data into the new scope.\r
- // TODO: see also print security concerns in egHatch\r
- var result = element.html(value);\r
- var context = scope.$eval(attrs.context);\r
- var print_scope = scope.$new(true);\r
- angular.forEach(context, function (val, key) {\r
- print_scope[key] = val;\r
- })\r
- $compile(element.contents())(print_scope);\r
- }\r
- );\r
- };\r
-}])\r
-\r
-.filter('cn_wrap', function () {\r
- return function (input, w, h, wrap_type) {\r
- var names;\r
- var prefix = input[0];\r
- var callnum = input[1];\r
- var suffix = input[2];\r
-\r
- if (!w) { w = 8; }\r
- if (!h) { h = 9; }\r
-\r
- /* handle spine labels differently if using LC */\r
- if (wrap_type == 'lc' || wrap_type == 3) {\r
- /* Establish a pattern where every return value should be isolated on its own line \r
- on the spine label: subclass letters, subclass numbers, cutter numbers, trailing stuff (date) */\r
- 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;\r
- var result = callnum.match(patt1);\r
- if (result) {\r
- callnum = result.slice(1).join('\t');\r
- } else {\r
- callnum = callnum.split(/\s+/).join('\t');\r
- }\r
-\r
- /* If result is null, leave callnum alone. Can't parse this malformed call num */\r
- } else {\r
- callnum = callnum.split(/\s+/).join('\t');\r
- }\r
-\r
- if (prefix) {\r
- callnum = prefix + '\t' + callnum;\r
- }\r
- if (suffix) {\r
- callnum += '\t' + suffix;\r
- }\r
-\r
- /* At this point, the call number pieces are separated by tab characters. This allows\r
- * some space-containing constructs like "v. 1" to appear on one line\r
- */\r
- callnum = callnum.replace(/\t\t/g, '\t'); /* Squeeze out empties */\r
- names = callnum.split('\t');\r
- var j = 0; var tb = [];\r
- while (j < h) {\r
-\r
- /* spine */\r
- if (j < w) {\r
-\r
- var name = names.shift();\r
- if (name) {\r
- name = String(name);\r
-\r
- /* if the name is greater than the label width... */\r
- if (name.length > w) {\r
- /* then try to split it on periods */\r
- var sname = name.split(/\./);\r
- if (sname.length > 1) {\r
- /* if we can, then put the periods back in on each splitted element */\r
- if (name.match(/^\./)) sname[0] = '.' + sname[0];\r
- for (var k = 1; k < sname.length; k++) sname[k] = '.' + sname[k];\r
- /* and put all but the first one back into the names array */\r
- names = sname.slice(1).concat(names);\r
- /* if the name fragment is still greater than the label width... */\r
- if (sname[0].length > w) {\r
- /* then just truncate and throw the rest back into the names array */\r
- tb[j] = sname[0].substr(0, w);\r
- names = [sname[0].substr(w)].concat(names);\r
- } else {\r
- /* otherwise we're set */\r
- tb[j] = sname[0];\r
- }\r
- } else {\r
- /* if we can't split on periods, then just truncate and throw the rest back into the names array */\r
- tb[j] = name.substr(0, w);\r
- names = [name.substr(w)].concat(names);\r
- }\r
- } else {\r
- /* otherwise we're set */\r
- tb[j] = name;\r
- }\r
- }\r
- }\r
- j++;\r
- }\r
- return tb.join('\n');\r
- }\r
-})\r
-\r
-.filter("columnRowRange", function () {\r
- return function (i) {\r
- var res = [];\r
- for (var j = 0; j < i; j++) {\r
- res.push(j);\r
- }\r
- return res;\r
- }\r
-})\r
-\r
-//Accepts $scope.preview_scope.copies and $scope.preview_scope.toolbox_settings as its parameters.\r
-.filter("labelOutputRows", function () {\r
- return function (copies, settings) {\r
- var cols = [], rows = [];\r
- for (var j = 0; j < (settings.page.start_position.row - 1) ; j++) {\r
- cols = [];\r
- for (var k = 0; k < settings.page.dimensions.columns; k++) {\r
- cols.push({ c: null, index: k, cls: getPrintLabelOutputClass(k, settings), styl: getPrintLabelStyle(k, settings) });\r
- }\r
- rows.push({ columns: cols });\r
- }\r
- cols = [];\r
- for (var j = 0; j < (settings.page.start_position.column - 1) ; j++) {\r
- cols.push({ c: null, index: j, cls: getPrintLabelOutputClass(j, settings), styl: getPrintLabelStyle(j, settings) });\r
- }\r
- var m = cols.length;\r
- for (var j = 0; j < copies.length; j++) {\r
- for (var n = 0; n < settings.page.label.set.size; n++) {\r
- if (m < settings.page.dimensions.columns) {\r
- cols.push({ c: copies[j], index: cols.length, cls: getPrintLabelOutputClass(m, settings), styl: getPrintLabelStyle(m, settings) });\r
- m += 1;\r
- }\r
- if (m === settings.page.dimensions.columns) {\r
- m = 0;\r
- rows.push({ columns: cols });\r
- cols = [];\r
- n = settings.page.label.set.size;\r
- }\r
- }\r
- }\r
- cols.length > 0 ? rows.push({ columns: cols }) : false;\r
- if (rows.length > 0) {\r
- while ((rows[(rows.length - 1)].columns.length) < settings.page.dimensions.columns) {\r
- 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) });\r
- }\r
- }\r
- return rows;\r
- }\r
-})\r
-\r
-.filter('wrap', function () {\r
- return function (input, w, wrap_type, indent) {\r
- var output;\r
-\r
- if (!w) return input;\r
- if (!indent) indent = '';\r
-\r
- function wrap_on_space(\r
- text,\r
- length,\r
- wrap_just_once,\r
- if_cant_wrap_then_truncate,\r
- idx\r
- ) {\r
- if (idx > 10) {\r
- console.log('possible infinite recursion, aborting');\r
- return '';\r
- }\r
- if (String(text).length <= length) {\r
- return text;\r
- } else {\r
- var truncated_text = String(text).substr(0, length);\r
- var pivot_pos = truncated_text.lastIndexOf(' ');\r
- var left_chunk = text.substr(0, pivot_pos).replace(/\s*$/, '');\r
- var right_chunk = String(text).substr(pivot_pos + 1);\r
-\r
- var wrapped_line;\r
- if (left_chunk.length == 0) {\r
- if (if_cant_wrap_then_truncate) {\r
- wrapped_line = truncated_text;\r
- } else {\r
- wrapped_line = text;\r
- }\r
- } else {\r
- wrapped_line =\r
- left_chunk + '\n'\r
- + indent + (\r
- wrap_just_once\r
- ? right_chunk\r
- : (\r
- right_chunk.length > length\r
- ? wrap_on_space(\r
- right_chunk,\r
- length,\r
- false,\r
- if_cant_wrap_then_truncate,\r
- idx + 1)\r
- : right_chunk\r
- )\r
- )\r
- ;\r
- }\r
- return wrapped_line;\r
- }\r
- }\r
-\r
- switch (wrap_type) {\r
- case 'once':\r
- output = wrap_on_space(input, w, true, false, 0);\r
- break;\r
- default:\r
- output = wrap_on_space(input, w, false, false, 0);\r
- break;\r
- }\r
-\r
- return output;\r
- }\r
-});\r
-\r
-function getPrintLabelOutputClass(index, settings) {\r
- return settings.page.column_class[index % settings.page.label.set.size];\r
-}\r
-\r
-function getPrintLabelStyle(index, settings) {\r
- return index > 0 && (index % settings.page.label.set.size === 0) ? settings.page.label.gap.size : "";\r
+/**
+ * Vol/Copy Editor
+ */
+
+angular.module('egPrintLabels',
+ ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
+
+.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(); }]
+ };
+
+ $routeProvider.when('/cat/printlabels/:dataKey', {
+ templateUrl: './cat/printlabels/t_view',
+ controller: 'LabelCtrl',
+ resolve: resolver
+ });
+
+})
+
+.factory('itemSvc',
+ ['egCore',
+function (egCore) {
+
+ var service = {
+ 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'],
+ acn: ['record', 'prefix', 'suffix'],
+ bre: ['simple_record', 'creator', 'editor']
+ },
+ select: {
+ // avoid fleshing MARC on the bre
+ // note: don't add simple_record.. not sure why
+ bre: ['id', 'tcn_value', 'creator', 'editor'],
+ }
+ }
+
+ // resolved with the last received copy
+ service.fetch = function (barcode, id, noListDupes) {
+ var promise;
+
+ if (barcode) {
+ 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 },
+ null, // error
+
+ // notify reads the stream of copies, one at a time.
+ function (copy) {
+
+ var flatCopy;
+ if (noListDupes) {
+ // use the existing copy if possible
+ flatCopy = service.copies.filter(
+ function (c) { return c.id == copy.id() })[0];
+ }
+
+ if (!flatCopy) {
+ flatCopy = egCore.idl.toHash(copy, true);
+ flatCopy.index = service.index++;
+ service.copies.unshift(flatCopy);
+ }
+
+ return lastRes = {
+ copy: copy,
+ index: flatCopy.index
+ }
+ }
+ );
+ }
+
+ return service;
+}])
+
+/**
+ * Label controller!
+ */
+.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'
+ };
+
+ 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) {
+
+ egNet.request(
+ 'open-ils.actor',
+ 'open-ils.actor.anon_cache.get_value',
+ dataKey, 'print-labels-these-copies'
+ ).then(function (data) {
+
+ if (data) {
+
+ $scope.preview_scope = {
+ '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];
+ if (manual_cn && manual_cn.value) {
+ return manual_cn.value;
+ } else {
+ return '..';
+ }
+ } else {
+ return '...';
+ }
+ }
+ , 'get_bib_for': function (copy) {
+ return $scope.record_details[copy['call_number.record.id']];
+ }
+ , 'get_cn_prefix': function (copy) {
+ return copy['call_number.prefix.label'];
+ }
+ , 'get_cn_suffix': function (copy) {
+ return copy['call_number.suffix.label'];
+ }
+ , 'get_location_prefix': function (copy) {
+ return copy['location.label_prefix'];
+ }
+ , 'get_location_suffix': function (copy) {
+ return copy['location.label_suffix'];
+ }
+ , '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;
+ prefix = prefix.trim();
+ if (separator && prefix != '') { prefix += separator; }
+ return prefix;
+ }
+ , '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;
+ suffix = suffix.trim();
+ 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 = {};
+
+ 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'
+ ];
+
+ promises.push(
+ egCore.pcrud.search('coust', { name: $scope.org_unit_setting_list }).then(
+ null
+ , 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) {
+ $scope.preview_scope.settings = res;
+ 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];
+ }
+ }
+ });
+ })
+ );
+
+ angular.forEach(data.copies, function (copy) {
+ promises.push(
+ 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;
+ })
+ )
+ });
+
+ $q.all(promises).then(function () {
+
+ var promises2 = [];
+ angular.forEach($scope.record_details, function (el, k, obj) {
+ promises2.push(
+ egNet.request(
+ 'open-ils.search',
+ 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
+ k
+ ).then(function (data) {
+ obj[k] = egCore.idl.toHash(data, true);
+ })
+ );
+ });
+
+ $q.all(promises2).then(function () {
+ // today, staff, current_location, etc.
+ egCore.print.fleshPrintScope($scope.preview_scope);
+ $scope.template_changed(); // load the default
+ $scope.rebuild_cn_set();
+ });
+
+ });
+ } else {
+ ngToast.danger(egCore.strings.KEY_EXPIRED);
+ }
+
+ });
+
+ }
+
+ $scope.fetchTemplates = function (set_default) {
+ 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) {
+ $scope.template_name = d;
+ }
+ });
+ }
+ }
+ });
+ }
+ $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();
+ }
+ egCore.hatch.setItem('cat.printlabels.default_template', n);
+ $scope.save_locally();
+ }
+
+ $scope.deleteTemplate = function (n) {
+ if (n) {
+ delete $scope.templates[n]
+ $scope.template_name_list = Object.keys($scope.templates);
+ $scope.template_name = '';
+ 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) {
+ if (d && d == n) {
+ egCore.hatch.removeItem('cat.printlabels.default_template');
+ }
+ });
+ }
+ }
+
+ $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
+ };
+ $scope.template_name_list = Object.keys($scope.templates);
+
+ egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
+ $scope.fetchTemplates();
+
+ $scope.dirty = false;
+ } else {
+ // save all templates, as we might do after an import
+ egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
+ $scope.fetchTemplates();
+ }
+ ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_SAVE);
+ }
+
+ $scope.templates = {};
+ $scope.imported_templates = { data: '' };
+ $scope.template_name = '';
+ $scope.template_name_list = [];
+
+ $scope.print_labels = function () {
+ return egCore.print.print({
+ context: $scope.print.template_context,
+ template: $scope.print.template_name,
+ scope: $scope.preview_scope,
+ });
+ }
+
+ $scope.template_changed = function () {
+ $scope.print.load_failed = false;
+ egCore.print.getPrintTemplate('item_label')
+ .then(
+ function (html) {
+ $scope.print.template_content = html;
+ },
+ function () {
+ $scope.print.template_content = '';
+ $scope.print.load_failed = true;
+ }
+ );
+ egCore.print.getPrintTemplateContext('item_label')
+ .then(function (template_context) {
+ $scope.print.template_context = template_context;
+ });
+ egCore.print.getPrintTemplate('item_label_cn')
+ .then(
+ function (html) {
+ $scope.print.cn_template_content = html;
+ },
+ function () {
+ $scope.print.cn_template_content = '';
+ $scope.print.load_failed = true;
+ }
+ );
+ egCore.hatch.getItem('cat.printlabels.last_settings').then(function (s) {
+ if (s) {
+ $scope.preview_scope.settings = s;
+ }
+ });
+ }
+
+ $scope.reset_to_default = function () {
+ egCore.print.removePrintTemplate(
+ 'item_label'
+ );
+ egCore.print.removePrintTemplateContext(
+ 'item_label'
+ );
+ egCore.print.removePrintTemplate(
+ 'item_label_cn'
+ );
+ egCore.hatch.removeItem('cat.printlabels.last_settings');
+ for (s in $scope.preview_scope.settings) {
+ $scope.preview_scope.settings[s] = undefined;
+ }
+ $scope.preview_scope.settings = {};
+ egCore.org.settings($scope.org_unit_setting_list).then(function (res) {
+ $scope.preview_scope.settings = res;
+ });
+
+ $scope.template_changed();
+ }
+
+ $scope.save_locally = function () {
+ egCore.print.storePrintTemplate(
+ 'item_label',
+ $scope.print.template_content
+ );
+ egCore.print.storePrintTemplateContext(
+ 'item_label',
+ $scope.print.template_context
+ );
+ egCore.print.storePrintTemplate(
+ 'item_label_cn',
+ $scope.print.cn_template_content
+ );
+ egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);
+ }
+
+ $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) {
+ $scope.templates[k] = {
+ content: el.content
+ , context: el.context
+ , cn_content: el.cn_content
+ , settings: el.settings
+ , toolbox_settings: el.toolbox_settings
+ };
+ });
+ $scope.saveTemplate();
+ $scope.template_changed(); // refresh
+ ngToast.create(egCore.strings.PRINT_TEMPLATES_SUCCESS_IMPORT);
+ } catch (E) {
+ ngToast.warning(egCore.strings.PRINT_TEMPLATES_FAIL_IMPORT);
+ }
+ }
+ });
+
+ $scope.rendered_call_number_set = {};
+ $scope.rendered_cn_key_by_copy_id = {};
+ $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);
+ 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
+ };
+ }
+ $scope.rendered_cn_key_by_copy_id[copy.id] = key;
+ }
+ }
+ $scope.preview_scope.tickle = Date() + ' ' + Math.random();
+ });
+ }
+
+ $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) {
+ 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();
+ }
+ });
+
+ $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.current_tab = tab;
+ }
+
+}])
+
+.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) {
+ return scope.$eval(attrs.content);
+ },
+ 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) {
+ print_scope[key] = val;
+ })
+ $compile(element.contents())(print_scope);
+ }
+ );
+ };
+}])
+
+.filter('cn_wrap', function () {
+ return function (input, w, h, wrap_type) {
+ var names;
+ var prefix = input[0];
+ var callnum = input[1];
+ var suffix = input[2];
+
+ if (!w) { w = 8; }
+ if (!h) { h = 9; }
+
+ /* handle spine labels differently if using LC */
+ if (wrap_type == 'lc' || wrap_type == 3) {
+ /* Establish a pattern where every return value should be isolated on its own line
+ 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');
+ } else {
+ callnum = callnum.split(/\s+/).join('\t');
+ }
+
+ /* If result is null, leave callnum alone. Can't parse this malformed call num */
+ } else {
+ callnum = callnum.split(/\s+/).join('\t');
+ }
+
+ if (prefix) {
+ callnum = prefix + '\t' + callnum;
+ }
+ if (suffix) {
+ callnum += '\t' + suffix;
+ }
+
+ /* 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 */
+ 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);
+
+ /* if the name is greater than the label width... */
+ if (name.length > w) {
+ /* then try to split it on periods */
+ var sname = name.split(/\./);
+ if (sname.length > 1) {
+ /* if we can, then put the periods back in on each splitted element */
+ 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);
+ /* 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);
+ } 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);
+ }
+ } else {
+ /* otherwise we're set */
+ tb[j] = name;
+ }
+ }
+ }
+ j++;
+ }
+ return tb.join('\n');
+ }
+})
+
+.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;
+ if (!indent) indent = '';
+
+ function wrap_on_space(
+ text,
+ length,
+ wrap_just_once,
+ if_cant_wrap_then_truncate,
+ idx
+ ) {
+ 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 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 wrapped_line;
+ if (left_chunk.length == 0) {
+ if (if_cant_wrap_then_truncate) {
+ wrapped_line = truncated_text;
+ } else {
+ wrapped_line = text;
+ }
+ } else {
+ wrapped_line =
+ left_chunk + '\n'
+ + indent + (
+ wrap_just_once
+ ? right_chunk
+ : (
+ right_chunk.length > length
+ ? wrap_on_space(
+ right_chunk,
+ length,
+ false,
+ if_cant_wrap_then_truncate,
+ idx + 1)
+ : right_chunk
+ )
+ )
+ ;
+ }
+ return wrapped_line;
+ }
+ }
+
+ switch (wrap_type) {
+ case 'once':
+ output = wrap_on_space(input, w, true, false, 0);
+ break;
+ default:
+ 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 : "";
}
\ No newline at end of file