[open-ils-commits] [GIT] Evergreen ILS branch rel_3_3 updated. 52220e9875e1de7d5c7d5dfa9fabcd053a4068a5

Evergreen Git git at git.evergreen-ils.org
Mon Aug 26 12:11:44 EDT 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, rel_3_3 has been updated
       via  52220e9875e1de7d5c7d5dfa9fabcd053a4068a5 (commit)
       via  0a2082b3615954df919b52f1ff6f6f504b173bd2 (commit)
       via  a2fb172de32ea15484c59565f052286e3e9e8b23 (commit)
       via  1732ea9971b5c917a595220bff1b65121f18bdf7 (commit)
       via  b3578d8ae2f18803f444dcb40752f7df1a6a282b (commit)
      from  06e6d483c7423566e18a60fde12bd164ecefadff (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 52220e9875e1de7d5c7d5dfa9fabcd053a4068a5
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Jul 10 11:57:18 2019 -0400

    LP1834665 Bib summary formats and jacket
    
    Display the format icon and label along the top of the Angular bib
    record summary.
    
    Display the jacket image along the left of the bib summary when the
    summary is in expaded mode.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.css b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.css
new file mode 100644
index 0000000000..11e8804226
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.css
@@ -0,0 +1,11 @@
+
+
+.eg-bib-summary .card-header {
+    padding: .25rem .5rem
+}
+
+.jacket {
+    padding: 3px;
+    width: 100%;
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
index 530e1080e2..45345d2501 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
@@ -3,6 +3,15 @@
   <div class="card-header d-flex">
     <div class="font-weight-bold">
       Record Summary
+      <ng-container *ngIf="summary.attributes.icon_format && summary.attributes.icon_format[0]">
+        <ng-container *ngFor="let icon of summary.attributes.icon_format">
+          <span class="pr-1 pl-2">
+            <img class="pr-1"
+              src="/images/format_icons/icon_format/{{icon}}.png"/>
+            <span class="font-weight-normal">{{iconFormatLabel(icon)}}</span>
+          </span>
+        </ng-container>
+      </ng-container>
     </div>
     <div class="flex-1"></div>
     <div>
@@ -18,53 +27,70 @@
       </a>
     </div>
   </div>
-  <div class="card-body">
-    <ul class="list-group list-group-flush">
-      <li class="list-group-item">
-        <div class="d-flex">
-          <div class="flex-1 font-weight-bold" i18n>Title:</div>
-          <div class="flex-3">{{summary.display.title}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Edition:</div>
-          <div class="flex-1">{{summary.display.edition}}</div>
-          <div class="flex-1 font-weight-bold" i18n>TCN:</div>
-          <div class="flex-1">{{summary.record.tcn_value()}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Created By:</div>
-          <div class="flex-1" *ngIf="summary.record.creator().usrname">
-            <a href="/eg/staff/circ/patron/{{summary.record.creator().id()}}/checkout">
-              {{summary.record.creator().usrname()}}
-            </a>
-          </div>
-        </div>
-      </li>
-      <li class="list-group-item" *ngIf="expand">
-        <div class="d-flex">
-          <div class="flex-1 font-weight-bold" i18n>Author:</div>
-          <div class="flex-3">{{summary.display.author}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Pubdate:</div>
-          <div class="flex-1">{{summary.display.pubdate}}</div>
-          <div class="flex-1 font-weight-bold" i18n>Database ID:</div>
-          <div class="flex-1">{{summary.id}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Last Edited By:</div>
-          <div class="flex-1" *ngIf="summary.record.editor().usrname">
-            <a href="/eg/staff/circ/patron/{{summary.record.editor().id()}}/checkout">
-              {{summary.record.editor().usrname()}}
-            </a>
-          </div>
-        </div>
-      </li>
-      <li class="list-group-item" *ngIf="expand">
-        <div class="d-flex">
-          <div class="flex-1 font-weight-bold" i18n>Bib Call #:</div>
-          <div class="flex-3">{{summary.bibCallNumber}}</div>
-          <div class="flex-1 font-weight-bold" i18n>Record Owner:</div>
-          <div class="flex-1">{{orgName(summary.record.owner())}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Created On:</div>
-          <div class="flex-1">{{summary.record.create_date() | date:'short'}}</div>
-          <div class="flex-1 font-weight-bold pl-1" i18n>Last Edited On:</div>
-          <div class="flex-1">{{summary.record.edit_date() | date:'short'}}</div>
-        </div>
-      </li>
-    </ul>
-  </div>
+  <div class="row">
+
+    <!-- in expanded display, show the jacket image along the left -->
+    <ng-container *ngIf="expand">
+      <div class="col-lg-1 pr-0">
+        <a href="/opac/extras/ac/jacket/large/r/{{summary.id}}">
+          <img class="jacket jacket-medium"
+            alt="Jacket Image" i18n-alt
+            src="/opac/extras/ac/jacket/medium/r/{{summary.id}}"/>
+        </a>
+      </div>
+    </ng-container>
+    
+    <!-- make room for the jacket image when expanded -->
+    <div [ngClass]="{'col-lg-11 pl-0': expand, 'col-lg-12': !expand}">
+      <div class="card-body">
+        <ul class="list-group list-group-flush">
+          <li class="list-group-item">
+            <div class="d-flex">
+              <div class="flex-1 font-weight-bold" i18n>Title:</div>
+              <div class="flex-3">{{summary.display.title}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Edition:</div>
+              <div class="flex-1">{{summary.display.edition}}</div>
+              <div class="flex-1 font-weight-bold" i18n>TCN:</div>
+              <div class="flex-1">{{summary.record.tcn_value()}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Created By:</div>
+              <div class="flex-1" *ngIf="summary.record.creator().usrname">
+                <a href="/eg/staff/circ/patron/{{summary.record.creator().id()}}/checkout">
+                  {{summary.record.creator().usrname()}}
+                </a>
+              </div>
+            </div>
+          </li>
+          <li class="list-group-item" *ngIf="expand">
+            <div class="d-flex">
+              <div class="flex-1 font-weight-bold" i18n>Author:</div>
+              <div class="flex-3">{{summary.display.author}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Pubdate:</div>
+              <div class="flex-1">{{summary.display.pubdate}}</div>
+              <div class="flex-1 font-weight-bold" i18n>Database ID:</div>
+              <div class="flex-1">{{summary.id}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Last Edited By:</div>
+              <div class="flex-1" *ngIf="summary.record.editor().usrname">
+                <a href="/eg/staff/circ/patron/{{summary.record.editor().id()}}/checkout">
+                  {{summary.record.editor().usrname()}}
+                </a>
+              </div>
+            </div>
+          </li>
+          <li class="list-group-item" *ngIf="expand">
+            <div class="d-flex">
+              <div class="flex-1 font-weight-bold" i18n>Bib Call #:</div>
+              <div class="flex-3">{{summary.bibCallNumber}}</div>
+              <div class="flex-1 font-weight-bold" i18n>Record Owner:</div>
+              <div class="flex-1">{{orgName(summary.record.owner())}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Created On:</div>
+              <div class="flex-1">{{summary.record.create_date() | date:'short'}}</div>
+              <div class="flex-1 font-weight-bold pl-1" i18n>Last Edited On:</div>
+              <div class="flex-1">{{summary.record.edit_date() | date:'short'}}</div>
+            </div>
+          </li>
+        </ul>
+      </div>
+    </div><!-- col -->
+  </div><!-- row -->
 </div>
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
index 954cb8bfe3..39b8944d3a 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
@@ -3,11 +3,12 @@ import {OrgService} from '@eg/core/org.service';
 import {BibRecordService, BibRecordSummary
     } from '@eg/share/catalog/bib-record.service';
 import {ServerStoreService} from '@eg/core/server-store.service';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
 
 @Component({
   selector: 'eg-bib-summary',
   templateUrl: 'bib-summary.component.html',
-  styles: ['.eg-bib-summary .card-header {padding: .25rem .5rem}']
+  styleUrls: ['bib-summary.component.css']
 })
 export class BibSummaryComponent implements OnInit {
 
@@ -38,7 +39,8 @@ export class BibSummaryComponent implements OnInit {
     constructor(
         private bib: BibRecordService,
         private org: OrgService,
-        private store: ServerStoreService
+        private store: ServerStoreService,
+        private cat: CatalogService
     ) {}
 
     ngOnInit() {
@@ -66,7 +68,6 @@ export class BibSummaryComponent implements OnInit {
             summary.getBibCallNumber();
             this.bib.fleshBibUsers([summary.record]);
             this.summary = summary;
-            console.log(this.summary.display);
         });
     }
 
@@ -76,6 +77,9 @@ export class BibSummaryComponent implements OnInit {
         }
     }
 
+    iconFormatLabel(code: string): string {
+        return this.cat.iconFormatLabel(code);
+    }
 }
 
 

commit 0a2082b3615954df919b52f1ff6f6f504b173bd2
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Jul 10 11:56:31 2019 -0400

    LP1834665 MARC editor success/fail toasts
    
    Indicate to the user when a MARC edit succeeds or fails via
    toast in the Angular MARC editor component.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
index fdaf7e50cd..f8eef36976 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
@@ -14,6 +14,9 @@
   dialogBody="Records with holdings attached cannot be deleted.">
 </eg-alert-dialog>
 
+<eg-string #successMsg i18n-text text="Record successfully updated"></eg-string>
+<eg-string #failMsg i18n-text text="Record failed to update"></eg-string>
+
 <div class="row d-flex p-2 m-2">
   <div class="flex-1"></div>
   <div class="mr-2">
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
index cea199052a..fa9fbe1259 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
@@ -5,6 +5,8 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {OrgService} from '@eg/core/org.service';
 import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
 import {MarcRecord} from './marcrecord';
 import {ComboboxEntry, ComboboxComponent
   } from '@eg/share/combobox/combobox.component';
@@ -50,6 +52,8 @@ export class MarcEditorComponent implements OnInit {
     @ViewChild('confirmDelete') confirmDelete: ConfirmDialogComponent;
     @ViewChild('confirmUndelete') confirmUndelete: ConfirmDialogComponent;
     @ViewChild('cannotDelete') cannotDelete: ConfirmDialogComponent;
+    @ViewChild('successMsg') successMsg: StringComponent;
+    @ViewChild('failMsg') failMsg: StringComponent;
 
     constructor(
         private evt: EventService,
@@ -57,7 +61,8 @@ export class MarcEditorComponent implements OnInit {
         private net: NetService,
         private auth: AuthService,
         private org: OrgService,
-        private pcrud: PcrudService
+        private pcrud: PcrudService,
+        private toast: ToastService
     ) {
         this.sources = [];
         this.recordSaved = new EventEmitter<string>();
@@ -101,10 +106,11 @@ export class MarcEditorComponent implements OnInit {
                 const evt = this.evt.parse(response);
                 if (evt) {
                     console.error(evt);
-                    // TODO: toast
+                    this.failMsg.current().then(msg => this.toast.warning(msg));
+                    return;
                 }
 
-                // TODO: toast
+                this.successMsg.current().then(msg => this.toast.success(msg));
                 this.recordSaved.emit(xml);
                 return response;
             });

commit a2fb172de32ea15484c59565f052286e3e9e8b23
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Jul 10 11:51:53 2019 -0400

    LP1834665 Flat text editor uses '$' delimiter
    
    Consistent with the AngJS flat text MARC editor, use '$' as the subfield
    delimiter instead of the default '‡'.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
index 1b0c488e46..df1a492762 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
@@ -4,6 +4,9 @@
 
 declare var MARC21;
 
+// MARC breaker delimiter
+const DELIMITER = '$';
+
 export class MarcRecord {
 
     id: number; // Database ID when known.
@@ -12,7 +15,7 @@ export class MarcRecord {
     breakerText: string;
 
     constructor(xml: string) {
-        this.record = new MARC21.Record({marcxml: xml});
+        this.record = new MARC21.Record({marcxml: xml, delimiter: DELIMITER});
         this.breakerText = this.record.toBreaker();
     }
 
@@ -25,7 +28,8 @@ export class MarcRecord {
     }
 
     absorbBreakerChanges() {
-        this.record = new MARC21.Record({marcbreaker: this.breakerText});
+        this.record = new MARC21.Record(
+            {marcbreaker: this.breakerText, delimiter: DELIMITER});
     }
 }
 

commit 1732ea9971b5c917a595220bff1b65121f18bdf7
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Jun 28 12:29:07 2019 -0400

    LP1834665 Angular catalog MARC flat text editor
    
    Adds a set of components for editing MARC records.  The main component
    acts as a container with various actions (source selector, delete,
    undelete, and save options).  The body of this component is a tabbed
    interface, one tab for the Enriched editor and one for the Flat Text
    editor.
    
    The Enriched editor tab directs the user to the AngJS version of the page.
    the Flat Text editor tab implements the standard MARC flat text editor
    interface.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index e78a951e62..e991852764 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -26,6 +26,7 @@ import {HoldingsMaintenanceComponent} from './record/holdings.component';
 import {ConjoinedComponent} from './record/conjoined.component';
 import {CnBrowseComponent} from './cnbrowse.component';
 import {CnBrowseResultsComponent} from './cnbrowse/results.component';
+import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module';
 
 @NgModule({
   declarations: [
@@ -56,7 +57,8 @@ import {CnBrowseResultsComponent} from './cnbrowse/results.component';
     CatalogRoutingModule,
     HoldsModule,
     HoldingsModule,
-    BookingModule
+    BookingModule,
+    MarcEditModule
   ],
   providers: [
     StaffCatalogService
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
index 8be4524985..077fdd1ad5 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
@@ -35,12 +35,9 @@
       <!-- NOTE some tabs send the user over to the AngJS app -->
       <ngb-tab title="MARC Edit" i18n-title id="marc_edit">
         <ng-template ngbTabContent>
-          <div class="alert alert-info mt-3" i18n>
-            MARC Edit not yet implemented.  See the
-            <a target="_blank"
-              href="/eg/staff/cat/catalog/record/{{recordId}}/marc_edit">
-              AngularJS MARC Edit Tab.
-            </a>
+          <div class="mt-3">
+            <eg-marc-editor (recordSaved)="handleMarcRecordSaved()" 
+              [recordId]="recordId"></eg-marc-editor>
           </div>
         </ng-template>
       </ngb-tab>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
index c70b5658be..e397444819 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
@@ -110,6 +110,11 @@ export class RecordComponent implements OnInit {
         }
         return null;
     }
+
+    handleMarcRecordSaved() {
+        this.staffCat.currentDetailRecordSummary = null;
+        this.loadRecord();
+    }
 }
 
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
new file mode 100644
index 0000000000..fdaf7e50cd
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
@@ -0,0 +1,73 @@
+
+<eg-confirm-dialog #confirmDelete
+  i18n-dialogTitle dialogTitle="Confirm Delete"
+  i18n-dialogBody dialogBody="Delete Record ID {{record ? record.id : ''}}?">
+</eg-confirm-dialog>
+
+<eg-confirm-dialog #confirmUndelete
+  i18n-dialogTitle dialogTitle="Confirm Undelete"
+  i18n-dialogBody dialogBody="Undelete Record ID {{record ? record.id : ''}}?">
+</eg-confirm-dialog>
+
+<eg-alert-dialog #cannotDelete
+  i18n-dialogBody 
+  dialogBody="Records with holdings attached cannot be deleted.">
+</eg-alert-dialog>
+
+<div class="row d-flex p-2 m-2">
+  <div class="flex-1"></div>
+  <div class="mr-2">
+    <eg-combobox #sourceSelector
+      [entries]="sources"
+      placeholder="Select a Source..."
+      i18n-placeholder>
+    </eg-combobox>
+  </div>
+
+  <ng-container *ngIf="record && record.id">
+    <button *ngIf="!record.deleted" class="btn btn-warning" 
+      (click)="deleteRecord()" i18n>Delete Record</button>
+    <button *ngIf="record.deleted" class="btn btn-info" 
+      (click)="undeleteRecord()" i18n>Undelete Record</button>
+  </ng-container>
+
+  <button class="btn btn-success ml-2" (click)="saveRecord()" 
+    [disabled]="record && record.deleted" i18n>Save Changes</button>
+</div>
+
+<div class="row">
+  <div class="col-lg-12">
+    <ngb-tabset [activeId]="editorTab">
+      <ngb-tab title="Enhanced MARC Editor" i18n-title id="rich">
+        <ng-template ngbTabContent>
+          <div class="alert alert-info mt-3" i18n>
+          Enhanced MARC Editor is not yet implemented.  See the
+          <ng-container *ngIf="record && record.id">
+            <a target="_blank"
+              href="/eg/staff/cat/catalog/record/{{record.id}}/marc_edit">
+              AngularJS MARC Editor.
+            </a>
+          </ng-container>
+          <ng-container *ngIf="!record || !record.id">
+            <a target="_blank" href="/eg/staff/cat/catalog/new_bib">
+              AngularJS MARC Editor.
+            </a>
+          </ng-container>
+          </div>
+        </ng-template>
+      </ngb-tab>
+      <ngb-tab title="Flat Text Editor" i18n-title id="flat">
+        <ng-template ngbTabContent>
+          <eg-marc-flat-editor></eg-marc-flat-editor>
+        </ng-template>
+      </ngb-tab>
+    </ngb-tabset>
+  </div>
+</div>
+
+<div class="row d-flex p-2 m-2 flex-row-reverse">
+  <button class="btn btn-success" (click)="saveRecord()"
+    [disabled]="record && record.deleted" i18n>Save Changes</button>
+</div>
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
new file mode 100644
index 0000000000..cea199052a
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
@@ -0,0 +1,182 @@
+import {Component, Input, Output, OnInit, EventEmitter, ViewChild} from '@angular/core';
+import {IdlService} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {MarcRecord} from './marcrecord';
+import {ComboboxEntry, ComboboxComponent
+  } from '@eg/share/combobox/combobox.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+
+/**
+ * MARC Record editor main interface.
+ */
+
+ at Component({
+  selector: 'eg-marc-editor',
+  templateUrl: './editor.component.html'
+})
+
+export class MarcEditorComponent implements OnInit {
+
+    record: MarcRecord;
+    editorTab: 'rich' | 'flat';
+    sources: ComboboxEntry[];
+
+    @Input() set recordId(id: number) {
+        if (!id) { return; }
+        if (this.record && this.record.id === id) { return; }
+        this.fromId(id);
+    }
+
+    @Input() set recordXml(xml: string) {
+        if (xml) { this.fromXml(xml); }
+    }
+
+    // If true, saving records to the database is assumed to
+    // happen externally.  IOW, the record editor is just an
+    // in-place MARC modification interface.
+    inPlaceMode: boolean;
+
+    // In inPlaceMode, this is emitted in lieu of saving the record
+    // in th database.  When inPlaceMode is false, this is emitted after
+    // the record is successfully saved.
+    @Output() recordSaved: EventEmitter<string>;
+
+    @ViewChild('sourceSelector') sourceSelector: ComboboxComponent;
+    @ViewChild('confirmDelete') confirmDelete: ConfirmDialogComponent;
+    @ViewChild('confirmUndelete') confirmUndelete: ConfirmDialogComponent;
+    @ViewChild('cannotDelete') cannotDelete: ConfirmDialogComponent;
+
+    constructor(
+        private evt: EventService,
+        private idl: IdlService,
+        private net: NetService,
+        private auth: AuthService,
+        private org: OrgService,
+        private pcrud: PcrudService
+    ) {
+        this.sources = [];
+        this.recordSaved = new EventEmitter<string>();
+    }
+
+    ngOnInit() {
+        // Default to flat for now since it's all that's supported.
+        this.editorTab = 'flat';
+
+        this.pcrud.retrieveAll('cbs').subscribe(
+            src => this.sources.push({id: +src.id(), label: src.source()}),
+            _ => {},
+            () => {
+                this.sources = this.sources.sort((a, b) =>
+                    a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1
+                );
+            }
+        );
+    }
+
+    saveRecord(): Promise<any> {
+        const xml = this.record.toXml();
+
+        if (this.inPlaceMode) {
+            // Let the caller have the modified XML and move on.
+            this.recordSaved.emit(xml);
+            return Promise.resolve();
+        }
+
+        const source = this.sourceSelector.selected ?
+            this.sourceSelector.selected.label : null; // 'label' not a typo
+
+        if (this.record.id) { // Editing an existing record
+
+            const method = 'open-ils.cat.biblio.record.marc.replace';
+
+            return this.net.request('open-ils.cat', method,
+                this.auth.token(), this.record.id, xml, source
+            ).toPromise().then(response => {
+
+                const evt = this.evt.parse(response);
+                if (evt) {
+                    console.error(evt);
+                    // TODO: toast
+                }
+
+                // TODO: toast
+                this.recordSaved.emit(xml);
+                return response;
+            });
+
+        } else {
+            // TODO: create a new record
+        }
+    }
+
+    fromId(id: number): Promise<any> {
+        return this.pcrud.retrieve('bre', id)
+        .toPromise().then(bib => {
+            this.record = new MarcRecord(bib.marc());
+            this.record.id = id;
+            this.record.deleted = bib.deleted() === 't';
+            if (bib.source()) {
+                this.sourceSelector.applyEntryId(+bib.source());
+            }
+        });
+    }
+
+    fromXml(xml: string) {
+        this.record = new MarcRecord(xml);
+        this.record.id = null;
+    }
+
+    deleteRecord(): Promise<any> {
+
+        return this.confirmDelete.open().toPromise()
+        .then(yes => {
+            if (!yes) { return; }
+
+            return this.net.request('open-ils.cat',
+                'open-ils.cat.biblio.record_entry.delete',
+                this.auth.token(), this.record.id).toPromise()
+
+            .then(resp => {
+
+                const evt = this.evt.parse(resp);
+                if (evt) {
+                    if (evt.textcode === 'RECORD_NOT_EMPTY') {
+                        return this.cannotDelete.open().toPromise();
+                    } else {
+                        console.error(evt);
+                        return alert(evt);
+                    }
+                }
+                return this.fromId(this.record.id)
+                .then(_ => this.recordSaved.emit(this.record.toXml()));
+            });
+        });
+    }
+
+    undeleteRecord(): Promise<any> {
+
+        return this.confirmUndelete.open().toPromise()
+        .then(yes => {
+            if (!yes) { return; }
+
+            return this.net.request('open-ils.cat',
+                'open-ils.cat.biblio.record_entry.undelete',
+                this.auth.token(), this.record.id).toPromise()
+
+            .then(resp => {
+
+                const evt = this.evt.parse(resp);
+                if (evt) { console.error(evt); return alert(evt); }
+
+                return this.fromId(this.record.id)
+                .then(_ => this.recordSaved.emit(this.record.toXml()));
+            });
+        });
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css
new file mode 100644
index 0000000000..12e912b8f1
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css
@@ -0,0 +1,11 @@
+
+
+.flat-editor-content {
+    font-family: 'Lucida Console', Monaco, monospace; 
+    display: inline-block; 
+    /*
+    min-width: 1ch; 
+    margin: 0 -1px; 
+    */
+    padding: 0;
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html
new file mode 100644
index 0000000000..eaf54a92c1
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html
@@ -0,0 +1,7 @@
+
+<div *ngIf="record">
+  <textarea class="form-control flat-editor-content" 
+    (blur)="record.absorbBreakerChanges()"
+    [(ngModel)]="record.breakerText" rows="{{rowCount()}}" spellcheck="false">
+  </textarea>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts
new file mode 100644
index 0000000000..b5e2f41277
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts
@@ -0,0 +1,45 @@
+import {Component, Input, OnInit, Host} from '@angular/core';
+import {IdlService} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {MarcEditorComponent} from './editor.component';
+import {MarcRecord} from './marcrecord';
+
+/**
+ * MARC Record flat text (marc-breaker) editor.
+ */
+
+ at Component({
+  selector: 'eg-marc-flat-editor',
+  templateUrl: './flat-editor.component.html',
+  styleUrls: ['flat-editor.component.css']
+})
+
+export class MarcFlatEditorComponent implements OnInit {
+
+    get record(): MarcRecord {
+        return this.editor.record;
+    }
+
+    constructor(
+        private idl: IdlService,
+        private org: OrgService,
+        private store: ServerStoreService,
+        @Host() private editor: MarcEditorComponent
+    ) {
+    }
+
+    ngOnInit() {}
+
+    // When we have breaker text, limit the vertical expansion of the
+    // text area to the size of the data plus a little padding.
+    rowCount(): number {
+        if (this.record && this.record.breakerText) {
+            return this.record.breakerText.split(/\n/).length + 2;
+        }
+        return 40;
+    }
+}
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts
new file mode 100644
index 0000000000..a18eb0b7a4
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts
@@ -0,0 +1,24 @@
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {MarcEditorComponent} from './editor.component';
+import {MarcRichEditorComponent} from './rich-editor.component';
+import {MarcFlatEditorComponent} from './flat-editor.component';
+
+ at NgModule({
+    declarations: [
+        MarcEditorComponent,
+        MarcRichEditorComponent,
+        MarcFlatEditorComponent
+    ],
+    imports: [
+        StaffCommonModule
+    ],
+    exports: [
+        MarcEditorComponent
+    ],
+    providers: [
+    ]
+})
+
+export class MarcEditModule { }
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
new file mode 100644
index 0000000000..1b0c488e46
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
@@ -0,0 +1,31 @@
+/**
+  * Simple wrapper class for our external MARC21.Record JS library.
+  */
+
+declare var MARC21;
+
+export class MarcRecord {
+
+    id: number; // Database ID when known.
+    deleted: boolean;
+    record: any; // MARC21.Record object
+    breakerText: string;
+
+    constructor(xml: string) {
+        this.record = new MARC21.Record({marcxml: xml});
+        this.breakerText = this.record.toBreaker();
+    }
+
+    toXml(): string {
+        return this.record.toXmlString();
+    }
+
+    toBreaker(): string {
+        return this.record.toBreaker();
+    }
+
+    absorbBreakerChanges() {
+        this.record = new MARC21.Record({marcbreaker: this.breakerText});
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts
new file mode 100644
index 0000000000..7f8ac334e3
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts
@@ -0,0 +1,28 @@
+import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter,
+    OnDestroy} from '@angular/core';
+import {IdlService} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+
+/**
+ * MARC Record rich editor interface.
+ */
+
+ at Component({
+  selector: 'eg-marc-rich-editor',
+  templateUrl: './rich-editor.component.html',
+  styleUrls: ['rich-editor.component.css']
+})
+
+export class MarcRichEditorComponent implements OnInit {
+
+    constructor(
+        private idl: IdlService,
+        private org: OrgService
+    ) {
+    }
+
+    ngOnInit() {}
+}
+
+
+

commit b3578d8ae2f18803f444dcb40752f7df1a6a282b
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Jun 28 12:27:35 2019 -0400

    LP1834665 Import marcrecord.js to Angular
    
    Port the marcrecord.js file into the Angular build configuration so it
    may be used by the MARC editor code.
    
    Additionally, port jquery-dependent code to vanilla JS to avoid the
    jquery dependency.  As a part of this, some code from the source file
    was removed since it was not needed (yet, anyway).  Code added back will
    need to be similarly ported (mostly changing XML parsing).
    
    Note as-is, the JS is loaded on every Angular page.  We could optimize
    this and have it loaded only when needed if we port it to Typescript and
    integrate it into the application instead of loading it as a 3rd-party
    dependancy.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/angular.json b/Open-ILS/src/eg2/angular.json
index ee7329df0c..d26b37e03b 100644
--- a/Open-ILS/src/eg2/angular.json
+++ b/Open-ILS/src/eg2/angular.json
@@ -25,7 +25,9 @@
             "styles": [
               "src/styles.css"
             ],
-            "scripts": []
+            "scripts": [
+              "src/assets/js/marcrecord.js"
+            ]
           },
           "configurations": {
             "production": {
diff --git a/Open-ILS/src/eg2/src/assets/js/marcrecord.js b/Open-ILS/src/eg2/src/assets/js/marcrecord.js
new file mode 100644
index 0000000000..04d8c74cce
--- /dev/null
+++ b/Open-ILS/src/eg2/src/assets/js/marcrecord.js
@@ -0,0 +1,1317 @@
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2009-2015  Equinox Software, Inc.
+ * Mike Rylander <miker at esilibrary.com>
+ *
+ * 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.
+ * ---------------------------------------------------------------------------
+ */
+
+/*
+ * Copy of file from Open-ILS/web/js/ui/default/staff/marcedit.js
+ *
+ * This copy of the the MARC21 library heavily modified by
+ * Bill Erickson <berickxx at gmail.com> 2019 circa Evergreen 3.3.
+ *
+ * 1. All jquery dependencies have been replaced with Vanilla JS.
+ * 2. Many features from the original have been removed (for now,
+ *    anyway) since they were not needed at the time and would have
+ *    required additional jquery porting work.
+ * 
+ * Code is otherwise unchanged.
+ */
+
+/**
+ * As an external dependency, this JS is loaded on every Angular page.
+ * We could migrate it into the Angular app as Typescript so it's only
+ * loaded when needed (e.g. in the marc editor component).
+ */
+
+var MARC21 = {
+
+    Record : function(kwargs) {
+        if (!kwargs) kwargs = {};
+
+        this.generate008 = function () {
+            var f;
+            var s;
+            var orig008 = '                                        ';
+            var now = new Date();
+            var y = now.getUTCFullYear().toString().substr(2,2);
+            var m = now.getUTCMonth() + 1;
+            if (m < 10) m = '0' + m;
+            var d = now.getUTCDate();
+            if (d < 10) d = '0' + d;
+
+
+            if (f = this.field('008',true)[0]) {
+                orig008 = f.data;
+            }
+        
+            /* lang code from 041a */
+            var lang = orig008.substr(35, 3);
+            
+            if (f = this.field('041',true)[0]) {
+                if (s = f.subfield('a',true)[0]) {
+                    if(s[1]) lang = s[1];
+                }
+            }
+        
+            /* country code from 044a */
+            var country = orig008.substr(15, 3);
+            if (f = this.field('044',true)[0]) {
+                if (s = f.subfield('a',true)[0]) {
+                    if(s[1]) country = s[1];
+                }
+            }
+            while (country.length < 3) country = country + ' ';
+            if (country.length > 3) country = country.substr(0,3);
+        
+            /* date1 from 260c */
+            var date1 = now.getUTCFullYear().toString();
+            if (f = this.field('260',true)[0]) {
+                if (s = f.subfield('c',true)[0]) {
+                    if (s[1]) {
+                        var tmpd = s[1];
+                        tmpd = tmpd.replace(/[^0-9]/g, '');
+                        if (tmpd.match(/^\d\d\d\d/)) {
+                            date1 = tmpd.substr(0, 4);
+                        }
+                    }
+                }
+            }
+        
+            var date2 = orig008.substr(11, 4);
+            var datetype = orig008.substr(6, 1);
+            var modded = orig008.substr(38, 1);
+            var catsrc = orig008.substr(39, 1);
+        
+            return '' + y + m + d + datetype + date1 + date2 + country + '                 ' + lang + modded + catsrc;
+        
+        }
+
+        this.title = function () { return this.subfield('245','a')[1] }
+
+        this.field = function (spec, wantarray) {
+            var list = this.fields.filter(function (f) {
+                if (f.tag.match(spec)) return true;
+                return false;
+            });
+
+            if (!wantarray && list.length == 1) return list[0];
+            return list;
+        }
+
+        this.subfield = function (spec, code) {
+            var f = this.field(spec, true)[0];
+            if (f) return f.subfield(code)
+            return null;
+        }
+
+        this.appendFields = function () {
+            var me = this;
+            Array.prototype.slice.call(arguments).forEach( function (f) { f.position = me.fields.length; 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]) {
+                    args.forEach( function (f) {
+                        f.record = me;
+                        me.fields.splice(j++,0,f);
+                    });
+                    break;
+                }
+            }
+            for (var j = 0; j < this.fields.length; j++) {
+                this.fields[j].position = j;
+            }
+        }
+
+        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) {
+                        f.record = me;
+                        me.fields.splice(++j,0,f);
+                    });
+                    break;
+                }
+            }
+            for (var j = 0; j < this.fields.length; j++) {
+                this.fields[j].position = j;
+            }
+        }
+
+        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,1);
+                        counter++
+                        break;
+                    }
+                }
+            }
+            for (var j = 0; j < this.fields.length; j++) {
+                this.fields[j].position = j;
+            }
+            return counter;
+        }
+
+        this.fromXmlString = function (mxml) {
+            var xmlDoc = new DOMParser().parseFromString(mxml, "text/xml");
+            this.fromXmlDocument(xmlDoc.getElementsByTagName('record')[0]);
+        }
+
+        this.fromXmlDocument = function (mxml) {
+            var me = this;
+            var ldr =  mxml.getElementsByTagName('leader')[0];
+            me.leader = (ldr ? ldr.textContent : '') || '00000cam a2200205Ka 4500';
+
+            var cfNodes = mxml.getElementsByTagName('controlfield');
+            for (var idx = 0; idx < cfNodes.length; idx++) {
+                var cf = cfNodes[idx];
+                me.fields.push(
+                    new MARC21.Field({
+                          record : me,
+                          tag    : cf.getAttribute('tag'),
+                          data   : cf.textContent
+                    })
+                );
+            }
+
+            var dfNodes = mxml.getElementsByTagName('datafield');
+            for (var idx = 0; idx < dfNodes.length; idx++) {
+                var df = dfNodes[idx];
+
+                var sfNodes = df.getElementsByTagName('subfield');
+                var subfields = [];
+                for (var idx2 = 0; idx2 < sfNodes.length; idx2++) {
+                    var sf = sfNodes[idx2];
+                    subfields.push(
+                        [sf.getAttribute('code'), sf.textContent, idx2]);
+                }
+
+                me.fields.push(
+                    new MARC21.Field({
+                        record    : me,
+                        tag       : df.getAttribute('tag'),
+                        ind1      : df.getAttribute('ind1'),
+                        ind2      : df.getAttribute('ind2'),
+                        subfields : subfields
+                    })
+                );
+            }
+
+            for (var j = 0; j < this.fields.length; j++) {
+                this.fields[j].position = j;
+            }
+
+            me.ready = true;
+        }
+
+        this.toXmlDocument = function () {
+
+            var doc = new DOMParser().parseFromString(
+                '<record xmlns="http://www.loc.gov/MARC21/slim"/>', 'text/xml');
+
+            var rec_node = doc.getElementsByTagName('record')[0];
+
+            var ldr = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'leader');
+            ldr.textContent = this.leader;
+            rec_node.appendChild( 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.appendChild(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, ind) {
+
+                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 MARC21.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 indicators 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 local_delimiter = me.delimiter;
+                    if (data.indexOf('\u2021') > -1)
+                        local_delimiter = '\u2021';
+
+                    var sf_list = data.split(local_delimiter);
+                    sf_list.shift();
+
+                    me.fields.push(
+                        new MARC21.Field({
+                                record    : me,
+                                tag       : line_tag(current_line),
+                                ind1      : df_ind1(current_line),
+                                ind2      : df_ind2(current_line),
+                                subfields : sf_list.map( function (sf, i) {
+                                                var sf_data = sf.substring(1);
+                                                if (local_delimiter == '$') sf_data = sf_data.replace(/\{dollar\}/g, '$');
+                                                return [ sf.substring(0,1), sf_data, i ];
+                                            })
+                        })
+                    );
+                }
+            });
+
+            for (var j = 0; j < this.fields.length; j++) {
+                this.fields[j].position = j;
+            }
+
+            me.ready = true;
+            return this;
+        }
+
+        this.pruneEmptyFieldsAndSubfields = function() {
+            var me = this;
+            var fields_to_remove = [];
+            for (var i = 0; i < this.fields.length; i++) {
+                var f = this.fields[i];
+                if (f.isControlfield()) {
+                    if (!f.data){
+                        fields_to_remove.push(f);
+                    }
+                } else {
+                    f.pruneEmptySubfields();
+                    if (f.isEmptyDatafield()) {
+                        fields_to_remove.push(f);
+                    }
+                }
+            }
+            fields_to_remove.forEach(function(f) {
+                me.deleteField(f);
+            });
+        }
+
+        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(MARC21.Record._ff_pos.Type.ldr.BKS.start, MARC21.Record._ff_pos.Type.ldr.BKS.len);
+            var _b = this.leader.substr(MARC21.Record._ff_pos.BLvl.ldr.BKS.start, MARC21.Record._ff_pos.BLvl.ldr.BKS.len);
+        
+            for (var t in MARC21.Record._recType) {
+                if (_t.match(MARC21.Record._recType[t].Type) && _b.match(MARC21.Record._recType[t].BLvl)) {
+                    return t;
+                }
+            }
+            return 'BKS'; // default
+        }
+        
+        this.extractFixedField = function (field, dflt) {
+        if (!MARC21.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 (MARC21.Record._ff_pos[field].ldr && _l) {
+                if (MARC21.Record._ff_pos[field].ldr[rtype]) {
+                    val = _l.substr(
+                        MARC21.Record._ff_pos[field].ldr[rtype].start,
+                        MARC21.Record._ff_pos[field].ldr[rtype].len
+                    );
+                }
+            } else if (MARC21.Record._ff_pos[field]._8 && _8) {
+                if (MARC21.Record._ff_pos[field]._8[rtype]) {
+                    val = _8.substr(
+                        MARC21.Record._ff_pos[field]._8[rtype].start,
+                        MARC21.Record._ff_pos[field]._8[rtype].len
+                    );
+                }
+            }
+        
+            if (!val && MARC21.Record._ff_pos[field]._6 && _6) {
+                if (MARC21.Record._ff_pos[field]._6[rtype]) {
+                    val = _6.substr(
+                        MARC21.Record._ff_pos[field]._6[rtype].start,
+                        MARC21.Record._ff_pos[field]._6[rtype].len
+                    );
+                }
+            }
+    
+            if (!val && dflt) {
+                val = '';
+                var d;
+                var p;
+                if (MARC21.Record._ff_pos[field].ldr && MARC21.Record._ff_pos[field].ldr[rtype]) {
+                    d = MARC21.Record._ff_pos[field].ldr[rtype].def;
+                    p = 'ldr';
+                }
+    
+                if (MARC21.Record._ff_pos[field]._8 && MARC21.Record._ff_pos[field]._8[rtype]) {
+                    d = MARC21.Record._ff_pos[field]._8[rtype].def;
+                    p = '_8';
+                }
+    
+                if (!val && MARC21.Record._ff_pos[field]._6 && MARC21.Record._ff_pos[field]._6[rtype]) {
+                    d = MARC21.Record._ff_pos[field]._6[rtype].def;
+                    p = '_6';
+                }
+    
+                if (p) {
+                    for (var j = 0; j < MARC21.Record._ff_pos[field][p][rtype].len; j++) {
+                        val += d;
+                    }
+                } else {
+                    val = null;
+                }
+            }
+    
+            return val;
+        }
+    
+        this.setFixedField = function (field, value) {
+            if (!MARC21.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 done = false;
+            if (MARC21.Record._ff_pos[field].ldr && _l) {
+                if (MARC21.Record._ff_pos[field].ldr[rtype]) { // It's in the leader
+                    if (value.length > MARC21.Record._ff_pos[field].ldr[rtype].len)
+                        value = value.substr(0, MARC21.Record._ff_pos[field].ldr[rtype].len);
+                    while (value.length < MARC21.Record._ff_pos[field].ldr[rtype].len)
+                        value += MARC21.Record._ff_pos[field].ldr[rtype].def;
+                    this.leader =
+                        _l.substring(0, MARC21.Record._ff_pos[field].ldr[rtype].start) +
+                        value +
+                        _l.substring(
+                            MARC21.Record._ff_pos[field].ldr[rtype].start
+                            + MARC21.Record._ff_pos[field].ldr[rtype].len
+                        );
+                    done = true;
+                }
+            } else if (MARC21.Record._ff_pos[field]._8 && _8) {
+                if (MARC21.Record._ff_pos[field]._8[rtype]) { // Nope, it's in the 008
+                    if (value.length > MARC21.Record._ff_pos[field]._8[rtype].len)
+                        value = value.substr(0, MARC21.Record._ff_pos[field]._8[rtype].len);
+                    while (value.length < MARC21.Record._ff_pos[field]._8[rtype].len)
+                        value += MARC21.Record._ff_pos[field]._8[rtype].def;
+
+                    // first ensure that 008 is padded to appropriate length
+                    var f008_length = (rtype in MARC21.Record._ff_lengths['008']) ?
+                        MARC21.Record._ff_lengths['008'][rtype] :
+                        MARC21.Record._ff_lengths['008']['default'];
+                    if (_8.length < f008_length) {
+                        for (var i = _8.length; i < f008_length; i++) {
+                            _8 += ' ';
+                        }
+                    }
+                    this.field('008').update(
+                        _8.substring(0, MARC21.Record._ff_pos[field]._8[rtype].start) +
+                        value +
+                        _8.substring(
+                            MARC21.Record._ff_pos[field]._8[rtype].start
+                            + MARC21.Record._ff_pos[field]._8[rtype].len
+                        )
+                    );
+                    done = true;
+                }
+            }
+        
+            if (!done && MARC21.Record._ff_pos[field]._6 && _6) {
+                if (MARC21.Record._ff_pos[field]._6[rtype]) { // ok, maybe the 006?
+                    if (value.length > MARC21.Record._ff_pos[field]._6[rtype].len)
+                        value = value.substr(0, MARC21.Record._ff_pos[field]._6[rtype].len);
+                    while (value.length < MARC21.Record._ff_pos[field]._6[rtype].len)
+                        value += MARC21.Record._ff_pos[field]._6[rtype].def;
+                    this.field('006').update(
+                        _6.substring(0, MARC21.Record._ff_pos[field]._6[rtype].start) +
+                        value +
+                        _6.substring(
+                            MARC21.Record._ff_pos[field]._6[rtype].start
+                            + MARC21.Record._ff_pos[field]._6[rtype].len
+                        )
+                    );
+                }
+            }
+    
+            return value;
+        }
+
+        this.ready = false;
+        this.fields = [];
+        this.delimiter = MARC21.Record.delimiter ? MARC21.Record.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, wantarray) {
+            var list = this.subfields.filter( function (s) {
+                if (s[0] == code) return true; return false;
+            });
+            if (!wantarray && 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.deleteExactSubfields = function () {
+            var me = this;
+            var counter = 0;
+            var done = false;
+            for ( var i in arguments ) {
+                var f = arguments[i];
+                for (var j = 0; j < me.subfields.length; j++) {
+                    if (f === me.subfields[j]) {
+                        me.subfields.splice(j,1);
+                        counter++
+                        j++;
+                        done = true;
+                    }
+                    if (done && me.subfields[j])
+                        me.subfields[j][2] -= 1;
+                }
+            }
+            return counter;
+        }
+
+
+        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.length; 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.pruneEmptySubfields = function () {
+            if (this.isControlfield()) return;
+            var me = this;
+            var subfields_to_remove = [];
+            this.subfields.forEach( function(f) {
+                if (f[1] == '') {
+                    subfields_to_remove.push(f);
+                }
+            });
+            subfields_to_remove.forEach(function(f) {
+                me.deleteExactSubfields(f);
+            });
+        }
+        this.isEmptyDatafield = function () {
+            if (this.isControlfield()) return false;
+            var isEmpty = true;
+            this.subfields.forEach( function(f) {
+                if (isEmpty && f[1] != '') {
+                    isEmpty = false;
+                }
+            });
+            return isEmpty;
+        }
+
+        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.position = kwargs.position;
+        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 = [];
+
+    }
+};
+
+MARC21.Record._recType = {
+    BKS : { Type : /[at]{1}/,    BLvl : /[acdm]{1}/ },
+    SER : { Type : /[a]{1}/,    BLvl : /[bsi]{1}/ },
+    VIS : { Type : /[gkro]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    MIX : { Type : /[p]{1}/,    BLvl : /[cdi]{1}/ },
+    MAP : { Type : /[ef]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    SCO : { Type : /[cd]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    REC : { Type : /[ij]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    COM : { Type : /[m]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    AUT : { Type : /[z]{1}/,    BLvl : /.{1}/ },
+    MFHD : { Type : /[uvxy]{1}/,  BLvl : /.{1}/ }
+};
+
+MARC21.Record._ff_lengths = {
+    '008' : {
+        default : 40,
+        MFHD    : 32
+    }
+}
+
+MARC21.Record._ff_pos = {
+    AccM : {
+        _8 : {
+            SCO : {start: 24, len : 6, def : ' ' },
+            REC : {start: 24, len : 6, def : ' ' }
+        },
+        _6 : {
+            SCO : {start: 7, len : 6, def : ' ' },
+            REC : {start: 7, len : 6, def : ' ' }
+        }
+    },
+    Alph : {
+        _8 : {
+            SER : {start : 33, len : 1, def : ' ' }
+        },
+        _6 : {
+            SER : {start : 16, len : 1, def : ' ' }
+        }
+    },
+    Audn : {
+        _8 : {
+            BKS : {start : 22, len : 1, def : ' ' },
+            SER : {start : 22, len : 1, def : ' ' },
+            VIS : {start : 22, len : 1, def : ' ' },
+            SCO : {start : 22, len : 1, def : ' ' },
+            REC : {start : 22, len : 1, def : ' ' },
+            COM : {start : 22, len : 1, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 5, len : 1, def : ' ' },
+            SER : {start : 5, len : 1, def : ' ' },
+            VIS : {start : 5, len : 1, def : ' ' },
+            SCO : {start : 5, len : 1, def : ' ' },
+            REC : {start : 5, len : 1, def : ' ' },
+            COM : {start : 5, len : 1, def : ' ' }
+        }
+    },
+    Biog : {
+        _8 : {
+            BKS : {start : 34, len : 1, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 17, len : 1, def : ' ' }
+        }
+    },
+    BLvl : {
+        ldr : {
+            BKS : {start : 7, len : 1, def : 'm' },
+            SER : {start : 7, len : 1, def : 's' },
+            VIS : {start : 7, len : 1, def : 'm' },
+            MIX : {start : 7, len : 1, def : 'c' },
+            MAP : {start : 7, len : 1, def : 'm' },
+            SCO : {start : 7, len : 1, def : 'm' },
+            REC : {start : 7, len : 1, def : 'm' },
+            COM : {start : 7, len : 1, def : 'm' }
+        }
+    },
+    Comp : {
+        _8 : {
+            SCO : {start : 18, len : 2, def : 'uu'},
+            REC : {start : 18, len : 2, def : 'uu'}
+        },
+        _6 : {
+            SCO : {start : 1, len : 2, def : 'uu'},
+            REC : {start : 1, len : 2, def : 'uu'}
+        },
+    },
+    Conf : {
+        _8 : {
+            BKS : {start : 29, len : 1, def : '0' },
+            SER : {start : 29, len : 1, def : '0' }
+        },
+        _6 : {
+            BKS : {start : 11, len : 1, def : '0' },
+            SER : {start : 11, len : 1, def : '0' }
+        }
+    },
+    Cont : {
+        _8 : {
+            BKS : {start : 24, len : 4, def : ' ' },
+            SER : {start : 25, len : 3, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 7, len : 4, def : ' ' },
+            SER : {start : 8, len : 3, def : ' ' }
+        }
+    },
+    CrTp : {
+        _8 : {
+            MAP : {start: 25, len : 1, def : 'a' }
+        },
+        _6 : { 
+            MAP : {start : 8, len : 1, def : 'a' }
+        }
+    },
+    Ctrl : {
+        ldr : {
+            BKS : {start : 8, len : 1, def : ' ' },
+            SER : {start : 8, len : 1, def : ' ' },
+            VIS : {start : 8, len : 1, def : ' ' },
+            MIX : {start : 8, len : 1, def : ' ' },
+            MAP : {start : 8, len : 1, def : ' ' },
+            SCO : {start : 8, len : 1, def : ' ' },
+            REC : {start : 8, len : 1, def : ' ' },
+            COM : {start : 8, len : 1, def : ' ' }
+        }
+    },
+    Ctry : {
+            _8 : {
+                BKS : {start : 15, len : 3, def : ' ' },
+                SER : {start : 15, len : 3, def : ' ' },
+                VIS : {start : 15, len : 3, def : ' ' },
+                MIX : {start : 15, len : 3, def : ' ' },
+                MAP : {start : 15, len : 3, def : ' ' },
+                SCO : {start : 15, len : 3, def : ' ' },
+                REC : {start : 15, len : 3, def : ' ' },
+                COM : {start : 15, len : 3, def : ' ' }
+            }
+        },
+    Date1 : {
+        _8 : {
+            BKS : {start : 7, len : 4, def : ' ' },
+            SER : {start : 7, len : 4, def : ' ' },
+            VIS : {start : 7, len : 4, def : ' ' },
+            MIX : {start : 7, len : 4, def : ' ' },
+            MAP : {start : 7, len : 4, def : ' ' },
+            SCO : {start : 7, len : 4, def : ' ' },
+            REC : {start : 7, len : 4, def : ' ' },
+            COM : {start : 7, len : 4, def : ' ' }
+        }
+    },
+    Date2 : {
+        _8 : {
+            BKS : {start : 11, len : 4, def : ' ' },
+            SER : {start : 11, len : 4, def : '9' },
+            VIS : {start : 11, len : 4, def : ' ' },
+            MIX : {start : 11, len : 4, def : ' ' },
+            MAP : {start : 11, len : 4, def : ' ' },
+            SCO : {start : 11, len : 4, def : ' ' },
+            REC : {start : 11, len : 4, def : ' ' },
+            COM : {start : 11, len : 4, def : ' ' }
+        }
+    },
+    Desc : {
+        ldr : {
+            BKS : {start : 18, len : 1, def : ' ' },
+            SER : {start : 18, len : 1, def : ' ' },
+            VIS : {start : 18, len : 1, def : ' ' },
+            MIX : {start : 18, len : 1, def : ' ' },
+            MAP : {start : 18, len : 1, def : ' ' },
+            SCO : {start : 18, len : 1, def : ' ' },
+            REC : {start : 18, len : 1, def : ' ' },
+            COM : {start : 18, len : 1, def : ' ' }
+        }
+    },
+    DtSt : {
+        _8 : {
+            BKS : {start : 6, len : 1, def : ' ' },
+            SER : {start : 6, len : 1, def : 'c' },
+            VIS : {start : 6, len : 1, def : ' ' },
+            MIX : {start : 6, len : 1, def : ' ' },
+            MAP : {start : 6, len : 1, def : ' ' },
+            SCO : {start : 6, len : 1, def : ' ' },
+            REC : {start : 6, len : 1, def : ' ' },
+            COM : {start : 6, len : 1, def : ' ' }
+        }
+    },
+    ELvl : {
+        ldr : {
+            BKS : {start : 17, len : 1, def : ' ' },
+            SER : {start : 17, len : 1, def : ' ' },
+            VIS : {start : 17, len : 1, def : ' ' },
+            MIX : {start : 17, len : 1, def : ' ' },
+            MAP : {start : 17, len : 1, def : ' ' },
+            SCO : {start : 17, len : 1, def : ' ' },
+            REC : {start : 17, len : 1, def : ' ' },
+            COM : {start : 17, len : 1, def : ' ' },
+            AUT : {start : 17, len : 1, def : 'n' },
+            MFHD : {start : 17, len : 1, def : 'u' }
+        }
+    },
+    EntW : {
+        _8 : {
+            SER : {start : 24, len : 1, def : ' '}
+        },
+        _6 : {
+            SER : {start : 7, len : 1, def : ' '}
+        }
+    },
+    Fest : {
+        _8 : {
+            BKS : {start : 30, len : 1, def : '0' }
+        },
+        _6 : {
+            BKS : {start : 13, len : 1, def : '0' }
+        }
+    },
+    File : {
+        _8 : {
+            COM : {start: 26, len : 1, def : 'u' }
+        },
+        _6 : {
+            COM : {start: 9, len : 1, def : 'u' }
+        }
+    },
+    FMus : {
+        _8 : {
+            SCO : {start : 20, len : 1, def : 'u'},
+            REC : {start : 20, len : 1, def : 'n'}
+        },
+        _6 : {
+            SCO : {start : 3, len : 1, def : 'u'},
+            REC : {start : 3, len : 1, def : 'n'}
+        },
+    },
+    Form : {
+        _8 : {
+            BKS : {start : 23, len : 1, def : ' ' },
+            SER : {start : 23, len : 1, def : ' ' },
+            VIS : {start : 29, len : 1, def : ' ' },
+            MIX : {start : 23, len : 1, def : ' ' },
+            MAP : {start : 29, len : 1, def : ' ' },
+            SCO : {start : 23, len : 1, def : ' ' },
+            REC : {start : 23, len : 1, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 6, len : 1, def : ' ' },
+            SER : {start : 6, len : 1, def : ' ' },
+            VIS : {start : 12, len : 1, def : ' ' },
+            MIX : {start : 6, len : 1, def : ' ' },
+            MAP : {start : 12, len : 1, def : ' ' },
+            SCO : {start : 6, len : 1, def : ' ' },
+            REC : {start : 6, len : 1, def : ' ' }
+        }
+    },
+    Freq : {
+        _8 : {
+            SER : {start : 18, len : 1, def : ' '}
+        },
+        _6 : {
+            SER : {start : 1, len : 1, def : ' '}
+        }
+    },
+    GPub : {
+        _8 : {
+            BKS : {start : 28, len : 1, def : ' ' },
+            SER : {start : 28, len : 1, def : ' ' },
+            VIS : {start : 28, len : 1, def : ' ' },
+            MAP : {start : 28, len : 1, def : ' ' },
+            COM : {start : 28, len : 1, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 11, len : 1, def : ' ' },
+            SER : {start : 11, len : 1, def : ' ' },
+            VIS : {start : 11, len : 1, def : ' ' },
+            MAP : {start : 11, len : 1, def : ' ' },
+            COM : {start : 11, len : 1, def : ' ' }
+        }
+    },
+    Ills : {
+        _8 : {
+            BKS : {start : 18, len : 4, def : ' ' }
+        },
+        _6 : {
+            BKS : {start : 1, len : 4, def : ' ' }
+        }
+    },
+    Indx : {
+        _8 : {
+            BKS : {start : 31, len : 1, def : '0' },
+            MAP : {start : 31, len : 1, def : '0' }
+        },
+        _6 : {
+            BKS : {start : 14, len : 1, def : '0' },
+            MAP : {start : 14, len : 1, def : '0' }
+        }
+    },
+    Item : {
+        ldr : {
+            MFHD : {start : 18, len : 1, def : 'i' }
+        }
+    },
+    Lang : {
+        _8 : {
+            BKS : {start : 35, len : 3, def : ' ' },
+            SER : {start : 35, len : 3, def : ' ' },
+            VIS : {start : 35, len : 3, def : ' ' },
+            MIX : {start : 35, len : 3, def : ' ' },
+            MAP : {start : 35, len : 3, def : ' ' },
+            SCO : {start : 35, len : 3, def : ' ' },
+            REC : {start : 35, len : 3, def : ' ' },
+            COM : {start : 35, len : 3, def : ' ' }
+        }
+    },
+    LitF : {
+        _8 : {
+            BKS : {start : 33, len : 1, def : '0' }
+        },
+        _6 : {
+            BKS : {start : 16, len : 1, def : '0' }
+        }
+    },
+    LTxt : {
+        _8 : {
+            SCO : {start : 30, len : 2, def : 'n'},
+            REC : {start : 30, len : 2, def : ' '}
+        },
+        _6 : {
+            SCO : {start : 13, len : 2, def : 'n'},
+            REC : {start : 13, len : 2, def : ' '}
+        },
+    },
+    MRec : {
+        _8 : {
+            BKS : {start : 38, len : 1, def : ' ' },
+            SER : {start : 38, len : 1, def : ' ' },
+            VIS : {start : 38, len : 1, def : ' ' },
+            MIX : {start : 38, len : 1, def : ' ' },
+            MAP : {start : 38, len : 1, def : ' ' },
+            SCO : {start : 38, len : 1, def : ' ' },
+            REC : {start : 38, len : 1, def : ' ' },
+            COM : {start : 38, len : 1, def : ' ' }
+        }
+    },
+    Orig : {
+        _8 : {
+            SER : {start : 22, len : 1, def : ' '}
+        },
+        _6 : {
+            SER : {start: 5, len : 1, def: ' '}
+        }
+    },
+    Part : {
+        _8 : {
+            SCO : {start : 21, len : 1, def : ' '},
+            REC : {start : 21, len : 1, def : 'n'}
+        },
+        _6 : {
+            SCO : {start : 4, len : 1, def : ' '},
+            REC : {start : 4, len : 1, def : 'n'}
+        },
+    },
+    Proj : {
+        _8 : {
+            MAP : {start : 22, len : 2, def : ' ' }
+        },
+        _6 : {
+            MAP: {start : 5, len : 2, def : ' ' }
+        }
+    },
+    RecStat : {
+        ldr : {
+            BKS : {start : 5, len : 1, def : 'n' },
+            SER : {start : 5, len : 1, def : 'n' },
+            VIS : {start : 5, len : 1, def : 'n' },
+            MIX : {start : 5, len : 1, def : 'n' },
+            MAP : {start : 5, len : 1, def : 'n' },
+            SCO : {start : 5, len : 1, def : 'n' },
+            REC : {start : 5, len : 1, def : 'n' },
+            COM : {start : 5, len : 1, def : 'n' },
+            MFHD: {start : 5, len : 1, def : 'n' },
+            AUT : {start : 5, len : 1, def : 'n' }
+        }
+    },
+    Regl : {
+        _8 : {
+            SER : {start : 19, len : 1, def : ' '}
+        },
+        _6 : {
+            SER : {start : 2, len : 1, def : ' '}
+        }
+    },
+    Relf : {
+        _8 : {
+            MAP : {start: 18, len : 4, def : ' '}
+        },
+        _6 : {
+            MAP : {start: 1, len : 4, def : ' '}
+        }
+    },
+    'S/L' : {
+        _8 : {
+            SER : {start : 34, len : 1, def : '0' }
+        },
+        _6 : {
+            SER : {start : 17, len : 1, def : '0' }
+        }
+    },
+    SpFM : {
+        _8 : {
+            MAP : {start: 33, len : 2, def : ' ' }
+        },
+        _6 : {
+            MAP : {start: 16, len : 2, def : ' '}
+        }
+    },
+    Srce : {
+        _8 : {
+            BKS : {start : 39, len : 1, def : 'd' },
+            SER : {start : 39, len : 1, def : 'd' },
+            VIS : {start : 39, len : 1, def : 'd' },
+            SCO : {start : 39, len : 1, def : 'd' },
+            REC : {start : 39, len : 1, def : 'd' },
+            COM : {start : 39, len : 1, def : 'd' },
+            MFHD : {start : 39, len : 1, def : 'd' },
+            "AUT" : {"start" : 39, "len" : 1, "def" : 'd' }
+        }
+    },
+    SrTp : {
+        _8 : {
+            SER : {start : 21, len : 1, def : ' '}
+        },
+        _6 : {
+            SER : {start : 4, len : 1, def : ' '}
+        }
+    },
+    Tech : {
+        _8 : {
+            VIS : {start : 34, len : 1, def : ' '}
+        },
+        _6 : {
+            VIS : {start : 17, len : 1, def : ' '}
+        }
+    },
+    Time : {
+        _8 : {
+            VIS : {start : 18, len : 3, def : ' '}
+        },
+        _6 : {
+            VIS : {start : 1, len : 3, def : ' '}
+        }
+    },
+    TMat : {
+        _8 : {
+            VIS : {start : 33, len : 1, def : ' ' }
+        },
+        _6 : {
+            VIS : {start : 16, len : 1, def : ' ' }
+        }
+    },
+    TrAr : {
+        _8 : {
+            SCO : {start : 33, len : 1, def : ' ' },
+            REC : {start : 33, len : 1, def : 'n' }
+        },
+        _6 : {
+            SCO : {start : 16, len : 1, def : ' ' },
+            REC : {start : 16, len : 1, def : 'n' }
+        }
+    },
+    Type : {
+        ldr : {
+            BKS : {start : 6, len : 1, def : 'a' },
+            SER : {start : 6, len : 1, def : 'a' },
+            VIS : {start : 6, len : 1, def : 'g' },
+            MIX : {start : 6, len : 1, def : 'p' },
+            MAP : {start : 6, len : 1, def : 'e' },
+            SCO : {start : 6, len : 1, def : 'c' },
+            REC : {start : 6, len : 1, def : 'i' },
+            COM : {start : 6, len : 1, def : 'm' },
+            AUT : {start : 6, len : 1, def : 'z' },
+            MFHD : {start : 6, len : 1, def : 'y' }
+        }
+    },
+    "GeoDiv" : {
+         "_8" : {
+             "AUT" : {"start" : 6, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Roman" : {
+         "_8" : {
+             "AUT" : {"start" : 7, "len" : 1, "def" : ' ' }
+         }
+     },
+     "CatLang" : {
+         "_8" : {
+             "AUT" : {"start" : 8, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Kind" : {
+         "_8" : {
+             "AUT" : {"start" : 9, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Rules" : {
+         "_8" : {
+             "AUT" : {"start" : 10, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Subj" : {
+         "_8" : {
+             "AUT" : {"start" : 11, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Series" : {
+         "_8" : {
+             "AUT" : {"start" : 12, "len" : 1, "def" : ' ' }
+         }
+     },
+     "SerNum" : {
+         "_8" : {
+             "AUT" : {"start" : 13, "len" : 1, "def" : ' ' }
+         }
+     },
+     "NameUse" : {
+         "_8" : {
+             "AUT" : {"start" : 14, "len" : 1, "def" : ' ' }
+         }
+     },
+     "SubjUse" : {
+         "_8" : {
+             "AUT" : {"start" : 15, "len" : 1, "def" : ' ' }
+         }
+     },
+     "SerUse" : {
+         "_8" : {
+             "AUT" : {"start" : 16, "len" : 1, "def" : ' ' }
+         }
+     },
+     "TypeSubd" : {
+         "_8" : {
+             "AUT" : {"start" : 17, "len" : 1, "def" : ' ' }
+         }
+     },
+     "GovtAgn" : {
+         "_8" : {
+             "AUT" : {"start" : 28, "len" : 1, "def" : ' ' }
+         }
+     },
+     "RefStatus" : {
+         "_8" : {
+             "AUT" : {"start" : 29, "len" : 1, "def" : ' ' }
+         }
+     },
+     "UpdStatus" : {
+         "_8" : {
+             "AUT" : {"start" : 31, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Name" : {
+         "_8" : {
+             "AUT" : {"start" : 32, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Status" : {
+         "_8" : {
+             "AUT" : {"start" : 33, "len" : 1, "def" : ' ' }
+         }
+     },
+     "ModRec" : {
+         "_8" : {
+             "AUT" : {"start" : 38, "len" : 1, "def" : ' ' }
+         }
+     },
+     "Source" : {
+         "_8" : {
+             "AUT" : {"start" : 39, "len" : 1, "def" : ' ' }
+         }
+     }
+};
+

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/src/eg2/angular.json                      |    4 +-
 .../eg2/src/app/staff/catalog/catalog.module.ts    |    4 +-
 .../app/staff/catalog/record/record.component.html |    9 +-
 .../app/staff/catalog/record/record.component.ts   |    5 +
 .../share/bib-summary/bib-summary.component.css    |   11 +
 .../share/bib-summary/bib-summary.component.html   |  122 +-
 .../share/bib-summary/bib-summary.component.ts     |   10 +-
 .../staff/share/marc-edit/editor.component.html    |   76 ++
 .../app/staff/share/marc-edit/editor.component.ts  |  188 +++
 .../share/marc-edit/flat-editor.component.css      |   11 +
 .../share/marc-edit/flat-editor.component.html     |    7 +
 .../staff/share/marc-edit/flat-editor.component.ts |   45 +
 .../app/staff/share/marc-edit/marc-edit.module.ts  |   24 +
 .../src/app/staff/share/marc-edit/marcrecord.ts    |   35 +
 .../share/marc-edit/rich-editor.component.css}     |    0
 .../share/marc-edit/rich-editor.component.html}    |    0
 .../staff/share/marc-edit/rich-editor.component.ts |   28 +
 Open-ILS/src/eg2/src/assets/js/marcrecord.js       | 1317 ++++++++++++++++++++
 18 files changed, 1837 insertions(+), 59 deletions(-)
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.css
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts
 copy Open-ILS/src/eg2/src/{assets/.gitkeep => app/staff/share/marc-edit/rich-editor.component.css} (100%)
 copy Open-ILS/src/eg2/src/{assets/.gitkeep => app/staff/share/marc-edit/rich-editor.component.html} (100%)
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts
 create mode 100644 Open-ILS/src/eg2/src/assets/js/marcrecord.js


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list