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?|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 () {
249 egCore.hatch.getItem('cat.printlabels.templates').then(function(t) {
251 $scope.templates = t;
252 $scope.template_name_list = Object.keys(t);
256 $scope.fetchTemplates();
258 $scope.applyTemplate = function (n) {
259 $scope.print.cn_template_content = $scope.templates[n].cn_content;
260 $scope.print.template_content = $scope.templates[n].content;
261 $scope.print.template_context = $scope.templates[n].context;
262 for (var s in $scope.templates[n].settings) {
263 $scope.preview_scope.settings[s] = $scope.templates[n].settings[s];
267 $scope.deleteTemplate = function (n) {
269 delete $scope.templates[n]
270 $scope.template_name_list = Object.keys($scope.templates);
271 $scope.template_name = '';
272 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
273 $scope.fetchTemplates();
274 ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_DELETE);
278 $scope.saveTemplate = function (n) {
281 $scope.templates[n] = {
282 content : $scope.print.template_content
283 ,context : $scope.print.template_context
284 ,cn_content : $scope.print.cn_template_content
285 ,settings : $scope.preview_scope.settings
287 $scope.template_name_list = Object.keys($scope.templates);
289 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
290 $scope.fetchTemplates();
292 $scope.dirty = false;
294 // save all templates, as we might do after an import
295 egCore.hatch.setItem('cat.printlabels.templates', $scope.templates);
296 $scope.fetchTemplates();
298 ngToast.create(egCore.strings.PRINT_LABEL_TEMPLATE_SUCCESS_SAVE);
301 $scope.templates = {};
302 $scope.imported_templates = { data : '' };
303 $scope.template_name = '';
304 $scope.template_name_list = [];
306 $scope.print_labels = function() {
307 $scope.save_locally();
308 return egCore.print.print({
309 context : $scope.print.template_context,
310 template : $scope.print.template_name,
311 scope : $scope.preview_scope,
315 $scope.template_changed = function() {
316 $scope.print.load_failed = false;
317 egCore.print.getPrintTemplate('item_label')
320 $scope.print.template_content = html;
323 $scope.print.template_content = '';
324 $scope.print.load_failed = true;
327 egCore.print.getPrintTemplateContext('item_label')
328 .then(function(template_context) {
329 $scope.print.template_context = template_context;
331 egCore.print.getPrintTemplate('item_label_cn')
334 $scope.print.cn_template_content = html;
337 $scope.print.cn_template_content = '';
338 $scope.print.load_failed = true;
341 egCore.hatch.getItem('cat.printlabels.last_settings').then(function(s) {
343 $scope.preview_scope.settings = s;
349 $scope.reset_to_default = function() {
350 egCore.print.removePrintTemplate(
353 egCore.print.removePrintTemplateContext(
356 egCore.print.removePrintTemplate(
359 egCore.hatch.removeItem('cat.printlabels.last_settings');
360 for (s in $scope.preview_scope.settings) {
361 $scope.preview_scope.settings[s] = undefined;
363 $scope.preview_scope.settings = {};
364 egCore.org.settings($scope.org_unit_setting_list).then(function(res) {
365 $scope.preview_scope.settings = res;
368 $scope.template_changed();
371 $scope.save_locally = function() {
372 egCore.print.storePrintTemplate(
374 $scope.print.template_content
376 egCore.print.storePrintTemplateContext(
378 $scope.print.template_context
380 egCore.print.storePrintTemplate(
382 $scope.print.cn_template_content
384 egCore.hatch.setItem('cat.printlabels.last_settings', $scope.preview_scope.settings);
387 $scope.imported_print_templates = { data : '' };
388 $scope.$watch('imported_templates.data', function(newVal, oldVal) {
389 if (newVal && newVal != oldVal) {
391 var data = JSON.parse(newVal);
392 angular.forEach(data, function(el,k) {
393 $scope.templates[k] = {
395 ,context : el.context
396 ,cn_content : el.cn_content
397 ,settings : el.settings
400 $scope.saveTemplate();
401 $scope.template_changed(); // refresh
402 ngToast.create(egCore.strings.PRINT_TEMPLATES_SUCCESS_IMPORT);
404 ngToast.warning(egCore.strings.PRINT_TEMPLATES_FAIL_IMPORT);
409 $scope.rendered_call_number_set = {};
410 $scope.rendered_cn_key_by_copy_id = {};
411 $scope.rebuild_cn_set = function() {
413 $scope.rendered_call_number_set = {};
414 $scope.rendered_cn_key_by_copy_id = {};
415 for (var i = 0; i < $scope.preview_scope.copies.length; i++) {
416 var copy = $scope.preview_scope.copies[i];
417 var rendered_cn = document.getElementById('cn_for_copy_'+copy.id);
418 if (rendered_cn && rendered_cn.textContent) {
419 var key = rendered_cn.textContent;
420 if (typeof $scope.rendered_call_number_set[key] == 'undefined') {
421 $scope.rendered_call_number_set[key] = {
425 $scope.rendered_cn_key_by_copy_id[copy.id] = key;
428 $scope.preview_scope.tickle = Date() + ' ' + Math.random();
432 $scope.$watch('print.cn_template_content', function(newVal, oldVal) {
433 if (newVal && newVal != oldVal) {
434 $scope.rebuild_cn_set();
438 $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_height']", function(newVal, oldVal) {
439 if (newVal && newVal != oldVal) {
440 $scope.rebuild_cn_set();
444 $scope.$watch("preview_scope.settings['webstaff.cat.label.call_number_wrap_filter_width']", function(newVal, oldVal) {
445 if (newVal && newVal != oldVal) {
446 $scope.rebuild_cn_set();
450 $scope.current_tab = 'call_numbers';
451 $scope.set_tab = function(tab) {
452 $scope.current_tab = tab;
458 .directive('egPrintTemplateOutput', ['$compile',function($compile) {
459 return function(scope, element, attrs) {
462 return scope.$eval(attrs.content);
465 // create an isolate scope and copy the print context
466 // data into the new scope.
467 // TODO: see also print security concerns in egHatch
468 var result = element.html(value);
469 var context = scope.$eval(attrs.context);
470 var print_scope = scope.$new(true);
471 angular.forEach(context, function(val, key) {
472 print_scope[key] = val;
474 $compile(element.contents())(print_scope);
480 .filter('cn_wrap', function() {
481 return function(input, w, h, wrap_type) {
483 var prefix = input[0];
484 var callnum = input[1];
485 var suffix = input[2];
490 /* handle spine labels differently if using LC */
491 if (wrap_type == 'lc' || wrap_type == 3) {
492 /* Establish a pattern where every return value should be isolated on its own line
493 on the spine label: subclass letters, subclass numbers, cutter numbers, trailing stuff (date) */
494 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;
495 var result = callnum.match(patt1);
497 callnum = result.slice(1).join('\t');
499 callnum = callnum.split(/\s+/).join('\t');
502 /* If result is null, leave callnum alone. Can't parse this malformed call num */
504 callnum = callnum.split(/\s+/).join('\t');
508 callnum = prefix + '\t' + callnum;
511 callnum += '\t' + suffix;
514 /* At this point, the call number pieces are separated by tab characters. This allows
515 * some space-containing constructs like "v. 1" to appear on one line
517 callnum = callnum.replace(/\t\t/g,'\t'); /* Squeeze out empties */
518 names = callnum.split('\t');
519 var j = 0; var tb = [];
525 var name = names.shift();
527 name = String( name );
529 /* if the name is greater than the label width... */
530 if (name.length > w) {
531 /* then try to split it on periods */
532 var sname = name.split(/\./);
533 if (sname.length > 1) {
534 /* if we can, then put the periods back in on each splitted element */
535 if (name.match(/^\./)) sname[0] = '.' + sname[0];
536 for (var k = 1; k < sname.length; k++) sname[k] = '.' + sname[k];
537 /* and put all but the first one back into the names array */
538 names = sname.slice(1).concat( names );
539 /* if the name fragment is still greater than the label width... */
540 if (sname[0].length > w) {
541 /* then just truncate and throw the rest back into the names array */
542 tb[j] = sname[0].substr(0,w);
543 names = [ sname[0].substr(w) ].concat( names );
545 /* otherwise we're set */
549 /* if we can't split on periods, then just truncate and throw the rest back into the names array */
550 tb[j] = name.substr(0,w);
551 names = [ name.substr(w) ].concat( names );
554 /* otherwise we're set */
561 return tb.join('\n');
565 .filter('wrap', function() {
566 return function(input, w, wrap_type, indent) {
569 if (!w) return input;
570 if (!indent) indent = '';
572 function wrap_on_space(
576 if_cant_wrap_then_truncate,
580 console.log('possible infinite recursion, aborting');
583 if (String(text).length <= length) {
586 var truncated_text = String(text).substr(0,length);
587 var pivot_pos = truncated_text.lastIndexOf(' ');
588 var left_chunk = text.substr(0,pivot_pos).replace(/\s*$/,'');
589 var right_chunk = String(text).substr(pivot_pos+1);
592 if (left_chunk.length == 0) {
593 if (if_cant_wrap_then_truncate) {
594 wrapped_line = truncated_text;
605 right_chunk.length > length
610 if_cant_wrap_then_truncate,
623 output = wrap_on_space(input,w,true,false,0);
626 output = wrap_on_space(input,w,false,false,0);