]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/services/record.js
LP2061136 - Stamping 1405 DB upgrade script
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / cat / services / record.js
1 /**
2  * Simple directive for rending the HTML view of a MARC record.
3  *
4  * <eg-record-html record-id="myRecordIdScopeVariable"></eg-record-id>
5  * OR
6  * <eg-record-html marc-xml="myMarcXmlVariable"></eg-record-html>
7  *
8  * The value of myRecordIdScopeVariable is watched internally and the 
9  * record is updated to match.
10  */
11 angular.module('egCoreMod')
12
13 .directive('egRecordHtml', function() {
14     return {
15         restrict : 'AE',
16         scope : {
17             recordId : '=',
18             marcXml  : '@',
19         },
20         link : function(scope, element, attrs) {
21             scope.element = angular.element(element);
22
23             // kill refs to destroyed DOM elements
24             element.bind("$destroy", function() {
25                 delete scope.element;
26             });
27         },
28         controller : 
29                    ['$scope','egCore',
30             function($scope , egCore) {
31
32                 function loadRecordHtml() {
33                     egCore.net.request(
34                         'open-ils.search',
35                         'open-ils.search.biblio.record.html',
36                         $scope.recordId,
37                         false,
38                         $scope.marcXml
39                     ).then(function(html) {
40                         if (!html) return;
41
42                         // Remove those pesky non-i8n labels / actions.
43                         // Note: for printing, use the browser print page
44                         // option.  The end result is the same.
45                         html = html.replace(
46                             /<button onclick="window.print(.*?)<\/button>/,'');
47                         html = html.replace(/<title>(.*?)<\/title>/,'');
48
49                         // remove reference to nonexistant CSS file
50                         html = html.replace(/<link(.*?)\/>/,'');
51
52                         $scope.element.html(html);
53                     });
54                 }
55
56                 $scope.$watch('recordId', 
57                     function(newVal, oldVal) {
58                         if (newVal && newVal !== oldVal) {
59                             loadRecordHtml();
60                         }
61                     }
62                 );
63                 $scope.$watch('marcXml', 
64                     function(newVal, oldVal) {
65                         if (newVal && newVal !== oldVal) {
66                             loadRecordHtml();
67                         }
68                     }
69                 );
70
71                 if ($scope.recordId || $scope.marcXml) 
72                     loadRecordHtml();
73             }
74         ]
75     }
76 })
77
78 .directive('egRecordBreaker', function() {
79     return {
80         restrict : 'AE',
81         template : '<pre>{{breaker}}</pre>',
82         scope : {
83             recordId : '=',
84             marcXml  : '=',
85         },
86         link : function(scope, element, attrs) {
87             scope.element = angular.element(element);
88
89             // kill refs to destroyed DOM elements
90             element.bind("$destroy", function() {
91                 delete scope.element;
92             });
93         },
94         controller : 
95                    ['$scope','egCore',
96             function($scope , egCore) {
97
98                 // Match the MARC flat-text editor
99                 MARC21.Record.delimiter = '$';
100
101                 function loadRecordBreaker() {
102                     var xml;
103                     if ($scope.marcXml) {
104                         $scope.breaker = new MARC21.Record({ marcxml : $scope.marcXml }).toBreaker();
105                     } else {
106                         egCore.pcrud.retrieve('bre', $scope.recordId)
107                         .then(function(rec) {
108                             $scope.breaker = new MARC21.Record({ marcxml : rec.marc() }).toBreaker();
109                         });
110                     }
111                 }
112
113                 $scope.$watch('recordId', 
114                     function(newVal, oldVal) {
115                         if (newVal && newVal !== oldVal) {
116                             loadRecordBreaker();
117                         }
118                     }
119                 );
120                 $scope.$watch('marcXml', 
121                     function(newVal, oldVal) {
122                         if (newVal && newVal !== oldVal) {
123                             loadRecordBreaker();
124                         }
125                     }
126                 );
127
128                 if ($scope.recordId || $scope.marcXml) 
129                     loadRecordBreaker();
130             }
131         ]
132     }
133 })
134
135 /*
136  * A record='foo' attribute is required as a storage location of the 
137  * retrieved record
138  */
139 .directive('egRecordSummary', function() {
140     return {
141         restrict : 'AE',
142         scope : {
143             recordId : '=',
144             record : '=',
145             noMarcLink : '@',
146             mode: '<'
147         },
148         templateUrl : function(element, attrs) {
149             if (attrs.mode == "slim") {
150                 return  './cat/share/t_record_summary_slim';
151             }
152             return './cat/share/t_record_summary';
153         },
154         controller : 
155                    ['$scope','egCore','$sce','egBibDisplay',
156             function($scope , egCore , $sce , egBibDisplay) {
157
158                 function loadRecord() {
159                     egCore.pcrud.retrieve('bre', $scope.recordId, {
160                         flesh : 1,
161                         flesh_fields : {
162                             bre : ['creator','editor','flat_display_entries']
163                         }
164                     }).then(function(rec) {
165                         rec.owner(egCore.org.get(rec.owner()));
166                         $scope.record = rec;
167                         $scope.rec_display = 
168                             egBibDisplay.mfdeToHash(rec.flat_display_entries());
169                     });
170                     $scope.bib_cn = null;
171                     $scope.bib_cn_tooltip = '';
172                     var label_class = 1;
173                     egCore.org.settings(['cat.default_classification_scheme'])
174                     .then(function(s) {
175                         var scheme = s['cat.default_classification_scheme'];
176                         label_class = scheme || 1;
177
178                         return egCore.net.request(
179                             'open-ils.cat',
180                             'open-ils.cat.biblio.record.marc_cn.retrieve',
181                             $scope.recordId,
182                             label_class
183                         )
184                     }).then(function(cn_array) {
185                         var tooltip = '';
186                         if (cn_array.length > 0) {
187                             for (var field in cn_array[0]) {
188                                 $scope.bib_cn = cn_array[0][field];
189                             }
190                             for (var i in cn_array) {
191                                 for (var field in cn_array[i]) {
192                                     tooltip += 
193                                         field + ' : ' + cn_array[i][field] + '<br>';
194                                 }
195                             }
196                             $scope.bib_cn_tooltip = $sce.trustAsHtml(tooltip);
197                         }
198                     });
199                 }
200
201                 $scope.$watch('recordId', 
202                     function(newVal, oldVal) {
203                         if (newVal && newVal !== oldVal) {
204                             loadRecord();
205                         }
206                     }
207                 );
208
209
210                 if ($scope.recordId) 
211                     loadRecord();
212
213                 $scope.toggle_expand_summary = function() {
214                     if ($scope.collapseRecordSummary) {
215                         $scope.collapseRecordSummary = false;
216                         egCore.hatch.removeItem('eg.cat.record.summary.collapse');
217                     } else {
218                         $scope.collapseRecordSummary = true;
219                         egCore.hatch.setItem('eg.cat.record.summary.collapse', true);
220                     }
221                 }
222             
223                 $scope.collapse_summary = function() {
224                     return $scope.collapseRecordSummary;
225                 }
226             
227                 egCore.hatch.getItem('eg.cat.record.summary.collapse')
228                 .then(function(val) {$scope.collapseRecordSummary = Boolean(val)});
229
230             }
231         ]
232     }
233 })
234
235 /**
236  * Utility functions for translating bib record display fields into
237  * various formats / structures.
238  *
239  * Note that 'mwde' objects (which are proper IDL objects) only contain
240  * the prescribed fields from the IDL (and database view), while the
241  * 'mfde' hash-based objects contain all configured display fields,
242  * including custom fields.
243  * 
244  * MWDE objects are best suited to cases where the available set of
245  * display fields must be auto-generated from the IDL.  They work well
246  * with egGrids because it can automatically determine from the IDL
247  * which fields should be added to the column picker.
248  *
249  * MFDE lists are well suited to cases where the set of fields to
250  * display is known in advance (e.g. hard-coded in the template) or when
251  * the caller needs data for custom fields.  FWIW, MFDE data is slightly
252  * leaner for retrieval in that it does not require the JSON round-trip
253  * for delivery.
254  *
255  * Example:
256  *
257  *  --
258  *  // MVR-style canned fields
259  *
260  *  $scope.record = copy.call_number().record();
261  *
262  *  // translate wide display entry values inline
263  *  egBibDisplay.mwdeJSONToJS($scope.record.wide_display_entry());
264  *
265  *  <div>Title:</div>
266  *  <div>{{record.wide_display_entry().title()}}</div>
267  *
268  *  ---
269  *  //  Display any field using known keys
270  *
271  *  $scope.all_display_fields = 
272  *      egBibDisplay.mfdeToHash(record.flat_display_entries());
273  *
274  *  <div>Title:</div>
275  *  <div>{{all_display_fields.title}}</div>
276  *
277  *  ---
278  *  // Display all fields dynamically, using confgured labels
279  *
280  *  $scope.all_display_fields_with_meta = 
281  *      egBibDisplay.mfdeToMetaHash(record.flat_display_entries());
282  *
283  *  <div ng-repeat="(key, content) in all_display_fields_with_meta">
284  *    <div>Field Label</div><div>{{content.label}}</div>
285  *    <div ng-if="content.multi == 't'">
286  *      <div ng-repeat="val in content.value">
287  *        <div>Field Value</div><div>{{val}}</div>
288  *      </div>
289  *    </div>
290  *    <div ng-if="content.multi == 'f'">
291  *      <div>Field Value</div><div>{{content.value}}</div>
292  *    </div>
293  *  </div>
294  *
295  */
296 .factory('egBibDisplay', ['$q', 'egCore', function($q, egCore) {
297     var service = {};
298
299     /**
300      * Converts JSON-encoded values within a mwde object to Javascript
301      * native strings, numbers, and arrays.
302      *
303      * @collapseMulti collapse multi=true array values down to a single 
304      * comma-separated string.  This is useful for quickly  building 
305      * displays (e.g. grids) without having to first munge the array 
306      * into a string.
307      */
308     service.mwdeJSONToJS = function(entry, collapseMulti) {
309         angular.forEach(egCore.idl.classes.mwde.fields, function(f) {
310             if (f.virtual) return;
311             var val = JSON.parse(entry[f.name]());
312             if (collapseMulti && angular.isArray(val))
313                 val = val.join(', ');
314             entry[f.name](val);
315         });
316     }
317
318     /**
319      * Converts a list of 'mfde' entry objects to a simple key=>value hash.
320      * Non-multi values are strings or numbers.
321      * Multi values are arrays of strings or numbers.
322      *
323      * @collapseMulti See egBibDisplay.mwdeJSONToJS()
324      */
325     service.mfdeToHash = function(entries, collapseMulti) {
326         var hash = service.mfdeToMetaHash(entries, collapseMulti);
327         angular.forEach(hash, 
328             function(sub_hash, name) { hash[name] = sub_hash.value });
329         return hash;
330     }
331
332     /**
333      * Converts a list of 'mfde' entry objects to a nested hash like so:
334      * {name => field_name, label => field_label, value => scalar_or_array}
335      * The scalar_or_array value is a string/number or an array of
336      * string/numbers
337      *
338      * @collapseMulti See egBibDisplay.mwdeJSONToJS()
339      */
340     service.mfdeToMetaHash = function(entries, collapseMulti) {
341         var hash = {};
342         angular.forEach(entries, function(entry) {
343
344             if (!hash[entry.name()]) {
345                 hash[entry.name()] = {
346                     name : entry.name(),
347                     label : entry.label(),
348                     multi : entry.multi() == 't',
349                     value : entry.multi() == 't' ? [] : null
350                 }
351             }
352
353             if (entry.multi() == 't') {
354                 if (collapseMulti) {
355                     if (angular.isArray(hash[entry.name()].value)) {
356                         // start a new collapsed string
357                         hash[entry.name()].value = entry.value();
358                     } else {
359                         // append to collapsed string in progress
360                         hash[entry.name()].value += ', ' + entry.value();
361                     }
362                 } else {
363                     hash[entry.name()].value.push(entry.value());
364                 }
365             } else {
366                 hash[entry.name()].value = entry.value();
367             }
368         });
369
370         return hash;
371     }
372
373     return service;
374 }])