3 [% Asset.js("lib/codemirror/codemirror-compressed.js") | $raw %]
4 [% Asset.js("lib/filesaver.js") | $raw %]
5 [% Asset.js("lib/koha/cateditor/marc-mode.js") | $raw %]
6 [% Asset.js("lib/require.js") | $raw %]
9 [%- FOREACH authtag = authtags -%]
10 [% authtag.tagfield | html %]: {
11 subfield: '[% authtag.tagsubfield | html %]',
12 authtypecode: '[% authtag.authtypecode | html %]',
17 baseUrl: '[% interface | html %]/lib/koha/cateditor/',
20 marcflavour: '[% marcflavour | html %]',
21 themelang: '[% themelang | html %]',
28 [% IF marcflavour == 'MARC21' %]
29 [% PROCESS 'cateditor-widgets-marc21.inc' %]
31 <script>var editorWidgets = {};</script>
35 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'resources', 'text-marc', 'widget' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, Resources, TextMARC, Widget ) {
37 'koha:biblioserver': {
38 name: _("Local catalog"),
42 [%- FOREACH server = z3950_servers -%]
43 [% server.id | html %]: {
44 name: '[% server.servername | html %]',
45 recordtype: '[% server.recordtype | html %]',
46 checked: [% server.checked ? 'true' : 'false' | html %],
51 // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config
53 [ "local_number", _("Local number") ],
54 [ "title", _("Title") ],
55 [ "series", _("Series title") ],
56 [ "author", _("Author") ],
57 [ "lccn", _("LCCN") ],
58 [ "isbn", _("ISBN") ],
59 [ "issn", _("ISSN") ],
60 [ "medium", _("Medium") ],
61 [ "edition", _("Edition") ],
62 [ "notes", _("Notes") ],
67 saveBackend: 'catalog',
74 function makeAuthorisedValueWidgets( frameworkCode ) {
75 $.each( KohaBackend.GetAllTagsInfo( frameworkCode ), function( tag, tagInfo ) {
76 $.each( tagInfo.subfields, function( subfield, subfieldInfo ) {
77 if ( !subfieldInfo.authorised_value ) return;
78 var authvals = KohaBackend.GetAuthorisedValues( subfieldInfo.authorised_value );
79 if ( !authvals ) return;
81 var defaultvalue = subfield.defaultvalue || authvals[0].value;
83 Widget.Register( tag + subfield, {
85 var $result = $( '<span class="subfield-widget"></span>' );
89 postCreate: function() {
90 var value = defaultvalue;
93 $.each( authvals, function() {
94 if ( this.value == widget.text ) {
99 this.setText( value );
101 $( '<select></select>' ).appendTo( this.node );
102 var $node = $( this.node ).find( 'select' );
103 $.each( authvals, function( undef, authval ) {
104 $node.append( '<option value="' + authval.value + '"' + (authval.value == value ? ' selected="selected"' : '') + '>' + authval.lib + '</option>' );
106 $node.val( this.text );
108 $node.change( $.proxy( function() {
109 this.setText( $node.val() );
112 makeTemplate: function() {
120 function bindGlobalKeys() {
121 shortcut.add( 'ctrl+s', function(event) {
122 $( '#save-record' ).click();
124 event.preventDefault();
127 shortcut.add( 'alt+ctrl+k', function(event) {
128 $( '#search-by-keywords' ).focus();
133 shortcut.add( 'alt+ctrl+a', function(event) {
134 $( '#search-by-author' ).focus();
139 shortcut.add( 'alt+ctrl+i', function(event) {
140 $( '#search-by-isbn' ).focus();
145 shortcut.add( 'alt+ctrl+t', function(event) {
146 $( '#search-by-title' ).focus();
151 shortcut.add( 'ctrl+h', function() {
152 var field = editor.getCurrentField();
154 if ( !field ) return;
156 window.open( getFieldHelpURL( field.tag ) );
159 $('#quicksearch .search-box').each( function() {
160 shortcut.add( 'enter', $.proxy( function() {
163 $('#quicksearch .search-box').each( function() {
164 if ( !this.value ) return;
166 terms.push( [ $(this).data('qualifier'), this.value ] );
169 if ( !terms.length ) return;
171 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
172 $("#search-overlay").show();
177 }, this), { target: this, type: 'keypress' } );
181 function getFieldHelpURL( tag ) {
182 [% IF Koha.Preference('marcfielddocurl') %]
183 var docurl = "[% Koha.Preference('marcfielddocurl').replace('"','"') | html %]";
184 docurl = docurl.replace("{MARC}", "[% marcflavour | html %]");
185 docurl = docurl.replace("{FIELD}", ""+tag);
186 docurl = docurl.replace("{LANG}", "[% lang | html %]");
188 [% ELSIF ( marcflavour == 'MARC21' ) %]
189 if ( tag == '000' ) {
190 return "http://www.loc.gov/marc/bibliographic/bdleader.html";
191 } else if ( tag >= '090' && tag < '100' ) {
192 return "http://www.loc.gov/marc/bibliographic/bd09x.html";
193 } else if ( tag < '900' ) {
194 return "http://www.loc.gov/marc/bibliographic/bd" + tag + ".html";
196 return "http://www.loc.gov/marc/bibliographic/bd9xx.html";
198 [% ELSIF ( marcflavour == 'UNIMARC' ) %]
199 /* http://archive.ifla.org/VI/3/p1996-1/ is an outdated version of UNIMARC, but
200 seems to be the only version available that can be linked to per tag. More recent
201 versions of the UNIMARC standard are available on the IFLA website only as
204 if ( tag == '000' ) {
205 return "http://archive.ifla.org/VI/3/p1996-1/uni.htm";
208 var url = "http://archive.ifla.org/VI/3/p1996-1/uni" + first + ".htm#";
209 if ( first == '0' ) url += "b";
210 if ( first != '9' ) url += tag;
220 titleForRecord: _("Editing new record"),
221 get: function( id, callback ) {
222 record = new MARC.Record();
223 KohaBackend.FillRecord( '', record );
229 titleForRecord: _("Editing new full record"),
230 get: function( id, callback ) {
231 record = new MARC.Record();
232 KohaBackend.FillRecord( '', record, true );
238 titleForRecord: _("Editing duplicate record of #{ID}"),
239 get: function( id, callback ) {
240 if ( !id ) return false;
242 KohaBackend.GetRecord( id, callback );
244 save: function( id, record, done ) {
245 function finishCb( data ) {
246 done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } );
249 KohaBackend.CreateRecord( record, finishCb );
253 titleForRecord: _("Editing catalog record #{ID}"),
255 { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}" },
256 { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber={ID}" },
258 saveLabel: _("Save to catalog"),
259 get: function( id, callback ) {
260 if ( !id ) return false;
262 KohaBackend.GetRecord( id, callback );
264 save: function( id, record, done ) {
265 function finishCb( data ) {
266 done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } );
270 KohaBackend.SaveRecord( id, record, finishCb );
272 KohaBackend.CreateRecord( record, finishCb );
277 saveLabel: _("Save as MARC (.mrc) file"),
278 save: function( id, record, done ) {
279 saveAs( new Blob( [record.toISO2709()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.mrc' );
285 saveLabel: _("Save as MARCXML (.xml) file"),
286 save: function( id, record, done ) {
287 saveAs( new Blob( [record.toXML()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.xml' );
293 titleForRecord: _("Editing search result"),
294 get: function( id, callback ) {
295 if ( !id ) return false;
296 if ( !backends.search.records[ id ] ) {
297 callback( { error: _( "Invalid record" ) } );
301 callback( backends.search.records[ id ] );
307 function setSource(parts) {
308 state.backend = parts[0];
309 state.recordID = parts[1];
310 state.canSave = backends[ state.backend ].save != null;
311 state.saveBackend = state.canSave ? state.backend : 'catalog';
313 var backend = backends[state.backend];
315 document.location.hash = '#' + parts[0] + '/' + parts[1];
317 $('#title').text( backend.titleForRecord.replace( '{ID}', parts[1] ) );
319 $.each( backend.links || [], function( i, link ) {
320 $('#title').append(' <a target="_blank" href="' + link.href.replace( '{ID}', parts[1] ) + '">(' + link.title + ')</a>' );
322 $( 'title', document.head ).html( _("Koha › Cataloging › ") + backend.titleForRecord.replace( '{ID}', parts[1] ) );
323 $('#save-record span').text( backends[ state.saveBackend ].saveLabel );
326 function saveRecord( recid, editor, callback ) {
327 var parts = recid.split('/');
328 if ( parts.length != 2 ) return false;
330 if ( !backends[ parts[0] ] || !backends[ parts[0] ].save ) return false;
332 editor.removeErrors();
333 var record = editor.getRecord();
335 if ( record.errors ) {
336 state.saving = false;
337 callback( { error: 'syntax', errors: record.errors } );
341 var errors = KohaBackend.ValidateRecord( '', record );
342 if ( errors.length ) {
343 state.saving = false;
344 callback( { error: 'invalid', errors: errors } );
348 backends[ parts[0] ].save( parts[1], record, function(data) {
349 state.saving = false;
351 if (data.newRecord) {
352 var record = new MARC.Record();
353 record.loadMARCXML(data.newRecord);
354 record.frameworkcode = data.newRecord.frameworkcode;
355 editor.displayRecord( record );
359 setSource(data.newId);
361 setSource( [ state.backend, state.recordID ] );
364 if (callback) callback( data );
368 function loadRecord( recid, editor, callback ) {
369 var parts = recid.split('/');
370 if ( parts.length != 2 ) return false;
372 if ( !backends[ parts[0] ] || !backends[ parts[0] ].get ) return false;
374 backends[ parts[0] ].get( parts[1], function( record ) {
375 if ( !record.error ) {
376 editor.displayRecord( record );
380 if (callback) callback(record);
386 function openRecord( recid, editor, callback ) {
387 return loadRecord( recid, editor, function ( record ) {
388 setSource( recid.split('/') );
390 if (callback) callback( record );
395 function showAdvancedSearch() {
396 $('#advanced-search-servers').empty();
397 $.each( z3950Servers, function( server_id, server ) {
398 $('#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>' );
400 $('#advanced-search-ui').modal('show');
403 function startAdvancedSearch() {
406 $('#advanced-search-ui .search-box').each( function() {
407 if ( !this.value ) return;
409 terms.push( [ $(this).data('qualifier'), this.value ] );
412 if ( !terms.length ) return;
414 if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
415 $('#advanced-search-ui').modal('hide');
416 $("#search-overlay").show();
421 function showResultsBox(data) {
422 $('#search-top-pages, #search-bottom-pages').find('nav').empty();
423 $('#searchresults thead tr').empty();
424 $('#searchresults tbody').empty();
425 $('#search-serversinfo').empty().append('<li>' + _("Loading...") + '</li>');
426 $('#search-results-ui').modal('show');
429 function showSearchSorting( sort_key, sort_direction ) {
430 var $th = $('#searchresults thead tr th[data-sort-label="' + sort_key + '"]');
431 $th.parent().find( 'th[data-sort-label]' ).attr( 'class', 'sorting' );
433 if ( sort_direction == 'asc' ) {
435 $th.attr( 'class', 'sorting_asc' );
438 $th.attr( 'class', 'sorting_desc' );
442 function showSearchResults( editor, data ) {
443 backends.search.records = {};
445 $('#searchresults thead tr').empty();
446 $('#searchresults tbody').empty();
447 $('#search-serversinfo').empty();
449 $.each( z3950Servers, function( server_id, server ) {
450 var num_fetched = data.num_fetched[server_id];
452 if ( data.errors[server_id] ) {
453 num_fetched = data.errors[server_id];
454 } else if ( num_fetched == null ) {
456 } else if ( num_fetched < data.num_hits[server_id] ) {
460 $('#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>' );
463 var seenColumns = {};
465 $.each( data.hits, function( undef, hit ) {
466 $.each( hit.metadata, function(key) {
467 seenColumns[key] = true;
471 $('#searchresults thead tr').append('<th>' + _("Source") + '</th>');
473 $.each( z3950Labels, function( undef, label ) {
474 if ( seenColumns[ label[0] ] ) {
475 $('#searchresults thead tr').append( '<th class="sorting" data-sort-label="' + label[0] + '">' + label[1] + '</th>' );
479 showSearchSorting( data.sort_key, data.sort_direction );
481 $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>');
483 var bibnumMap = KohaBackend.GetSubfieldForKohaField('biblio.biblionumber');
484 $.each( data.hits, function( undef, hit ) {
485 backends.search.records[ hit.server + ':' + hit.index ] = hit.record;
487 switch ( hit.server ) {
488 case 'koha:biblioserver':
489 var bibnumField = hit.record.field( bibnumMap[0] );
491 if ( bibnumField && bibnumField.hasSubfield( bibnumMap[1] ) ) {
492 hit.id = 'catalog/' + bibnumField.subfield( bibnumMap[1] );
496 // Otherwise, fallthrough
499 hit.id = 'search/' + hit.server + ':' + hit.index;
503 result += '<td class="sourcecol">' + z3950Servers[ hit.server ].name + '</td>';
505 $.each( z3950Labels, function( undef, label ) {
506 if ( !seenColumns[ label[0] ] ) return;
508 if ( hit.metadata[ label[0] ] ) {
509 result += '<td class="infocol">' + hit.metadata[ label[0] ] + '</td>';
511 result += '<td class="infocol"> </td>';
515 result += '<td class="toolscol"><ul><li><a href="#" class="marc-link">' + _("View MARC") + '</a></li>';
516 result += '<li><a href="#" class="open-link">' + ( hit.server == 'koha:biblioserver' ? _("Edit") : _("Import") ) + '</a></li>';
517 if ( state.canSave ) result += '<li><a href="#" class="substitute-link" title="' + _("Replace the current record's contents") + '">' + _("Substitute") + '</a></li>';
518 result += '</ul></td></tr>';
520 var $tr = $( result );
521 $tr.find( '.marc-link' ).click( function() {
522 var $info_columns = $tr.find( '.infocol' );
523 var $marc_column = $tr.find( '.marccol' );
525 if ( !$marc_column.length ) {
526 $marc_column = $( '<td class="marccol" colspan="' + $info_columns.length + '"></td>' ).insertAfter( $info_columns.eq(-1) ).hide();
527 CodeMirror.runMode( TextMARC.RecordToText( hit.record ), 'marc', $marc_column[0] );
530 if ( $marc_column.is(':visible') ) {
531 $tr.find('.marc-link').text( _("View MARC") );
532 $info_columns.show();
535 $tr.find('.marc-link').text( _("Hide MARC") );
537 $info_columns.hide();
542 $tr.find( '.open-link' ).click( function() {
543 $( '#search-results-ui' ).modal('hide');
544 openRecord( hit.id, editor );
548 $tr.find( '.substitute-link' ).click( function() {
549 $( '#search-results-ui' ).modal('hide');
550 loadRecord( hit.id, editor );
554 $('#searchresults tbody').append( $tr );
558 var cur_page = data.offset / data.page_size;
559 var max_page = Math.ceil( data.total_fetched / data.page_size ) - 1;
561 if ( cur_page != 0 ) {
562 pages.push( '<li><a class="search-nav" href="#" data-offset="' + (data.offset - data.page_size) + '"><span aria-hidden="true">«</span> ' + _("Previous") + '</a></li>' );
565 for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) {
566 if ( page == cur_page ) {
567 pages.push( ' <li class="active"><a href="#">' + ( page + 1 ) + '</a></li>' );
569 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + ( page * data.page_size ) + '">' + ( page + 1 ) + '</a></li>' );
573 if ( cur_page < max_page ) {
574 pages.push( ' <li><a class="search-nav" href="#" data-offset="' + (data.offset + data.page_size) + '">' + _("Next") + ' <span aria-hidden="true">»</span></a></li>' );
577 $( '#search-top-pages, #search-bottom-pages' ).find( 'nav' ).html( pages.length > 1 ? ( '<ul class="pagination pagination-sm">' + pages.join( '' ) + '</ul>' ) : '' );
579 var $overlay = $('#search-overlay');
580 $overlay.find('span').text(_("Loading"));
581 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
583 if ( data.activeclients ) {
584 $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
587 $overlay.find('.bar').css( { display: 'block', width: '100%' } );
589 $('#searchresults')[0].focus();
593 function invalidateSearchResults() {
594 var $overlay = $('#search-overlay');
595 $overlay.find('span').text(_("Search expired, please try again"));
596 $overlay.find('.bar').css( { display: 'none' } );
600 function handleSearchError(error) {
601 if (error.code == 1) {
602 invalidateSearchResults();
605 humanMsg.displayMsg( '<h3>' + _("Internal search error") + '</h3><p>' + error.responseText + '</p><p>' + _("Please refresh the page and try again.") + '</p>', { className: 'humanError' } );
609 function handleSearchInitError(error) {
610 $('#quicksearch-overlay').fadeIn().find('p').text(error);
613 // Preference functions
614 function showPreference( pref ) {
615 var value = Preferences.user[pref];
619 $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") );
622 $( '#editor .CodeMirror' ).css( { fontFamily: value } );
626 $( '#editor .CodeMirror' ).css( { fontSize: value } );
630 // Macros loaded on first show of modal
632 case 'selected_search_targets':
633 $.each( z3950Servers, function( server_id, server ) {
634 var saved_val = Preferences.user.selected_search_targets[server_id];
636 if ( saved_val != null ) server.checked = saved_val;
642 function bindPreference( editor, pref ) {
643 function _addHandler( sel, event, handler ) {
644 $( sel ).on( event, function (e) {
646 handler( e, Preferences.user[pref] );
647 Preferences.Save( [% logged_in_user.borrowernumber | html %] );
648 showPreference(pref);
654 _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) {
655 editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets );
659 _addHandler( '#prefs-menu .set-font', 'click', function( e, oldValue ) {
660 Preferences.user.font = $( e.target ).css( 'font-family' );
664 _addHandler( '#prefs-menu .set-fontSize', 'click', function( e, oldValue ) {
665 Preferences.user.fontSize = $( e.target ).css( 'font-size' );
668 case 'selected_search_targets':
669 $( document ).on( 'change', 'input.search-toggle-server', function() {
670 var server_id = $( this ).closest('li').data('server-id');
671 Preferences.user.selected_search_targets[server_id] = this.checked;
672 Preferences.Save( [% logged_in_user.borrowernumber | html %] );
678 function displayPreferences( editor ) {
679 $.each( Preferences.user, function( pref, value ) {
680 showPreference( pref );
681 bindPreference( editor, pref );
686 function loadMacro( name ) {
687 $( '#macro-list li' ).removeClass( 'active' );
688 macroEditor.activeMacro = name;
691 macroEditor.setValue( '' );
695 $( '#macro-list li[data-name="' + name + '"]' ).addClass( 'active' );
696 var macro = Preferences.user.macros[name];
697 macroEditor.setValue( macro.contents );
698 macroEditor.setOption( 'readOnly', false );
699 $( '#macro-format' ).val( macro.format || 'its' );
700 if ( macro.history ) macroEditor.setHistory( macro.history );
703 function storeMacro( name, macro ) {
705 Preferences.user.macros[name] = macro;
707 delete Preferences.user.macros[name];
710 Preferences.Save( [% logged_in_user.borrowernumber | html %] );
713 function showSavedMacros( macros ) {
714 var scrollTop = $('#macro-list').scrollTop();
715 $( '#macro-list' ).empty();
716 var macro_list = $.map( Preferences.user.macros, function( macro, name ) {
717 return $.extend( { name: name }, macro );
719 macro_list.sort( function( a, b ) {
720 return a.name.localeCompare(b.name);
722 $.each( macro_list, function( undef, macro ) {
723 var $li = $( '<li data-name="' + macro.name + '"><a href="#">' + macro.name + '</a><ol class="macro-info"></ol></li>' );
724 $li.click( function() {
725 loadMacro(macro.name);
728 if ( macro.name == macroEditor.activeMacro ) $li.addClass( 'active' );
729 var modified = macro.modified && new Date(macro.modified);
730 $li.find( '.macro-info' ).append(
731 '<li><span class="label">' + _("Last changed:") + '</span>' +
732 ( modified ? ( modified.toLocaleDateString() + ', ' + modified.toLocaleTimeString() ) : _("never") ) + '</li>'
734 $('#macro-list').append($li);
736 var $new_li = $( '<li class="new-macro"><a href="#">' + _("New macro...") + '</a></li>' );
737 $new_li.click( function() {
738 // TODO: make this a bit less retro
739 var name = prompt(_("Please enter the name for the new macro:"));
742 if ( !Preferences.user.macros[name] ) storeMacro( name, { format: "rancor", contents: "" } );
746 $('#macro-list').append($new_li);
747 $('#macro-list').scrollTop(scrollTop);
750 function saveMacro() {
751 var name = macroEditor.activeMacro;
753 if ( !name || macroEditor.savedGeneration == macroEditor.changeGeneration() ) return;
755 macroEditor.savedGeneration = macroEditor.changeGeneration();
756 storeMacro( name, { contents: macroEditor.getValue(), modified: (new Date()).valueOf(), history: macroEditor.getHistory(), format: $('#macro-format').val() } );
757 $('#macro-save-message').text(_("Saved"));
761 $(document).ready( function() {
763 editor = new MARCEditor( {
764 onCursorActivity: function() {
765 $('#status-tag-info').empty();
766 $('#status-subfield-info').empty();
768 var field = editor.getCurrentField();
769 var cur = editor.getCursor();
771 if ( !field ) return;
773 var taginfo = KohaBackend.GetTagInfo( '', field.tag );
774 $('#status-tag-info').html( '<strong>' + field.tag + ':</strong> ' );
777 $('#status-tag-info').append( '<a href="' + getFieldHelpURL( field.tag ) + '" target="_blank" class="show-field-help" title="' + _("Show help for this tag") + '">[?]</a> ' + taginfo.lib );
779 var subfield = field.getSubfieldAt( cur.ch );
780 if ( !subfield ) return;
782 var subfieldinfo = taginfo.subfields[ subfield.code ];
783 $('#status-subfield-info').html( '<strong>‡' + subfield.code + ':</strong> ' );
785 if ( subfieldinfo ) {
786 $('#status-subfield-info').append( subfieldinfo.lib );
788 $('#status-subfield-info').append( '<em>' + _("Unknown subfield") + '</em>' );
791 $('#status-tag-info').append( '<em>' + _("Unknown tag") + '</em>' );
794 position: function (elt) { $(elt).insertAfter('#toolbar') },
797 // Automatically detect resizes and change the height of the editor and position of modals.
798 var resizeTimer = null;
799 function onResize() {
800 if ( resizeTimer == null ) resizeTimer = setTimeout( function() {
803 var pos = $('#editor .CodeMirror').offset();
804 $('#editor .CodeMirror').height( $(window).height() - pos.top - 24 - $('#changelanguage').height() ); // 24 is hardcoded value but works well
806 $('.modal-body').each( function() {
807 $(this).height( $(window).height() * .8 - $(this).prevAll('.modal-header').height() );
812 $( '#macro-ui' ).on( 'shown.bs.modal', function() {
813 if ( macroEditor ) return;
815 macroEditor = CodeMirror(
816 $('#macro-editor')[0],
819 'Ctrl-D': function( cm ) {
820 var cur = cm.getCursor();
822 cm.replaceRange( "‡", cur, null );
831 macroEditor.on( 'change', function( cm, change ) {
832 $('#macro-save-message').empty();
833 if ( change.origin == 'setValue' ) return;
835 if ( saveTimeout ) clearTimeout( saveTimeout );
836 saveTimeout = setTimeout( function() {
846 var saveableBackends = [];
847 $.each( backends, function( id, backend ) {
848 if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] );
850 saveableBackends.sort();
851 $.each( saveableBackends, function( undef, backend ) {
852 $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + backend[0] + '</a></li>' );
855 var macro_format_list = $.map( Macros.formats, function( format, name ) {
856 return $.extend( { name: name }, format );
858 macro_format_list.sort( function( a, b ) {
859 return a.description.localeCompare(b.description);
861 $.each( macro_format_list, function() {
862 $('#macro-format').append( '<option value="' + this.name + '">' + this.description + '</option>' );
866 $( '#save-record, #save-dropdown a' ).click( function() {
867 $( '#save-record' ).find('i').attr( 'class', 'fa fa-spinner fa-spin' ).siblings( 'span' ).text( _("Saving...") );
869 function finishCb(result) {
870 if ( result.error == 'syntax' ) {
871 humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } );
872 } else if ( result.error == 'invalid' ) {
873 humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } );
874 } else if ( !result.error ) {
875 humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } );
878 $.each( result.errors || [], function( undef, error ) {
879 switch ( error.type ) {
881 editor.addError( error.line, _("Invalid tag number") );
884 editor.addError( error.line, _("Invalid indicators") );
887 editor.addError( error.line, _("Tag has no subfields") );
890 editor.addError( null, _("Missing mandatory tag: ") + error.tag );
892 case 'missingSubfield':
893 if ( error.subfield == '@' ) {
894 editor.addError( error.line, _("Missing control field contents") );
896 editor.addError( error.line, _("Missing mandatory subfield: ‡") + error.subfield );
899 case 'unrepeatableTag':
900 editor.addError( error.line, _("Tag ") + error.tag + _(" cannot be repeated") );
902 case 'unrepeatableSubfield':
903 editor.addError( error.line, _("Subfield ‡") + error.subfield + _(" cannot be repeated") );
905 case 'itemTagUnsupported':
906 editor.addError( error.line, _("Item tags cannot currently be saved") );
911 $( '#save-record' ).find('i').attr( 'class', 'fa fa-hdd-o' );
913 if ( result.error ) {
914 // Reset backend info
915 setSource( [ state.backend, state.recordID ] );
919 var backend = $( this ).data( 'backend' ) || ( state.saveBackend );
920 if ( state.backend == backend ) {
921 saveRecord( backend + '/' + state.recordID, editor, finishCb );
923 saveRecord( backend + '/', editor, finishCb );
929 $('#import-records').click( function() {
930 $('#import-records-input')
932 .change( function() {
933 if ( !this.files || !this.files.length ) return;
935 var file = this.files[0];
936 var reader = new FileReader();
938 reader.onload = function() {
939 var record = new MARC.Record();
941 if ( /\.(mrc|marc|iso|iso2709|marcstd)$/.test( file.name ) ) {
942 record.loadISO2709( reader.result );
943 } else if ( /\.(xml|marcxml)$/.test( file.name ) ) {
944 record.loadMARCXML( reader.result );
946 humanMsg.displayAlert( _("Unknown record type, cannot import"), { className: 'humanError' } );
950 if (record.marc8_corrupted) humanMsg.displayMsg( '<h3>' + _("Possible record corruption") + '</h3><p>' + _("Record not marked as UTF-8, may be corrupted") + '</p>', { className: 'humanError' } );
952 editor.displayRecord( record );
955 reader.readAsText( file );
962 $('#open-macros').click( function() {
963 $('#macro-ui').modal('show');
968 $('#run-macro').click( function() {
969 var result = Macros.Run( editor, $('#macro-format').val(), macroEditor.getValue() );
971 if ( !result.errors.length ) {
972 $('#macro-ui').modal('hide');
973 editor.focus(); //Return cursor to editor after macro run
978 $.each( result.errors, function() {
979 var error = '<b>' + _("Line ") + (this.line + 1) + ':</b> ';
981 switch ( this.error ) {
982 case 'failed': error += _("failed to run"); break;
983 case 'unrecognized': error += _("unrecognized command"); break;
989 humanMsg.displayMsg( '<h3>' + _("Failed to run macro:") + '</h3><ul><li>' + errors.join('</li><li>') + '</li></ul>', { className: 'humanError' } );
994 $('#delete-macro').click( function() {
995 if ( !macroEditor.activeMacro || !confirm( _("Are you sure you want to delete this macro?") ) ) return;
997 storeMacro( macroEditor.activeMacro, undefined );
999 loadMacro( undefined );
1004 $( '#switch-editor' ).click( function() {
1005 if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return;
1007 $.cookie( 'catalogue_editor_[% logged_in_user.borrowernumber | html %]', 'basic', { expires: 365, path: '/' } );
1009 if ( state.backend == 'catalog' ) {
1010 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + state.recordID;
1011 } else if ( state.backend == 'new' ) {
1012 window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl';
1014 humanMsg.displayAlert( _("Cannot open this record in the basic editor"), { className: 'humanError' } );
1018 $( '#show-advanced-search' ).click( function() {
1019 showAdvancedSearch();
1024 $('#advanced-search').submit( function() {
1025 startAdvancedSearch();
1030 $( document ).on( 'click', 'a.search-nav', function() {
1031 if ( Search.Fetch( { offset: $( this ).data( 'offset' ) } ) ) {
1032 $("#search-overlay").show();
1038 $( document ).on( 'click', 'th[data-sort-label]', function() {
1041 if ( $( this ).hasClass( 'sorting_asc' ) ) {
1047 if ( Search.Fetch( { sort_key: $( this ).data( 'sort-label' ), sort_direction: direction } ) ) {
1048 showSearchSorting( $( this ).data( 'sort-label' ), direction );
1050 $("#search-overlay").show();
1056 $( document ).on( 'change', 'input.search-toggle-server', function() {
1057 var server = z3950Servers[ $( this ).closest('li').data('server-id') ];
1058 server.checked = this.checked;
1060 if ( $('#search-results-ui').is( ':visible' ) && Search.Fetch() ) {
1061 $("#search-overlay").show();
1069 $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() {
1070 $(this).modal({ show: false });
1073 var $quicksearch = $('#quicksearch fieldset');
1074 $('<div id="quicksearch-overlay"><h3>' + _("Search unavailable") + '</h3> <p></p></div>').css({
1075 position: 'absolute',
1076 top: $quicksearch.offset().top,
1077 left: $quicksearch.offset().left,
1078 height: $quicksearch.outerHeight(),
1079 width: $quicksearch.outerWidth(),
1080 }).appendTo(document.body).hide();
1082 var prevAlerts = [];
1083 humanMsg.logMsg = function(msg, options) {
1084 $('#show-alerts').popover('hide');
1085 prevAlerts.unshift('<li>' + msg + '</li>');
1086 prevAlerts.splice(5, 999); // Truncate old messages
1089 $('#show-alerts').popover({
1091 placement: 'bottom',
1092 content: function() {
1093 return '<div id="alerts-container"><ul>' + prevAlerts.join('') + '</ul></div>';
1097 $('#show-shortcuts').popover({
1099 placement: 'bottom',
1100 content: function() {
1101 return '<div id="shortcuts-container">' + $('#shortcuts-contents').html() + '</div>';
1105 $('#new-record' ).click( function() {
1106 if ( editor.modified && !confirm( _("Are you sure you want to erase your changes?") ) ) return;
1108 openRecord( 'new/', editor );
1112 window.onbeforeunload = function() {
1113 if(editor.modified )
1116 { return undefined; }
1119 $('a.change-framework').click( function() {
1120 $("#loading").show();
1121 editor.setFrameworkCode(
1122 $(this).data( 'frameworkcode' ),
1124 function ( error ) {
1125 if ( typeof error !== 'undefined' ) {
1126 humanMsg.displayAlert( _("Failed to change framework"), { className: 'humanError' } );
1128 $('#loading').hide();
1134 Preferences.Load( [% logged_in_user.borrowernumber || 0 | html %] );
1135 displayPreferences(editor);
1136 makeAuthorisedValueWidgets( '' );
1139 onresults: function(data) { showSearchResults( editor, data ) },
1140 onerror: handleSearchError,
1143 function finishCb( data ) {
1145 humanMsg.displayAlert( data.error );
1146 openRecord( 'new/', editor, finishCb );
1149 Resources.GetAll().done( function() {
1150 $("#loading").hide();
1151 $( window ).resize( onResize ).resize();
1156 if ( "[% auth_forwarded_hash | html %]" ) {
1157 document.location.hash = "[% auth_forwarded_hash | html %]";
1160 if ( !document.location.hash || !openRecord( document.location.hash.slice(1), editor, finishCb ) ) {
1161 openRecord( 'new/', editor, finishCb );