LP#1930933: fix issue with over-escaping in search results title attributes
[working/Evergreen.git] / Open-ILS / web / js / dojo / MARC / Record.js
1 /* ---------------------------------------------------------------------------
2  * Copyright (C) 2009  Equinox Software, Inc.
3  * Mike Rylander <miker@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * ---------------------------------------------------------------------------
15  */
16
17 if(!dojo._hasResource["MARC.Record"]) {
18
19     dojo.require('dojox.xml.parser');
20     dojo.require('MARC.Field');
21
22     dojo._hasResource["MARC.Record"] = true;
23     dojo.provide("MARC.Record");
24     dojo.declare('MARC.Record', null, {
25
26         delimiter : '\u2021', // default subfield delimiter
27
28         constructor : function(kwargs) {
29             this.fields = [];
30             this.leader = '00000cam a2200205Ka 4500';
31
32             if (kwargs.delimiter) this.delimiter = kwargs.delimiter;
33             if (kwargs.onLoad) this.onLoad = kwargs.onLoad;
34             if (kwargs.url) {
35                 this.fromXmlURL(kwargs.url);
36             } else if (kwargs.marcxml) {
37                 this.fromXmlString(kwargs.marcxml);
38                 if (this.onLoad) this.onLoad();
39             } else if (kwargs.xml) {
40                 this.fromXmlDocument(kwargs.xml);
41                 if (this.onLoad) this.onLoad();
42             } else if (kwargs.marcbreaker) {
43                 this.fromBreaker(kwargs.marcbreaker);
44                 if (this.onLoad) this.onLoad();
45             }
46
47             if (kwargs.rtype == 'AUT') {
48                 dojo.require('MARC.FixedFields');
49                 this.setFixedField('Type','z');
50             }
51
52         },
53
54         title : function () { return this.subfield('245','a') },
55
56         field : function (spec) {
57             var list = dojo.filter( this.fields, function (f) {
58                 if (f.tag.match(spec)) return true;
59                 return false;
60             });
61
62             if (list.length == 1) return list[0];
63             return list;
64         },
65
66         subfield : function (spec, code) {
67             var f = this.field(spec);
68             if (dojo.isArray(f)) {
69                 if (!f.length) return f;
70                 f = f[0];
71             }
72             return f.subfield(code)
73         },
74
75         appendFields : function () {
76             var me = this;
77             dojo.forEach( arguments, function (f) { me.fields.push( f ) } );
78         },
79
80         deleteField : function (f) { return this.deleteFields(f) },
81
82         insertOrderedFields : function () {
83             var me = this;
84             for (var i = 0; i < arguments.length; i++) {
85                 var f = arguments[i];
86                 var done = false;
87                 for (var j = 0; j < this.fields.length; j++) {
88                     if (f.tag < this.fields[j].tag) {
89                         this.insertFieldsBefore(this.fields[j], f);
90                         done = true;
91                         break;
92                     }
93                 }
94                 if (!done) this.appendFields(f);
95             }
96         },
97
98         insertFieldsBefore : function (target) {
99             var args = Array.prototype.slice.call(arguments);
100             args.splice(0,1);
101             var me = this;
102             for (var j = 0; j < this.fields.length; j++) {
103                 if (target === this.fields[j]) {
104                     j--;
105                     dojo.forEach( args, function (f) {
106                         me.fields.splice(j++,0,f);
107                     });
108                     break;
109                 }
110             }
111         },
112
113         insertFieldsAfter : function (target) {
114             var args = Array.prototype.slice.call(arguments);
115             args.splice(0,1);
116             var me = this;
117             for (var j = 0; j < this.fields.length; j++) {
118                 if (target === this.fields[j]) {
119                     dojo.forEach( args, function (f) {
120                         me.fields.splice(j++,0,f);
121                     });
122                     break;
123                 }
124             }
125         },
126
127         deleteFields : function () {
128             var me = this;
129             var counter = 0;
130             for ( var i in arguments ) {
131                 var f = arguments[i];
132                 for (var j = 0; j < me.fields.length; j++) {
133                     if (f === me.fields[j]) {
134                         me.fields[j].record = null;
135                         me.fields.splice(j,0);
136                         counter++
137                         break;
138                     }
139                 }
140             }
141             return counter;
142         },
143
144         clone : function () { return dojo.clone(this) },
145
146         fromXmlURL : function (url) {
147             this.ready   = false;
148             var me = this;
149             dojo.xhrGet({
150                 url     : url,
151                 sync    : true,
152                 handleAs: 'xml',
153                 load    : function (mxml) {
154                     me.fromXmlDocument(dojo.query('record', mxml)[0]);
155                     me.ready = true;
156                     if (me.onLoad) me.onLoad();
157                 }
158             });
159         },
160
161         fromXmlString : function (mxml) {
162                 return this.fromXmlDocument( dojox.xml.parser.parse( mxml ) );
163         },
164
165         fromXmlDocument : function (mxml) {
166             var me = this;
167             me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '00000cam a2200205Ka 4500';
168
169             dojo.forEach( dojo.query('controlfield', mxml), function (cf) {
170                 me.fields.push(
171                     new MARC.Field({
172                           record : me,
173                           tag    : cf.getAttribute('tag'),
174                           data   : dojox.xml.parser.textContent(cf)
175                     })
176                 )
177             });
178
179             dojo.forEach( dojo.query('datafield', mxml), function (df) {
180                 me.fields.push(
181                     new MARC.Field({
182                         record    : me,
183                         tag       : df.getAttribute('tag'),
184                         ind1      : df.getAttribute('ind1'),
185                         ind2      : df.getAttribute('ind2'),
186                         subfields : dojo.map(
187                             dojo.query('subfield', df),
188                             function (sf) {
189                                 return [ sf.getAttribute('code'), dojox.xml.parser.textContent(sf) ];
190                             }
191                         )
192                     })
193                 )
194             });
195
196             return this;
197         },
198
199         toXmlDocument : function () {
200
201             var doc = dojox.xml.parser.parse('<record xmlns="http://www.loc.gov/MARC21/slim"/>');
202             var rec_node = dojo.query('record', doc)[0];
203
204             var ldr = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'leader');
205             dojox.xml.parser.textContent(ldr, this.leader);
206             rec_node.appendChild( ldr );
207
208             dojo.forEach( this.fields, function (f) {
209                 var element = f.isControlfield() ? 'controlfield' : 'datafield';
210                 var f_node = doc.createElementNS( 'http://www.loc.gov/MARC21/slim', element );
211                 f_node.setAttribute('tag', f.tag);
212                 
213                 if (f.isControlfield()) {
214                     if (f.data) dojox.xml.parser.textContent(f_node, f.data);
215                 } else {
216                     f_node.setAttribute('ind1', f.indicator(1));
217                     f_node.setAttribute('ind2', f.indicator(2));
218                     dojo.forEach( f.subfields, function (sf) {
219                         var sf_node = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'subfield');
220                         sf_node.setAttribute('code', sf[0]);
221                         dojox.xml.parser.textContent(sf_node, sf[1]);
222                         f_node.appendChild(sf_node);
223                     });
224                 }
225
226                 rec_node.appendChild(f_node);
227             });
228
229             return doc;
230         },
231
232         toXmlString : function () {
233             return dojox.xml.parser.innerXML( this.toXmlDocument() );
234         },
235
236         fromBreaker : function (marctxt) {
237             var me = this;
238
239             function cf_line_data (l) { return l.substring(4) || '' };
240             function df_line_data (l) { return l.substring(6) || '' };
241             function line_tag (l) { return l.substring(0,3) };
242             function df_ind1 (l) { return l.substring(4,5).replace('\\',' ') };
243             function df_ind2 (l) { return l.substring(5,6).replace('\\',' ') };
244             function isControlField (l) {
245                 var x = line_tag(l);
246                 return (x == 'LDR' || x < '010') ? true : false;
247             }
248             
249             var lines = marctxt.replace(/^=/gm,'').split('\n');
250             dojo.forEach(lines, function (current_line) {
251
252                 if (current_line.match(/^#/)) {
253                     // skip comment lines
254                 } else if (isControlField(current_line)) {
255                     if (line_tag(current_line) == 'LDR') {
256                         me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500';
257                     } else {
258                         me.fields.push(
259                             new MARC.Field({
260                                 record : me,
261                                 tag    : line_tag(current_line),
262                                 data   : cf_line_data(current_line).replace('\\',' ','g')
263                             })
264                         );
265                     }
266                 } else {
267                     if (current_line.substring(4,5) == me.delimiter) // add delimiters if they were left out
268                         current_line = current_line.substring(0,3) + ' \\\\' + current_line.substring(4);
269
270                     var data = df_line_data(current_line);
271                     if (!(data.substring(0,1) == me.delimiter)) data = me.delimiter + 'a' + data;
272
273                     var sf_list = data.split(me.delimiter);
274                     sf_list.shift();
275
276                     me.fields.push(
277                         new MARC.Field({
278                                 record    : me,
279                                 tag       : line_tag(current_line),
280                                 ind1      : df_ind1(current_line),
281                                 ind2      : df_ind2(current_line),
282                                 subfields : dojo.map(
283                                     sf_list,
284                                     function (sf) {
285                                         var sf_data = sf.substring(1);
286                                         if (me.delimiter == '$') sf_data = sf_data.replace(/\{dollar\}/g, '$');
287                                          return [ sf.substring(0,1), sf_data ];
288                                     }
289                                 )
290                         })
291                     );
292                 }
293             });
294
295             return this;
296         },
297
298         toBreaker : function () {
299
300             var me = this;
301             var mtxt = '=LDR ' + this.leader + '\n';
302
303             mtxt += dojo.map( this.fields, function (f) {
304                 if (f.isControlfield()) {
305                     if (f.data) return '=' + f.tag + ' ' + f.data.replace(' ','\\','g');
306                     return '=' + f.tag;
307                 } else {
308                     return '=' + f.tag + ' ' +
309                         f.indicator(1).replace(' ','\\') + 
310                         f.indicator(2).replace(' ','\\') + 
311                         dojo.map( f.subfields, function (sf) {
312                             var sf_data = sf[1];
313                             if (me.delimiter == '$') sf_data = sf_data.replace(/\$/g, '{dollar}');
314                             return me.delimiter + sf[0] + sf_data;
315                         }).join('');
316                 }
317             }).join('\n');
318
319             return mtxt;
320         }
321     });
322 }