]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/services/ui.js
LP#1522638 Record holds tab gets modal progress bar
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / ui.js
1 /**
2   * UI tools and directives.
3   */
4 angular.module('egUiMod', ['egCoreMod', 'ui.bootstrap'])
5
6
7 /**
8  * <input focus-me="iAmOpen"/>
9  * $scope.iAmOpen = true;
10  */
11 .directive('focusMe', 
12        ['$timeout','$parse', 
13 function($timeout , $parse) {
14     return {
15         link: function(scope, element, attrs) {
16             var model = $parse(attrs.focusMe);
17             scope.$watch(model, function(value) {
18                 if(value === true) 
19                     $timeout(function() {element[0].focus()});
20             });
21             element.bind('blur', function() {
22                 scope.$apply(model.assign(scope, false));
23             })
24         }
25     };
26 }])
27
28 /**
29  * <input blur-me="pleaseBlurMe"/>
30  * $scope.pleaseBlurMe = true
31  * Useful for de-focusing when no other obvious focus target exists
32  */
33 .directive('blurMe', 
34        ['$timeout','$parse', 
35 function($timeout , $parse) {
36     return {
37         link: function(scope, element, attrs) {
38             var model = $parse(attrs.blurMe);
39             scope.$watch(model, function(value) {
40                 if(value === true) 
41                     $timeout(function() {element[0].blur()});
42             });
43             element.bind('focus', function() {
44                 scope.$apply(model.assign(scope, false));
45             })
46         }
47     };
48 }])
49
50
51 // <input select-me="iWantToBeSelected"/>
52 // $scope.iWantToBeSelected = true;
53 .directive('selectMe', 
54        ['$timeout','$parse', 
55 function($timeout , $parse) {
56     return {
57         link: function(scope, element, attrs) {
58             var model = $parse(attrs.selectMe);
59             scope.$watch(model, function(value) {
60                 if(value === true) 
61                     $timeout(function() {element[0].select()});
62             });
63             element.bind('blur', function() {
64                 scope.$apply(model.assign(scope, false));
65             })
66         }
67     };
68 }])
69
70
71 // 'reverse' filter 
72 // <div ng-repeat="item in items | reverse">{{item.name}}</div>
73 // http://stackoverflow.com/questions/15266671/angular-ng-repeat-in-reverse
74 // TODO: perhaps this should live elsewhere
75 .filter('reverse', function() {
76     return function(items) {
77         return items.slice().reverse();
78     };
79 })
80
81
82 /**
83  * egProgressModal.open();
84  */
85 .factory('egProgressModal', 
86
87         ['$uibModal','$interpolate',
88 function($uibModal, $interpolate){
89     var service = {};
90
91     service.open = function() {
92         return $uibModal.open({
93             templateUrl: './share/t_progress_bar',
94             controller: ['$scope', '$uibModalInstance',
95                 function($scope, $uibModalInstance) {
96                   service.currentInstance = $uibModalInstance;
97                 }
98             ]
99         });
100     };
101     service.close = function() {
102         if (service.currentInstance) {
103             service.currentInstance.close();
104             delete service.currentInstance;
105         }
106     }
107     return service;
108 }])
109
110 /**
111  * egAlertDialog.open({message : 'hello {{name}}'}).result.then(
112  *     function() { console.log('alert closed') });
113  */
114 .factory('egAlertDialog', 
115
116         ['$uibModal','$interpolate',
117 function($uibModal , $interpolate) {
118     var service = {};
119
120     service.open = function(message, msg_scope) {
121         return $uibModal.open({
122             templateUrl: './share/t_alert_dialog',
123             controller: ['$scope', '$uibModalInstance',
124                 function($scope, $uibModalInstance) {
125                     $scope.message = $interpolate(message)(msg_scope);
126                     $scope.ok = function() {
127                         if (msg_scope && msg_scope.ok) msg_scope.ok();
128                         $uibModalInstance.close()
129                     }
130                 }
131             ]
132         });
133     }
134
135     return service;
136 }])
137
138 /**
139  * egConfirmDialog.open("some message goes {{here}}", {
140  *  here : 'foo', ok : function() {}, cancel : function() {}},
141  *  'OK', 'Cancel');
142  */
143 .factory('egConfirmDialog', 
144     
145        ['$uibModal','$interpolate',
146 function($uibModal, $interpolate) {
147     var service = {};
148
149     service.open = function(title, message, msg_scope, ok_button_label, cancel_button_label) {
150         return $uibModal.open({
151             templateUrl: './share/t_confirm_dialog',
152             controller: ['$scope', '$uibModalInstance',
153                 function($scope, $uibModalInstance) {
154                     $scope.title = $interpolate(title)(msg_scope);
155                     $scope.message = $interpolate(message)(msg_scope);
156                     $scope.ok_button_label = $interpolate(ok_button_label || '')(msg_scope);
157                     $scope.cancel_button_label = $interpolate(cancel_button_label || '')(msg_scope);
158                     $scope.ok = function() {
159                         if (msg_scope.ok) msg_scope.ok();
160                         $uibModalInstance.close()
161                     }
162                     $scope.cancel = function() {
163                         if (msg_scope.cancel) msg_scope.cancel();
164                         $uibModalInstance.dismiss();
165                     }
166                 }
167             ]
168         })
169     }
170
171     return service;
172 }])
173
174 /**
175  * egPromptDialog.open(
176  *    "prompt message goes {{here}}", 
177  *    promptValue,  // optional
178  *    {
179  *      here : 'foo',  
180  *      ok : function(value) {console.log(value)}, 
181  *      cancel : function() {console.log('prompt denied')}
182  *    }
183  *  );
184  */
185 .factory('egPromptDialog', 
186     
187        ['$uibModal','$interpolate',
188 function($uibModal, $interpolate) {
189     var service = {};
190
191     service.open = function(message, promptValue, msg_scope) {
192         return $uibModal.open({
193             templateUrl: './share/t_prompt_dialog',
194             controller: ['$scope', '$uibModalInstance',
195                 function($scope, $uibModalInstance) {
196                     $scope.message = $interpolate(message)(msg_scope);
197                     $scope.args = {value : promptValue || ''};
198                     $scope.focus = true;
199                     $scope.ok = function() {
200                         if (msg_scope.ok) msg_scope.ok($scope.args.value);
201                         $uibModalInstance.close()
202                     }
203                     $scope.cancel = function() {
204                         if (msg_scope.cancel) msg_scope.cancel();
205                         $uibModalInstance.dismiss();
206                     }
207                 }
208             ]
209         })
210     }
211
212     return service;
213 }])
214
215 /**
216  * egSelectDialog.open(
217  *    "message goes {{here}}", 
218  *    list,           // ['values','for','dropdown'],
219  *    selectedValue,  // optional
220  *    {
221  *      here : 'foo',
222  *      ok : function(value) {console.log(value)}, 
223  *      cancel : function() {console.log('prompt denied')}
224  *    }
225  *  );
226  */
227 .factory('egSelectDialog', 
228     
229        ['$uibModal','$interpolate',
230 function($uibModal, $interpolate) {
231     var service = {};
232
233     service.open = function(message, inputList, selectedValue, msg_scope) {
234         return $uibModal.open({
235             templateUrl: './share/t_select_dialog',
236             controller: ['$scope', '$uibModalInstance',
237                 function($scope, $uibModalInstance) {
238                     $scope.message = $interpolate(message)(msg_scope);
239                     $scope.args = {
240                         list  : inputList,
241                         value : selectedValue
242                     };
243                     $scope.focus = true;
244                     $scope.ok = function() {
245                         if (msg_scope.ok) msg_scope.ok($scope.args.value);
246                         $uibModalInstance.close()
247                     }
248                     $scope.cancel = function() {
249                         if (msg_scope.cancel) msg_scope.cancel();
250                         $uibModalInstance.dismiss();
251                     }
252                 }
253             ]
254         })
255     }
256
257     return service;
258 }])
259
260 /**
261  * Warn on page unload and give the user a chance to avoid navigating
262  * away from the current page.  
263  * Only one handler is supported per page.
264  * NOTE: we can't use an egUnloadDialog as the dialog builder, because
265  * it renders asynchronously, which allows the page to redirect before
266  * the dialog appears.
267  */
268 .factory('egUnloadPrompt', [
269         '$window','egStrings', 
270 function($window , egStrings) {
271     var service = {attached : false};
272
273     // attach a page/scope unload prompt
274     service.attach = function($scope, msg) {
275         if (service.attached) return;
276         service.attached = true;
277
278         // handle page change
279         $($window).on('beforeunload', function() { 
280             service.clear();
281             return msg || egStrings.EG_UNLOAD_PAGE_PROMPT_MSG;
282         });
283
284         if (!$scope) return;
285
286         // If a scope was provided, attach a scope-change handler,
287         // similar to the page-page prompt.
288         service.locChangeCancel = 
289             $scope.$on('$locationChangeStart', function(evt, next, current) {
290             if (confirm(msg || egStrings.EG_UNLOAD_CTRL_PROMPT_MSG)) {
291                 // user allowed the page to change.  
292                 // Clear the unload handler.
293                 service.clear();
294             } else {
295                 evt.preventDefault();
296             }
297         });
298     };
299
300     // remove the page unload prompt
301     service.clear = function() {
302         $($window).off('beforeunload');
303         if (service.locChangeCancel)
304             service.locChangeCancel();
305         service.attached = false;
306     }
307
308     return service;
309 }])
310
311 .directive('aDisabled', function() {
312     return {
313         restrict : 'A',
314         compile: function(tElement, tAttrs, transclude) {
315             //Disable ngClick
316             tAttrs["ngClick"] = ("ng-click", "!("+tAttrs["aDisabled"]+") && ("+tAttrs["ngClick"]+")");
317
318             //Toggle "disabled" to class when aDisabled becomes true
319             return function (scope, iElement, iAttrs) {
320                 scope.$watch(iAttrs["aDisabled"], function(newValue) {
321                     if (newValue !== undefined) {
322                         iElement.toggleClass("disabled", newValue);
323                     }
324                 });
325
326                 //Disable href on click
327                 iElement.on("click", function(e) {
328                     if (scope.$eval(iAttrs["aDisabled"])) {
329                         e.preventDefault();
330                     }
331                 });
332             };
333         }
334     };
335 })
336
337 .directive('egBasicComboBox', function() {
338     return {
339         restrict: 'E',
340         replace: true,
341         scope: {
342             list: "=", // list of strings
343             selected: "=",
344             egDisabled: "=",
345             allowAll: "@",
346         },
347         template:
348             '<div class="input-group">'+
349                 '<input type="text" ng-disabled="egDisabled" class="form-control" ng-model="selected" ng-change="makeOpen()">'+
350                 '<div class="input-group-btn" dropdown ng-class="{open:isopen}">'+
351                     '<button type="button" ng-click="showAll()" class="btn btn-default dropdown-toggle"><span class="caret"></span></button>'+
352                     '<ul class="dropdown-menu dropdown-menu-right">'+
353                         '<li ng-repeat="item in list|filter:selected"><a href ng-click="changeValue(item)">{{item}}</a></li>'+
354                         '<li ng-if="complete_list" class="divider"><span></span></li>'+
355                         '<li ng-if="complete_list" ng-repeat="item in list"><a href ng-click="changeValue(item)">{{item}}</a></li>'+
356                     '</ul>'+
357                 '</div>'+
358             '</div>',
359         controller: ['$scope','$filter',
360             function( $scope , $filter) {
361
362                 $scope.complete_list = false;
363                 $scope.isopen = false;
364                 $scope.clickedopen = false;
365                 $scope.clickedclosed = null;
366
367                 $scope.showAll = function () {
368
369                     $scope.clickedopen = !$scope.clickedopen;
370
371                     if ($scope.clickedclosed === null) {
372                         if (!$scope.clickedopen) {
373                             $scope.clickedclosed = true;
374                         }
375                     } else {
376                         $scope.clickedclosed = !$scope.clickedopen;
377                     }
378
379                     if ($scope.selected.length > 0) $scope.complete_list = true;
380                     if ($scope.selected.length == 0) $scope.complete_list = false;
381                     $scope.makeOpen();
382                 }
383
384                 $scope.makeOpen = function () {
385                     $scope.isopen = $scope.clickedopen || ($filter('filter')(
386                         $scope.list,
387                         $scope.selected
388                     ).length > 0 && $scope.selected.length > 0);
389                     if ($scope.clickedclosed) $scope.isopen = false;
390                 }
391
392                 $scope.changeValue = function (newVal) {
393                     $scope.selected = newVal;
394                     $scope.isopen = false;
395                     $scope.clickedclosed = null;
396                     $scope.clickedopen = false;
397                     if ($scope.selected.length == 0) $scope.complete_list = false;
398                 }
399
400             }
401         ]
402     };
403 })
404
405 /**
406  * Nested org unit selector modeled as a Bootstrap dropdown button.
407  */
408 .directive('egOrgSelector', function() {
409     return {
410         restrict : 'AE',
411         transclude : true,
412         replace : true, // makes styling easier
413         scope : {
414             selected : '=', // defaults to workstation or root org,
415                             // unless the nodefault attibute exists
416
417             // Each org unit is passed into this function and, for
418             // any org units where the response value is true, the
419             // org unit will not be added to the selector.
420             hiddenTest : '=',
421
422             // Each org unit is passed into this function and, for
423             // any org units where the response value is true, the
424             // org unit will not be available for selection.
425             disableTest : '=',
426
427             // if set to true, disable the UI element altogether
428             alldisabled : '@',
429
430             // Caller can either $watch(selected, ..) or register an
431             // onchange handler.
432             onchange : '=',
433
434             // optional primary drop-down button label
435             label : '@',
436
437             // optional name of settings key for persisting
438             // the last selected org unit
439             stickySetting : '@'
440         },
441
442         // any reason to move this into a TT2 template?
443         template : 
444             '<div class="btn-group eg-org-selector" uib-dropdown>'
445             + '<button type="button" class="btn btn-default" uib-dropdown-toggle ng-disabled="disable_button">'
446              + '<span style="padding-right: 5px;">{{getSelectedName()}}</span>'
447              + '<span class="caret"></span>'
448            + '</button>'
449            + '<ul uib-dropdown-menu class="scrollable-menu">'
450              + '<li ng-repeat="org in orgList" ng-hide="hiddenTest(org.id)">'
451                + '<a href ng-click="orgChanged(org)" a-disabled="disableTest(org.id)" '
452                  + 'style="padding-left: {{org.depth * 10 + 5}}px">'
453                  + '{{org.shortname}}'
454                + '</a>'
455              + '</li>'
456            + '</ul>'
457           + '</div>',
458
459         controller : ['$scope','$timeout','egOrg','egAuth','egCore','egStartup',
460               function($scope , $timeout , egOrg , egAuth , egCore , egStartup) {
461
462             if ($scope.alldisabled) {
463                 $scope.disable_button = $scope.alldisabled == 'true' ? true : false;
464             } else {
465                 $scope.disable_button = false;
466             }
467
468             $scope.egOrg = egOrg; // for use in the link function
469             $scope.egAuth = egAuth; // for use in the link function
470             $scope.hatch = egCore.hatch // for use in the link function
471
472             // avoid linking the full fleshed tree to the scope by 
473             // tossing in a flattened list.
474             // --
475             // Run-time code referencing post-start data should be run
476             // from within a startup block, otherwise accessing this
477             // module before startup completes will lead to failure.
478             egStartup.go().then(function() {
479
480                 $scope.orgList = egOrg.list().map(function(org) {
481                     return {
482                         id : org.id(),
483                         shortname : org.shortname(), 
484                         depth : org.ou_type().depth()
485                     }
486                 });
487
488                 if (!$scope.selected && !$scope.nodefault)
489                     $scope.selected = egOrg.get(egAuth.user().ws_ou());
490             });
491
492             $scope.getSelectedName = function() {
493                 if ($scope.selected && $scope.selected.shortname)
494                     return $scope.selected.shortname();
495                 return $scope.label;
496             }
497
498             $scope.orgChanged = function(org) {
499                 $scope.selected = egOrg.get(org.id);
500                 if ($scope.stickySetting) {
501                     egCore.hatch.setLocalItem($scope.stickySetting, org.id);
502                 }
503                 if ($scope.onchange) $scope.onchange($scope.selected);
504             }
505
506         }],
507         link : function(scope, element, attrs, egGridCtrl) {
508
509             // boolean fields are presented as value-less attributes
510             angular.forEach(
511                 ['nodefault'],
512                 function(field) {
513                     if (angular.isDefined(attrs[field]))
514                         scope[field] = true;
515                     else
516                         scope[field] = false;
517                 }
518             );
519
520             if (scope.stickySetting) {
521                 var orgId = scope.hatch.getLocalItem(scope.stickySetting);
522                 if (orgId) {
523                     scope.selected = scope.egOrg.get(orgId);
524                     if (scope.onchange)
525                         scope.onchange(scope.selected);
526                 }
527             }
528
529             if (!scope.selected && !scope.nodefault)
530                 scope.selected = scope.egOrg.get(scope.egAuth.user().ws_ou());
531         }
532
533     }
534 })
535
536 /* http://eric.sau.pe/angularjs-detect-enter-key-ngenter/ */
537 .directive('egEnter', function () {
538     return function (scope, element, attrs) {
539         element.bind("keydown keypress", function (event) {
540             if(event.which === 13) {
541                 scope.$apply(function (){
542                     scope.$eval(attrs.egEnter);
543                 });
544  
545                 event.preventDefault();
546             }
547         });
548     };
549 })
550
551 /*
552 * Handy wrapper directive for uib-datapicker-popup
553 */
554 .directive(
555     'egDateInput', ['egStrings', 'egCore',
556     function(egStrings, egCore) {
557         return {
558             scope : {
559                 closeText : '@',
560                 ngModel : '=',
561                 ngChange : '=',
562                 ngBlur : '=',
563                 ngDisabled : '=',
564                 ngRequired : '=',
565                 hideDatePicker : '=',
566                 dateFormat : '=?'
567             },
568             require: 'ngModel',
569             templateUrl: './share/t_datetime',
570             replace: true,
571             link : function(scope, elm, attrs) {
572                 if (!scope.closeText)
573                     scope.closeText = egStrings.EG_DATE_INPUT_CLOSE_TEXT;
574
575                 if ('showTimePicker' in attrs)
576                     scope.showTimePicker = true;
577
578                 var default_format = 'mediumDate';
579                 egCore.org.settings(['format.date']).then(function(set) {
580                     default_format = set['format.date'];
581                     scope.date_format = (scope.dateFormat) ?
582                         scope.dateFormat :
583                         default_format;
584                 });
585             }
586         };
587     }
588 ])
589
590 /*
591  *  egFmValueSelector - widget for selecting a value from list specified
592  *                      by IDL class
593  */
594 .directive('egFmValueSelector', function() {
595     return {
596         restrict : 'E',
597         transclude : true,
598         scope : {
599             idlClass : '@',
600             ngModel : '=',
601
602             // optional filter for refining the set of rows that
603             // get returned. Example:
604             //
605             // filter="{'column':{'=':null}}"
606             filter : '=',
607
608             // optional name of settings key for persisting
609             // the last selected value
610             stickySetting : '@',
611
612             // optional OU setting for fetching default value;
613             // used only if sticky setting not set
614             ouSetting : '@'
615         },
616         require: 'ngModel',
617         templateUrl : './share/t_fm_value_selector',
618         controller : ['$scope','egCore', function($scope , egCore) {
619
620             $scope.org = egCore.org; // for use in the link function
621             $scope.auth = egCore.auth; // for use in the link function
622             $scope.hatch = egCore.hatch // for use in the link function
623
624             function flatten_linked_values(cls, list) {
625                 var results = [];
626                 var fields = egCore.idl.classes[cls].fields;
627                 var id_field;
628                 var selector;
629                 angular.forEach(fields, function(fld) {
630                     if (fld.datatype == 'id') {
631                         id_field = fld.name;
632                         selector = fld.selector ? fld.selector : id_field;
633                         return;
634                     }
635                 });
636                 angular.forEach(list, function(item) {
637                     var rec = egCore.idl.toHash(item);
638                     results.push({
639                         id : rec[id_field],
640                         name : rec[selector]
641                     });
642                 });
643                 return results;
644             }
645
646             var search = {};
647             search[egCore.idl.classes[$scope.idlClass].pkey] = {'!=' : null};
648             if ($scope.filter) {
649                 angular.extend(search, $scope.filter);
650             }
651             egCore.pcrud.search(
652                 $scope.idlClass, search, {}, {atomic : true}
653             ).then(function(list) {
654                 $scope.linked_values = flatten_linked_values($scope.idlClass, list);
655             });
656
657             $scope.handleChange = function(value) {
658                 if ($scope.stickySetting) {
659                     egCore.hatch.setLocalItem($scope.stickySetting, value);
660                 }
661             }
662
663         }],
664         link : function(scope, element, attrs) {
665             if (scope.stickySetting && (angular.isUndefined(scope.ngModel) || (scope.ngModel === null))) {
666                 var value = scope.hatch.getLocalItem(scope.stickySetting);
667                 scope.ngModel = value;
668             }
669             if (scope.ouSetting && (angular.isUndefined(scope.ngModel) || (scope.ngModel === null))) {
670                 scope.org.settings([scope.ouSetting], scope.auth.user().ws_ou())
671                 .then(function(set) {
672                     var value = parseInt(set[scope.ouSetting]);
673                     if (!isNaN(value))
674                         scope.ngModel = value;
675                 });
676             }
677         }
678     }
679 })
680
681 .factory('egWorkLog', ['egCore', function(egCore) {
682     var service = {};
683
684     service.retrieve_all = function() {
685         var workLog = egCore.hatch.getLocalItem('eg.work_log') || [];
686         var patronLog = egCore.hatch.getLocalItem('eg.patron_log') || [];
687
688         return { 'work_log' : workLog, 'patron_log' : patronLog };
689     }
690
691     service.record = function(message,data) {
692         var max_entries;
693         var max_patrons;
694         if (typeof egCore != 'undefined') {
695             if (typeof egCore.env != 'undefined') {
696                 if (typeof egCore.env.aous != 'undefined') {
697                     max_entries = egCore.env.aous['ui.admin.work_log.max_entries'];
698                     max_patrons = egCore.env.aous['ui.admin.patron_log.max_entries'];
699                 } else {
700                     console.log('worklog: missing egCore.env.aous');
701                 }
702             } else {
703                 console.log('worklog: missing egCore.env');
704             }
705         } else {
706             console.log('worklog: missing egCore');
707         }
708         if (!max_entries) {
709             if (typeof egCore.org != 'undefined') {
710                 if (typeof egCore.org.cachedSettings != 'undefined') {
711                     max_entries = egCore.org.cachedSettings['ui.admin.work_log.max_entries'];
712                 } else {
713                     console.log('worklog: missing egCore.org.cachedSettings');
714                 }
715             } else {
716                 console.log('worklog: missing egCore.org');
717             }
718         }
719         if (!max_patrons) {
720             if (typeof egCore.org != 'undefined') {
721                 if (typeof egCore.org.cachedSettings != 'undefined') {
722                     max_patrons = egCore.org.cachedSettings['ui.admin.patron_log.max_entries'];
723                 } else {
724                     console.log('worklog: missing egCore.org.cachedSettings');
725                 }
726             } else {
727                 console.log('worklog: missing egCore.org');
728             }
729         }
730         if (!max_entries) {
731             max_entries = 20;
732             console.log('worklog: defaulting to max_entries = ' + max_entries);
733         }
734         if (!max_patrons) {
735             max_patrons = 10;
736             console.log('worklog: defaulting to max_patrons = ' + max_patrons);
737         }
738
739         var workLog = egCore.hatch.getLocalItem('eg.work_log') || [];
740         var patronLog = egCore.hatch.getLocalItem('eg.patron_log') || [];
741         var entry = {
742             'when' : new Date(),
743             'msg' : message,
744             'data' : data,
745             'action' : data.action,
746             'actor' : egCore.auth.user().usrname()
747         };
748         if (data.action == 'checkin') {
749             entry['item'] = data.response.params.copy_barcode;
750             entry['item_id'] = data.response.data.acp.id();
751             if (data.response.data.au) {
752                 entry['user'] = data.response.data.au.family_name();
753                 entry['patron_id'] = data.response.data.au.id();
754             }
755         }
756         if (data.action == 'checkout') {
757             entry['item'] = data.response.params.copy_barcode;
758             entry['user'] = data.response.data.au.family_name();
759             entry['item_id'] = data.response.data.acp.id();
760             entry['patron_id'] = data.response.data.au.id();
761         }
762         if (data.action == 'renew') {
763             entry['item'] = data.response.params.copy_barcode;
764             entry['user'] = data.response.data.au.family_name();
765             entry['item_id'] = data.response.data.acp.id();
766             entry['patron_id'] = data.response.data.au.id();
767         }
768         if (data.action == 'requested_hold'
769             || data.action == 'edited_patron'
770             || data.action == 'registered_patron'
771             || data.action == 'paid_bill') {
772             entry['patron_id'] = data.patron_id;
773         }
774         if (data.action == 'paid_bill') {
775             entry['amount'] = data.total_amount;
776         }
777
778         workLog.push( entry );
779         if (workLog.length > max_entries) workLog.shift();
780         egCore.hatch.setLocalItem('eg.work_log',workLog); // hatch JSONifies the data, so should be okay re: memory leaks?
781
782         if (entry['patron_id']) {
783             var temp = [];
784             for (var i = 0; i < patronLog.length; i++) { // filter out any matching patron
785                 if (patronLog[i]['patron_id'] != entry['patron_id']) temp.push(patronLog[i]);
786             }
787             temp.push( entry );
788             if (temp.length > max_patrons) temp.shift();
789             patronLog = temp;
790             egCore.hatch.setLocalItem('eg.patron_log',patronLog);
791         }
792
793         console.log('worklog',entry);
794     }
795
796     return service;
797 }]);