5 angular.module('egPrintLabels',
6 ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
8 .config(function($routeProvider, $locationProvider, $compileProvider) {
9 $locationProvider.html5Mode(true);
10 $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
13 delay : ['egStartup', function(egStartup) { return egStartup.go(); }]
16 $routeProvider.when('/cat/printlabels/:dataKey', {
17 templateUrl: './cat/printlabels/t_view',
18 controller: 'LabelCtrl',
29 copies : [], // copy barcode search results
30 index : 0 // search grid index
36 acp : ['call_number','location','status','location','floating','circ_modifier','age_protect'],
37 acn : ['record','prefix','suffix'],
38 bre : ['simple_record','creator','editor']
41 // avoid fleshing MARC on the bre
42 // note: don't add simple_record.. not sure why
43 bre : ['id','tcn_value','creator','editor'],
47 // resolved with the last received copy
48 service.fetch = function(barcode, id, noListDupes) {
52 promise = egCore.pcrud.search('acp',
53 {barcode : barcode, deleted : 'f'}, service.flesh);
55 promise = egCore.pcrud.retrieve('acp', id, service.flesh);
60 function() {return lastRes},
63 // notify reads the stream of copies, one at a time.
68 // use the existing copy if possible
69 flatCopy = service.copies.filter(
70 function(c) {return c.id == copy.id()})[0];
74 flatCopy = egCore.idl.toHash(copy, true);
75 flatCopy.index = service.index++;
76 service.copies.unshift(flatCopy);
81 index : flatCopy.index
93 .controller('LabelCtrl',
94 ['$scope','$q','$window','$routeParams','$location','$timeout','egCore','egNet','ngToast','itemSvc',
95 function($scope , $q , $window , $routeParams , $location , $timeout , egCore , egNet , ngToast , itemSvc ) {
97 var dataKey = $routeParams.dataKey;
98 console.debug('dataKey: ' + dataKey);
101 template_name : 'item_label',
102 template_output : '',
103 template_context : 'default'
107 if (dataKey && dataKey.length > 0) {
111 'open-ils.actor.anon_cache.get_value',
112 dataKey, 'print-labels-these-copies'
113 ).then(function (data) {
117 $scope.preview_scope = {
120 ,'get_cn_for' : function(copy) {
121 var key = $scope.rendered_cn_key_by_copy_id[copy.id];
123 var manual_cn = $scope.rendered_call_number_set[key];
124 if (manual_cn && manual_cn.value) {
125 return manual_cn.value;
133 ,'get_bib_for' : function(copy) {
134 return $scope.record_details[copy['call_number.record.id']];
136 ,'get_cn_prefix' : function(copy) {
137 return copy['call_number.prefix.label'];
139 ,'get_cn_suffix' : function(copy) {
140 return copy['call_number.suffix.label'];
142 ,'get_location_prefix' : function(copy) {
143 return copy['location.label_prefix'];
145 ,'get_location_suffix' : function(copy) {
146 return copy['location.label_suffix'];
148 ,'get_cn_and_location_prefix' : function(copy,separator) {
149 var acpl_prefix = copy['location.label_prefix'] || '';
150 var cn_prefix = copy['call_number.prefix.label'] || '';
151 var prefix = acpl_prefix + ' ' + cn_prefix;
152 prefix = prefix.trim();
153 if (separator && prefix != '') { prefix += separator; }
156 ,'get_cn_and_location_suffix' : function(copy,separator) {
157 var acpl_suffix = copy['location.label_suffix'] || '';
158 var cn_suffix = copy['call_number.suffix.label'] || '';
159 var suffix = cn_suffix + ' ' + acpl_suffix;
160 suffix = suffix.trim();
161 if (separator && suffix != '') { suffix = separator + suffix; }
165 $scope.record_details = {};
166 $scope.org_unit_settings = {};
169 $scope.org_unit_setting_list = [
170 'webstaff.cat.label.font.family'
171 ,'webstaff.cat.label.font.size'
172 ,'webstaff.cat.label.font.weight'
173 ,'webstaff.cat.label.inline_css'
174 ,'webstaff.cat.label.left_label.height'
175 ,'webstaff.cat.label.left_label.left_margin'
176 ,'webstaff.cat.label.left_label.width'
177 ,'webstaff.cat.label.right_label.height'
178 ,'webstaff.cat.label.right_label.left_margin'
179 ,'webstaff.cat.label.right_label.width'
180 ,'webstaff.cat.label.call_number_wrap_filter_height'
181 ,'webstaff.cat.label.call_number_wrap_filter_width'
185 egCore.pcrud.search('coust',{name:$scope.org_unit_setting_list}).then(
189 $scope.org_unit_settings[yaous.name()] = egCore.idl.toHash(yaous, true);
195 egCore.org.settings($scope.org_unit_setting_list).then(function(res) {
196 $scope.preview_scope.settings = res;
197 egCore.hatch.getItem('cat.printlabels.last_settings').then(function(last_settings) {
199 for (s in last_settings) {
200 $scope.preview_scope.settings[s] = last_settings[s];
207 angular.forEach(data.copies, function(copy) {
209 itemSvc.fetch(null,copy).then(function(res) {
210 var flat_copy = egCore.idl.toHash(res.copy, true);
211 $scope.preview_scope.copies.push(flat_copy);
212 $scope.record_details[ flat_copy['call_number.record.id'] ] = 1;
217 $q.all(promises).then(function() {
220 angular.forEach($scope.record_details, function(el,k,obj) {
224 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
226 ).then(function (data) {
227 obj[k] = egCore.idl.toHash(data, true);
232 $q.all(promises2).then(function() {
233 // today, staff, current_location, etc.
234 egCore.print.fleshPrintScope($scope.preview_scope);
235 $scope.template_changed(); // load the default
236 $scope.rebuild_cn_set();
241 ngToast.danger(egCore.strings.KEY_EXPIRED);
248 $scope.fetchTemplates = function (set_default) {
249 return egCore.hatch.getItem('cat.printlabels.templates').then(function(t) {
251 $scope.templates = t;
252 $scope.template_name_list = Object.keys(t);
254 egCore.hatch.getItem('cat.printlabels.default_template').then(function(d) {
255 if ($scope.template_name_list.indexOf(d,0) > -1) {
256 $scope.template_name = d;
263 $scope.fetchTemplates(true);
265 $scope.applyTemplate = function (n) {
266 $scope.print.cn_template_content = $scope.templates[n].cn_content;
267 $scope.print.template_content = $scope.templates[n].content;
268 $scope.print.template_context = $scope.templates[n].context;
269 for (var s in $scope.templates[n].settings) {
270 $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
272 egCore.hatch.setItem('cat.printlabels.default_template', n);
273 $scope.save_locally();
276 $scope.deleteTemplate = function (n) {
278 delete $scope.templates[n]
279 $scope.template_name_list = Object.keys($scope.templates);
280 $scope.template_name = '';
281 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
282 $scope.fetchTemplates();
283 ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_DELETE);
284 egCore.hatch.getItem('cat.printlabels.default_template').then(function(d) {
286 egCore.hatch.removeItem('cat.printlabels.default_template');
292 $scope.saveTemplate = function (n) {
295 $scope.templates[n] = {
296 content : $scope.print.template_content
297 ,context : $scope.print.template_context
298 ,cn_content : $scope.print.cn_template_content
299 ,settings : $scope.preview_scope.settings
301 $scope.template_name_list = Object.keys($scope.templates);
303 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
304 $scope.fetchTemplates();
306 $scope.dirty = false;
308 // save all templates, as we might do after an import
309 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
310 $scope.fetchTemplates();
312 ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_SAVE);
315 $scope.templates = {};
316 $scope.imported_templates = { data : '' };
317 $scope.template_name = '';
318 $scope.template_name_list = [];
320 $scope.print_labels = function() {
321 return egCore.print.print({
322 context : $scope.print.template_context,
323 template : $scope.print.template_name,
324 scope : $scope.preview_scope,
328 $scope.template_changed = function() {
329 $scope.print.load_failed = false;
330 egCore.print.getPrintTemplate('item_label')
333 $scope.print.template_content = html;
336 $scope.print.template_content = '';
337 $scope.print.load_failed = true;
340 egCore.print.getPrintTemplateContext('item_label')
341 .then(function(template_context) {
342 $scope.print.template_context = template_context;
344 egCore.print.getPrintTemplate('item_label_cn')
347 $scope.print.cn_template_content = html;
350 $scope.print.cn_template_content = '';
351 $scope.print.load_failed = true;
354 egCore.hatch.getItem('cat.printlabels.last_settings').then(function(s) {
356 $scope.preview_scope.settings = s;
362 $scope.reset_to_default = function() {
363 egCore.print.removePrintTemplate(
366 egCore.print.removePrintTemplateContext(
369 egCore.print.removePrintTemplate(
372 egCore.hatch.removeItem('cat.printlabels.last_settings');
373 for (s in $scope.preview_scope.settings) {
374 $scope.preview_scope.settings[s] = undefined;
376 $scope.preview_scope.settings = {};
377 egCore.org.settings($scope.org_unit_setting_list).then(function(res) {
378 $scope.preview_scope.settings = res;
381 $scope.template_changed();
384 $scope.save_locally = function() {
385 egCore.print.storePrintTemplate(
387 $scope.print.template_content
389 egCore.print.storePrintTemplateContext(
391 $scope.print.template_context
393 egCore.print.storePrintTemplate(
395 $scope.print.cn_template_content
397 egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);
400 $scope.imported_print_templates = { data : '' };
401 $scope.$watch('imported_templates.data', function(newVal, oldVal) {
402 if (newVal && newVal != oldVal) {
404 var data = JSON.parse(newVal);
405 angular.forEach(data, function(el,k) {
406 $scope.templates[k] = {
408 ,context : el.context
409 ,cn_content : el.cn_content
410 ,settings : el.settings
413 $scope.saveTemplate();
414 $scope.template_changed(); // refresh
415 ngToast.create(egCore.strings.PRINT_TEMPLATES_SUCCESS_IMPORT);
417 ngToast.warning(egCore.strings.PRINT_TEMPLATES_FAIL_IMPORT);
422 $scope.rendered_call_number_set = {};
423 $scope.rendered_cn_key_by_copy_id = {};
424 $scope.rebuild_cn_set = function() {
426 $scope.rendered_call_number_set = {};
427 $scope.rendered_cn_key_by_copy_id = {};
428 for (var i = 0; i < $scope.preview_scope.copies.length; i++) {
429 var copy = $scope.preview_scope.copies[i];
430 var rendered_cn = document.getElementById('cn_for_copy_'+copy.id);
431 if (rendered_cn && rendered_cn.textContent) {
432 var key = rendered_cn.textContent;
433 if (typeof $scope.rendered_call_number_set[key] == 'undefined') {
434 $scope.rendered_call_number_set[key] = {
438 $scope.rendered_cn_key_by_copy_id[copy.id] = key;
441 $scope.preview_scope.tickle = Date() + ' ' + Math.random();
445 $scope.$watch('print.cn_template_content', function(newVal, oldVal) {
446 if (newVal && newVal != oldVal) {
447 $scope.rebuild_cn_set();
451 $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function(newVal, oldVal) {
452 if (newVal && newVal != oldVal) {
453 $scope.rebuild_cn_set();
457 $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function(newVal, oldVal) {
458 if (newVal && newVal != oldVal) {
459 $scope.rebuild_cn_set();
463 $scope.current_tab = 'call_numbers';
464 $scope.set_tab = function(tab) {
465 $scope.current_tab = tab;
471 .directive('egPrintTemplateOutput', ['$compile',function($compile) {
472 return function(scope, element, attrs) {
475 return scope.$eval(attrs.content);
478 // create an isolate scope and copy the print context
479 // data into the new scope.
480 // TODO: see also print security concerns in egHatch
481 var result = element.html(value);
482 var context = scope.$eval(attrs.context);
483 var print_scope = scope.$new(true);
484 angular.forEach(context, function(val, key) {
485 print_scope[key] = val;
487 $compile(element.contents())(print_scope);
493 .filter('cn_wrap', function() {
494 return function(input, w, h, wrap_type) {
496 var prefix = input[0];
497 var callnum = input[1];
498 var suffix = input[2];
503 /* handle spine labels differently if using LC */
504 if (wrap_type == 'lc' || wrap_type == 3) {
505 /* Establish a pattern where every return value should be isolated on its own line
506 on the spine label: subclass letters, subclass numbers, cutter numbers, trailing stuff (date) */
507 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;
508 var result = callnum.match(patt1);
510 callnum = result.slice(1).join('\t');
512 callnum = callnum.split(/\s+/).join('\t');
515 /* If result is null, leave callnum alone. Can't parse this malformed call num */
517 callnum = callnum.split(/\s+/).join('\t');
521 callnum = prefix + '\t' + callnum;
524 callnum += '\t' + suffix;
527 /* At this point, the call number pieces are separated by tab characters. This allows
528 * some space-containing constructs like "v. 1" to appear on one line
530 callnum = callnum.replace(/\t\t/g,'\t'); /* Squeeze out empties */
531 names = callnum.split('\t');
532 var j = 0; var tb = [];
538 var name = names.shift();
540 name = String( name );
542 /* if the name is greater than the label width... */
543 if (name.length > w) {
544 /* then try to split it on periods */
545 var sname = name.split(/\./);
546 if (sname.length > 1) {
547 /* if we can, then put the periods back in on each splitted element */
548 if (name.match(/^\./)) sname[0] = '.' + sname[0];
549 for (var k = 1; k < sname.length; k++) sname[k] = '.' + sname[k];
550 /* and put all but the first one back into the names array */
551 names = sname.slice(1).concat( names );
552 /* if the name fragment is still greater than the label width... */
553 if (sname[0].length > w) {
554 /* then just truncate and throw the rest back into the names array */
555 tb[j] = sname[0].substr(0,w);
556 names = [ sname[0].substr(w) ].concat( names );
558 /* otherwise we're set */
562 /* if we can't split on periods, then just truncate and throw the rest back into the names array */
563 tb[j] = name.substr(0,w);
564 names = [ name.substr(w) ].concat( names );
567 /* otherwise we're set */
574 return tb.join('\n');
578 .filter('wrap', function() {
579 return function(input, w, wrap_type, indent) {
582 if (!w) return input;
583 if (!indent) indent = '';
585 function wrap_on_space(
589 if_cant_wrap_then_truncate,
593 console.log('possible infinite recursion, aborting');
596 if (String(text).length <= length) {
599 var truncated_text = String(text).substr(0,length);
600 var pivot_pos = truncated_text.lastIndexOf(' ');
601 var left_chunk = text.substr(0,pivot_pos).replace(/\s*$/,'');
602 var right_chunk = String(text).substr(pivot_pos+1);
605 if (left_chunk.length == 0) {
606 if (if_cant_wrap_then_truncate) {
607 wrapped_line = truncated_text;
618 right_chunk.length > length
623 if_cant_wrap_then_truncate,
636 output = wrap_on_space(input,w,true,false,0);
639 output = wrap_on_space(input,w,false,false,0);