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 if ( Search.IsAvailable() ) $( '#search-by-keywords' ).focus();
113 shortcut.add( 'alt+ctrl+a', function(event) {
114 if ( Search.IsAvailable() ) $( '#search-by-author' ).focus();
119 shortcut.add( 'alt+ctrl+i', function(event) {
120 if ( Search.IsAvailable() ) $( '#search-by-isbn' ).focus();
125 shortcut.add( 'alt+ctrl+t', function(event) {
126 if ( Search.IsAvailable() ) $( '#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 + '"><input class="search-toggle-server" type="checkbox"' + ( server.checked ? ' checked="checked">' : '>' ) + server.name + '</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 + '"><input class="search-toggle-server" type="checkbox"' + ( server.checked ? ' checked="checked">' : '>' ) + server.name + ' (' + num_fetched + ')' + '</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%' } );
533 function invalidateSearchResults() {
534 var $overlay = $('#search-overlay');
535 $overlay.find('span').text(_("Search expired, please try again"));
536 $overlay.find('.bar').css( { display: 'none' } );
540 function handleSearchError(error) {
541 if (error.code == 1) {
542 invalidateSearchResults();
545 humanMsg.displayMsg( _("<h3>Internal search error</h3>") + '<p>' + error + '</p>' + _("<p>Please <b>refresh</b> the page and try again."), { className: 'humanError' } );
549 function handleSearchInitError(error) {
550 $('#quicksearch-overlay').fadeIn().find('p').text(error);
553 // Preference functions
554 function showPreference( pref ) {
555 var value = Preferences.user[pref];
559 $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") );
562 $( '#editor .CodeMirror' ).css( { fontFamily: value } );
566 $( '#editor .CodeMirror' ).css( { fontSize: value } );
572 case 'selected_search_targets':
573 $.each( z3950Servers, function( server_id, server ) {
574 var saved_val = Preferences.user.selected_search_targets[server_id];
576 if ( saved_val != null ) server.checked = saved_val;
582 function bindPreference( editor, pref ) {
583 function _addHandler( sel, event, handler ) {
584 $( sel ).on( event, function (e) {
586 handler( e, Preferences.user[pref] );
587 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
588 showPreference(pref);
594 _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) {
595 editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets );
599 _addHandler( '#prefs-menu .set-font', 'click', function( e, oldValue ) {
600 Preferences.user.font = $( e.target ).css( 'font-family' );
604 _addHandler( '#prefs-menu .set-fontSize', 'click', function( e, oldValue ) {
605 Preferences.user.fontSize = $( e.target ).css( 'font-size' );
608 case 'selected_search_targets':
609 $( document ).on( 'change', 'input.search-toggle-server', function() {
610 var server_id = $( this ).parent().data('server-id');
611 Preferences.user.selected_search_targets[server_id] = this.checked;
612 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
618 function displayPreferences( editor ) {
619 $.each( Preferences.user, function( pref, value ) {
620 showPreference( pref );
621 bindPreference( editor, pref );
626 function loadMacro( name ) {
627 $( '#macro-list li' ).removeClass( 'active' );
628 macroEditor.activeMacro = name;
631 macroEditor.setValue( '' );
635 $( '#macro-list li[data-name="' + name + '"]' ).addClass( 'active' );
636 var macro = Preferences.user.macros[name];
637 macroEditor.setValue( macro.contents );
638 $( '#macro-format' ).val( macro.format || 'its' );
639 if ( macro.history ) macroEditor.setHistory( macro.history );
642 function storeMacro( name, macro ) {
644 Preferences.user.macros[name] = macro;
646 delete Preferences.user.macros[name];
649 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
652 function showSavedMacros( macros ) {
653 var scrollTop = $('#macro-list').scrollTop();
654 $( '#macro-list' ).empty();
655 var macro_list = $.map( Preferences.user.macros, function( macro, name ) {
656 return $.extend( { name: name }, macro );
658 macro_list.sort( function( a, b ) {
659 return a.name.localeCompare(b.name);
661 $.each( macro_list, function( undef, macro ) {
662 var $li = $( '<li data-name="' + macro.name + '"><a href="#">' + macro.name + '</a><ol class="macro-info"></ol></li>' );
663 $li.click( function() {
664 loadMacro(macro.name);
667 if ( macro.name == macroEditor.activeMacro ) $li.addClass( 'active' );
668 var modified = macro.modified && new Date(macro.modified);
669 $li.find( '.macro-info' ).append(
670 '<li><span class="label">' + _("Last changed:") + '</span>' +
671 ( modified ? modified.toLocaleFormat() : _("never") ) + '</li>'
673 $('#macro-list').append($li);
675 var $new_li = $( '<li class="new-macro"><a href="#">' + _("New macro...") + '</a></li>' );
676 $new_li.click( function() {
677 // TODO: make this a bit less retro
678 var name = prompt(_("Please enter the name for the new macro:"));
681 if ( !Preferences.user.macros[name] ) storeMacro( name, { format: "rancor", contents: "" } );
685 $('#macro-list').append($new_li);
686 $('#macro-list').scrollTop(scrollTop);
689 function saveMacro() {
690 var name = macroEditor.activeMacro;
692 if ( !name || macroEditor.savedGeneration == macroEditor.changeGeneration() ) return;
694 macroEditor.savedGeneration = macroEditor.changeGeneration();
695 storeMacro( name, { contents: macroEditor.getValue(), modified: (new Date()).valueOf(), history: macroEditor.getHistory(), format: $('#macro-format').val() } );
696 $('#macro-save-message').text(_("Saved"));
700 $(document).ready( function() {
702 editor = new MARCEditor( {
703 onCursorActivity: function() {
704 $('#status-tag-info').empty();
705 $('#status-subfield-info').empty();
707 var field = editor.getCurrentField();
708 var cur = editor.getCursor();
710 if ( !field ) return;
712 var taginfo = KohaBackend.GetTagInfo( '', field.tag );
713 $('#status-tag-info').html( '<strong>' + field.tag + ':</strong> ' );
716 $('#status-tag-info').append( '<a href="' + getFieldHelpURL( field.tag ) + '" target="_blank" class="show-field-help" title="' + _("Show help for this tag") + '">[?]</a> ' + taginfo.lib );
718 var subfield = field.getSubfieldAt( cur.ch );
719 if ( !subfield ) return;
721 var subfieldinfo = taginfo.subfields[ subfield.code ];
722 $('#status-subfield-info').html( '<strong>$' + subfield.code + ':</strong> ' );
724 if ( subfieldinfo ) {
725 $('#status-subfield-info').append( subfieldinfo.lib );
727 $('#status-subfield-info').append( '<em>' + _("Unknown subfield") + '</em>' );
730 $('#status-tag-info').append( '<em>' + _("Unknown tag") + '</em>' );
733 position: function (elt) { $(elt).insertAfter('#toolbar') },
736 macroEditor = CodeMirror(
737 $('#macro-editor')[0],
744 // Automatically detect resizes and change the height of the editor and position of modals.
745 var resizeTimer = null;
746 $( window ).resize( function() {
747 if ( resizeTimer == null ) resizeTimer = setTimeout( function() {
750 var pos = $('#editor .CodeMirror').position();
751 $('#editor .CodeMirror').height( $(window).height() - pos.top - 24 - $('#changelanguage').height() ); // 24 is hardcoded value but works well
753 $('.modal-body').each( function() {
754 $(this).height( $(window).height() * .8 - $(this).prevAll('.modal-header').height() );
758 $("#advanced-search-ui, #search-results-ui, #macro-ui").css( {
759 marginLeft: function() {
760 return -($(this).width() / 2);
766 var saveableBackends = [];
767 $.each( backends, function( id, backend ) {
768 if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] );
770 saveableBackends.sort();
771 $.each( saveableBackends, function( undef, backend ) {
772 $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + backend[0] + '</a></li>' );
775 var macro_format_list = $.map( Macros.formats, function( format, name ) {
776 return $.extend( { name: name }, format );
778 macro_format_list.sort( function( a, b ) {
779 return a.description.localeCompare(b.description);
781 $.each( macro_format_list, function() {
782 $('#macro-format').append( '<option value="' + this.name + '">' + this.description + '</option>' );
786 $( '#save-record, #save-dropdown a' ).click( function() {
787 $( '#save-record' ).find('i').attr( 'class', 'icon-loading' ).siblings( 'span' ).text( _("Saving...") );
789 function finishCb(result) {
790 if ( result.error == 'syntax' ) {
791 humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } );
792 } else if ( result.error == 'invalid' ) {
793 humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } );
794 } else if ( !result.error ) {
795 humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } );
798 $.each( result.errors || [], function( undef, error ) {
799 switch ( error.type ) {
801 editor.addError( error.line, _("Invalid tag number") );
804 editor.addError( error.line, _("Invalid indicators") );
807 editor.addError( null, _("Missing mandatory tag: ") + error.tag );
809 case 'missingSubfield':
810 if ( error.subfield == '@' ) {
811 editor.addError( error.line, _("Missing control field contents") );
813 editor.addError( error.line, _("Missing mandatory subfield: $") + error.subfield );
816 case 'unrepeatableTag':
817 editor.addError( error.line, _("Tag ") + error.tag + _(" cannot be repeated") );
819 case 'unrepeatableSubfield':
820 editor.addError( error.line, _("Subfield $") + error.subfield + _(" cannot be repeated") );
825 $( '#save-record' ).find('i').attr( 'class', 'icon-hdd' );
827 if ( result.error ) {
828 // Reset backend info
829 setSource( [ state.backend, state.recordID ] );
833 var backend = $( this ).data( 'backend' ) || ( state.saveBackend );
834 if ( state.backend == backend ) {
835 saveRecord( backend + '/' + state.recordID, editor, finishCb );
837 saveRecord( backend + '/', editor, finishCb );
843 $('#import-records').click( function() {
844 $('#import-records-input')
846 .change( function() {
847 if ( !this.files || !this.files.length ) return;
849 var file = this.files[0];
850 var reader = new FileReader();
852 reader.onload = function() {
853 var record = new MARC.Record();
855 if ( /\.mrc$/.test( file.name ) ) {
856 record.loadISO2709( reader.result );
857 } else if ( /\.xml$/.test( file.name ) ) {
858 record.loadMARCXML( reader.result );
860 humanMsg.displayAlert( _("Unknown record type, cannot import"), { className: 'humanError' } );
864 editor.displayRecord( record );
867 reader.readAsText( file );
874 $('#open-macros').click( function() {
875 $('#macro-ui').modal('show');
880 $('#run-macro').click( function() {
881 var result = Macros.Run( editor, $('#macro-format').val(), macroEditor.getValue() );
883 if ( !result.errors.length ) {
884 $('#macro-ui').modal('hide');
889 $.each( result.errors, function() {
890 var error = '<b>' + _("Line ") + (this.line + 1) + ':</b> ';
892 switch ( this.error ) {
893 case 'failed': error += _("failed to run"); break;
894 case 'unrecognized': error += _("unrecognized command"); break;
900 humanMsg.displayMsg( _("<h3>Failed to run macro:</h3>") + '<ul><li>' + errors.join('</li><li>') + '</li></ul>', { className: 'humanError' } );
905 $('#delete-macro').click( function() {
906 if ( !macroEditor.activeMacro || !confirm( _("Are you sure you want to delete this macro?") ) ) return;
908 storeMacro( macroEditor.activeMacro, undefined );
910 loadMacro( undefined );
916 macroEditor.on( 'change', function( cm, change ) {
917 $('#macro-save-message').empty();
918 if ( change.origin == 'setValue' ) return;
920 if ( saveTimeout ) clearTimeout( saveTimeout );
921 saveTimeout = setTimeout( function() {
928 $( '#switch-editor' ).click( function() {
929 if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return;
931 $.cookie( 'catalogue_editor_[% USER_INFO.0.borrowernumber %]', 'basic', { expires: 365, path: '/' } );
933 if ( state.backend == 'catalog' ) {
934 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + state.recordID;
935 } else if ( state.backend == 'new' ) {
936 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl';
938 humanMsg.displayAlert( _("Cannot open this record in the basic editor"), { className: 'humanError' } );
942 $( '#show-advanced-search' ).click( function() {
943 showAdvancedSearch();
948 $('#advanced-search').submit( function() {
949 startAdvancedSearch();
954 $( document ).on( 'click', 'a.search-nav', function() {
955 $("#search-overlay").show();
956 Search.Fetch( { offset: $( this ).data( 'offset' ) } );
960 $( document ).on( 'click', 'th[data-sort-label]', function() {
961 $("#search-overlay").show();
964 if ( $( this ).hasClass( 'sorting_asc' ) ) {
970 showSearchSorting( $( this ).data( 'sort-label' ), direction );
972 Search.Fetch( { sort_key: $( this ).data( 'sort-label' ), sort_direction: direction } );
976 $( document ).on( 'change', 'input.search-toggle-server', function() {
977 var server = z3950Servers[ $( this ).parent().data('server-id') ];
978 server.checked = this.checked;
980 if ( $('#search-results-ui').is( ':visible' ) ) {
981 $("#search-overlay").show();
990 $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() {
991 $(this).modal({ show: false });
994 var $quicksearch = $('#quicksearch fieldset');
995 $('<div id="quicksearch-overlay"><h3>' + _("Search unavailable") + '</h3> <p></p></div>').css({
996 position: 'absolute',
997 top: $quicksearch.offset().top,
998 left: $quicksearch.offset().left,
999 height: $quicksearch.outerHeight(),
1000 width: $quicksearch.outerWidth(),
1001 }).appendTo(document.body).hide();
1003 var prevAlerts = [];
1004 humanMsg.logMsg = function(msg, options) {
1005 $('#show-alerts').popover('hide');
1006 prevAlerts.unshift('<li>' + msg + '</li>');
1007 prevAlerts.splice(5, 999); // Truncate old messages
1010 $('#show-alerts').popover({
1012 placement: 'bottom',
1013 content: function() {
1014 return '<div id="alerts-container"><ul>' + prevAlerts.join('') + '</ul></div>';
1017 $('#new-record' ).click( function() {
1018 openRecord( 'new/', editor );
1023 Preferences.Load( [% USER_INFO.0.borrowernumber || 0 %] );
1024 displayPreferences(editor);
1025 makeAuthorisedValueWidgets( '' );
1028 onresults: function(data) { showSearchResults( editor, data ) },
1029 onerror: handleSearchError,
1032 function finishCb( data ) {
1033 if ( data.error ) openRecord( 'new/', editor, finishCb );
1035 Resources.GetAll().done( function() {
1036 $("#loading").hide();
1041 if ( "[% auth_forwarded_hash %]" ) {
1042 document.location.hash = "[% auth_forwarded_hash %]";
1045 if ( !document.location.hash || !openRecord( document.location.hash.slice(1), editor, finishCb ) ) {
1046 openRecord( 'new/', editor, finishCb );