1 <script src="/intranet-tmpl/lib/codemirror/codemirror-compressed.js"></script>
2 <script src="/intranet-tmpl/lib/filesaver.js"></script>
3 <script src="/intranet-tmpl/lib/koha/cateditor/marc-mode.js"></script>
4 <script src="/intranet-tmpl/lib/require.js"></script>
7 baseUrl: '/intranet-tmpl/lib/koha/cateditor/',
10 themelang: '[% themelang %]',
17 [% IF marcflavour == 'MARC21' %]
18 [% PROCESS 'cateditor-widgets-marc21.inc' %]
20 <script>var editorWidgets = {};</script>
24 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'resources', 'text-marc', 'widget' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, Resources, TextMARC, Widget ) {
26 'koha:biblioserver': {
27 name: _("Local catalog"),
31 [%- FOREACH server = z3950_servers -%]
33 name: '[% server.servername %]',
34 recordtype: '[% server.recordtype %]',
35 checked: [% server.checked ? 'true' : 'false' %],
40 // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config
42 [ "local_number", _("Local number") ],
43 [ "title", _("Title") ],
44 [ "series", _("Series title") ],
45 [ "author", _("Author") ],
46 [ "lccn", _("LCCN") ],
47 [ "isbn", _("ISBN") ],
48 [ "issn", _("ISSN") ],
49 [ "medium", _("Medium") ],
50 [ "edition", _("Edition") ],
51 [ "notes", _("Notes") ],
56 saveBackend: 'catalog',
63 function makeAuthorisedValueWidgets( frameworkCode ) {
64 $.each( KohaBackend.GetAllTagsInfo( frameworkCode ), function( tag, tagInfo ) {
65 $.each( tagInfo.subfields, function( subfield, subfieldInfo ) {
66 if ( !subfieldInfo.authorised_value ) return;
67 var authvals = KohaBackend.GetAuthorisedValues( subfieldInfo.authorised_value );
68 if ( !authvals ) return;
70 var defaultvalue = subfield.defaultvalue || authvals[0].value;
72 Widget.Register( tag + subfield, {
74 var $result = $( '<span class="subfield-widget"></span>' );
78 postCreate: function() {
79 this.setText( defaultvalue );
81 $( '<select></select>' ).appendTo( this.node );
82 var $node = $( this.node ).find( 'select' );
83 $.each( authvals, function( undef, authval ) {
84 $node.append( '<option value="' + authval.value + '"' + (authval.value == defaultvalue ? ' selected="selected"' : '') + '>' + authval.lib + '</option>' );
86 $node.val( this.text );
88 $node.change( $.proxy( function() {
89 this.setText( $node.val() );
92 makeTemplate: function() {
100 function bindGlobalKeys() {
101 shortcut.add( 'ctrl+s', function(event) {
102 $( '#save-record' ).click();
104 event.preventDefault();
107 shortcut.add( 'alt+ctrl+k', function(event) {
108 $( '#search-by-keywords' ).focus();
113 shortcut.add( 'alt+ctrl+a', function(event) {
114 $( '#search-by-author' ).focus();
119 shortcut.add( 'alt+ctrl+i', function(event) {
120 $( '#search-by-isbn' ).focus();
125 shortcut.add( 'alt+ctrl+t', function(event) {
126 $( '#search-by-title' ).focus();
131 shortcut.add( 'ctrl+h', function() {
132 var field = editor.getCurrentField();
134 if ( !field ) return;
136 window.open( getFieldHelpURL( field.tag ) );
139 $('#quicksearch .search-box').each( function() {
140 shortcut.add( 'enter', $.proxy( function() {
143 $('#quicksearch .search-box').each( function() {
144 if ( !this.value ) return;
146 terms.push( [ $(this).data('qualifier'), this.value ] );
149 if ( !terms.length ) return;
151 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
152 $("#search-overlay").show();
157 }, this), { target: this, type: 'keypress' } );
161 function getFieldHelpURL( tag ) {
162 [% IF ( marcflavour == 'MARC21' ) %]
163 if ( tag == '000' ) {
164 return "http://www.loc.gov/marc/bibliographic/bdleader.html";
165 } else if ( tag < '900' ) {
166 return "http://www.loc.gov/marc/bibliographic/bd" + tag + ".html";
168 return "http://www.loc.gov/marc/bibliographic/bd9xx.html";
170 [% ELSIF ( marcflavour == 'UNIMARC' ) %]
171 /* http://archive.ifla.org/VI/3/p1996-1/ is an outdated version of UNIMARC, but
172 seems to be the only version available that can be linked to per tag. More recent
173 versions of the UNIMARC standard are available on the IFLA website only as
176 if ( tag == '000' ) {
177 return "http://archive.ifla.org/VI/3/p1996-1/uni.htm";
180 var url = "http://archive.ifla.org/VI/3/p1996-1/uni" + first + ".htm#";
181 if ( first == '0' ) url += "b";
182 if ( first != '9' ) url += field;
192 titleForRecord: _("Editing new record"),
193 get: function( id, callback ) {
194 record = new MARC.Record();
195 KohaBackend.FillRecord( '', record );
201 titleForRecord: _("Editing new full record"),
202 get: function( id, callback ) {
203 record = new MARC.Record();
204 KohaBackend.FillRecord( '', record, true );
210 titleForRecord: _("Editing catalog record #{ID}"),
212 { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}" },
213 { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber={ID}" },
215 saveLabel: _("Save to catalog"),
216 get: function( id, callback ) {
217 if ( !id ) return false;
219 KohaBackend.GetRecord( id, callback );
221 save: function( id, record, done ) {
222 function finishCb( data ) {
223 done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } );
227 KohaBackend.SaveRecord( id, record, finishCb );
229 KohaBackend.CreateRecord( record, finishCb );
234 saveLabel: _("Save as ISO2709 (.mrc) file"),
235 save: function( id, record, done ) {
236 saveAs( new Blob( [record.toISO2709()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.mrc' );
242 saveLabel: _("Save as MARCXML (.xml) file"),
243 save: function( id, record, done ) {
244 saveAs( new Blob( [record.toXML()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.xml' );
250 titleForRecord: _("Editing search result"),
251 get: function( id, callback ) {
252 if ( !id ) return false;
253 if ( !backends.search.records[ id ] ) {
254 callback( { error: _( "Invalid record" ) } );
258 callback( backends.search.records[ id ] );
264 function setSource(parts) {
265 state.backend = parts[0];
266 state.recordID = parts[1];
267 state.canSave = backends[ state.backend ].save != null;
268 state.saveBackend = state.canSave ? state.backend : 'catalog';
270 var backend = backends[state.backend];
272 document.location.hash = '#' + parts[0] + '/' + parts[1];
274 $('#title').text( backend.titleForRecord.replace( '{ID}', parts[1] ) );
276 $.each( backend.links || [], function( i, link ) {
277 $('#title').append(' <a target="_blank" href="' + link.href.replace( '{ID}', parts[1] ) + '">(' + link.title + ')</a>' );
279 $( 'title', document.head ).html( _("Koha › Cataloging › ") + backend.titleForRecord.replace( '{ID}', parts[1] ) );
280 $('#save-record span').text( backends[ state.saveBackend ].saveLabel );
283 function saveRecord( recid, editor, callback ) {
284 var parts = recid.split('/');
285 if ( parts.length != 2 ) return false;
287 if ( !backends[ parts[0] ] || !backends[ parts[0] ].save ) return false;
289 editor.removeErrors();
290 var record = editor.getRecord();
292 if ( record.errors ) {
293 state.saving = false;
294 callback( { error: 'syntax', errors: record.errors } );
298 var errors = KohaBackend.ValidateRecord( '', record );
299 if ( errors.length ) {
300 state.saving = false;
301 callback( { error: 'invalid', errors: errors } );
305 backends[ parts[0] ].save( parts[1], record, function(data) {
306 state.saving = false;
308 if (data.newRecord) {
309 var record = new MARC.Record();
310 record.loadMARCXML(data.newRecord);
311 editor.displayRecord( record );
315 setSource(data.newId);
317 setSource( [ state.backend, state.recordID ] );
320 if (callback) callback( data );
324 function loadRecord( recid, editor, callback ) {
325 var parts = recid.split('/');
326 if ( parts.length != 2 ) return false;
328 if ( !backends[ parts[0] ] || !backends[ parts[0] ].get ) return false;
330 backends[ parts[0] ].get( parts[1], function( record ) {
331 if ( !record.error ) {
332 editor.displayRecord( record );
336 if (callback) callback(record);
342 function openRecord( recid, editor, callback ) {
343 return loadRecord( recid, editor, function ( record ) {
344 setSource( recid.split('/') );
346 if (callback) callback( record );
351 function showAdvancedSearch() {
352 $('#advanced-search-servers').empty();
353 $.each( z3950Servers, function( server_id, server ) {
354 $('#advanced-search-servers').append( '<li data-server-id="' + server_id + '"><label><input class="search-toggle-server" type="checkbox"' + ( server.checked ? ' checked="checked">' : '>' ) + server.name + '</label></li>' );
356 $('#advanced-search-ui').modal('show');
359 function startAdvancedSearch() {
362 $('#advanced-search-ui .search-box').each( function() {
363 if ( !this.value ) return;
365 terms.push( [ $(this).data('qualifier'), this.value ] );
368 if ( !terms.length ) return;
370 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
371 $('#advanced-search-ui').modal('hide');
372 $("#search-overlay").show();
377 function showResultsBox(data) {
378 $('#search-top-pages, #search-bottom-pages').find('.pagination').empty();
379 $('#searchresults thead tr').empty();
380 $('#searchresults tbody').empty();
381 $('#search-serversinfo').empty().append('<li>' + _("Loading...") + '</li>');
382 $('#search-results-ui').modal('show');
385 function showSearchSorting( sort_key, sort_direction ) {
386 var $th = $('#searchresults thead tr th[data-sort-label="' + sort_key + '"]');
387 $th.parent().find( 'th[data-sort-label]' ).attr( 'class', 'sorting' );
389 if ( sort_direction == 'asc' ) {
391 $th.attr( 'class', 'sorting_asc' );
394 $th.attr( 'class', 'sorting_desc' );
398 function showSearchResults( editor, data ) {
399 backends.search.records = {};
401 $('#searchresults thead tr').empty();
402 $('#searchresults tbody').empty();
403 $('#search-serversinfo').empty();
405 $.each( z3950Servers, function( server_id, server ) {
406 var num_fetched = data.num_fetched[server_id];
408 if ( data.errors[server_id] ) {
409 num_fetched = data.errors[server_id];
410 } else if ( num_fetched == null ) {
412 } else if ( num_fetched < data.num_hits[server_id] ) {
416 $('#search-serversinfo').append( '<li data-server-id="' + server_id + '"><label><input class="search-toggle-server" type="checkbox"' + ( server.checked ? ' checked="checked">' : '>' ) + server.name + ' (' + num_fetched + ')' + '</label></li>' );
419 var seenColumns = {};
421 $.each( data.hits, function( undef, hit ) {
422 $.each( hit.metadata, function(key) {
423 seenColumns[key] = true;
427 $('#searchresults thead tr').append('<th>' + _("Source") + '</th>');
429 $.each( z3950Labels, function( undef, label ) {
430 if ( seenColumns[ label[0] ] ) {
431 $('#searchresults thead tr').append( '<th class="sorting" data-sort-label="' + label[0] + '">' + label[1] + '</th>' );
435 showSearchSorting( data.sort_key, data.sort_direction );
437 $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>');
439 $.each( data.hits, function( undef, hit ) {
440 backends.search.records[ hit.server + ':' + hit.index ] = hit.record;
441 hit.id = 'search/' + hit.server + ':' + hit.index;
444 result += '<td class="sourcecol">' + z3950Servers[ hit.server ].name + '</td>';
446 $.each( z3950Labels, function( undef, label ) {
447 if ( !seenColumns[ label[0] ] ) return;
449 if ( hit.metadata[ label[0] ] ) {
450 result += '<td class="infocol">' + hit.metadata[ label[0] ] + '</td>';
452 result += '<td class="infocol"> </td>';
456 result += '<td class="toolscol"><ul><li><a href="#" class="marc-link">' + _("View MARC") + '</a></li>';
457 result += '<li><a href="#" class="open-link">' + _("Import") + '</a></li>';
458 if ( state.canSave ) result += '<li><a href="#" class="substitute-link" title="' + _("Replace the current record's contents") + '">' + _("Substitute") + '</a></li>';
459 result += '</ul></td></tr>';
461 var $tr = $( result );
462 $tr.find( '.marc-link' ).click( function() {
463 var $info_columns = $tr.find( '.infocol' );
464 var $marc_column = $tr.find( '.marccol' );
466 if ( !$marc_column.length ) {
467 $marc_column = $( '<td class="marccol" colspan="' + $info_columns.length + '"></td>' ).insertAfter( $info_columns.eq(-1) ).hide();
468 CodeMirror.runMode( TextMARC.RecordToText( hit.record ), 'marc', $marc_column[0] );
471 if ( $marc_column.is(':visible') ) {
472 $tr.find('.marc-link').text( _("View MARC") );
473 $info_columns.show();
476 $tr.find('.marc-link').text( _("Hide MARC") );
478 $info_columns.hide();
483 $tr.find( '.open-link' ).click( function() {
484 $( '#search-results-ui' ).modal('hide');
485 openRecord( hit.id, editor );
489 $tr.find( '.substitute-link' ).click( function() {
490 $( '#search-results-ui' ).modal('hide');
491 loadRecord( hit.id, editor );
495 $('#searchresults tbody').append( $tr );
499 var cur_page = data.offset / data.page_size;
500 var max_page = Math.ceil( data.total_fetched / data.page_size ) - 1;
502 if ( cur_page != 0 ) {
503 pages.push( '<li><a class="search-nav" href="#" data-offset="' + (data.offset - data.page_size) + '">« ' + _("Previous") + '</a></li>' );
506 for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) {
507 if ( page == cur_page ) {
508 pages.push( ' <li class="active"><a href="#">' + ( page + 1 ) + '</a></li>' );
510 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + ( page * data.page_size ) + '">' + ( page + 1 ) + '</a></li>' );
514 if ( cur_page < max_page ) {
515 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + (data.offset + data.page_size) + '">' + _("Next") + ' »</a></li>' );
518 if ( pages.length > 1 ) $( '#search-top-pages, #search-bottom-pages' ).find( '.pagination' ).html( '<ul>' + pages.join( '' ) + '</ul>');
520 var $overlay = $('#search-overlay');
521 $overlay.find('span').text(_("Loading"));
522 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
524 if ( data.activeclients ) {
525 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
528 $overlay.find('.bar').css( { display: 'block', width: '100%' } );
530 $('#searchresults')[0].focus();
534 function invalidateSearchResults() {
535 var $overlay = $('#search-overlay');
536 $overlay.find('span').text(_("Search expired, please try again"));
537 $overlay.find('.bar').css( { display: 'none' } );
541 function handleSearchError(error) {
542 if (error.code == 1) {
543 invalidateSearchResults();
546 humanMsg.displayMsg( _("<h3>Internal search error</h3>") + '<p>' + error + '</p>' + _("<p>Please <b>refresh</b> the page and try again."), { className: 'humanError' } );
550 function handleSearchInitError(error) {
551 $('#quicksearch-overlay').fadeIn().find('p').text(error);
554 // Preference functions
555 function showPreference( pref ) {
556 var value = Preferences.user[pref];
560 $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") );
563 $( '#editor .CodeMirror' ).css( { fontFamily: value } );
567 $( '#editor .CodeMirror' ).css( { fontSize: value } );
573 case 'selected_search_targets':
574 $.each( z3950Servers, function( server_id, server ) {
575 var saved_val = Preferences.user.selected_search_targets[server_id];
577 if ( saved_val != null ) server.checked = saved_val;
583 function bindPreference( editor, pref ) {
584 function _addHandler( sel, event, handler ) {
585 $( sel ).on( event, function (e) {
587 handler( e, Preferences.user[pref] );
588 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
589 showPreference(pref);
595 _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) {
596 editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets );
600 _addHandler( '#prefs-menu .set-font', 'click', function( e, oldValue ) {
601 Preferences.user.font = $( e.target ).css( 'font-family' );
605 _addHandler( '#prefs-menu .set-fontSize', 'click', function( e, oldValue ) {
606 Preferences.user.fontSize = $( e.target ).css( 'font-size' );
609 case 'selected_search_targets':
610 $( document ).on( 'change', 'input.search-toggle-server', function() {
611 var server_id = $( this ).closest('li').data('server-id');
612 Preferences.user.selected_search_targets[server_id] = this.checked;
613 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
619 function displayPreferences( editor ) {
620 $.each( Preferences.user, function( pref, value ) {
621 showPreference( pref );
622 bindPreference( editor, pref );
627 function loadMacro( name ) {
628 $( '#macro-list li' ).removeClass( 'active' );
629 macroEditor.activeMacro = name;
632 macroEditor.setValue( '' );
636 $( '#macro-list li[data-name="' + name + '"]' ).addClass( 'active' );
637 var macro = Preferences.user.macros[name];
638 macroEditor.setValue( macro.contents );
639 $( '#macro-format' ).val( macro.format || 'its' );
640 if ( macro.history ) macroEditor.setHistory( macro.history );
643 function storeMacro( name, macro ) {
645 Preferences.user.macros[name] = macro;
647 delete Preferences.user.macros[name];
650 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
653 function showSavedMacros( macros ) {
654 var scrollTop = $('#macro-list').scrollTop();
655 $( '#macro-list' ).empty();
656 var macro_list = $.map( Preferences.user.macros, function( macro, name ) {
657 return $.extend( { name: name }, macro );
659 macro_list.sort( function( a, b ) {
660 return a.name.localeCompare(b.name);
662 $.each( macro_list, function( undef, macro ) {
663 var $li = $( '<li data-name="' + macro.name + '"><a href="#">' + macro.name + '</a><ol class="macro-info"></ol></li>' );
664 $li.click( function() {
665 loadMacro(macro.name);
668 if ( macro.name == macroEditor.activeMacro ) $li.addClass( 'active' );
669 var modified = macro.modified && new Date(macro.modified);
670 $li.find( '.macro-info' ).append(
671 '<li><span class="label">' + _("Last changed:") + '</span>' +
672 ( modified ? modified.toLocaleFormat() : _("never") ) + '</li>'
674 $('#macro-list').append($li);
676 var $new_li = $( '<li class="new-macro"><a href="#">' + _("New macro...") + '</a></li>' );
677 $new_li.click( function() {
678 // TODO: make this a bit less retro
679 var name = prompt(_("Please enter the name for the new macro:"));
682 if ( !Preferences.user.macros[name] ) storeMacro( name, { format: "rancor", contents: "" } );
686 $('#macro-list').append($new_li);
687 $('#macro-list').scrollTop(scrollTop);
690 function saveMacro() {
691 var name = macroEditor.activeMacro;
693 if ( !name || macroEditor.savedGeneration == macroEditor.changeGeneration() ) return;
695 macroEditor.savedGeneration = macroEditor.changeGeneration();
696 storeMacro( name, { contents: macroEditor.getValue(), modified: (new Date()).valueOf(), history: macroEditor.getHistory(), format: $('#macro-format').val() } );
697 $('#macro-save-message').text(_("Saved"));
701 $(document).ready( function() {
703 editor = new MARCEditor( {
704 onCursorActivity: function() {
705 $('#status-tag-info').empty();
706 $('#status-subfield-info').empty();
708 var field = editor.getCurrentField();
709 var cur = editor.getCursor();
711 if ( !field ) return;
713 var taginfo = KohaBackend.GetTagInfo( '', field.tag );
714 $('#status-tag-info').html( '<strong>' + field.tag + ':</strong> ' );
717 $('#status-tag-info').append( '<a href="' + getFieldHelpURL( field.tag ) + '" target="_blank" class="show-field-help" title="' + _("Show help for this tag") + '">[?]</a> ' + taginfo.lib );
719 var subfield = field.getSubfieldAt( cur.ch );
720 if ( !subfield ) return;
722 var subfieldinfo = taginfo.subfields[ subfield.code ];
723 $('#status-subfield-info').html( '<strong>$' + subfield.code + ':</strong> ' );
725 if ( subfieldinfo ) {
726 $('#status-subfield-info').append( subfieldinfo.lib );
728 $('#status-subfield-info').append( '<em>' + _("Unknown subfield") + '</em>' );
731 $('#status-tag-info').append( '<em>' + _("Unknown tag") + '</em>' );
734 position: function (elt) { $(elt).insertAfter('#toolbar') },
737 macroEditor = CodeMirror(
738 $('#macro-editor')[0],
745 // Automatically detect resizes and change the height of the editor and position of modals.
746 var resizeTimer = null;
747 $( window ).resize( function() {
748 if ( resizeTimer == null ) resizeTimer = setTimeout( function() {
751 var pos = $('#editor .CodeMirror').position();
752 $('#editor .CodeMirror').height( $(window).height() - pos.top - 24 - $('#changelanguage').height() ); // 24 is hardcoded value but works well
754 $('.modal-body').each( function() {
755 $(this).height( $(window).height() * .8 - $(this).prevAll('.modal-header').height() );
759 $("#advanced-search-ui, #search-results-ui, #macro-ui").css( {
760 marginLeft: function() {
761 return -($(this).width() / 2);
767 var saveableBackends = [];
768 $.each( backends, function( id, backend ) {
769 if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] );
771 saveableBackends.sort();
772 $.each( saveableBackends, function( undef, backend ) {
773 $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + backend[0] + '</a></li>' );
776 var macro_format_list = $.map( Macros.formats, function( format, name ) {
777 return $.extend( { name: name }, format );
779 macro_format_list.sort( function( a, b ) {
780 return a.description.localeCompare(b.description);
782 $.each( macro_format_list, function() {
783 $('#macro-format').append( '<option value="' + this.name + '">' + this.description + '</option>' );
787 $( '#save-record, #save-dropdown a' ).click( function() {
788 $( '#save-record' ).find('i').attr( 'class', 'icon-loading' ).siblings( 'span' ).text( _("Saving...") );
790 function finishCb(result) {
791 if ( result.error == 'syntax' ) {
792 humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } );
793 } else if ( result.error == 'invalid' ) {
794 humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } );
795 } else if ( !result.error ) {
796 humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } );
799 $.each( result.errors || [], function( undef, error ) {
800 switch ( error.type ) {
802 editor.addError( error.line, _("Invalid tag number") );
805 editor.addError( error.line, _("Invalid indicators") );
808 editor.addError( null, _("Missing mandatory tag: ") + error.tag );
810 case 'missingSubfield':
811 if ( error.subfield == '@' ) {
812 editor.addError( error.line, _("Missing control field contents") );
814 editor.addError( error.line, _("Missing mandatory subfield: $") + error.subfield );
817 case 'unrepeatableTag':
818 editor.addError( error.line, _("Tag ") + error.tag + _(" cannot be repeated") );
820 case 'unrepeatableSubfield':
821 editor.addError( error.line, _("Subfield $") + error.subfield + _(" cannot be repeated") );
826 $( '#save-record' ).find('i').attr( 'class', 'icon-hdd' );
828 if ( result.error ) {
829 // Reset backend info
830 setSource( [ state.backend, state.recordID ] );
834 var backend = $( this ).data( 'backend' ) || ( state.saveBackend );
835 if ( state.backend == backend ) {
836 saveRecord( backend + '/' + state.recordID, editor, finishCb );
838 saveRecord( backend + '/', editor, finishCb );
844 $('#import-records').click( function() {
845 $('#import-records-input')
847 .change( function() {
848 if ( !this.files || !this.files.length ) return;
850 var file = this.files[0];
851 var reader = new FileReader();
853 reader.onload = function() {
854 var record = new MARC.Record();
856 if ( /\.mrc$/.test( file.name ) ) {
857 record.loadISO2709( reader.result );
858 } else if ( /\.xml$/.test( file.name ) ) {
859 record.loadMARCXML( reader.result );
861 humanMsg.displayAlert( _("Unknown record type, cannot import"), { className: 'humanError' } );
865 editor.displayRecord( record );
868 reader.readAsText( file );
875 $('#open-macros').click( function() {
876 $('#macro-ui').modal('show');
881 $('#run-macro').click( function() {
882 var result = Macros.Run( editor, $('#macro-format').val(), macroEditor.getValue() );
884 if ( !result.errors.length ) {
885 $('#macro-ui').modal('hide');
890 $.each( result.errors, function() {
891 var error = '<b>' + _("Line ") + (this.line + 1) + ':</b> ';
893 switch ( this.error ) {
894 case 'failed': error += _("failed to run"); break;
895 case 'unrecognized': error += _("unrecognized command"); break;
901 humanMsg.displayMsg( _("<h3>Failed to run macro:</h3>") + '<ul><li>' + errors.join('</li><li>') + '</li></ul>', { className: 'humanError' } );
906 $('#delete-macro').click( function() {
907 if ( !macroEditor.activeMacro || !confirm( _("Are you sure you want to delete this macro?") ) ) return;
909 storeMacro( macroEditor.activeMacro, undefined );
911 loadMacro( undefined );
917 macroEditor.on( 'change', function( cm, change ) {
918 $('#macro-save-message').empty();
919 if ( change.origin == 'setValue' ) return;
921 if ( saveTimeout ) clearTimeout( saveTimeout );
922 saveTimeout = setTimeout( function() {
929 $( '#switch-editor' ).click( function() {
930 if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return;
932 $.cookie( 'catalogue_editor_[% USER_INFO.0.borrowernumber %]', 'basic', { expires: 365, path: '/' } );
934 if ( state.backend == 'catalog' ) {
935 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + state.recordID;
936 } else if ( state.backend == 'new' ) {
937 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl';
939 humanMsg.displayAlert( _("Cannot open this record in the basic editor"), { className: 'humanError' } );
943 $( '#show-advanced-search' ).click( function() {
944 showAdvancedSearch();
949 $('#advanced-search').submit( function() {
950 startAdvancedSearch();
955 $( document ).on( 'click', 'a.search-nav', function() {
956 $("#search-overlay").show();
957 Search.Fetch( { offset: $( this ).data( 'offset' ) } );
961 $( document ).on( 'click', 'th[data-sort-label]', function() {
962 $("#search-overlay").show();
965 if ( $( this ).hasClass( 'sorting_asc' ) ) {
971 showSearchSorting( $( this ).data( 'sort-label' ), direction );
973 Search.Fetch( { sort_key: $( this ).data( 'sort-label' ), sort_direction: direction } );
977 $( document ).on( 'change', 'input.search-toggle-server', function() {
978 var server = z3950Servers[ $( this ).closest('li').data('server-id') ];
979 server.checked = this.checked;
981 if ( $('#search-results-ui').is( ':visible' ) ) {
982 $("#search-overlay").show();
991 $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() {
992 $(this).modal({ show: false });
995 var $quicksearch = $('#quicksearch fieldset');
996 $('<div id="quicksearch-overlay"><h3>' + _("Search unavailable") + '</h3> <p></p></div>').css({
997 position: 'absolute',
998 top: $quicksearch.offset().top,
999 left: $quicksearch.offset().left,
1000 height: $quicksearch.outerHeight(),
1001 width: $quicksearch.outerWidth(),
1002 }).appendTo(document.body).hide();
1004 var prevAlerts = [];
1005 humanMsg.logMsg = function(msg, options) {
1006 $('#show-alerts').popover('hide');
1007 prevAlerts.unshift('<li>' + msg + '</li>');
1008 prevAlerts.splice(5, 999); // Truncate old messages
1011 $('#show-alerts').popover({
1013 placement: 'bottom',
1014 content: function() {
1015 return '<div id="alerts-container"><ul>' + prevAlerts.join('') + '</ul></div>';
1018 $('#new-record' ).click( function() {
1019 openRecord( 'new/', editor );
1024 Preferences.Load( [% USER_INFO.0.borrowernumber || 0 %] );
1025 displayPreferences(editor);
1026 makeAuthorisedValueWidgets( '' );
1029 onresults: function(data) { showSearchResults( editor, data ) },
1030 onerror: handleSearchError,
1033 function finishCb( data ) {
1034 if ( data.error ) openRecord( 'new/', editor, finishCb );
1036 Resources.GetAll().done( function() {
1037 $("#loading").hide();
1042 if ( "[% auth_forwarded_hash %]" ) {
1043 document.location.hash = "[% auth_forwarded_hash %]";
1046 if ( !document.location.hash || !openRecord( document.location.hash.slice(1), editor, finishCb ) ) {
1047 openRecord( 'new/', editor, finishCb );