2 * Copyright 2015 ByWater Solutions
4 * This file is part of Koha.
6 * Koha is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * Koha is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Koha; if not, see <http://www.gnu.org/licenses>.
20 define( [ 'marc-editor' ], function( MARCEditor ) {
21 // These are the generators for targets that appear on the left-hand side of an assignment.
22 var _lhsGenerators = [
23 // Field; will replace the entire contents of the tag except for indicators.
25 // * 245 - will return the first 245 tag it finds, or create a new one
26 // * new 245 - will always create a new 245
27 // * new 245 grouped - will always create a new 245, and insert it at the end of the 2xx
29 [ /^(new )?(\w{3})( (grouped))?$/, function( forceCreate, tag, position, positionGrouped ) {
30 if ( !forceCreate && positionGrouped ) return null;
32 // The extra argument allows the delete command to prevent this from needlessly creating
33 // a tag that it is about to delete.
34 return function( editor, state, extra ) {
38 var result = editor.getFirstField(tag);
40 if ( result != null || extra.dontCreate ) return result;
43 if ( positionGrouped ) {
44 return editor.createFieldGrouped(tag);
46 return editor.createFieldOrdered(tag);
51 // This regex is a little complicated, but allows for the following possibilities:
52 // * 245a - Finds the first 245 field, then tries to find an a subfield within it. If none
53 // exists, it is created. Will still fail if there is no 245 field.
54 // * new 245a - always creates a new a subfield.
55 // * new 245a at end - does the same as the above.
56 // * $a or new $a - does the same as the above, but for the last-used tag.
57 // * new 245a after b - creates a new subfield, placing it after the first subfield $b.
58 [ /^(new )?(\w{3}|\$)(\w)( (at end)| after (\w))?$/, function( forceCreate, tag, code, position, positionAtEnd, positionAfterSubfield ) {
59 if ( tag != '$' && tag < '010' ) return null;
60 if ( !forceCreate && position ) return null;
62 return function( editor, state, extra ) {
70 field = editor.getFirstField(tag);
72 if ( field == null || field.isControlField ) return null;
75 var subfield = field.getFirstSubfield(code)
77 if ( subfield != null || extra.dontCreate ) return subfield;
80 if ( !position || position == ' at end' ) {
81 return field.appendSubfield(code);
82 } else if ( positionAfterSubfield ) {
83 var afterSubfield = field.getFirstSubfield(positionAfterSubfield);
85 if ( afterSubfield == null ) return null;
87 return field.insertSubfield( code, afterSubfield.index + 1 );
92 // Can set indicatators either for a particular field or the last-used tag.
93 [ /^((\w{3}) )?indicators$/, function( undef, tag ) {
94 if ( tag && tag < '010' ) return null;
96 return function( editor, state ) {
102 field = editor.getFirstField(tag);
104 if ( field == null || field.isControlField ) return null;
108 setText: function( text ) {
109 field.setIndicator1( text.substr( 0, 1 ) );
110 field.setIndicator2( text.substr( 1, 1 ) );
117 // These patterns, on the other hand, appear inside interpolations on the right hand side.
118 var _rhsGenerators = [
119 [ /^(\w{3})$/, function( tag ) {
120 return function( editor, state, extra ) {
121 return editor.getFirstField(tag);
124 [ /^(\w{3})(\w)$/, function( tag, code ) {
125 if ( tag < '010' ) return null;
127 return function( editor, state, extra ) {
130 var field = editor.getFirstField(tag);
131 if ( field == null ) return null;
133 return field.getFirstSubfield(code);
138 var _commandGenerators = [
139 [ /^delete (.+)$/, function( target ) {
140 var target_closure = _generate( _lhsGenerators, target );
141 if ( !target_closure ) return null;
143 return function( editor, state ) {
144 var target = target_closure( editor, state, { dontCreate: true } );
145 if ( target == null ) return;
146 if ( !target.delete ) return false;
148 state.field = null; // As other fields may have been invalidated
152 [ /^([^=]+)=([^=]*)$/, function( lhs_desc, rhs_desc ) {
153 var lhs_closure = _generate( _lhsGenerators, lhs_desc );
154 if ( !lhs_closure ) return null;
156 var rhs_closure = _generateInterpolation( _rhsGenerators, rhs_desc );
157 if ( !rhs_closure ) return null;
159 return function( editor, state ) {
160 var lhs = lhs_closure( editor, state );
161 if ( lhs == null ) return;
163 state.field = lhs.field || lhs;
166 return lhs.setText( rhs_closure( editor, state ) );
168 if ( e instanceof MARCEditor.FieldError ) {
178 function _generate( set, contents ) {
181 if ( contents.match(/^\s*$/) ) return;
183 $.each( set, function( undef, gen ) {
186 if ( !( match = gen[0].exec( contents ) ) ) return;
188 closure = gen[1].apply(null, match.slice(1));
195 function _generateInterpolation( set, contents ) {
196 // While this regex will not match at all for an empty string, that just leaves an empty
197 // parts array which yields an empty string (which is what we want.)
198 var matcher = /\{([^}]+)\}|([^{]+)/g;
203 while ( ( match = matcher.exec(contents) ) ) {
206 // Found an interpolation
207 var rhs_closure = _generate( set, match[1] );
208 if ( rhs_closure == null ) return null;
210 closure = ( function(rhs_closure) { return function( editor, state ) {
211 var rhs = rhs_closure( editor, state );
213 return rhs ? rhs.getText() : '';
214 } } )( rhs_closure );
216 // Plain text (needs artificial closure to keep match)
217 closure = ( function(text) { return function() { return text }; } )( match[2] );
220 parts.push( closure );
223 return function( editor, state ) {
225 $.each( parts, function( i, part ) {
226 result += part( editor, state );
234 Compile: function( macro ) {
235 var result = { commands: [], errors: [] };
237 $.each( macro.split(/\r\n|\n/), function( line, contents ) {
238 contents = contents.replace( /#.*$/, '' );
239 if ( contents.match(/^\s*$/) ) return;
241 var command = _generate( _commandGenerators, contents );
244 result.errors.push( { line: line, error: 'unrecognized' } );
247 result.commands.push( { func: command, orig: contents, line: line } );
252 Run: function( editor, macro ) {
253 var compiled = RancorMacro.Compile(macro);
254 if ( compiled.errors.length ) return { errors: compiled.errors };
259 var run_result = { errors: [] };
261 editor.cm.operation( function() {
262 $.each( compiled.commands, function( undef, command ) {
263 var result = command.func( editor, state );
265 if ( result === false ) {
266 run_result.errors.push( { line: command.line, error: 'failed' } );