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 marcflavour: '[% marcflavour %]',
11 themelang: '[% themelang %]',
18 [% IF marcflavour == 'MARC21' %]
19 [% PROCESS 'cateditor-widgets-marc21.inc' %]
21 <script>var editorWidgets = {};</script>
25 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'resources', 'text-marc', 'widget' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, Resources, TextMARC, Widget ) {
27 'koha:biblioserver': {
28 name: _("Local catalog"),
32 [%- FOREACH server = z3950_servers -%]
34 name: '[% server.servername %]',
35 recordtype: '[% server.recordtype %]',
36 checked: [% server.checked ? 'true' : 'false' %],
41 // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config
43 [ "local_number", _("Local number") ],
44 [ "title", _("Title") ],
45 [ "series", _("Series title") ],
46 [ "author", _("Author") ],
47 [ "lccn", _("LCCN") ],
48 [ "isbn", _("ISBN") ],
49 [ "issn", _("ISSN") ],
50 [ "medium", _("Medium") ],
51 [ "edition", _("Edition") ],
52 [ "notes", _("Notes") ],
57 saveBackend: 'catalog',
64 function makeAuthorisedValueWidgets( frameworkCode ) {
65 $.each( KohaBackend.GetAllTagsInfo( frameworkCode ), function( tag, tagInfo ) {
66 $.each( tagInfo.subfields, function( subfield, subfieldInfo ) {
67 if ( !subfieldInfo.authorised_value ) return;
68 var authvals = KohaBackend.GetAuthorisedValues( subfieldInfo.authorised_value );
69 if ( !authvals ) return;
71 var defaultvalue = subfield.defaultvalue || authvals[0].value;
73 Widget.Register( tag + subfield, {
75 var $result = $( '<span class="subfield-widget"></span>' );
79 postCreate: function() {
80 this.setText( defaultvalue );
82 $( '<select></select>' ).appendTo( this.node );
83 var $node = $( this.node ).find( 'select' );
84 $.each( authvals, function( undef, authval ) {
85 $node.append( '<option value="' + authval.value + '"' + (authval.value == defaultvalue ? ' selected="selected"' : '') + '>' + authval.lib + '</option>' );
87 $node.val( this.text );
89 $node.change( $.proxy( function() {
90 this.setText( $node.val() );
93 makeTemplate: function() {
101 function bindGlobalKeys() {
102 shortcut.add( 'ctrl+s', function(event) {
103 $( '#save-record' ).click();
105 event.preventDefault();
108 shortcut.add( 'alt+ctrl+k', function(event) {
109 $( '#search-by-keywords' ).focus();
114 shortcut.add( 'alt+ctrl+a', function(event) {
115 $( '#search-by-author' ).focus();
120 shortcut.add( 'alt+ctrl+i', function(event) {
121 $( '#search-by-isbn' ).focus();
126 shortcut.add( 'alt+ctrl+t', function(event) {
127 $( '#search-by-title' ).focus();
132 shortcut.add( 'ctrl+h', function() {
133 var field = editor.getCurrentField();
135 if ( !field ) return;
137 window.open( getFieldHelpURL( field.tag ) );
140 $('#quicksearch .search-box').each( function() {
141 shortcut.add( 'enter', $.proxy( function() {
144 $('#quicksearch .search-box').each( function() {
145 if ( !this.value ) return;
147 terms.push( [ $(this).data('qualifier'), this.value ] );
150 if ( !terms.length ) return;
152 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
153 $("#search-overlay").show();
158 }, this), { target: this, type: 'keypress' } );
162 function getFieldHelpURL( tag ) {
163 [% IF ( marcflavour == 'MARC21' ) %]
164 if ( tag == '000' ) {
165 return "http://www.loc.gov/marc/bibliographic/bdleader.html";
166 } else if ( tag < '900' ) {
167 return "http://www.loc.gov/marc/bibliographic/bd" + tag + ".html";
169 return "http://www.loc.gov/marc/bibliographic/bd9xx.html";
171 [% ELSIF ( marcflavour == 'UNIMARC' ) %]
172 /* http://archive.ifla.org/VI/3/p1996-1/ is an outdated version of UNIMARC, but
173 seems to be the only version available that can be linked to per tag. More recent
174 versions of the UNIMARC standard are available on the IFLA website only as
177 if ( tag == '000' ) {
178 return "http://archive.ifla.org/VI/3/p1996-1/uni.htm";
181 var url = "http://archive.ifla.org/VI/3/p1996-1/uni" + first + ".htm#";
182 if ( first == '0' ) url += "b";
183 if ( first != '9' ) url += tag;
193 titleForRecord: _("Editing new record"),
194 get: function( id, callback ) {
195 record = new MARC.Record();
196 KohaBackend.FillRecord( '', record );
202 titleForRecord: _("Editing new full record"),
203 get: function( id, callback ) {
204 record = new MARC.Record();
205 KohaBackend.FillRecord( '', record, true );
211 titleForRecord: _("Editing catalog record #{ID}"),
213 { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}" },
214 { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber={ID}" },
216 saveLabel: _("Save to catalog"),
217 get: function( id, callback ) {
218 if ( !id ) return false;
220 KohaBackend.GetRecord( id, callback );
222 save: function( id, record, done ) {
223 function finishCb( data ) {
224 done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } );
228 KohaBackend.SaveRecord( id, record, finishCb );
230 KohaBackend.CreateRecord( record, finishCb );
235 saveLabel: _("Save as ISO2709 (.mrc) file"),
236 save: function( id, record, done ) {
237 saveAs( new Blob( [record.toISO2709()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.mrc' );
243 saveLabel: _("Save as MARCXML (.xml) file"),
244 save: function( id, record, done ) {
245 saveAs( new Blob( [record.toXML()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.xml' );
251 titleForRecord: _("Editing search result"),
252 get: function( id, callback ) {
253 if ( !id ) return false;
254 if ( !backends.search.records[ id ] ) {
255 callback( { error: _( "Invalid record" ) } );
259 callback( backends.search.records[ id ] );
265 function setSource(parts) {
266 state.backend = parts[0];
267 state.recordID = parts[1];
268 state.canSave = backends[ state.backend ].save != null;
269 state.saveBackend = state.canSave ? state.backend : 'catalog';
271 var backend = backends[state.backend];
273 document.location.hash = '#' + parts[0] + '/' + parts[1];
275 $('#title').text( backend.titleForRecord.replace( '{ID}', parts[1] ) );
277 $.each( backend.links || [], function( i, link ) {
278 $('#title').append(' <a target="_blank" href="' + link.href.replace( '{ID}', parts[1] ) + '">(' + link.title + ')</a>' );
280 $( 'title', document.head ).html( _("Koha › Cataloging › ") + backend.titleForRecord.replace( '{ID}', parts[1] ) );
281 $('#save-record span').text( backends[ state.saveBackend ].saveLabel );
284 function saveRecord( recid, editor, callback ) {
285 var parts = recid.split('/');
286 if ( parts.length != 2 ) return false;
288 if ( !backends[ parts[0] ] || !backends[ parts[0] ].save ) return false;
290 editor.removeErrors();
291 var record = editor.getRecord();
293 if ( record.errors ) {
294 state.saving = false;
295 callback( { error: 'syntax', errors: record.errors } );
299 var errors = KohaBackend.ValidateRecord( '', record );
300 if ( errors.length ) {
301 state.saving = false;
302 callback( { error: 'invalid', errors: errors } );
306 backends[ parts[0] ].save( parts[1], record, function(data) {
307 state.saving = false;
309 if (data.newRecord) {
310 var record = new MARC.Record();
311 record.loadMARCXML(data.newRecord);
312 editor.displayRecord( record );
316 setSource(data.newId);
318 setSource( [ state.backend, state.recordID ] );
321 if (callback) callback( data );
325 function loadRecord( recid, editor, callback ) {
326 var parts = recid.split('/');
327 if ( parts.length != 2 ) return false;
329 if ( !backends[ parts[0] ] || !backends[ parts[0] ].get ) return false;
331 backends[ parts[0] ].get( parts[1], function( record ) {
332 if ( !record.error ) {
333 editor.displayRecord( record );
337 if (callback) callback(record);
343 function openRecord( recid, editor, callback ) {
344 return loadRecord( recid, editor, function ( record ) {
345 setSource( recid.split('/') );
347 if (callback) callback( record );
352 function showAdvancedSearch() {
353 $('#advanced-search-servers').empty();
354 $.each( z3950Servers, function( server_id, server ) {
355 $('#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>' );
357 $('#advanced-search-ui').modal('show');
360 function startAdvancedSearch() {
363 $('#advanced-search-ui .search-box').each( function() {
364 if ( !this.value ) return;
366 terms.push( [ $(this).data('qualifier'), this.value ] );
369 if ( !terms.length ) return;
371 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
372 $('#advanced-search-ui').modal('hide');
373 $("#search-overlay").show();
378 function showResultsBox(data) {
379 $('#search-top-pages, #search-bottom-pages').find('.pagination').empty();
380 $('#searchresults thead tr').empty();
381 $('#searchresults tbody').empty();
382 $('#search-serversinfo').empty().append('<li>' + _("Loading...") + '</li>');
383 $('#search-results-ui').modal('show');
386 function showSearchSorting( sort_key, sort_direction ) {
387 var $th = $('#searchresults thead tr th[data-sort-label="' + sort_key + '"]');
388 $th.parent().find( 'th[data-sort-label]' ).attr( 'class', 'sorting' );
390 if ( sort_direction == 'asc' ) {
392 $th.attr( 'class', 'sorting_asc' );
395 $th.attr( 'class', 'sorting_desc' );
399 function showSearchResults( editor, data ) {
400 backends.search.records = {};
402 $('#searchresults thead tr').empty();
403 $('#searchresults tbody').empty();
404 $('#search-serversinfo').empty();
406 $.each( z3950Servers, function( server_id, server ) {
407 var num_fetched = data.num_fetched[server_id];
409 if ( data.errors[server_id] ) {
410 num_fetched = data.errors[server_id];
411 } else if ( num_fetched == null ) {
413 } else if ( num_fetched < data.num_hits[server_id] ) {
417 $('#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>' );
420 var seenColumns = {};
422 $.each( data.hits, function( undef, hit ) {
423 $.each( hit.metadata, function(key) {
424 seenColumns[key] = true;
428 $('#searchresults thead tr').append('<th>' + _("Source") + '</th>');
430 $.each( z3950Labels, function( undef, label ) {
431 if ( seenColumns[ label[0] ] ) {
432 $('#searchresults thead tr').append( '<th class="sorting" data-sort-label="' + label[0] + '">' + label[1] + '</th>' );
436 showSearchSorting( data.sort_key, data.sort_direction );
438 $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>');
440 $.each( data.hits, function( undef, hit ) {
441 backends.search.records[ hit.server + ':' + hit.index ] = hit.record;
442 hit.id = 'search/' + hit.server + ':' + hit.index;
445 result += '<td class="sourcecol">' + z3950Servers[ hit.server ].name + '</td>';
447 $.each( z3950Labels, function( undef, label ) {
448 if ( !seenColumns[ label[0] ] ) return;
450 if ( hit.metadata[ label[0] ] ) {
451 result += '<td class="infocol">' + hit.metadata[ label[0] ] + '</td>';
453 result += '<td class="infocol"> </td>';
457 result += '<td class="toolscol"><ul><li><a href="#" class="marc-link">' + _("View MARC") + '</a></li>';
458 result += '<li><a href="#" class="open-link">' + _("Import") + '</a></li>';
459 if ( state.canSave ) result += '<li><a href="#" class="substitute-link" title="' + _("Replace the current record's contents") + '">' + _("Substitute") + '</a></li>';
460 result += '</ul></td></tr>';
462 var $tr = $( result );
463 $tr.find( '.marc-link' ).click( function() {
464 var $info_columns = $tr.find( '.infocol' );
465 var $marc_column = $tr.find( '.marccol' );
467 if ( !$marc_column.length ) {
468 $marc_column = $( '<td class="marccol" colspan="' + $info_columns.length + '"></td>' ).insertAfter( $info_columns.eq(-1) ).hide();
469 CodeMirror.runMode( TextMARC.RecordToText( hit.record ), 'marc', $marc_column[0] );
472 if ( $marc_column.is(':visible') ) {
473 $tr.find('.marc-link').text( _("View MARC") );
474 $info_columns.show();
477 $tr.find('.marc-link').text( _("Hide MARC") );
479 $info_columns.hide();
484 $tr.find( '.open-link' ).click( function() {
485 $( '#search-results-ui' ).modal('hide');
486 openRecord( hit.id, editor );
490 $tr.find( '.substitute-link' ).click( function() {
491 $( '#search-results-ui' ).modal('hide');
492 loadRecord( hit.id, editor );
496 $('#searchresults tbody').append( $tr );
500 var cur_page = data.offset / data.page_size;
501 var max_page = Math.ceil( data.total_fetched / data.page_size ) - 1;
503 if ( cur_page != 0 ) {
504 pages.push( '<li><a class="search-nav" href="#" data-offset="' + (data.offset - data.page_size) + '">« ' + _("Previous") + '</a></li>' );
507 for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) {
508 if ( page == cur_page ) {
509 pages.push( ' <li class="active"><a href="#">' + ( page + 1 ) + '</a></li>' );
511 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + ( page * data.page_size ) + '">' + ( page + 1 ) + '</a></li>' );
515 if ( cur_page < max_page ) {
516 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + (data.offset + data.page_size) + '">' + _("Next") + ' »</a></li>' );
519 if ( pages.length > 1 ) $( '#search-top-pages, #search-bottom-pages' ).find( '.pagination' ).html( '<ul>' + pages.join( '' ) + '</ul>');
521 var $overlay = $('#search-overlay');
522 $overlay.find('span').text(_("Loading"));
523 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
525 if ( data.activeclients ) {
526 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
529 $overlay.find('.bar').css( { display: 'block', width: '100%' } );
531 $('#searchresults')[0].focus();
535 function invalidateSearchResults() {
536 var $overlay = $('#search-overlay');
537 $overlay.find('span').text(_("Search expired, please try again"));
538 $overlay.find('.bar').css( { display: 'none' } );
542 function handleSearchError(error) {
543 if (error.code == 1) {
544 invalidateSearchResults();
547 humanMsg.displayMsg( _("<h3>Internal search error</h3>") + '<p>' + error + '</p>' + _("<p>Please <b>refresh</b> the page and try again."), { className: 'humanError' } );
551 function handleSearchInitError(error) {
552 $('#quicksearch-overlay').fadeIn().find('p').text(error);
555 // Preference functions
556 function showPreference( pref ) {
557 var value = Preferences.user[pref];
561 $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") );
564 $( '#editor .CodeMirror' ).css( { fontFamily: value } );
568 $( '#editor .CodeMirror' ).css( { fontSize: value } );
572 // Macros loaded on first show of modal
574 case 'selected_search_targets':
575 $.each( z3950Servers, function( server_id, server ) {
576 var saved_val = Preferences.user.selected_search_targets[server_id];
578 if ( saved_val != null ) server.checked = saved_val;
584 function bindPreference( editor, pref ) {
585 function _addHandler( sel, event, handler ) {
586 $( sel ).on( event, function (e) {
588 handler( e, Preferences.user[pref] );
589 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
590 showPreference(pref);
596 _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) {
597 editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets );
601 _addHandler( '#prefs-menu .set-font', 'click', function( e, oldValue ) {
602 Preferences.user.font = $( e.target ).css( 'font-family' );
606 _addHandler( '#prefs-menu .set-fontSize', 'click', function( e, oldValue ) {
607 Preferences.user.fontSize = $( e.target ).css( 'font-size' );
610 case 'selected_search_targets':
611 $( document ).on( 'change', 'input.search-toggle-server', function() {
612 var server_id = $( this ).closest('li').data('server-id');
613 Preferences.user.selected_search_targets[server_id] = this.checked;
614 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
620 function displayPreferences( editor ) {
621 $.each( Preferences.user, function( pref, value ) {
622 showPreference( pref );
623 bindPreference( editor, pref );
628 function loadMacro( name ) {
629 $( '#macro-list li' ).removeClass( 'active' );
630 macroEditor.activeMacro = name;
633 macroEditor.setValue( '' );
637 $( '#macro-list li[data-name="' + name + '"]' ).addClass( 'active' );
638 var macro = Preferences.user.macros[name];
639 macroEditor.setValue( macro.contents );
640 $( '#macro-format' ).val( macro.format || 'its' );
641 if ( macro.history ) macroEditor.setHistory( macro.history );
644 function storeMacro( name, macro ) {
646 Preferences.user.macros[name] = macro;
648 delete Preferences.user.macros[name];
651 Preferences.Save( [% USER_INFO.0.borrowernumber %] );
654 function showSavedMacros( macros ) {
655 var scrollTop = $('#macro-list').scrollTop();
656 $( '#macro-list' ).empty();
657 var macro_list = $.map( Preferences.user.macros, function( macro, name ) {
658 return $.extend( { name: name }, macro );
660 macro_list.sort( function( a, b ) {
661 return a.name.localeCompare(b.name);
663 $.each( macro_list, function( undef, macro ) {
664 var $li = $( '<li data-name="' + macro.name + '"><a href="#">' + macro.name + '</a><ol class="macro-info"></ol></li>' );
665 $li.click( function() {
666 loadMacro(macro.name);
669 if ( macro.name == macroEditor.activeMacro ) $li.addClass( 'active' );
670 var modified = macro.modified && new Date(macro.modified);
671 $li.find( '.macro-info' ).append(
672 '<li><span class="label">' + _("Last changed:") + '</span>' +
673 ( modified ? modified.toLocaleFormat() : _("never") ) + '</li>'
675 $('#macro-list').append($li);
677 var $new_li = $( '<li class="new-macro"><a href="#">' + _("New macro...") + '</a></li>' );
678 $new_li.click( function() {
679 // TODO: make this a bit less retro
680 var name = prompt(_("Please enter the name for the new macro:"));
683 if ( !Preferences.user.macros[name] ) storeMacro( name, { format: "rancor", contents: "" } );
687 $('#macro-list').append($new_li);
688 $('#macro-list').scrollTop(scrollTop);
691 function saveMacro() {
692 var name = macroEditor.activeMacro;
694 if ( !name || macroEditor.savedGeneration == macroEditor.changeGeneration() ) return;
696 macroEditor.savedGeneration = macroEditor.changeGeneration();
697 storeMacro( name, { contents: macroEditor.getValue(), modified: (new Date()).valueOf(), history: macroEditor.getHistory(), format: $('#macro-format').val() } );
698 $('#macro-save-message').text(_("Saved"));
702 $(document).ready( function() {
704 editor = new MARCEditor( {
705 onCursorActivity: function() {
706 $('#status-tag-info').empty();
707 $('#status-subfield-info').empty();
709 var field = editor.getCurrentField();
710 var cur = editor.getCursor();
712 if ( !field ) return;
714 var taginfo = KohaBackend.GetTagInfo( '', field.tag );
715 $('#status-tag-info').html( '<strong>' + field.tag + ':</strong> ' );
718 $('#status-tag-info').append( '<a href="' + getFieldHelpURL( field.tag ) + '" target="_blank" class="show-field-help" title="' + _("Show help for this tag") + '">[?]</a> ' + taginfo.lib );
720 var subfield = field.getSubfieldAt( cur.ch );
721 if ( !subfield ) return;
723 var subfieldinfo = taginfo.subfields[ subfield.code ];
724 $('#status-subfield-info').html( '<strong>$' + subfield.code + ':</strong> ' );
726 if ( subfieldinfo ) {
727 $('#status-subfield-info').append( subfieldinfo.lib );
729 $('#status-subfield-info').append( '<em>' + _("Unknown subfield") + '</em>' );
732 $('#status-tag-info').append( '<em>' + _("Unknown tag") + '</em>' );
735 position: function (elt) { $(elt).insertAfter('#toolbar') },
738 // Automatically detect resizes and change the height of the editor and position of modals.
739 var resizeTimer = null;
740 $( window ).resize( function() {
741 if ( resizeTimer == null ) resizeTimer = setTimeout( function() {
744 var pos = $('#editor .CodeMirror').position();
745 $('#editor .CodeMirror').height( $(window).height() - pos.top - 24 - $('#changelanguage').height() ); // 24 is hardcoded value but works well
747 $('.modal-body').each( function() {
748 $(this).height( $(window).height() * .8 - $(this).prevAll('.modal-header').height() );
752 $("#advanced-search-ui, #search-results-ui, #macro-ui").css( {
753 marginLeft: function() {
754 return -($(this).width() / 2);
760 $( '#macro-ui' ).on( 'shown', function() {
761 if ( macroEditor ) return;
763 macroEditor = CodeMirror(
764 $('#macro-editor')[0],
771 macroEditor.on( 'change', function( cm, change ) {
772 $('#macro-save-message').empty();
773 if ( change.origin == 'setValue' ) return;
775 if ( saveTimeout ) clearTimeout( saveTimeout );
776 saveTimeout = setTimeout( function() {
786 var saveableBackends = [];
787 $.each( backends, function( id, backend ) {
788 if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] );
790 saveableBackends.sort();
791 $.each( saveableBackends, function( undef, backend ) {
792 $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + backend[0] + '</a></li>' );
795 var macro_format_list = $.map( Macros.formats, function( format, name ) {
796 return $.extend( { name: name }, format );
798 macro_format_list.sort( function( a, b ) {
799 return a.description.localeCompare(b.description);
801 $.each( macro_format_list, function() {
802 $('#macro-format').append( '<option value="' + this.name + '">' + this.description + '</option>' );
806 $( '#save-record, #save-dropdown a' ).click( function() {
807 $( '#save-record' ).find('i').attr( 'class', 'icon-loading' ).siblings( 'span' ).text( _("Saving...") );
809 function finishCb(result) {
810 if ( result.error == 'syntax' ) {
811 humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } );
812 } else if ( result.error == 'invalid' ) {
813 humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } );
814 } else if ( !result.error ) {
815 humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } );
818 $.each( result.errors || [], function( undef, error ) {
819 switch ( error.type ) {
821 editor.addError( error.line, _("Invalid tag number") );
824 editor.addError( error.line, _("Invalid indicators") );
827 editor.addError( error.line, _("Tag has no subfields") );
830 editor.addError( null, _("Missing mandatory tag: ") + error.tag );
832 case 'missingSubfield':
833 if ( error.subfield == '@' ) {
834 editor.addError( error.line, _("Missing control field contents") );
836 editor.addError( error.line, _("Missing mandatory subfield: $") + error.subfield );
839 case 'unrepeatableTag':
840 editor.addError( error.line, _("Tag ") + error.tag + _(" cannot be repeated") );
842 case 'unrepeatableSubfield':
843 editor.addError( error.line, _("Subfield $") + error.subfield + _(" cannot be repeated") );
848 $( '#save-record' ).find('i').attr( 'class', 'icon-hdd' );
850 if ( result.error ) {
851 // Reset backend info
852 setSource( [ state.backend, state.recordID ] );
856 var backend = $( this ).data( 'backend' ) || ( state.saveBackend );
857 if ( state.backend == backend ) {
858 saveRecord( backend + '/' + state.recordID, editor, finishCb );
860 saveRecord( backend + '/', editor, finishCb );
866 $('#import-records').click( function() {
867 $('#import-records-input')
869 .change( function() {
870 if ( !this.files || !this.files.length ) return;
872 var file = this.files[0];
873 var reader = new FileReader();
875 reader.onload = function() {
876 var record = new MARC.Record();
878 if ( /\.(mrc|marc|iso|iso2709|marcstd)$/.test( file.name ) ) {
879 record.loadISO2709( reader.result );
880 } else if ( /\.(xml|marcxml)$/.test( file.name ) ) {
881 record.loadMARCXML( reader.result );
883 humanMsg.displayAlert( _("Unknown record type, cannot import"), { className: 'humanError' } );
887 editor.displayRecord( record );
890 reader.readAsText( file );
897 $('#open-macros').click( function() {
898 $('#macro-ui').modal('show');
903 $('#run-macro').click( function() {
904 var result = Macros.Run( editor, $('#macro-format').val(), macroEditor.getValue() );
906 if ( !result.errors.length ) {
907 $('#macro-ui').modal('hide');
912 $.each( result.errors, function() {
913 var error = '<b>' + _("Line ") + (this.line + 1) + ':</b> ';
915 switch ( this.error ) {
916 case 'failed': error += _("failed to run"); break;
917 case 'unrecognized': error += _("unrecognized command"); break;
923 humanMsg.displayMsg( _("<h3>Failed to run macro:</h3>") + '<ul><li>' + errors.join('</li><li>') + '</li></ul>', { className: 'humanError' } );
928 $('#delete-macro').click( function() {
929 if ( !macroEditor.activeMacro || !confirm( _("Are you sure you want to delete this macro?") ) ) return;
931 storeMacro( macroEditor.activeMacro, undefined );
933 loadMacro( undefined );
938 $( '#switch-editor' ).click( function() {
939 if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return;
941 $.cookie( 'catalogue_editor_[% USER_INFO.0.borrowernumber %]', 'basic', { expires: 365, path: '/' } );
943 if ( state.backend == 'catalog' ) {
944 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + state.recordID;
945 } else if ( state.backend == 'new' ) {
946 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl';
948 humanMsg.displayAlert( _("Cannot open this record in the basic editor"), { className: 'humanError' } );
952 $( '#show-advanced-search' ).click( function() {
953 showAdvancedSearch();
958 $('#advanced-search').submit( function() {
959 startAdvancedSearch();
964 $( document ).on( 'click', 'a.search-nav', function() {
965 $("#search-overlay").show();
966 Search.Fetch( { offset: $( this ).data( 'offset' ) } );
970 $( document ).on( 'click', 'th[data-sort-label]', function() {
971 $("#search-overlay").show();
974 if ( $( this ).hasClass( 'sorting_asc' ) ) {
980 showSearchSorting( $( this ).data( 'sort-label' ), direction );
982 Search.Fetch( { sort_key: $( this ).data( 'sort-label' ), sort_direction: direction } );
986 $( document ).on( 'change', 'input.search-toggle-server', function() {
987 var server = z3950Servers[ $( this ).closest('li').data('server-id') ];
988 server.checked = this.checked;
990 if ( $('#search-results-ui').is( ':visible' ) ) {
991 $("#search-overlay").show();
1000 $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() {
1001 $(this).modal({ show: false });
1004 var $quicksearch = $('#quicksearch fieldset');
1005 $('<div id="quicksearch-overlay"><h3>' + _("Search unavailable") + '</h3> <p></p></div>').css({
1006 position: 'absolute',
1007 top: $quicksearch.offset().top,
1008 left: $quicksearch.offset().left,
1009 height: $quicksearch.outerHeight(),
1010 width: $quicksearch.outerWidth(),
1011 }).appendTo(document.body).hide();
1013 var prevAlerts = [];
1014 humanMsg.logMsg = function(msg, options) {
1015 $('#show-alerts').popover('hide');
1016 prevAlerts.unshift('<li>' + msg + '</li>');
1017 prevAlerts.splice(5, 999); // Truncate old messages
1020 $('#show-alerts').popover({
1022 placement: 'bottom',
1023 content: function() {
1024 return '<div id="alerts-container"><ul>' + prevAlerts.join('') + '</ul></div>';
1027 $('#new-record' ).click( function() {
1028 openRecord( 'new/', editor );
1033 Preferences.Load( [% USER_INFO.0.borrowernumber || 0 %] );
1034 displayPreferences(editor);
1035 makeAuthorisedValueWidgets( '' );
1038 onresults: function(data) { showSearchResults( editor, data ) },
1039 onerror: handleSearchError,
1042 function finishCb( data ) {
1043 if ( data.error ) openRecord( 'new/', editor, finishCb );
1045 Resources.GetAll().done( function() {
1046 $("#loading").hide();
1051 if ( "[% auth_forwarded_hash %]" ) {
1052 document.location.hash = "[% auth_forwarded_hash %]";
1055 if ( !document.location.hash || !openRecord( document.location.hash.slice(1), editor, finishCb ) ) {
1056 openRecord( 'new/', editor, finishCb );