edd64d3018
Full test plan is posted on bug. Test plan for system preference: 1. Apply patch, clear cookies. 2. Go to "Cataloging." 3. Add new record, verify that basic editor is used. 4. Navigate to existing record, click on "Edit record", verify that basic editor is used. 5. Inside basic editor, verify that no button appears to switch to the advanced editor. 6. Enable the "EnableAdvancedCatalogingEditor" syspref. 7. Repeat above steps, should still go to basic editor, but button should appear to switch to the advanced editor; click it. 8. Now, adding new records and editing existing records should go to the advanced editor. Signed-off-by: Nick Clemens <nick@quecheelibrary.org> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
671 lines
23 KiB
JavaScript
671 lines
23 KiB
JavaScript
/**
|
|
* Copyright 2015 ByWater Solutions
|
|
*
|
|
* This file is part of Koha.
|
|
*
|
|
* Koha 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Koha 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Koha; if not, see <http://www.gnu.org/licenses>.
|
|
*/
|
|
|
|
define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ], function( MARC, KohaBackend, Preferences, TextMARC, Widget ) {
|
|
var NOTIFY_TIMEOUT = 250;
|
|
|
|
function editorCursorActivity( cm ) {
|
|
var editor = cm.marceditor;
|
|
var field = editor.getCurrentField();
|
|
if ( !field ) return;
|
|
|
|
// Set overwrite mode for tag numbers/indicators and contents of fixed fields
|
|
if ( field.isControlField || cm.getCursor().ch < 8 ) {
|
|
cm.toggleOverwrite(true);
|
|
} else {
|
|
cm.toggleOverwrite(false);
|
|
}
|
|
|
|
editor.onCursorActivity();
|
|
}
|
|
|
|
// This function exists to prevent inserting or partially deleting text that belongs to a
|
|
// widget. The 'marcAware' change source exists for other parts of the editor code to bypass
|
|
// this check.
|
|
function editorBeforeChange( cm, change ) {
|
|
var editor = cm.marceditor;
|
|
if ( editor.textMode || change.origin == 'marcAware' || change.origin == 'widget.clearToText' ) return;
|
|
|
|
// FIXME: Should only cancel changes if this is a control field/subfield widget
|
|
if ( change.from.line !== change.to.line || Math.abs( change.from.ch - change.to.ch ) > 1 || change.text.length != 1 || change.text[0].length != 0 ) return; // Not single-char change
|
|
|
|
if ( change.from.ch == change.to.ch - 1 && cm.findMarksAt( { line: change.from.line, ch: change.from.ch + 1 } ).length ) {
|
|
change.cancel();
|
|
} else if ( change.from.ch == change.to.ch && cm.findMarksAt(change.from).length && !change.text[0].match(/^[$|ǂ‡]$/) ) {
|
|
change.cancel();
|
|
}
|
|
}
|
|
|
|
function editorChanges( cm, changes ) {
|
|
var editor = cm.marceditor;
|
|
if ( editor.textMode ) return;
|
|
|
|
for (var i = 0; i < changes.length; i++) {
|
|
var change = changes[i];
|
|
|
|
var origin = change.from.line;
|
|
var newTo = CodeMirror.changeEnd(change);
|
|
|
|
for (var delLine = origin; delLine <= change.to.line; delLine++) {
|
|
// Line deleted; currently nothing to do
|
|
}
|
|
|
|
for (var line = origin; line <= newTo.line; line++) {
|
|
if ( Preferences.user.fieldWidgets ) Widget.UpdateLine( cm.marceditor, line );
|
|
if ( change.origin != 'setValue' && change.origin != 'marcWidgetPrefill' && change.origin != 'widget.clearToText' ) cm.addLineClass( line, 'wrapper', 'modified-line' );
|
|
}
|
|
}
|
|
|
|
Widget.ActivateAt( cm, cm.getCursor() );
|
|
cm.marceditor.startNotify();
|
|
}
|
|
|
|
// Editor helper functions
|
|
function activateTabPosition( cm, pos, idx ) {
|
|
// Allow tabbing to as-yet-nonexistent positions
|
|
var lenDiff = pos.ch - cm.getLine( pos.line ).length;
|
|
if ( lenDiff > 0 ) {
|
|
var extra = '';
|
|
while ( lenDiff-- > 0 ) extra += ' ';
|
|
if ( pos.prefill ) extra += pos.prefill;
|
|
cm.replaceRange( extra, { line: pos.line } );
|
|
}
|
|
|
|
cm.setCursor( pos );
|
|
Widget.ActivateAt( cm, pos, idx );
|
|
}
|
|
|
|
function getTabPositions( editor, cur ) {
|
|
cur = cur || editor.cm.getCursor();
|
|
var field = editor.getFieldAt( cur.line );
|
|
|
|
if ( field ) {
|
|
if ( field.isControlField ) {
|
|
var positions = [ { ch: 0 }, { ch: 4 } ];
|
|
|
|
$.each( positions, function( undef, pos ) {
|
|
pos.line = cur.line;
|
|
} );
|
|
|
|
return positions;
|
|
} else {
|
|
var positions = [ { ch: 0 }, { ch: 4, prefill: '_' }, { ch: 6, prefill: '_' } ];
|
|
|
|
$.each( positions, function( undef, pos ) {
|
|
pos.line = cur.line;
|
|
} );
|
|
$.each( field.getSubfields(), function( undef, subfield ) {
|
|
positions.push( { line: cur.line, ch: subfield.contentsStart } );
|
|
} );
|
|
|
|
// Allow to tab to start of empty field
|
|
if ( field.getSubfields().length == 0 ) {
|
|
positions.push( { line: cur.line, ch: 8 } );
|
|
}
|
|
|
|
return positions;
|
|
}
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
var _editorKeys = {
|
|
Enter: function( cm ) {
|
|
var cursor = cm.getCursor();
|
|
cm.replaceRange( '\n', { line: cursor.line }, null, 'marcAware' );
|
|
cm.setCursor( { line: cursor.line + 1, ch: 0 } );
|
|
},
|
|
|
|
'Ctrl-X': function( cm ) {
|
|
// Delete line (or cut)
|
|
if ( cm.somethingSelected() ) return true;
|
|
|
|
var field = cm.marceditor.getCurrentField();
|
|
if ( field ) field.delete();
|
|
},
|
|
|
|
'Shift-Ctrl-X': function( cm ) {
|
|
// Delete subfield
|
|
var field = cm.marceditor.getCurrentField();
|
|
if ( !field ) return;
|
|
|
|
var subfield = field.getSubfieldAt( cm.getCursor().ch );
|
|
if ( subfield ) subfield.delete();
|
|
},
|
|
|
|
Tab: function( cm ) {
|
|
// Move through parts of tag/fixed fields
|
|
var positions = getTabPositions( cm.marceditor );
|
|
var cur = cm.getCursor();
|
|
|
|
for ( var i = 0; i < positions.length; i++ ) {
|
|
if ( positions[i].ch > cur.ch ) {
|
|
activateTabPosition( cm, positions[i] );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
cm.setCursor( { line: cur.line + 1, ch: 0 } );
|
|
},
|
|
|
|
'Shift-Tab': function( cm ) {
|
|
// Move backwards through parts of tag/fixed fields
|
|
var positions = getTabPositions( cm.marceditor );
|
|
var cur = cm.getCursor();
|
|
|
|
for ( var i = positions.length - 1; i >= 0; i-- ) {
|
|
if ( positions[i].ch < cur.ch ) {
|
|
activateTabPosition( cm, positions[i] );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( cur.line == 0 ) return;
|
|
|
|
var prevPositions = getTabPositions( cm.marceditor, { line: cur.line - 1, ch: cm.getLine( cur.line - 1 ).length } );
|
|
|
|
if ( prevPositions.length ) {
|
|
activateTabPosition( cm, prevPositions[ prevPositions.length - 1 ], -1 );
|
|
} else {
|
|
cm.setCursor( { line: cur.line - 1, ch: 0 } );
|
|
}
|
|
},
|
|
|
|
'Ctrl-D': function( cm ) {
|
|
// Insert subfield delimiter
|
|
// This will be extended later to allow either a configurable subfield delimiter or just
|
|
// make it be the double cross.
|
|
var cur = cm.getCursor();
|
|
|
|
cm.replaceRange( "$", cur, null );
|
|
},
|
|
};
|
|
|
|
// The objects below are part of a field/subfield manipulation API, accessed through the base
|
|
// editor object.
|
|
//
|
|
// Each one is tied to a particular line; this means that using a field or subfield object after
|
|
// any other changes to the record will cause entertaining explosions. The objects are meant to
|
|
// be temporary, and should only be reused with great care. The macro code does this only
|
|
// because it is careful to dispose of the object after any other updates.
|
|
//
|
|
// Note, however, tha you can continue to use a field object after changing subfields. It's just
|
|
// the subfield objects that become invalid.
|
|
|
|
// This is an exception raised by the EditorSubfield and EditorField when an invalid change is
|
|
// attempted.
|
|
function FieldError(line, message) {
|
|
this.line = line;
|
|
this.message = message;
|
|
};
|
|
|
|
FieldError.prototype.toString = function() {
|
|
return 'FieldError(' + this.line + ', "' + this.message + '")';
|
|
};
|
|
|
|
// This is the temporary object for a particular subfield in a field. Any change to any other
|
|
// subfields will invalidate this subfield object.
|
|
function EditorSubfield( field, index, start, end ) {
|
|
this.field = field;
|
|
this.index = index;
|
|
this.start = start;
|
|
this.end = end;
|
|
|
|
if ( this.field.isControlField ) {
|
|
this.contentsStart = start;
|
|
this.code = '@';
|
|
} else {
|
|
this.contentsStart = start + 3;
|
|
this.code = this.field.contents.substr( this.start + 1, 1 );
|
|
}
|
|
|
|
this.cm = field.cm;
|
|
|
|
var marks = this.cm.findMarksAt( { line: field.line, ch: this.contentsStart } );
|
|
if ( marks[0] && marks[0].widget ) {
|
|
this.widget = marks[0].widget;
|
|
|
|
this.text = this.widget.text;
|
|
this.setText = this.widget.setText;
|
|
this.getFixed = this.widget.getFixed;
|
|
this.setFixed = this.widget.setFixed;
|
|
} else {
|
|
this.widget = null;
|
|
this.text = this.field.contents.substr( this.contentsStart, end - this.contentsStart );
|
|
}
|
|
};
|
|
|
|
$.extend( EditorSubfield.prototype, {
|
|
_invalid: function() {
|
|
return this.field._subfieldsInvalid();
|
|
},
|
|
|
|
focus: function() {
|
|
this.cm.setCursor( { line: this.field.line, ch: this.contentsStart } );
|
|
},
|
|
focusEnd: function() {
|
|
this.cm.setCursor( { line: this.field.line, ch: this.end } );
|
|
},
|
|
getText: function() {
|
|
return this.text;
|
|
},
|
|
setText: function( text ) {
|
|
if ( !this._invalid() ) throw new FieldError( this.field.line, 'subfield invalid' );
|
|
this.cm.replaceRange( text, { line: this.field.line, ch: this.contentsStart }, { line: this.field.line, ch: this.end }, 'marcAware' );
|
|
this.field._invalidateSubfields();
|
|
},
|
|
} );
|
|
|
|
function EditorField( editor, line ) {
|
|
this.editor = editor;
|
|
this.line = line;
|
|
|
|
this.cm = editor.cm;
|
|
|
|
this._updateInfo();
|
|
this.tag = this.contents.substr( 0, 3 );
|
|
this.isControlField = ( this.tag < '010' );
|
|
|
|
if ( this.isControlField ) {
|
|
this._ind1 = this.contents.substr( 4, 1 );
|
|
this._ind2 = this.contents.substr( 6, 1 );
|
|
} else {
|
|
this._ind1 = null;
|
|
this._ind2 = null;
|
|
}
|
|
|
|
this.subfields = null;
|
|
}
|
|
|
|
$.extend( EditorField.prototype, {
|
|
_subfieldsInvalid: function() {
|
|
return !this.subfields;
|
|
},
|
|
_invalidateSubfields: function() {
|
|
this._subfields = null;
|
|
},
|
|
|
|
_updateInfo: function() {
|
|
this.info = this.editor.getLineInfo( { line: this.line, ch: 0 } );
|
|
if ( this.info == null ) throw new FieldError( 'Invalid field' );
|
|
this.contents = this.info.contents;
|
|
},
|
|
_scanSubfields: function() {
|
|
this._updateInfo();
|
|
|
|
if ( this.isControlField ) {
|
|
this._subfields = [ new EditorSubfield( this, 0, 4, this.contents.length ) ];
|
|
} else {
|
|
var field = this;
|
|
var subfields = this.info.subfields;
|
|
this._subfields = [];
|
|
|
|
for (var i = 0; i < this.info.subfields.length; i++) {
|
|
var end = i == subfields.length - 1 ? this.contents.length : subfields[i+1].ch;
|
|
|
|
this._subfields.push( new EditorSubfield( this, i, subfields[i].ch, end ) );
|
|
}
|
|
}
|
|
},
|
|
|
|
delete: function() {
|
|
this.cm.replaceRange( "", { line: this.line, ch: 0 }, { line: this.line + 1, ch: 0 }, 'marcAware' );
|
|
},
|
|
focus: function() {
|
|
this.cm.setCursor( { line: this.line, ch: 0 } );
|
|
|
|
return this;
|
|
},
|
|
|
|
getText: function() {
|
|
var result = '';
|
|
|
|
$.each( this.getSubfields(), function() {
|
|
if ( this.code != '@' ) result += '$' + this.code + ' ';
|
|
|
|
result += this.getText();
|
|
} );
|
|
|
|
return result;
|
|
},
|
|
setText: function( text ) {
|
|
var indicator_match = /^([_ 0-9])([_ 0-9])\$/.exec( text );
|
|
if ( indicator_match ) {
|
|
text = text.substr(2);
|
|
this.setIndicator1( indicator_match[1] );
|
|
this.setIndicator2( indicator_match[2] );
|
|
}
|
|
|
|
this.cm.replaceRange( text, { line: this.line, ch: this.isControlField ? 4 : 8 }, { line: this.line }, 'marcAware' );
|
|
this._invalidateSubfields();
|
|
|
|
return this;
|
|
},
|
|
|
|
getIndicator1: function() {
|
|
return this._ind1;
|
|
},
|
|
getIndicator2: function() {
|
|
return this._ind2;
|
|
},
|
|
setIndicator1: function(val) {
|
|
if ( this.isControlField ) throw new FieldError('Cannot set indicators on control field');
|
|
|
|
this._ind1 = ( !val || val == ' ' ) ? '_' : val;
|
|
this.cm.replaceRange( this._ind1, { line: this.line, ch: 4 }, { line: this.line, ch: 5 }, 'marcAware' );
|
|
|
|
return this;
|
|
},
|
|
setIndicator2: function(val) {
|
|
if ( this.isControlField ) throw new FieldError('Cannot set indicators on control field');
|
|
|
|
this._ind2 = ( !val || val == ' ' ) ? '_' : val;
|
|
this.cm.replaceRange( this._ind2, { line: this.line, ch: 6 }, { line: this.line, ch: 7 }, 'marcAware' );
|
|
|
|
return this;
|
|
},
|
|
|
|
appendSubfield: function( code ) {
|
|
if ( this.isControlField ) throw new FieldError('Cannot add subfields to control field');
|
|
|
|
this._invalidateSubfields();
|
|
this.cm.replaceRange( '$' + code + ' ', { line: this.line }, null, 'marcAware' );
|
|
var subfields = this.getSubfields();
|
|
|
|
return subfields[ subfields.length - 1 ];
|
|
},
|
|
insertSubfield: function( code, position ) {
|
|
if ( this.isControlField ) throw new FieldError('Cannot add subfields to control field');
|
|
|
|
position = position || 0;
|
|
|
|
var subfields = this.getSubfields();
|
|
this._invalidateSubfields();
|
|
this.cm.replaceRange( '$' + code + ' ', { line: this.line, ch: subfields[position] ? subfields[position].start : null }, null, 'marcAware' );
|
|
subfields = this.getSubfields();
|
|
|
|
return subfields[ position ];
|
|
},
|
|
getSubfields: function( code ) {
|
|
if ( !this._subfields ) this._scanSubfields();
|
|
if ( code == null ) return this._subfields;
|
|
|
|
var result = [];
|
|
|
|
$.each( this._subfields, function() {
|
|
if ( code == null || this.code == code ) result.push(this);
|
|
} );
|
|
|
|
return result;
|
|
},
|
|
getFirstSubfield: function( code ) {
|
|
var result = this.getSubfields( code );
|
|
|
|
return ( result && result.length ) ? result[0] : null;
|
|
},
|
|
getSubfieldAt: function( ch ) {
|
|
var subfields = this.getSubfields();
|
|
|
|
for (var i = 0; i < subfields.length; i++) {
|
|
if ( subfields[i].start < ch && subfields[i].end >= ch ) return subfields[i];
|
|
}
|
|
},
|
|
} );
|
|
|
|
function MARCEditor( options ) {
|
|
this.cm = CodeMirror(
|
|
options.position,
|
|
{
|
|
extraKeys: _editorKeys,
|
|
gutters: [
|
|
'modified-line-gutter',
|
|
],
|
|
lineWrapping: true,
|
|
mode: {
|
|
name: 'marc',
|
|
nonRepeatableTags: KohaBackend.GetTagsBy( '', 'repeatable', '0' ),
|
|
nonRepeatableSubfields: KohaBackend.GetSubfieldsBy( '', 'repeatable', '0' )
|
|
}
|
|
}
|
|
);
|
|
this.cm.marceditor = this;
|
|
|
|
this.cm.on( 'beforeChange', editorBeforeChange );
|
|
this.cm.on( 'changes', editorChanges );
|
|
this.cm.on( 'cursorActivity', editorCursorActivity );
|
|
|
|
this.onCursorActivity = options.onCursorActivity;
|
|
|
|
this.subscribers = [];
|
|
this.subscribe( function( marceditor ) {
|
|
Widget.Notify( marceditor );
|
|
} );
|
|
}
|
|
|
|
MARCEditor.FieldError = FieldError;
|
|
|
|
$.extend( MARCEditor.prototype, {
|
|
setUseWidgets: function( val ) {
|
|
if ( val ) {
|
|
for ( var line = 0; line <= this.cm.lastLine(); line++ ) {
|
|
Widget.UpdateLine( this, line );
|
|
}
|
|
} else {
|
|
$.each( this.cm.getAllMarks(), function( undef, mark ) {
|
|
if ( mark.widget ) mark.widget.clearToText();
|
|
} );
|
|
}
|
|
},
|
|
|
|
focus: function() {
|
|
this.cm.focus();
|
|
},
|
|
|
|
getCursor: function() {
|
|
return this.cm.getCursor();
|
|
},
|
|
|
|
refresh: function() {
|
|
this.cm.refresh();
|
|
},
|
|
|
|
displayRecord: function( record ) {
|
|
this.cm.setValue( TextMARC.RecordToText(record) );
|
|
},
|
|
|
|
getRecord: function() {
|
|
this.textMode = true;
|
|
|
|
$.each( this.cm.getAllMarks(), function( undef, mark ) {
|
|
if ( mark.widget ) mark.widget.clearToText();
|
|
} );
|
|
var record = TextMARC.TextToRecord( this.cm.getValue() );
|
|
for ( var line = 0; line <= this.cm.lastLine(); line++ ) {
|
|
if ( Preferences.user.fieldWidgets ) Widget.UpdateLine( this, line );
|
|
}
|
|
|
|
this.textMode = false;
|
|
|
|
return record;
|
|
},
|
|
|
|
getLineInfo: function( pos ) {
|
|
var contents = this.cm.getLine( pos.line );
|
|
if ( contents == null ) return {};
|
|
|
|
var tagNumber = contents.match( /^([A-Za-z0-9]{3})/ );
|
|
|
|
if ( !tagNumber ) return null; // No tag at all on this line
|
|
tagNumber = tagNumber[1];
|
|
|
|
if ( tagNumber < '010' ) return { tagNumber: tagNumber, contents: contents }; // No current subfield
|
|
|
|
var matcher = /[$|ǂ‡]([a-z0-9%]) /g;
|
|
var match;
|
|
|
|
var subfields = [];
|
|
var currentSubfield;
|
|
|
|
while ( ( match = matcher.exec(contents) ) ) {
|
|
subfields.push( { code: match[1], ch: match.index } );
|
|
if ( match.index < pos.ch ) currentSubfield = match[1];
|
|
}
|
|
|
|
return { tagNumber: tagNumber, subfields: subfields, currentSubfield: currentSubfield, contents: contents };
|
|
},
|
|
|
|
addError: function( line, error ) {
|
|
var found = false;
|
|
var options = {};
|
|
|
|
if ( line == null ) {
|
|
line = 0;
|
|
options.above = true;
|
|
}
|
|
|
|
$.each( this.cm.getLineHandle(line).widgets || [], function( undef, widget ) {
|
|
if ( !widget.isErrorMarker ) return;
|
|
|
|
found = true;
|
|
|
|
$( widget.node ).append( '; ' + error );
|
|
widget.changed();
|
|
|
|
return false;
|
|
} );
|
|
|
|
if ( found ) return;
|
|
|
|
var node = $( '<div class="structure-error"><i class="icon-remove"></i> ' + error + '</div>' )[0];
|
|
var widget = this.cm.addLineWidget( line, node, options );
|
|
|
|
widget.node = node;
|
|
widget.isErrorMarker = true;
|
|
},
|
|
|
|
removeErrors: function() {
|
|
for ( var line = 0; line < this.cm.lineCount(); line++ ) {
|
|
$.each( this.cm.getLineHandle( line ).widgets || [], function( undef, lineWidget ) {
|
|
if ( lineWidget.isErrorMarker ) lineWidget.clear();
|
|
} );
|
|
}
|
|
},
|
|
|
|
startNotify: function() {
|
|
if ( this.notifyTimeout ) clearTimeout( this.notifyTimeout );
|
|
this.notifyTimeout = setTimeout( $.proxy( function() {
|
|
this.notifyAll();
|
|
|
|
this.notifyTimeout = null;
|
|
}, this ), NOTIFY_TIMEOUT );
|
|
},
|
|
|
|
notifyAll: function() {
|
|
$.each( this.subscribers, $.proxy( function( undef, subscriber ) {
|
|
subscriber(this);
|
|
}, this ) );
|
|
},
|
|
|
|
subscribe: function( subscriber ) {
|
|
this.subscribers.push( subscriber );
|
|
},
|
|
|
|
createField: function( tag, line ) {
|
|
var contents = tag + ( tag < '010' ? ' ' : ' _ _ ' );
|
|
|
|
if ( line > this.cm.lastLine() ) {
|
|
contents = '\n' + contents;
|
|
} else {
|
|
contents = contents + '\n';
|
|
}
|
|
|
|
this.cm.replaceRange( contents, { line: line, ch: 0 }, null, 'marcAware' );
|
|
|
|
return new EditorField( this, line );
|
|
},
|
|
|
|
createFieldOrdered: function( tag ) {
|
|
var line, contents;
|
|
|
|
for ( line = 0; (contents = this.cm.getLine(line)); line++ ) {
|
|
if ( contents && contents.substr(0, 3) > tag ) break;
|
|
}
|
|
|
|
return this.createField( tag, line );
|
|
},
|
|
|
|
createFieldGrouped: function( tag ) {
|
|
// Control fields should be inserted in actual order, whereas other fields should be
|
|
// inserted grouped
|
|
if ( tag < '010' ) return this.createFieldOrdered( tag );
|
|
|
|
var line, contents;
|
|
|
|
for ( line = 0; (contents = this.cm.getLine(line)); line++ ) {
|
|
if ( contents && contents[0] > tag[0] ) break;
|
|
}
|
|
|
|
return this.createField( tag, line );
|
|
},
|
|
|
|
getFieldAt: function( line ) {
|
|
try {
|
|
return new EditorField( this, line );
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
getCurrentField: function() {
|
|
return this.getFieldAt( this.cm.getCursor().line );
|
|
},
|
|
|
|
getFields: function( tag ) {
|
|
var result = [];
|
|
|
|
if ( tag != null ) tag += ' ';
|
|
|
|
for ( var line = 0; line < this.cm.lineCount(); line++ ) {
|
|
if ( tag && this.cm.getLine(line).substr( 0, 4 ) != tag ) continue;
|
|
|
|
// If this throws a FieldError, pretend it doesn't exist
|
|
try {
|
|
result.push( new EditorField( this, line ) );
|
|
} catch (e) {
|
|
if ( !( e instanceof FieldError ) ) throw e;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
getFirstField: function( tag ) {
|
|
var result = this.getFields( tag );
|
|
|
|
return ( result && result.length ) ? result[0] : null;
|
|
},
|
|
|
|
getAllFields: function( tag ) {
|
|
return this.getFields( null );
|
|
},
|
|
} );
|
|
|
|
return MARCEditor;
|
|
} );
|