From: Mike Rylander Date: Mon, 2 Feb 2015 21:50:28 +0000 (-0500) Subject: LP#1402797 Initial commit and sourcing of the jquery-ified marcrecord.js module X-Git-Tag: sprint4-merge-nov22~1516 X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=a89ae52d90e3598bca4e35fed896298c528f334c;p=working%2FEvergreen.git LP#1402797 Initial commit and sourcing of the jquery-ified marcrecord.js module Signed-off-by: Mike Rylander Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/templates/staff/base_js.tt2 b/Open-ILS/src/templates/staff/base_js.tt2 index 9cf9d6952c..8947ca3e8c 100644 --- a/Open-ILS/src/templates/staff/base_js.tt2 +++ b/Open-ILS/src/templates/staff/base_js.tt2 @@ -3,6 +3,7 @@ [% IF EXPAND_WEB_IMPORTS %] + diff --git a/Open-ILS/src/templates/staff/cat/catalog/index.tt2 b/Open-ILS/src/templates/staff/cat/catalog/index.tt2 index 9e799d4f69..919b900180 100644 --- a/Open-ILS/src/templates/staff/cat/catalog/index.tt2 +++ b/Open-ILS/src/templates/staff/cat/catalog/index.tt2 @@ -5,6 +5,7 @@ %] [% BLOCK APP_JS %] + diff --git a/Open-ILS/web/js/ui/default/staff/marcrecord.js b/Open-ILS/web/js/ui/default/staff/marcrecord.js new file mode 100644 index 0000000000..0802558b98 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/marcrecord.js @@ -0,0 +1,2270 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2009-2015 Equinox Software, Inc. + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +// !!! Head's up !!! +// This module requires a /real/ jQuery be used with your +// angularjs, so as to avoid using hand-rolled AJAX. + +var MARC = { + + Record : function(kwargs) { + if (!kwargs) kwargs = {}; + + this.title = function () { return this.subfield('245','a')[1] } + + this.field = function (spec) { + var list = this.fields.filter(function (f) { + if (f.tag.match(spec)) return true; + return false; + }); + + if (list.length == 1) return list[0]; + return list; + } + + this.subfield = function (spec, code) { + var f = this.field(spec); + if (Array.isArray(f)) { + if (!f.length) return f; + f = f[0]; + } + return f.subfield(code) + } + + this.appendFields = function () { + var me = this; + Array.prototype.slice.call(arguments).forEach( function (f) { me.fields.push( f ) } ); + } + + this.deleteField = function (f) { return this.deleteFields(f) }, + + this.insertOrderedFields = function () { + var me = this; + for (var i = 0; i < arguments.length; i++) { + var f = arguments[i]; + var done = false; + for (var j = 0; j < this.fields.length; j++) { + if (f.tag < this.fields[j].tag) { + this.insertFieldsBefore(this.fields[j], f); + done = true; + break; + } + } + if (!done) this.appendFields(f); + } + } + + this.insertFieldsBefore = function (target) { + var args = Array.prototype.slice.call(arguments); + args.splice(0,1); + var me = this; + for (var j = 0; j < this.fields.length; j++) { + if (target === this.fields[j]) { + j--; + args.forEach( function (f) { + me.fields.splice(j++,0,f); + }); + break; + } + } + } + + this.insertFieldsAfter = function (target) { + var args = Array.prototype.slice.call(arguments); + args.splice(0,1); + var me = this; + for (var j = 0; j < this.fields.length; j++) { + if (target === this.fields[j]) { + args.forEach( function (f) { + me.fields.splice(j++,0,f); + }); + break; + } + } + } + + this.deleteFields = function () { + var me = this; + var counter = 0; + for ( var i in arguments ) { + var f = arguments[i]; + for (var j = 0; j < me.fields.length; j++) { + if (f === me.fields[j]) { + me.fields[j].record = null; + me.fields.splice(j,0); + counter++ + break; + } + } + } + return counter; + } + + // this.clone = function () { return dojo.clone(this) } // maybe implement later... + + this.fromXmlURL = function (url) { + this.ready = false; + var me = this; + $.get( + url, + function (mxml) { + me.fromXmlDocument($('record', mxml)[0]); + me.ready = true; + if (me.onLoad) me.onLoad(); + }); + }, + + this.fromXmlString = function (mxml) { + this.fromXmlDocument( $.parseXML( mxml ).find('record')[0] ); + }, + + this.fromXmlDocument = function (mxml) { + var me = this; + me.leader = $($('leader',mxml)[0]).text() || '00000cam a2200205Ka 4500'; + + $('controlfield', mxml).each(function () { + var cf=$(this); + me.fields.push( + new MARC.Field({ + record : me, + tag : cf.attr('tag'), + data : cf.text() + }) + ) + }); + + $('datafield', mxml).each(function () { + var df=$(this); + me.fields.push( + new MARC.Field({ + record : me, + tag : df.attr('tag'), + ind1 : df.attr('ind1'), + ind2 : df.attr('ind2'), + subfields : $('subfield', df).map( + function (i, sf) { + return [[ $(sf).attr('code'), $(sf).text() ]]; + } + ).get() + }) + ) + }); + + }, + + this.toXmlDocument = function () { + + var doc = DOMParser(''); + var rec_node = $('record', doc)[0]; + + var ldr = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'leader'); + ldr.textContent = this.leader; + rec_node.append( ldr ); + + this.fields.forEach(function (f) { + var element = f.isControlfield() ? 'controlfield' : 'datafield'; + var f_node = doc.createElementNS( 'http://www.loc.gov/MARC21/slim', element ); + f_node.setAttribute('tag', f.tag); + + if (f.isControlfield()) { + if (f.data) f_node.textContent = f.data; + } else { + f_node.setAttribute('ind1', f.indicator(1)); + f_node.setAttribute('ind2', f.indicator(2)); + f.subfields.forEach( function (sf) { + var sf_node = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'subfield'); + sf_node.setAttribute('code', sf[0]); + sf_node.textContent = sf[1]; + f_node.appendChild(sf_node); + }); + } + + rec_node.append(f_node); + }); + + return doc; + }, + + this.toXmlString = function () { + return (new XMLSerializer()).serializeToString( this.toXmlDocument() ); + }, + + this.fromBreaker = function (marctxt) { + var me = this; + + function cf_line_data (l) { return l.substring(4) || '' }; + function df_line_data (l) { return l.substring(6) || '' }; + function line_tag (l) { return l.substring(0,3) }; + function df_ind1 (l) { return l.substring(4,5).replace('\\',' ') }; + function df_ind2 (l) { return l.substring(5,6).replace('\\',' ') }; + function isControlField (l) { + var x = line_tag(l); + return (x == 'LDR' || x < '010') ? true : false; + } + + var lines = marctxt.replace(/^=/gm,'').split('\n'); + lines.forEach(function (current_line) { + + if (current_line.match(/^#/)) { + // skip comment lines + } else if (isControlField(current_line)) { + if (line_tag(current_line) == 'LDR') { + me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500'; + } else { + me.fields.push( + new MARC.Field({ + record : me, + tag : line_tag(current_line), + data : cf_line_data(current_line).replace('\\',' ','g') + }) + ); + } + } else { + if (current_line.substring(4,5) == me.delimiter) // add delimiters if they were left out + current_line = current_line.substring(0,3) + ' \\\\' + current_line.substring(4); + + var data = df_line_data(current_line); + if (!(data.substring(0,1) == me.delimiter)) data = me.delimiter + 'a' + data; + + var sf_list = data.split(me.delimiter); + sf_list.shift(); + + me.fields.push( + new MARC.Field({ + record : me, + tag : line_tag(current_line), + ind1 : df_ind1(current_line), + ind2 : df_ind2(current_line), + subfields : sf_list.map( function (sf) { + var sf_data = sf.substring(1); + if (me.delimiter == '$') sf_data = sf_data.replace(/\{dollar\}/g, '$'); + return [ sf.substring(0,1), sf_data ]; + }) + }) + ); + } + }); + + return this; + }, + + this.toBreaker = function () { + + var me = this; + var mtxt = '=LDR ' + this.leader + '\n'; + + mtxt += this.fields.map( function (f) { + if (f.isControlfield()) { + if (f.data) return '=' + f.tag + ' ' + f.data.replace(' ','\\','g'); + return '=' + f.tag; + } else { + return '=' + f.tag + ' ' + + f.indicator(1).replace(' ','\\') + + f.indicator(2).replace(' ','\\') + + f.subfields.map( function (sf) { + var sf_data = sf[1]; + if (me.delimiter == '$') sf_data = sf_data.replace(/\$/g, '{dollar}'); + return me.delimiter + sf[0] + sf_data; + }).join(''); + } + }).join('\n'); + + return mtxt; + } + + this.recordType = function () { + + var _t = this.leader.substr(MARC.Record._ff_pos.Type.ldr.BKS.start, MARC.Record._ff_pos.Type.ldr.BKS.len); + var _b = this.leader.substr(MARC.Record._ff_pos.BLvl.ldr.BKS.start, MARC.Record._ff_pos.BLvl.ldr.BKS.len); + + for (var t in MARC.Record._recType) { + if (_t.match(MARC.Record._recType[t].Type) && _b.match(MARC.Record._recType[t].BLvl)) { + return t; + } + } + return 'BKS'; // default + } + + this.videorecordingFormatName = function () { + var _7 = this.field('007').data; + + if (_7 && _7.match(/^v/)) { + var _v_e = _7.substr( + MARC.Record._physical_characteristics.v.subfields.e.start, + MARC.Record._physical_characteristics.v.subfields.e.len + ); + + return MARC.Record._physical_characteristics.v.subfields.e.values[ _v_e ]; + } + + return null; + } + + this.videorecordingFormatCode = function () { + var _7 = this.field('007').data; + + if (_7 && _7.match(/^v/)) { + return _7.substr( + MARC.Record._physical_characteristics.v.subfields.e.start, + MARC.Record._physical_characteristics.v.subfields.e.len + ); + } + + return null; + } + + this.extractFixedField = function (field, dflt) { + if (!MARC.Record._ff_pos[field]) return null; + + var _l = this.leader; + var _8 = this.field('008').data; + var _6 = this.field('006').data; + + var rtype = this.recordType(); + + var val; + + if (MARC.Record._ff_pos[field].ldr && _l) { + if (MARC.Record._ff_pos[field].ldr[rtype]) { + val = _l.substr( + MARC.Record._ff_pos[field].ldr[rtype].start, + MARC.Record._ff_pos[field].ldr[rtype].len + ); + } + } else if (MARC.Record._ff_pos[field]._8 && _8) { + if (MARC.Record._ff_pos[field]._8[rtype]) { + val = _8.substr( + MARC.Record._ff_pos[field]._8[rtype].start, + MARC.Record._ff_pos[field]._8[rtype].len + ); + } + } + + if (!val && MARC.Record._ff_pos[field]._6 && _6) { + if (MARC.Record._ff_pos[field]._6[rtype]) { + val = _6.substr( + MARC.Record._ff_pos[field]._6[rtype].start, + MARC.Record._ff_pos[field]._6[rtype].len + ); + } + } + + if (!val && dflt) { + val = ''; + var d; + var p; + if (MARC.Record._ff_pos[field].ldr && MARC.Record._ff_pos[field].ldr[rtype]) { + d = MARC.Record._ff_pos[field].ldr[rtype].def; + p = 'ldr'; + } + + if (MARC.Record._ff_pos[field]._8 && MARC.Record._ff_pos[field]._8[rtype]) { + d = MARC.Record._ff_pos[field]._8[rtype].def; + p = '_8'; + } + + if (!val && MARC.Record._ff_pos[field]._6 && MARC.Record._ff_pos[field]._6[rtype]) { + d = MARC.Record._ff_pos[field]._6[rtype].def; + p = '_6'; + } + + if (p) { + for (var j = 0; j < MARC.Record._ff_pos[field][p][rtype].len; j++) { + val += d; + } + } else { + val = null; + } + } + + return val; + } + + this.setFixedField = function (field, value) { + if (!MARC.Record._ff_pos[field]) return null; + + var _l = this.leader; + var _8 = this.field('008').data; + var _6 = this.field('006').data; + + var rtype = this.recordType(); + + var val; + + if (MARC.Record._ff_pos[field].ldr && _l) { + if (MARC.Record._ff_pos[field].ldr[rtype]) { // It's in the leader + val = value.substr(0, MARC.Record._ff_pos[field].ldr[rtype].len); + this.leader = + _l.substring(0, MARC.Record._ff_pos[field].ldr[rtype].start) + + val + + _l.substring( + MARC.Record._ff_pos[field].ldr[rtype].start + + MARC.Record._ff_pos[field].ldr[rtype].len + ); + } + } else if (MARC.Record._ff_pos[field]._8 && _8) { + if (MARC.Record._ff_pos[field]._8[rtype]) { // Nope, it's in the 008 + val = value.substr(0, MARC.Record._ff_pos[field]._8[rtype].len); + this.field('008').update( + _8.substring(0, MARC.Record._ff_pos[field]._8[rtype].start) + + val + + _8.substring( + MARC.Record._ff_pos[field]._8[rtype].start + + MARC.Record._ff_pos[field]._8[rtype].len + ) + ); + } + } + + if (!val && MARC.Record._ff_pos[field]._6 && _6) { + if (MARC.Record._ff_pos[field]._6[rtype]) { // ok, maybe the 006? + val = value.substr(0, MARC.Record._ff_pos[field]._6[rtype].len); + this.field('006').update( + _6.substring(0, MARC.Record._ff_pos[field]._6[rtype].start) + + val + + _6.substring( + MARC.Record._ff_pos[field]._6[rtype].start + + MARC.Record._ff_pos[field]._6[rtype].len + ) + ); + } + } + + return val; + } + + this.fields = []; + this.delimiter = '\u2021'; + this.leader = '00000cam a2200205Ka 4500'; + + if (kwargs.delimiter) this.delimiter = kwargs.delimiter; + if (kwargs.onLoad) this.onLoad = kwargs.onLoad; + + if (kwargs.url) { + this.fromXmlURL(kwargs.url); + } else if (kwargs.marcxml) { + this.fromXmlString(kwargs.marcxml); + if (this.onLoad) this.onLoad(); + } else if (kwargs.xml) { + this.fromXmlDocument(kwargs.xml); + if (this.onLoad) this.onLoad(); + } else if (kwargs.marcbreaker) { + this.fromBreaker(kwargs.marcbreaker); + if (this.onLoad) this.onLoad(); + } + + if (kwargs.rtype == 'AUT') { + this.setFixedField('Type','z'); + } + + }, + + Field : function (kwargs) { + if (!kwargs) kwargs = {}; + + this.subfield = function (code) { + var list = this.subfields.filter( function (s) { + if (s[0] == code) return true; return false; + }); + if (list.length == 1) return list[0]; + return list; + } + + this.addSubfields = function () { + for (var i = 0; i < arguments.length; i++) { + var code = arguments[i]; + var value = arguments[++i]; + this.subfields.push( [ code, value ] ); + } + } + + this.deleteSubfields = function (c) { + return this.deleteSubfield( { code : c } ); + } + + this.deleteSubfield = function (args) { + var me = this; + if (!Array.isArray( args.code )) { + args.code = [ args.code ]; + } + + if (args.pos && !Array.isArray( args.pos )) { + args.pos = [ args.pos ]; + } + + for (var i = 0; i < args.code.length; i++) { + var sub_pos = {}; + for (var j = 0; j < me.subfields; j++) { + if (me.subfields[j][0] == args.code[i]) { + + if (!sub_pos[args.code[i]]) sub_pos[args.code[j]] = 0; + else sub_pos[args.code[i]]++; + + if (args.pos) { + for (var k = 0; k < args.pos.length; k++) { + if (sub_pos[args.code[i]] == args.pos[k]) me.subfields.splice(j,1); + } + } else if (args.match && me.subfields[j][1].match( args.match )) { + me.subfields.splice(j,1); + } else { + me.subfields.splice(j,1); + } + } + } + } + } + + this.update = function ( args ) { + if (this.isControlfield()) { + this.data = args; + } else { + if (args.ind1) this.ind1 = args.ind1; + if (args.ind2) this.ind2 = args.ind2; + if (args.tag) this.tag = args.tag; + + for (var i in args) { + if (i == 'tag' || i == 'ind1' || i == 'ind2') continue; + var done = 0; + this.subfields.forEach( function (f) { + if (!done && f[0] == i) { + f[1] = args[i]; + done = 1; + } + }); + } + } + } + + this.isControlfield = function () { + return this.tag < '010' ? true : false; + } + + this.indicator = function (num, value) { + if (value !== undefined) { + if (num == 1) this.ind1 = value; + else if (num == 2) this.ind2 = value; + else { this.error = true; return null; } + } + if (num == 1) return this.ind1; + else if (num == 2) return this.ind2; + else { this.error = true; return null; } + } + + this.error = false; + this.record = null; // MARC record pointer + this.tag = ''; // MARC tag + this.ind1 = ' '; // MARC indicator 1 + this.ind2 = ' '; // MARC indicator 2 + this.data = ''; // MARC data for a controlfield element + this.subfields = []; // list of MARC subfields for a datafield element + + this.record = kwargs.record; + this.tag = kwargs.tag; + this.ind1 = kwargs.ind1 || ' '; + this.ind2 = kwargs.ind2 || ' '; + this.data = kwargs.data; + + if (kwargs.subfields) this.subfields = kwargs.subfields; + else this.subfields = []; + + }, + + Batch : function(kwargs) { + + this.parse = function () { + if (this.source instanceof Object ) { // assume an xml collection document + this.source = $('record', this.source); + this.type = 'xml'; + } else if (this.source.match(/^\s*