2 * Simple directive for rending the HTML view of a MARC record.
4 * <eg-record-html record-id="myRecordIdScopeVariable"></eg-record-id>
6 * <eg-record-html marc-xml="myMarcXmlVariable"></eg-record-html>
8 * The value of myRecordIdScopeVariable is watched internally and the
9 * record is updated to match.
11 angular.module('egCoreMod')
13 .directive('egRecordHtml', function() {
20 link : function(scope, element, attrs) {
21 scope.element = angular.element(element);
23 // kill refs to destroyed DOM elements
24 element.bind("$destroy", function() {
30 function($scope , egCore) {
32 function loadRecordHtml() {
35 'open-ils.search.biblio.record.html',
39 ).then(function(html) {
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.
46 /<button onclick="window.print(.*?)<\/button>/,'');
47 html = html.replace(/<title>(.*?)<\/title>/,'');
49 // remove reference to nonexistant CSS file
50 html = html.replace(/<link(.*?)\/>/,'');
52 $scope.element.html(html);
56 $scope.$watch('recordId',
57 function(newVal, oldVal) {
58 if (newVal && newVal !== oldVal) {
63 $scope.$watch('marcXml',
64 function(newVal, oldVal) {
65 if (newVal && newVal !== oldVal) {
71 if ($scope.recordId || $scope.marcXml)
78 .directive('egRecordBreaker', function() {
81 template : '<pre>{{breaker}}</pre>',
86 link : function(scope, element, attrs) {
87 scope.element = angular.element(element);
89 // kill refs to destroyed DOM elements
90 element.bind("$destroy", function() {
96 function($scope , egCore) {
98 // Match the MARC flat-text editor
99 MARC21.Record.delimiter = '$';
101 function loadRecordBreaker() {
103 if ($scope.marcXml) {
104 $scope.breaker = new MARC21.Record({ marcxml : $scope.marcXml }).toBreaker();
106 egCore.pcrud.retrieve('bre', $scope.recordId)
107 .then(function(rec) {
108 $scope.breaker = new MARC21.Record({ marcxml : rec.marc() }).toBreaker();
113 $scope.$watch('recordId',
114 function(newVal, oldVal) {
115 if (newVal && newVal !== oldVal) {
120 $scope.$watch('marcXml',
121 function(newVal, oldVal) {
122 if (newVal && newVal !== oldVal) {
128 if ($scope.recordId || $scope.marcXml)
136 * A record='foo' attribute is required as a storage location of the
139 .directive('egRecordSummary', function() {
148 templateUrl : function(element, attrs) {
149 if (attrs.mode == "slim") {
150 return './cat/share/t_record_summary_slim';
152 return './cat/share/t_record_summary';
155 ['$scope','egCore','$sce','egBibDisplay',
156 function($scope , egCore , $sce , egBibDisplay) {
158 function loadRecord() {
159 egCore.pcrud.retrieve('bre', $scope.recordId, {
162 bre : ['creator','editor','flat_display_entries']
164 }).then(function(rec) {
165 rec.owner(egCore.org.get(rec.owner()));
168 egBibDisplay.mfdeToHash(rec.flat_display_entries());
170 $scope.bib_cn = null;
171 $scope.bib_cn_tooltip = '';
173 egCore.org.settings(['cat.default_classification_scheme'])
175 var scheme = s['cat.default_classification_scheme'];
176 label_class = scheme || 1;
178 return egCore.net.request(
180 'open-ils.cat.biblio.record.marc_cn.retrieve',
184 }).then(function(cn_array) {
186 if (cn_array.length > 0) {
187 for (var field in cn_array[0]) {
188 $scope.bib_cn = cn_array[0][field];
190 for (var i in cn_array) {
191 for (var field in cn_array[i]) {
193 field + ' : ' + cn_array[i][field] + '<br>';
196 $scope.bib_cn_tooltip = $sce.trustAsHtml(tooltip);
201 $scope.$watch('recordId',
202 function(newVal, oldVal) {
203 if (newVal && newVal !== oldVal) {
213 $scope.toggle_expand_summary = function() {
214 if ($scope.collapseRecordSummary) {
215 $scope.collapseRecordSummary = false;
216 egCore.hatch.removeItem('eg.cat.record.summary.collapse');
218 $scope.collapseRecordSummary = true;
219 egCore.hatch.setItem('eg.cat.record.summary.collapse', true);
223 $scope.collapse_summary = function() {
224 return $scope.collapseRecordSummary;
227 egCore.hatch.getItem('eg.cat.record.summary.collapse')
228 .then(function(val) {$scope.collapseRecordSummary = Boolean(val)});
236 * Utility functions for translating bib record display fields into
237 * various formats / structures.
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.
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.
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
258 * // MVR-style canned fields
260 * $scope.record = copy.call_number().record();
262 * // translate wide display entry values inline
263 * egBibDisplay.mwdeJSONToJS($scope.record.wide_display_entry());
266 * <div>{{record.wide_display_entry().title()}}</div>
269 * // Display any field using known keys
271 * $scope.all_display_fields =
272 * egBibDisplay.mfdeToHash(record.flat_display_entries());
275 * <div>{{all_display_fields.title}}</div>
278 * // Display all fields dynamically, using confgured labels
280 * $scope.all_display_fields_with_meta =
281 * egBibDisplay.mfdeToMetaHash(record.flat_display_entries());
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>
290 * <div ng-if="content.multi == 'f'">
291 * <div>Field Value</div><div>{{content.value}}</div>
296 .factory('egBibDisplay', ['$q', 'egCore', function($q, egCore) {
300 * Converts JSON-encoded values within a mwde object to Javascript
301 * native strings, numbers, and arrays.
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
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(', ');
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.
323 * @collapseMulti See egBibDisplay.mwdeJSONToJS()
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 });
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
338 * @collapseMulti See egBibDisplay.mwdeJSONToJS()
340 service.mfdeToMetaHash = function(entries, collapseMulti) {
342 angular.forEach(entries, function(entry) {
344 if (!hash[entry.name()]) {
345 hash[entry.name()] = {
347 label : entry.label(),
348 multi : entry.multi() == 't',
349 value : entry.multi() == 't' ? [] : null
353 if (entry.multi() == 't') {
355 if (angular.isArray(hash[entry.name()].value)) {
356 // start a new collapsed string
357 hash[entry.name()].value = entry.value();
359 // append to collapsed string in progress
360 hash[entry.name()].value += ', ' + entry.value();
363 hash[entry.name()].value.push(entry.value());
366 hash[entry.name()].value = entry.value();