]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
LP1907115 MARC editor correctly absorbs breaker changes
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / marc-edit / marcrecord.ts
1 import {EventEmitter} from '@angular/core';
2
3 /* Wrapper class for our external MARC21.Record JS library. */
4
5 declare var MARC21;
6
7 // MARC breaker delimiter
8 const DELIMITER = '$';
9
10 export interface MarcSubfield    // code, value, position
11     extends Array<string|number> { 0: string; 1: string; 2: number; }
12
13 // Only contains the attributes/methods we need so far.
14 export interface MarcField {
15     fieldId?: number;
16     data?: string;
17     tag?: string;
18     ind1?: string;
19     ind2?: string;
20     subfields?: MarcSubfield[];
21
22     // For authority validation
23     authValid: boolean;
24     authChecked: boolean;
25
26     // Fields are immutable when it comes to controlfield vs.
27     // data field.  Stamp the value when stamping field IDs.
28     isCtrlField: boolean;
29     indicator?: (ind: number) => any;
30
31     // Pass-through to marcrecord.js
32     isControlfield(): boolean;
33
34     deleteExactSubfields(...subfield: MarcSubfield[]): number;
35 }
36
37 export class MarcRecord {
38
39     id: number; // Database ID when known.
40     deleted: boolean;
41     record: any; // MARC21.Record object
42     breakerText: string;
43
44     // Let clients know some fixed field shuffling may have occured.
45     // Emits the fixed field code.
46     fixedFieldChange: EventEmitter<string>;
47
48     get leader(): string {
49         return this.record.leader;
50     }
51
52     set leader(l: string) {
53         this.record.leader = l;
54     }
55
56     get fields(): MarcField[] {
57        return this.record.fields;
58     }
59
60     set fields(f: MarcField[]) {
61         this.record.fields = f;
62     }
63
64     constructor(xml?: string) {
65         this.record = new MARC21.Record({marcxml: xml, delimiter: DELIMITER});
66         this.breakerText = this.record.toBreaker();
67         this.fixedFieldChange = new EventEmitter<string>();
68     }
69
70     toXml(): string {
71         return this.record.toXmlString();
72     }
73
74     toBreaker(): string {
75         return this.record.toBreaker();
76     }
77
78     recordType(): string {
79         return this.record.recordType();
80     }
81
82     absorbBreakerChanges() {
83         this.record = new MARC21.Record(
84             {marcbreaker: this.breakerText, delimiter: DELIMITER});
85         // Replacing the underlying record means regenerating the field metadata
86         this.stampFieldIds();
87     }
88
89     extractFixedField(fieldCode: string): string {
90         return this.record.extractFixedField(fieldCode);
91     }
92
93     setFixedField(fieldCode: string, fieldValue: string): string {
94         const response = this.record.setFixedField(fieldCode, fieldValue);
95         this.fixedFieldChange.emit(fieldCode);
96         return response;
97     }
98
99     // Give each field an identifier so it may be referenced later.
100     stampFieldIds() {
101         this.fields.forEach(f => this.stampFieldId(f));
102     }
103
104     // Stamp field IDs the the initial isCtrlField state.
105     stampFieldId(field: MarcField) {
106         if (!field.fieldId) {
107             field.fieldId = Math.floor(Math.random() * 10000000);
108         }
109
110         if (field.isCtrlField === undefined) {
111             field.isCtrlField = field.isControlfield();
112         }
113     }
114
115     field(spec: string, wantArray?: boolean): MarcField | MarcField[] {
116         return this.record.field(spec, wantArray);
117     }
118
119     appendFields(...newFields: MarcField[]) {
120         this.record.appendFields.apply(this.record, newFields);
121         this.stampFieldIds();
122     }
123
124     insertFieldsBefore(field: MarcField, ...newFields: MarcField[]) {
125         this.record.insertFieldsBefore.apply(
126             this.record, [field].concat(newFields));
127         this.stampFieldIds();
128     }
129
130     insertFieldsAfter(field: MarcField, ...newFields: MarcField[]) {
131         this.record.insertFieldsAfter.apply(
132             this.record, [field].concat(newFields));
133         this.stampFieldIds();
134     }
135
136     insertOrderedFields(...newFields: MarcField[]) {
137         this.record.insertOrderedFields.apply(this.record, newFields);
138         this.stampFieldIds();
139     }
140
141     generate008(): MarcField {
142         return this.record.generate008();
143     }
144
145
146     deleteFields(...fields: MarcField[]) {
147         this.record.deleteFields.apply(this.record, fields);
148     }
149
150     getField(id: number): MarcField {
151         return this.fields.filter(f => f.fieldId === id)[0];
152     }
153
154     getPreviousField(id: number): MarcField {
155         for (let idx = 0; idx < this.fields.length; idx++) {
156             if (this.fields[idx].fieldId === id) {
157                 return this.fields[idx - 1];
158             }
159         }
160     }
161
162     getNextField(id: number): MarcField {
163         for (let idx = 0; idx < this.fields.length; idx++) {
164             if (this.fields[idx].fieldId === id) {
165                 return this.fields[idx + 1];
166             }
167         }
168     }
169
170     // Turn an field-ish object into a proper MARC.Field
171     newField(props: any): MarcField {
172         const field = new MARC21.Field(props);
173         this.stampFieldId(field);
174         return field;
175     }
176
177     cloneField(field: any): MarcField {
178         const props: any = {tag: field.tag};
179
180         if (field.isControlfield()) {
181             props.data = field.data;
182
183         } else {
184             props.ind1 = field.ind1;
185             props.ind2 = field.ind2;
186             props.subfields = this.cloneSubfields(field.subfields);
187         }
188
189         return this.newField(props);
190     }
191
192     cloneSubfields(subfields: MarcSubfield[]): MarcSubfield[] {
193         const root = [];
194         subfields.forEach(sf => root.push([].concat(sf)));
195         return root;
196     }
197
198     // Returns a list of values for the tag + subfield combo
199     subfield(tag: string, subfield: string): string {
200         return this.record.subfield(tag, subfield);
201     }
202 }
203