]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
LP#1570091: Show (index / hit count) in the Back To Results button
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / cat / catalog / app.js
1 /**
2  * TPAC Frame App
3  *
4  * currently, this app doesn't use routes for each sub-ui, because 
5  * reloading the catalog each time is sloooow.  better so far to 
6  * swap out divs w/ ng-if / ng-show / ng-hide as needed.
7  *
8  */
9
10 angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','ngLocationUpdate','egCoreMod','egGridMod', 'egMarcMod', 'egUserMod', 'egHoldingsMod'])
11
12 .config(function($routeProvider, $locationProvider, $compileProvider) {
13     $locationProvider.html5Mode(true);
14     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
15
16     var resolver = {delay : ['egCore','egStartup','egUser', function(egCore, egStartup, egUser) {
17         egCore.env.classLoaders.aous = function() {
18             return egCore.org.settings([
19                 'cat.marc_control_number_identifier'
20             ]).then(function(settings) {
21                 // local settings are cached within egOrg.  Caching them
22                 // again in egEnv just simplifies the syntax for access.
23                 egCore.env.aous = settings;
24             });
25         }
26         egCore.env.loadClasses.push('aous');
27         return egStartup.go()
28     }]};
29
30     $routeProvider.when('/cat/catalog/index', {
31         templateUrl: './cat/catalog/t_catalog',
32         controller: 'CatalogCtrl',
33         resolve : resolver
34     });
35
36     $routeProvider.when('/cat/catalog/retrieve_by_id', {
37         templateUrl: './cat/catalog/t_retrieve_by_id',
38         controller: 'CatalogRecordRetrieve',
39         resolve : resolver
40     });
41
42     $routeProvider.when('/cat/catalog/retrieve_by_tcn', {
43         templateUrl: './cat/catalog/t_retrieve_by_tcn',
44         controller: 'CatalogRecordRetrieve',
45         resolve : resolver
46     });
47
48     $routeProvider.when('/cat/catalog/new_bib', {
49         templateUrl: './cat/catalog/t_new_bib',
50         controller: 'NewBibCtrl',
51         resolve : resolver
52     });
53
54     // create some catalog page-specific mappings
55     $routeProvider.when('/cat/catalog/record/:record_id', {
56         templateUrl: './cat/catalog/t_catalog',
57         controller: 'CatalogCtrl',
58         resolve : resolver
59     });
60
61     // create some catalog page-specific mappings
62     $routeProvider.when('/cat/catalog/record/:record_id/:record_tab', {
63         templateUrl: './cat/catalog/t_catalog',
64         controller: 'CatalogCtrl',
65         resolve : resolver
66     });
67
68     $routeProvider.when('/cat/catalog/batchEdit', {
69         templateUrl: './cat/catalog/t_batchedit',
70         controller: 'BatchEditCtrl',
71         resolve : resolver
72     });
73
74     $routeProvider.when('/cat/catalog/batchEdit/:container_type/:container_id', {
75         templateUrl: './cat/catalog/t_batchedit',
76         controller: 'BatchEditCtrl',
77         resolve : resolver
78     });
79
80     $routeProvider.when('/cat/catalog/vandelay', {
81         templateUrl: './cat/catalog/t_vandelay',
82         controller: 'VandelayCtrl',
83         resolve : resolver
84     });
85
86     $routeProvider.when('/cat/catalog/verifyURLs', {
87         templateUrl: './cat/catalog/t_verifyurls',
88         controller: 'URLVerifyCtrl',
89         resolve : resolver
90     });
91
92     $routeProvider.when('/cat/catalog/manageAuthorities', {
93         templateUrl: './cat/catalog/t_manageauthorities',
94         controller: 'ManageAuthoritiesCtrl',
95         resolve : resolver
96     });
97
98     $routeProvider.when('/cat/catalog/authority/:authority_id/marc_edit', {
99         templateUrl: './cat/catalog/t_authority',
100         controller: 'AuthorityCtrl',
101         resolve : resolver
102     });
103
104     $routeProvider.otherwise({redirectTo : '/cat/catalog/index'});
105 })
106
107
108 /**
109  * */
110 .controller('CatalogRecordRetrieve',
111        ['$scope','$routeParams','$location','$q','egCore',
112 function($scope , $routeParams , $location , $q , egCore ) {
113
114     $scope.focusMe = true;
115
116     // jump to the patron checkout UI
117     function loadRecord(record_id) {
118         $location
119         .path('/cat/catalog/record/' + record_id);
120     }
121
122     $scope.submitId = function(args) {
123         $scope.recordNotFound = null;
124         if (!args.record_id) return;
125
126         // blur so next time it's set to true it will re-apply select()
127         $scope.selectMe = false;
128
129         return loadRecord(args.record_id);
130     }
131
132     $scope.submitTCN = function(args) {
133         $scope.recordNotFound = null;
134         $scope.moreRecordsFound = null;
135         if (!args.record_tcn) return;
136
137         // blur so next time it's set to true it will re-apply select()
138         $scope.selectMe = false;
139
140         // lookup TCN
141         egCore.net.request(
142             'open-ils.search',
143             'open-ils.search.biblio.tcn',
144             args.record_tcn)
145
146         .then(function(resp) { // get_barcodes
147
148             if (evt = egCore.evt.parse(resp)) {
149                 alert(evt); // FIXME
150                 return;
151             }
152
153             if (!resp.count) {
154                 $scope.recordNotFound = args.record_tcn;
155                 $scope.selectMe = true;
156                 return;
157             }
158
159             if (resp.count > 1) {
160                 $scope.moreRecordsFound = args.record_tcn;
161                 $scope.selectMe = true;
162                 return;
163             }
164
165             var record_id = resp.ids[0];
166             return loadRecord(record_id);
167         });
168     }
169
170 }])
171
172 .controller('NewBibCtrl',
173        ['$scope','$routeParams','$location','$window','$q','egCore',
174         'egGridDataProvider','egHoldGridActions','$timeout','holdingsSvc',
175 function($scope , $routeParams , $location , $window , $q , egCore) {
176
177     $scope.have_template = false;
178     $scope.marc_template = '';
179     $scope.stop_unload = false;
180     $scope.template_list = [];
181     $scope.template_name = '';
182     $scope.new_bib_id = 0;
183
184     egCore.net.request(
185         'open-ils.cat',
186         'open-ils.cat.marc_template.types.retrieve'
187     ).then(function(resp) {
188         angular.forEach(resp, function(name) {
189             $scope.template_list.push(name);
190         });
191         $scope.template_list.sort();
192     });
193     $scope.template_name = egCore.hatch.getSessionItem('eg.cat.last_bib_marc_template');
194     if (!$scope.template_name) {
195         egCore.hatch.getItem('cat.default_bib_marc_template').then(function(template) {
196             $scope.template_name = template;
197         });
198     }
199
200     $scope.loadTemplate = function() {
201         if ($scope.template_name) {
202             egCore.net.request(
203                 'open-ils.cat',
204                 'open-ils.cat.biblio.marc_template.retrieve',
205                 $scope.template_name
206             ).then(function(template) {
207                 $scope.marc_template = template;
208                 $scope.have_template = true;
209                 egCore.hatch.setSessionItem('eg.cat.last_bib_marc_template', $scope.template_name);
210             });
211         }
212     }
213
214     $scope.setDefaultTemplate = function() {
215         var hatch_key = "cat.default_bib_marc_template";
216         if ($scope.template_name) {
217             egCore.hatch.setItem(hatch_key, $scope.template_name);
218         } else {
219             egCore.hatch.removeItem(hatch_key);
220         }
221     }
222
223     $scope.$watch('new_bib_id', function(newVal, oldVal) {
224         if (newVal) {
225             $location.path('/cat/catalog/record/' + $scope.new_bib_id);
226         }
227     });
228     
229
230 }])
231
232 .controller('CatalogCtrl',
233        ['$scope','$routeParams','$location','$window','$q','egCore','egHolds','egCirc','egConfirmDialog',
234         'egGridDataProvider','egHoldGridActions','$timeout','$modal','holdingsSvc','egUser','conjoinedSvc',
235 function($scope , $routeParams , $location , $window , $q , egCore , egHolds , egCirc,  egConfirmDialog,
236          egGridDataProvider , egHoldGridActions , $timeout , $modal , holdingsSvc , egUser , conjoinedSvc) {
237
238     var holdingsSvcInst = new holdingsSvc();
239
240     // set record ID on page load if available...
241     $scope.record_id = $routeParams.record_id;
242     $scope.summary_pane_record;
243
244     if ($routeParams.record_id) $scope.from_route = true;
245     else $scope.from_route = false;
246
247     // will hold a ref to the opac iframe
248     $scope.opac_iframe = null;
249     $scope.parts_iframe = null;
250
251     $scope.search_result_index = 1;
252     $scope.search_result_hit_count = 1;
253
254     $scope.$watch(
255         'opac_iframe.dom.contentWindow.search_result_index',
256         function (n,o) {
257             if (!isNaN(parseInt(n)))
258                 $scope.search_result_index = n + 1;
259         }
260     );
261
262     $scope.$watch(
263         'opac_iframe.dom.contentWindow.search_result_hit_count',
264         function (n,o) {
265             if (!isNaN(parseInt(n)))
266                 $scope.search_result_hit_count = n;
267         }
268     );
269
270     $scope.in_opac_call = false;
271     $scope.opac_call = function (opac_frame_function, force_opac_tab) {
272         if ($scope.opac_iframe) {
273             if (force_opac_tab) $scope.record_tab = 'catalog';
274             $scope.in_opac_call = true;
275             $scope.opac_iframe.dom.contentWindow[opac_frame_function]();
276             if (opac_frame_function == 'rdetailBackToResults') {
277                 $location.update_path('/cat/catalog/index');
278             }
279         }
280     }
281
282     $scope.add_to_record_bucket = function() {
283         var recId = $scope.record_id;
284         return $modal.open({
285             templateUrl: './cat/catalog/t_add_to_bucket',
286             animation: true,
287             size: 'md',
288             controller:
289                    ['$scope','$modalInstance',
290             function($scope , $modalInstance) {
291
292                 $scope.bucket_id = 0;
293                 $scope.newBucketName = '';
294                 $scope.allBuckets = [];
295                 egCore.net.request(
296                     'open-ils.actor',
297                     'open-ils.actor.container.retrieve_by_class.authoritative',
298                     egCore.auth.token(), egCore.auth.user().id(),
299                     'biblio', 'staff_client'
300                 ).then(function(buckets) { $scope.allBuckets = buckets; });
301
302                 $scope.add_to_bucket = function() {
303                     var item = new egCore.idl.cbrebi();
304                     item.bucket($scope.bucket_id);
305                     item.target_biblio_record_entry(recId);
306                     egCore.net.request(
307                         'open-ils.actor',
308                         'open-ils.actor.container.item.create',
309                         egCore.auth.token(), 'biblio', item
310                     ).then(function(resp) {
311                         $modalInstance.close();
312                     });
313                 }
314
315                 $scope.add_to_new_bucket = function() {
316                     var bucket = new egCore.idl.cbreb();
317                     bucket.owner(egCore.auth.user().id());
318                     bucket.name($scope.newBucketName);
319                     bucket.description('');
320                     bucket.btype('staff_client');
321
322                     egCore.net.request(
323                         'open-ils.actor',
324                         'open-ils.actor.container.create',
325                         egCore.auth.token(), 'biblio', bucket
326                     ).then(function(bucket) {
327                         $scope.bucket_id = bucket;
328                         $scope.add_to_bucket();
329                     });
330                 }
331
332                 $scope.cancel = function() {
333                     $modalInstance.dismiss();
334                 }
335             }]
336         });
337     }
338
339     $scope.current_overlay_target     = egCore.hatch.getLocalItem('eg.cat.marked_overlay_record');
340     $scope.current_voltransfer_target = egCore.hatch.getLocalItem('eg.cat.marked_volume_transfer_record');
341     $scope.current_conjoined_target   = egCore.hatch.getLocalItem('eg.cat.marked_conjoined_record');
342
343     $scope.markConjoined = function () {
344         $scope.current_conjoined_target = $scope.record_id;
345         egCore.hatch.setLocalItem('eg.cat.marked_conjoined_record',$scope.record_id);
346     };
347
348     $scope.markVolTransfer = function () {
349         $scope.current_voltransfer_target = $scope.record_id;
350         egCore.hatch.setLocalItem('eg.cat.marked_volume_transfer_record',$scope.record_id);
351     };
352
353     $scope.markOverlay = function () {
354         $scope.current_overlay_target = $scope.record_id;
355         egCore.hatch.setLocalItem('eg.cat.marked_overlay_record',$scope.record_id);
356     };
357
358     $scope.clearRecordMarks = function () {
359         $scope.current_overlay_target     = null;
360         $scope.current_voltransfer_target = null;
361         $scope.current_conjoined_target   = null;
362         egCore.hatch.removeLocalItem('eg.cat.marked_volume_transfer_record');
363         egCore.hatch.removeLocalItem('eg.cat.marked_conjoined_record');
364         egCore.hatch.removeLocalItem('eg.cat.marked_overlay_record');
365     }
366
367     $scope.stop_unload = false;
368     $scope.$watch('stop_unload',
369         function(newVal, oldVal) {
370             if (newVal && newVal != oldVal && $scope.opac_iframe) {
371                 $($scope.opac_iframe.dom.contentWindow).on('beforeunload', function(){
372                     return 'There is unsaved data in this record.'
373                 });
374             } else {
375                 if ($scope.opac_iframe)
376                     $($scope.opac_iframe.dom.contentWindow).off('beforeunload');
377             }
378         }
379     );
380
381     // Set the "last bib" cookie, if we have that
382     if ($scope.record_id)
383         egCore.hatch.setLocalItem("eg.cat.last_record_retrieved", $scope.record_id);
384
385     $scope.refresh_record_callback = function (record_id) {
386         egCore.pcrud.retrieve('bre', record_id, {
387             flesh : 1,
388             flesh_fields : {
389                 bre : ['simple_record','creator','editor']
390             }
391         }).then(function(rec) {
392             rec.owner(egCore.org.get(rec.owner()));
393             $scope.summary_pane_record = rec;
394         });
395
396         return record_id;
397     }
398
399     // also set it when the iframe changes to a new record
400     $scope.handle_page = function(url) {
401
402         if (!url || url == 'about:blank') {
403             // nothing loaded.  If we already have a record ID, leave it.
404             return;
405         }
406
407         var match = url.match(/\/+opac\/+record\/+(\d+)/);
408         if (match) {
409             $scope.record_id = match[1];
410             egCore.hatch.setLocalItem("eg.cat.last_record_retrieved", $scope.record_id);
411             $scope.holdings_record_id_changed($scope.record_id);
412             conjoinedSvc.fetch($scope.record_id).then(function(){
413                 $scope.conjoinedGridDataProvider.refresh();
414             });
415             init_parts_url();
416             $location.update_path('/cat/catalog/record/' + $scope.record_id);
417         } else {
418             delete $scope.record_id;
419             $scope.from_route = false;
420         }
421
422         // child scope is executing this function, so our digest doesn't fire ... thus,
423         $scope.$apply();
424
425         if (!$scope.in_opac_call) {
426             if ($scope.record_id) {
427                 $scope.default_tab = egCore.hatch.getLocalItem( 'eg.cat.default_record_tab' );
428                 tab = $routeParams.record_tab || $scope.default_tab || 'catalog';
429             } else {
430                 tab = $routeParams.record_tab || 'catalog';
431             }
432             $scope.set_record_tab(tab);
433         } else {
434             $scope.in_opac_call = false;
435         }
436     }
437
438     // xulG catalog handlers
439     $scope.handlers = { }
440
441     // ------------------------------------------------------------------
442     // Conjoined items
443
444     $scope.conjoinedGridControls = {};
445     $scope.conjoinedGridDataProvider = egGridDataProvider.instance({
446         get : function(offset, count) {
447             return this.arrayNotifier(conjoinedSvc.items, offset, count);
448         }
449     });
450
451     $scope.changeConjoinedType = function () {
452         var peers = egCore.idl.Clone($scope.conjoinedGridControls.selectedItems());
453         angular.forEach(peers, function (p) {
454             p.target_copy(p.target_copy().id());
455             p.peer_type(p.peer_type().id());
456         });
457
458         var conjoinedGridDataProviderRef = $scope.conjoinedGridDataProvider;
459
460         return $modal.open({
461             templateUrl: './cat/catalog/t_conjoined_selector',
462             animation: true,
463             controller:
464                    ['$scope','$modalInstance',
465             function($scope , $modalInstance) {
466                 $scope.update = true;
467
468                 $scope.peer_type = null;
469                 $scope.peer_type_list = [];
470                 conjoinedSvc.get_peer_types().then(function(list){
471                     $scope.peer_type_list = list;
472                 });
473     
474                 $scope.ok = function(type) {
475                     var promises = [];
476     
477                     angular.forEach(peers, function (p) {
478                         p.ischanged(1);
479                         p.peer_type(type);
480                         promises.push(egCore.pcrud.update(p));
481                     });
482     
483                     return $q.all(promises)
484                         .then(function(){$modalInstance.close()})
485                         .then(function(){return conjoinedSvc.fetch()})
486                         .then(function(){conjoinedGridDataProviderRef.refresh()});
487                 }
488     
489                 $scope.cancel = function($event) {
490                     $modalInstance.dismiss();
491                     $event.preventDefault();
492                 }
493             }]
494         });
495         
496     }
497
498     $scope.refreshConjoined = function () {
499         conjoinedSvc.fetch($scope.record_id)
500         .then(function(){$scope.conjoinedGridDataProvider.refresh();});
501     }
502
503     $scope.deleteSelectedConjoined = function () {
504         var peers = $scope.conjoinedGridControls.selectedItems();
505
506         if (peers.length > 0) {
507             egConfirmDialog.open(
508                 egCore.strings.CONFIRM_DELETE_PEERS,
509                 egCore.strings.CONFIRM_DELETE_PEERS_MESSAGE,
510                 {peers : peers.length}
511             ).result.then(function() {
512                 angular.forEach(peers, function (p) {
513                     p.isdeleted(1);
514                 });
515
516                 egCore.pcrud.remove(peers).then(function() {
517                     return conjoinedSvc.fetch();
518                 }).then(function() {
519                     $scope.conjoinedGridDataProvider.refresh();
520                 });
521             });
522         }
523     }
524     if ($scope.record_id)
525         conjoinedSvc.fetch($scope.record_id);
526
527     // ------------------------------------------------------------------
528     // Holdings
529
530     $scope.holdingsGridControls = {
531         activateItem : function (item) {
532             $scope.selectedHoldingsVolCopyEdit();
533         }
534     };
535     $scope.holdingsGridDataProvider = egGridDataProvider.instance({
536         get : function(offset, count) {
537             return this.arrayNotifier(holdingsSvcInst.copies, offset, count);
538         }
539     });
540
541     $scope.add_copies_to_bucket = function() {
542         var copy_list = gatherSelectedHoldingsIds();
543         if (copy_list.length == 0) return;
544
545         return $modal.open({
546             templateUrl: './cat/catalog/t_add_to_bucket',
547             animation: true,
548             size: 'md',
549             controller:
550                    ['$scope','$modalInstance',
551             function($scope , $modalInstance) {
552
553                 $scope.bucket_id = 0;
554                 $scope.newBucketName = '';
555                 $scope.allBuckets = [];
556
557                 egCore.net.request(
558                     'open-ils.actor',
559                     'open-ils.actor.container.retrieve_by_class.authoritative',
560                     egCore.auth.token(), egCore.auth.user().id(),
561                     'copy', 'staff_client'
562                 ).then(function(buckets) { $scope.allBuckets = buckets; });
563
564                 $scope.add_to_bucket = function() {
565                     var promises = [];
566                     angular.forEach(copy_list, function (cp) {
567                         var item = new egCore.idl.ccbi()
568                         item.bucket($scope.bucket_id);
569                         item.target_copy(cp);
570                         promises.push(
571                             egCore.net.request(
572                                 'open-ils.actor',
573                                 'open-ils.actor.container.item.create',
574                                 egCore.auth.token(), 'copy', item
575                             )
576                         );
577
578                         return $q.all(promises).then(function() {
579                             $modalInstance.close();
580                         });
581                     });
582                 }
583
584                 $scope.add_to_new_bucket = function() {
585                     var bucket = new egCore.idl.ccb();
586                     bucket.owner(egCore.auth.user().id());
587                     bucket.name($scope.newBucketName);
588                     bucket.description('');
589                     bucket.btype('staff_client');
590
591                     return egCore.net.request(
592                         'open-ils.actor',
593                         'open-ils.actor.container.create',
594                         egCore.auth.token(), 'copy', bucket
595                     ).then(function(bucket) {
596                         $scope.bucket_id = bucket;
597                         $scope.add_to_bucket();
598                     });
599                 }
600
601                 $scope.cancel = function() {
602                     $modalInstance.dismiss();
603                 }
604             }]
605         });
606     }
607
608     $scope.requestItems = function() {
609         var copy_list = gatherSelectedHoldingsIds();
610         if (copy_list.length == 0) return;
611
612         return $modal.open({
613             templateUrl: './cat/catalog/t_request_items',
614             animation: true,
615             controller:
616                    ['$scope','$modalInstance',
617             function($scope , $modalInstance) {
618                 $scope.user = null;
619                 $scope.first_user_fetch = true;
620
621                 $scope.hold_data = {
622                     hold_type : 'C',
623                     copy_list : copy_list,
624                     pickup_lib: egCore.org.get(egCore.auth.user().ws_ou()),
625                     user      : egCore.auth.user().id()
626                 };
627
628                 egUser.get( $scope.hold_data.user ).then(function(u) {
629                     $scope.user = u;
630                     $scope.barcode = u.card().barcode();
631                     $scope.user_name = egUser.format_name(u);
632                     $scope.hold_data.user = u.id();
633                 });
634
635                 $scope.user_name = '';
636                 $scope.barcode = '';
637                 $scope.$watch('barcode', function (n) {
638                     if (!$scope.first_user_fetch) {
639                         egUser.getByBarcode(n).then(function(u) {
640                             $scope.user = u;
641                             $scope.user_name = egUser.format_name(u);
642                             $scope.hold_data.user = u.id();
643                         }, function() {
644                             $scope.user = null;
645                             $scope.user_name = '';
646                             delete $scope.hold_data.user;
647                         });
648                     }
649                     $scope.first_user_fetch = false;
650                 });
651
652                 $scope.ok = function(h) {
653                     var args = {
654                         patronid  : h.user,
655                         hold_type : h.hold_type,
656                         pickup_lib: h.pickup_lib.id(),
657                         depth     : 0
658                     };
659
660                     egCore.net.request(
661                         'open-ils.circ',
662                         'open-ils.circ.holds.test_and_create.batch.override',
663                         egCore.auth.token(), args, h.copy_list
664                     );
665
666                     $modalInstance.close();
667                 }
668
669                 $scope.cancel = function($event) {
670                     $modalInstance.dismiss();
671                     $event.preventDefault();
672                 }
673             }]
674         });
675     }
676
677     $scope.replaceBarcodes = function() {
678         var copy_list = gatherSelectedRawCopies();
679         if (copy_list.length == 0) return;
680
681         var holdingsGridDataProviderRef = $scope.holdingsGridDataProvider;
682
683         angular.forEach(copy_list, function (cp) {
684             $modal.open({
685                 templateUrl: './cat/share/t_replace_barcode',
686                 animation: true,
687                 controller:
688                            ['$scope','$modalInstance',
689                     function($scope , $modalInstance) {
690                         $scope.isModal = true;
691                         $scope.focusBarcode = false;
692                         $scope.focusBarcode2 = true;
693                         $scope.barcode1 = cp.barcode();
694
695                         $scope.updateBarcode = function() {
696                             $scope.copyNotFound = false;
697                             $scope.updateOK = false;
698                 
699                             egCore.pcrud.search('acp',
700                                 {deleted : 'f', barcode : $scope.barcode1})
701                             .then(function(copy) {
702                 
703                                 if (!copy) {
704                                     $scope.focusBarcode = true;
705                                     $scope.copyNotFound = true;
706                                     return;
707                                 }
708                 
709                                 $scope.copyId = copy.id();
710                                 copy.barcode($scope.barcode2);
711                 
712                                 egCore.pcrud.update(copy).then(function(stat) {
713                                     $scope.updateOK = stat;
714                                     $scope.focusBarcode = true;
715                                     holdingsSvc.fetchAgain().then(function (){
716                                         holdingsGridDataProviderRef.refresh();
717                                     });
718                                 });
719
720                             });
721                             $modalInstance.close();
722                         }
723
724                         $scope.cancel = function($event) {
725                             $modalInstance.dismiss();
726                             $event.preventDefault();
727                         }
728                     }
729                 ]
730             });
731         });
732     }
733
734     // refresh the list of holdings when the record_id is changed.
735     $scope.holdings_record_id_changed = function(id) {
736         if ($scope.record_id != id) $scope.record_id = id;
737         console.log('record id changed to ' + id + ', loading new holdings');
738         holdingsSvcInst.fetch({
739             rid : $scope.record_id,
740             org : $scope.holdings_ou,
741             copy: $scope.holdings_show_copies,
742             vol : $scope.holdings_show_vols,
743             empty: $scope.holdings_show_empty
744         }).then(function() {
745             $scope.holdingsGridDataProvider.refresh();
746         });
747     }
748
749     // refresh the list of holdings when the filter lib is changed.
750     $scope.holdings_ou = egCore.org.get(egCore.auth.user().ws_ou());
751     $scope.holdings_ou_changed = function(org) {
752         $scope.holdings_ou = org;
753         holdingsSvcInst.fetch({
754             rid : $scope.record_id,
755             org : $scope.holdings_ou,
756             copy: $scope.holdings_show_copies,
757             vol : $scope.holdings_show_vols,
758             empty: $scope.holdings_show_empty
759         }).then(function() {
760             $scope.holdingsGridDataProvider.refresh();
761         });
762     }
763
764     $scope.holdings_cb_changed = function(cb,newVal,norefresh) {
765         $scope[cb] = newVal;
766         egCore.hatch.setItem('cat.' + cb, newVal);
767         if (!norefresh) holdingsSvcInst.fetch({
768             rid : $scope.record_id,
769             org : $scope.holdings_ou,
770             copy: $scope.holdings_show_copies,
771             vol : $scope.holdings_show_vols,
772             empty: $scope.holdings_show_empty
773         }).then(function() {
774             $scope.holdingsGridDataProvider.refresh();
775         });
776     }
777
778     egCore.hatch.getItem('cat.holdings_show_vols').then(function(x){
779         if (typeof x ==  'undefined') x = true;
780         $scope.holdings_cb_changed('holdings_show_vols',x,true);
781         $('#holdings_show_vols').prop('checked', x);
782     }).then(function(){
783         egCore.hatch.getItem('cat.holdings_show_copies').then(function(x){
784             if (typeof x ==  'undefined') x = true;
785             $scope.holdings_cb_changed('holdings_show_copies',x,true);
786             $('#holdings_show_copies').prop('checked', x);
787         }).then(function(){
788             egCore.hatch.getItem('cat.holdings_show_empty').then(function(x){
789                 if (typeof x ==  'undefined') x = true;
790                 $scope.holdings_cb_changed('holdings_show_empty',x);
791                 $('#holdings_show_empty').prop('checked', x);
792             })
793         })
794     });
795
796     $scope.vols_not_shown = function () {
797         return !$scope.holdings_show_vols;
798     }
799
800     $scope.copies_not_shown = function () {
801         return !$scope.holdings_show_copies;
802     }
803
804     $scope.holdings_checkbox_handler = function (item) {
805         $scope.holdings_cb_changed(item.checkbox,item.checked);
806     }
807
808     function gatherSelectedHoldingsIds () {
809         var cp_id_list = [];
810         angular.forEach(
811             $scope.holdingsGridControls.selectedItems(),
812             function (item) { cp_id_list = cp_id_list.concat(item.id_list) }
813         );
814         return cp_id_list;
815     }
816
817     function gatherSelectedRawCopies () {
818         var cp_list = [];
819         angular.forEach(
820             $scope.holdingsGridControls.selectedItems(),
821             function (item) { if (item.raw) cp_list = cp_list.concat(item.raw) }
822         );
823         return cp_list;
824     }
825
826     function gatherSelectedEmptyVolumeIds () {
827         var cn_id_list = [];
828         angular.forEach(
829             $scope.holdingsGridControls.selectedItems(),
830             function (item) {
831                 if (item.copy_count == 0)
832                     cn_id_list.push(item.call_number.id)
833             }
834         );
835         return cn_id_list;
836     }
837
838     function gatherSelectedVolumeIds () {
839         var cn_id_list = [];
840         angular.forEach(
841             $scope.holdingsGridControls.selectedItems(),
842             function (item) {
843                 if (cn_id_list.indexOf(item.call_number.id) == -1)
844                     cn_id_list.push(item.call_number.id)
845             }
846         );
847         return cn_id_list;
848     }
849
850     $scope.selectedHoldingsDelete = function (vols, copies) {
851
852         var cnHash = {};
853         var perCnCopies = {};
854
855         var cn_count = 0;
856         var cp_count = 0;
857
858         angular.forEach(
859             $scope.holdingsGridControls.selectedItems(),
860             function (item) {
861                 if (vols && item.raw_call_number) {
862                     cnHash[item.call_number.id] = egCore.idl.Clone(item.raw_call_number);
863                     cnHash[item.call_number.id].isdeleted(1);
864                     cn_count++;
865                 } else if (copies) {
866                     angular.forEach(egCore.idl.Clone(item.raw), function (cp) {
867                         cp.isdeleted(1);
868                         cp_count++;
869                         var cn_id = cp.call_number().id();
870                         if (!cnHash[cn_id]) {
871                             cnHash[cn_id] = cp.call_number();
872                             perCnCopies[cn_id] = [cp];
873                         } else {
874                             perCnCopies[cn_id].push(cp);
875                         }
876                         cp.call_number(cn_id); // prevent loops in JSON-ification
877                     });
878
879                 }
880             }
881         );
882
883         angular.forEach(perCnCopies, function (v, k) {
884             if (vols) {
885                 cnHash[k].isdeleted(1);
886                 cn_count++;
887             }
888             cnHash[k].copies(v);
889         });
890
891         cnList = [];
892         angular.forEach(cnHash, function (v, k) {
893             cnList.push(v);
894         });
895
896         if (cnList.length == 0) return;
897
898         var flags = {};
899         if (vols && copies) flags.force_delete_copies = 1;
900
901         egConfirmDialog.open(
902             egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES,
903             egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES_MESSAGE,
904             {copies : cp_count, volumes : cn_count}
905         ).result.then(function() {
906             egCore.net.request(
907                 'open-ils.cat',
908                 'open-ils.cat.asset.volume.fleshed.batch.update.override',
909                 egCore.auth.token(), cnList, 1, flags
910             ).then(function(update_count) {
911                 holdingsSvcInst.fetchAgain().then(function() {
912                     $scope.holdingsGridDataProvider.refresh();
913                 });
914             });
915         });
916     }
917     $scope.selectedHoldingsCopyDelete = function () { $scope.selectedHoldingsDelete(false,true) }
918     $scope.selectedHoldingsVolCopyDelete = function () { $scope.selectedHoldingsDelete(true,true) }
919     $scope.selectedHoldingsEmptyVolCopyDelete = function () { $scope.selectedHoldingsDelete(true,false) }
920
921     spawnHoldingsAdd = function (vols,copies){
922         var raw = [];
923         if (copies) { // just a copy on existing volumes
924             angular.forEach(gatherSelectedVolumeIds(), function (v) {
925                 raw.push( {callnumber : v} );
926             });
927         } else if (vols) {
928             angular.forEach(
929                 $scope.holdingsGridControls.selectedItems(),
930                 function (item) {
931                     raw.push({owner : item.owner_id});
932                 }
933             );
934         }
935
936         if (raw.length == 0) raw.push({});
937
938         egCore.net.request(
939             'open-ils.actor',
940             'open-ils.actor.anon_cache.set_value',
941             null, 'edit-these-copies', {
942                 record_id: $scope.record_id,
943                 raw: raw,
944                 hide_vols : false,
945                 hide_copies : false
946             }
947         ).then(function(key) {
948             if (key) {
949                 var url = egCore.env.basePath + 'cat/volcopy/' + key;
950                 $timeout(function() { $window.open(url, '_blank') });
951             } else {
952                 alert('Could not create anonymous cache key!');
953             }
954         });
955     }
956     $scope.selectedHoldingsVolCopyAdd = function () { spawnHoldingsAdd(true,false) }
957     $scope.selectedHoldingsCopyAdd = function () { spawnHoldingsAdd(false,true) }
958
959     spawnHoldingsEdit = function (hide_vols,hide_copies){
960         egCore.net.request(
961             'open-ils.actor',
962             'open-ils.actor.anon_cache.set_value',
963             null, 'edit-these-copies', {
964                 record_id: $scope.record_id,
965                 copies: gatherSelectedHoldingsIds(),
966                 raw: gatherSelectedEmptyVolumeIds().map(
967                     function(v){ return { callnumber : v } }
968                 ),
969                 hide_vols : hide_vols,
970                 hide_copies : hide_copies
971             }
972         ).then(function(key) {
973             if (key) {
974                 var url = egCore.env.basePath + 'cat/volcopy/' + key;
975                 $timeout(function() { $window.open(url, '_blank') });
976             } else {
977                 alert('Could not create anonymous cache key!');
978             }
979         });
980     }
981     $scope.selectedHoldingsVolCopyEdit = function () { spawnHoldingsEdit(false,false) }
982     $scope.selectedHoldingsVolEdit = function () { spawnHoldingsEdit(false,true) }
983     $scope.selectedHoldingsCopyEdit = function () { spawnHoldingsEdit(true,false) }
984
985     $scope.selectedHoldingsItemStatus = function (){
986         var url = egCore.env.basePath + 'cat/item/search/' + gatherSelectedHoldingsIds().join(',')
987         $timeout(function() { $window.open(url, '_blank') });
988     }
989
990     $scope.markVolAsItemTarget = function() {
991         if ($scope.holdingsGridControls.selectedItems()[0].call_number.id) { // cn.id missing when vols are collapsed
992             egCore.hatch.setLocalItem(
993                 'eg.cat.item_transfer_target',
994                 $scope.holdingsGridControls.selectedItems()[0].call_number.id
995             );
996         }
997     }
998
999     $scope.markLibAsVolTarget = function() {
1000         return $modal.open({
1001             templateUrl: './cat/catalog/t_choose_vol_target_lib',
1002             animation: true,
1003             controller:
1004                    ['$scope','$modalInstance',
1005             function($scope , $modalInstance) {
1006
1007                 var orgId = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target') || 1;
1008                 $scope.org = egCore.org.get(orgId);
1009                 $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
1010                 $scope.ok = function(org) {
1011                     egCore.hatch.setLocalItem(
1012                         'eg.cat.volume_transfer_target',
1013                         org.id()
1014                     );
1015                     $modalInstance.close();
1016                 }
1017                 $scope.cancel = function($event) {
1018                     $modalInstance.dismiss();
1019                     $event.preventDefault();
1020                 }
1021             }]
1022         });
1023     }
1024     $scope.markLibFromSelectedAsVolTarget = function() {
1025         egCore.hatch.setLocalItem(
1026             'eg.cat.volume_transfer_target',
1027             $scope.holdingsGridControls.selectedItems()[0].owner_id
1028         );
1029     }
1030
1031     $scope.selectedHoldingsItemStatusDetail = function (){
1032         angular.forEach(
1033             gatherSelectedHoldingsIds(),
1034             function (cid) {
1035                 var url = egCore.env.basePath +
1036                           'cat/item/' + cid;
1037                 $timeout(function() { $window.open(url, '_blank') });
1038             }
1039         );
1040     }
1041
1042     $scope.transferVolumesToRecord = function (){
1043         var target_record = egCore.hatch.getLocalItem('eg.cat.marked_volume_transfer_record');
1044         if (!target_record) return;
1045         if ($scope.record_id == target_record) return;
1046         var items = $scope.holdingsGridControls.selectedItems();
1047         if (!items.length) return;
1048
1049         var vols_to_move   = {};
1050         angular.forEach(items, function(item) {
1051             if (!(item.call_number.owning_lib in vols_to_move)) {
1052                 vols_to_move[item.call_number.owning_lib] = new Array;
1053             }
1054             vols_to_move[item.call_number.owning_lib].push(item.call_number.id);
1055         });
1056
1057         var promises = [];        
1058         angular.forEach(vols_to_move, function(vols, owning_lib) {
1059             promises.push(egCore.net.request(
1060                 'open-ils.cat',
1061                 'open-ils.cat.asset.volume.batch.transfer.override',
1062                 egCore.auth.token(), {
1063                     docid   : target_record,
1064                     lib     : owning_lib,
1065                     volumes : vols
1066                 }
1067             ));
1068         });
1069         $q.all(promises).then(function(success) {
1070             if (success) {
1071                 holdingsSvcInst.fetchAgain().then(function() {
1072                     $scope.holdingsGridDataProvider.refresh();
1073                 });
1074             } else {
1075                 alert('Could not transfer volumes!');
1076             }
1077         });
1078     }
1079
1080     $scope.transferVolumes = function (new_record){
1081         var xfer_target = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target');
1082
1083         if (xfer_target) {
1084             egCore.net.request(
1085                 'open-ils.cat',
1086                 'open-ils.cat.asset.volume.batch.transfer.override',
1087                 egCore.auth.token(), {
1088                     docid   : (new_record ? new_record : $scope.record_id),
1089                     lib     : xfer_target,
1090                     volumes : gatherSelectedVolumeIds()
1091                 }
1092             ).then(function(success) {
1093                 if (success) {
1094                     holdingsSvcInst.fetchAgain().then(function() {
1095                         $scope.holdingsGridDataProvider.refresh();
1096                     });
1097                 } else {
1098                     alert('Could not transfer volumes!');
1099                 }
1100             });
1101         }
1102         
1103     }
1104
1105     $scope.transferVolumesToRecordAndLibrary = function() {
1106         var target_record = egCore.hatch.getLocalItem('eg.cat.marked_volume_transfer_record');
1107         if (!target_record) return;
1108         $scope.transferVolumes(target_record);
1109     }
1110
1111     // this "transfers" selected copies to a new owning library,
1112     // auto-creating volumes and deleting unused volumes as required.
1113     $scope.changeItemOwningLib = function() {
1114         var xfer_target = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target');
1115         var items = $scope.holdingsGridControls.selectedItems();
1116         if (!xfer_target || !items.length) {
1117             return;
1118         }
1119         var vols_to_move   = {};
1120         var copies_to_move = {};
1121         angular.forEach(items, function(item) {
1122             if (item.call_number.owning_lib != xfer_target) {
1123                 if (item.call_number.id in vols_to_move) {
1124                     copies_to_move[item.call_number.id].push(item.id);
1125                 } else {
1126                     vols_to_move[item.call_number.id] = item.call_number;
1127                     copies_to_move[item.call_number.id] = new Array;
1128                     copies_to_move[item.call_number.id].push(item.id);
1129                 }
1130             }
1131         });
1132     
1133         var promises = [];
1134         angular.forEach(vols_to_move, function(vol) {
1135             promises.push(egCore.net.request(
1136                 'open-ils.cat',
1137                 'open-ils.cat.call_number.find_or_create',
1138                 egCore.auth.token(),
1139                 vol.label,
1140                 vol.record,
1141                 xfer_target,
1142                 vol.prefix.id,
1143                 vol.suffix.id,
1144                 vol.label_class
1145             ).then(function(resp) {
1146                 var evt = egCore.evt.parse(resp);
1147                 if (evt) return;
1148                 return egCore.net.request(
1149                     'open-ils.cat',
1150                     'open-ils.cat.transfer_copies_to_volume',
1151                     egCore.auth.token(),
1152                     resp.acn_id,
1153                     copies_to_move[vol.id]
1154                 );
1155             }));
1156         });
1157         $q.all(promises).then(function() {
1158             holdingsSvcInst.fetchAgain().then(function() {
1159                 $scope.holdingsGridDataProvider.refresh();
1160             });
1161         });
1162     }
1163
1164     $scope.transferItems = function (){
1165         var xfer_target = egCore.hatch.getLocalItem('eg.cat.item_transfer_target');
1166         var copy_ids = gatherSelectedHoldingsIds();
1167         if (xfer_target && copy_ids.length > 0) {
1168             egCore.net.request(
1169                 'open-ils.cat',
1170                 'open-ils.cat.transfer_copies_to_volume',
1171                 egCore.auth.token(),
1172                 xfer_target,
1173                 copy_ids
1174             ).then(
1175                 function(resp) { // oncomplete
1176                     var evt = egCore.evt.parse(resp);
1177                     if (evt) {
1178                         egConfirmDialog.open(
1179                             egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_TITLE,
1180                             egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY,
1181                             {'evt_desc': evt.desc}
1182                         ).result.then(function() {
1183                             egCore.net.request(
1184                                 'open-ils.cat',
1185                                 'open-ils.cat.transfer_copies_to_volume.override',
1186                                 egCore.auth.token(),
1187                                 xfer_target,
1188                                 copy_ids,
1189                                 { events: ['TITLE_LAST_COPY', 'COPY_DELETE_WARNING'] }
1190                             ).then(function(resp) {
1191                                 holdingsSvcInst.fetchAgain().then(function() {
1192                                     $scope.holdingsGridDataProvider.refresh();
1193                                 });
1194                             });
1195                         });
1196                     } else {
1197                         holdingsSvcInst.fetchAgain().then(function() {
1198                             $scope.holdingsGridDataProvider.refresh();
1199                         });
1200                     }
1201                 },
1202                 null, // onerror
1203                 null // onprogress
1204             )
1205         }
1206     }
1207
1208     $scope.selectedHoldingsItemStatusTgrEvt = function (){
1209         angular.forEach(
1210             gatherSelectedHoldingsIds(),
1211             function (cid) {
1212                 var url = egCore.env.basePath +
1213                           'cat/item/' + cid + '/triggered_events';
1214                 $timeout(function() { $window.open(url, '_blank') });
1215             }
1216         );
1217     }
1218
1219     $scope.selectedHoldingsItemStatusHolds = function (){
1220         angular.forEach(
1221             gatherSelectedHoldingsIds(),
1222             function (cid) {
1223                 var url = egCore.env.basePath +
1224                           'cat/item/' + cid + '/holds';
1225                 $timeout(function() { $window.open(url, '_blank') });
1226             }
1227         );
1228     }
1229
1230     $scope.selectedHoldingsDamaged = function () {
1231         egCirc.mark_damaged(gatherSelectedHoldingsIds()).then(function() {
1232             holdingsSvcInst.fetchAgain().then(function() {
1233                 $scope.holdingsGridDataProvider.refresh();
1234             });
1235         });
1236     }
1237
1238     $scope.selectedHoldingsMissing = function () {
1239         egCirc.mark_missing(gatherSelectedHoldingsIds()).then(function() {
1240             holdingsSvcInst.fetchAgain().then(function() {
1241                 $scope.holdingsGridDataProvider.refresh();
1242             });
1243         });
1244     }
1245
1246     $scope.attach_to_peer_bib = function() {
1247         var copy_list = gatherSelectedHoldingsIds();
1248         if (copy_list.length == 0) return;
1249
1250         egCore.hatch.getItem('eg.cat.marked_conjoined_record').then(function(target_record) {
1251             if (!target_record) return;
1252
1253             return $modal.open({
1254                 templateUrl: './cat/catalog/t_conjoined_selector',
1255                 animation: true,
1256                 controller:
1257                        ['$scope','$modalInstance',
1258                 function($scope , $modalInstance) {
1259                     $scope.update = false;
1260
1261                     $scope.peer_type = null;
1262                     $scope.peer_type_list = [];
1263                     conjoinedSvc.get_peer_types().then(function(list){
1264                         $scope.peer_type_list = list;
1265                     });
1266     
1267                     $scope.ok = function(type) {
1268                         var promises = [];
1269     
1270                         angular.forEach(copy_list, function (cp) {
1271                             var n = new egCore.idl.bpbcm();
1272                             n.isnew(true);
1273                             n.peer_record(target_record);
1274                             n.target_copy(cp);
1275                             n.peer_type(type);
1276                             promises.push(egCore.pcrud.create(n));
1277                         });
1278     
1279                         return $q.all(promises).then(function(){$modalInstance.close()});
1280                     }
1281     
1282                     $scope.cancel = function($event) {
1283                         $modalInstance.dismiss();
1284                         $event.preventDefault();
1285                     }
1286                 }]
1287             });
1288         });
1289     }
1290
1291
1292     // ------------------------------------------------------------------
1293     // Holds 
1294     var provider = egGridDataProvider.instance({});
1295     $scope.hold_grid_data_provider = provider;
1296     $scope.grid_actions = egHoldGridActions;
1297     $scope.grid_actions.refresh = function () { provider.refresh() };
1298     $scope.hold_grid_controls = {};
1299
1300     var hold_ids = []; // current list of holds
1301     function fetchHolds(offset, count) {
1302         var ids = hold_ids.slice(offset, offset + count);
1303         return egHolds.fetch_holds(ids).then(null, null,
1304             function(hold_data) { 
1305                 return hold_data;
1306             }
1307         );
1308     }
1309
1310     provider.get = function(offset, count) {
1311         if ($scope.record_tab != 'holds') return $q.when();
1312         var deferred = $q.defer();
1313         hold_ids = []; // no caching ATM
1314
1315         // fetch the IDs
1316         egCore.net.request(
1317             'open-ils.circ',
1318             'open-ils.circ.holds.retrieve_all_from_title',
1319             egCore.auth.token(), $scope.record_id, 
1320             {pickup_lib : egCore.org.descendants($scope.pickup_ou.id(), true)}
1321         ).then(
1322             function(hold_data) {
1323                 angular.forEach(hold_data, function(list, type) {
1324                     hold_ids = hold_ids.concat(list);
1325                 });
1326                 fetchHolds(offset, count).then(
1327                     deferred.resolve, null, deferred.notify);
1328             }
1329         );
1330
1331         return deferred.promise;
1332     }
1333
1334     $scope.detail_view = function(action, user_data, items) {
1335         if (h = items[0]) {
1336             $scope.detail_hold_id = h.hold.id();
1337         }
1338     }
1339
1340     $scope.list_view = function(items) {
1341          $scope.detail_hold_id = null;
1342     }
1343
1344     // refresh the list of record holds when the pickup lib is changed.
1345     $scope.pickup_ou = egCore.org.get(egCore.auth.user().ws_ou());
1346     $scope.pickup_ou_changed = function(org) {
1347         $scope.pickup_ou = org;
1348         provider.refresh();
1349     }
1350
1351     $scope.print_holds = function() {
1352         var holds = [];
1353         angular.forEach($scope.hold_grid_controls.allItems(), function(item) {
1354             holds.push({
1355                 hold : egCore.idl.toHash(item.hold),
1356                 patron_last : item.patron_last,
1357                 patron_alias : item.patron_alias,
1358                 patron_barcode : item.patron_barcode,
1359                 copy : egCore.idl.toHash(item.copy),
1360                 volume : egCore.idl.toHash(item.volume),
1361                 title : item.mvr.title(),
1362                 author : item.mvr.author()
1363             });
1364         });
1365
1366         egCore.print.print({
1367             context : 'receipt', 
1368             template : 'holds_for_bib', 
1369             scope : {holds : holds}
1370         });
1371     }
1372
1373     $scope.mark_hold_transfer_dest = function() {
1374         egCore.hatch.setLocalItem(
1375             'eg.circ.hold.title_transfer_target', $scope.record_id);
1376     }
1377
1378     // UI presents this option as "all holds"
1379     $scope.transfer_holds_to_marked = function() {
1380         var hold_ids = $scope.hold_grid_controls.allItems().map(
1381             function(hold_data) {return hold_data.hold.id()});
1382         egHolds.transfer_to_marked_title(hold_ids);
1383     }
1384
1385     // ------------------------------------------------------------------
1386     // Initialize the selected tab
1387
1388     function init_cat_url() {
1389         // Set the initial catalog URL.  This only happens once.
1390         // The URL is otherwise generated through user navigation.
1391         if ($scope.catalog_url) return; 
1392
1393         var url = $location.absUrl().replace(/\/staff.*/, '/opac/advanced');
1394
1395         // A record ID in the path indicates a request for the record-
1396         // specific page.
1397         if ($routeParams.record_id) {
1398             url = url.replace(/advanced/, '/record/' + $scope.record_id);
1399         }
1400
1401         $scope.catalog_url = url;
1402     }
1403
1404     function init_parts_url() {
1405         $scope.parts_url = $location
1406             .absUrl()
1407             .replace(
1408                 /\/staff.*/,
1409                 '/conify/global/biblio/monograph_part?r='+$scope.record_id
1410             );
1411     }
1412
1413     $scope.set_record_tab = function(tab) {
1414         $scope.record_tab = tab;
1415
1416         switch(tab) {
1417
1418             case 'monoparts':
1419                 init_parts_url();
1420                 break;
1421
1422             case 'catalog':
1423                 init_cat_url();
1424                 break;
1425
1426             case 'holds':
1427                 $scope.detail_hold_record_id = $scope.record_id; 
1428                 // refresh the holds grid
1429                 provider.refresh();
1430                 break;
1431         }
1432     }
1433
1434     $scope.set_default_record_tab = function() {
1435         egCore.hatch.setLocalItem(
1436             'eg.cat.default_record_tab', $scope.record_tab);
1437         $timeout(function(){$scope.default_tab = $scope.record_tab});
1438     }
1439
1440     var tab;
1441     if ($scope.record_id) {
1442         $scope.default_tab = egCore.hatch.getLocalItem( 'eg.cat.default_record_tab' );
1443         tab = $routeParams.record_tab || $scope.default_tab || 'catalog';
1444
1445     } else {
1446         tab = $routeParams.record_tab || 'catalog';
1447     }
1448     $scope.set_record_tab(tab);
1449
1450 }])
1451
1452 .controller('AuthorityCtrl',
1453        ['$scope','$routeParams','$location','$window','$q','egCore',
1454 function($scope , $routeParams , $location , $window , $q , egCore) {
1455
1456     // set record ID on page load if available...
1457     $scope.authority_id = $routeParams.authority_id;
1458
1459     if ($routeParams.authority_id) $scope.from_route = true;
1460     else $scope.from_route = false;
1461
1462     $scope.stop_unload = false;
1463 }])
1464
1465 .controller('URLVerifyCtrl',
1466        ['$scope','$location',
1467 function($scope , $location) {
1468     $scope.verifyurls_url = $location.absUrl().replace(/\/staff.*/, '/url_verify/sessions');
1469 }])
1470
1471 .controller('VandelayCtrl',
1472        ['$scope','$location',
1473 function($scope , $location) {
1474     $scope.vandelay_url = $location.absUrl().replace(/\/staff.*/, '/vandelay/vandelay');
1475 }])
1476
1477 .controller('ManageAuthoritiesCtrl',
1478        ['$scope','$location',
1479 function($scope , $location) {
1480     $scope.manageauthorities_url = $location.absUrl().replace(/\/staff.*/, '/cat/authority/list');
1481 }])
1482
1483 .controller('BatchEditCtrl',
1484        ['$scope','$location','$routeParams',
1485 function($scope , $location , $routeParams) {
1486     $scope.batchedit_url = $location.absUrl().replace(/\/eg.*/, '/opac/extras/merge_template');
1487     if ($routeParams.container_type) {
1488         switch ($routeParams.container_type) {
1489             case 'bucket':
1490                 $scope.batchedit_url += '?recordSource=b&containerid=' + $routeParams.container_id;
1491                 break;
1492             case 'record':
1493                 $scope.batchedit_url += '?recordSource=r&recid=' + $routeParams.container_id;
1494                 break;
1495         };
1496     }
1497 }])
1498
1499  
1500 .filter('boolText', function(){
1501     return function (v) {
1502         return v == 't';
1503     }
1504 })
1505
1506 .factory('conjoinedSvc', 
1507        ['egCore','$q',
1508 function(egCore , $q) {
1509
1510     var service = {
1511         items : [], // record search results
1512         index : 0, // search grid index
1513         rid : null
1514     };
1515
1516     service.flesh = {   
1517         flesh : 4, 
1518         flesh_fields : {
1519             bpbcm : ['target_copy','peer_type'],
1520             acp : ['call_number'],
1521             acn : ['record'],
1522             bre : ['simple_record']
1523         },
1524         // avoid fetching the MARC blob by specifying which
1525         // fields on the bre to select.  More may be needed.
1526         // note that fleshed fields are explicitly selected.
1527         select : { bre : ['id'] },
1528         order_by : { bpbcm : ['id'] },
1529     }
1530
1531     // resolved with the last received copy
1532     service.fetch = function(rid) {
1533         if (!rid && !service.rid) return $q.when();
1534
1535         if (rid) service.rid = rid;
1536         service.items = [];
1537         service.index = 0;
1538
1539         return egCore.pcrud.search(
1540             'bpbcm',
1541             {peer_record : service.rid},
1542             service.flesh,
1543             {atomic : true}
1544         ).then( function(list) { // finished
1545             service.items = list;
1546             return service.items;
1547         });
1548     }
1549
1550     // returns a promise resolved with the list of peer bib types
1551     service.get_peer_types = function() {
1552         if (egCore.env.bpt)
1553             return $q.when(egCore.env.bpt.list);
1554
1555         return egCore.pcrud.retrieveAll('bpt', null, {atomic : true})
1556         .then(function(list) {
1557             egCore.env.absorbList(list, 'bpt');
1558             return list;
1559         });
1560     };
1561
1562     return service;
1563 }])
1564
1565