/*global $, document, CustomEvent, VuFind, window */ VuFind.register('lightbox', function() { // State var _originalUrl = false; var _currentUrl = false; var _lightboxTitle = ''; var refreshOnClose = false; // Elements var _modal, _modalBody, _clickedButton = null; // Utilities var _storeClickedStatus = function() { _clickedButton = this; }; var _html = function(html) { _modalBody.html(html); // Set or update title if we have one if (_lightboxTitle != '') { var h2 = _modalBody.find('h2:first-child'); if (h2.length == 0) { h2 = $('<h2/>').prependTo(_modalBody); } h2.text(_lightboxTitle); _lightboxTitle = ''; } _modal.modal('handleUpdate'); }; var _emit = function(msg, details) { if ('undefined' == typeof details) { details = {}; } // Fallback to document.createEvent() if creating a new CustomEvent fails (e.g. IE 11) var event; try { event = new CustomEvent(msg, { detail: details, bubbles: true, cancelable: true }); } catch (e) { event = document.createEvent('CustomEvent'); event.initCustomEvent(msg, true, true, details); } return document.dispatchEvent(event); }; /** * Reload the page without causing trouble with POST parameters while keeping hash */ var _refreshPage = function() { var parts = window.location.href.split('#'); if (typeof parts[1] === 'undefined') { window.location.href = window.location.href; } else { var href = parts[0]; // Force reload with a timestamp href += href.indexOf('?') == -1 ? '?_=' : '&_='; href += new Date().getTime() + '#' + parts[1]; window.location.href = href; } }; // Public: Present an alert var showAlert = function(message, type) { if ('undefined' == typeof type) { type = 'info'; } _html('<div class="flash-message alert alert-'+type+'">'+message+'</div>' + '<button class="btn btn-default" data-dismiss="modal">' + VuFind.translate('close') + '</button>'); _modal.modal('show'); }; var flashMessage = function(message, type) { _modalBody.find('.flash-message,.fa.fa-spinner').remove(); _modalBody.find('h2:first-of-type') .after('<div class="flash-message alert alert-'+type+'">'+message+'</div>'); }; /** * Update content * * Form data options: * * data-lightbox-ignore = do not submit this form in lightbox */ var _update = function(html) { if (!html.match) { return; } // Isolate successes var htmlDiv = $('<div/>').html(html); var alerts = htmlDiv.find('.flash-message.alert-success'); if (alerts.length > 0) { showAlert(alerts[0].innerHTML, 'success'); return; } // Deframe HTML if (html.match('<!DOCTYPE html>')) { html = htmlDiv.find('.main > .container').html(); } // Fill HTML _html(html); _modal.modal('show'); // Attach capturing events _modalBody.find('a').click(_constrainLink); // Handle submit buttons attached to a form as well as those in a form. Store // information about which button was clicked here as checking focused button // doesn't work on all browsers and platforms. _modalBody.find('[type=submit]').click(_storeClickedStatus); var forms = _modalBody.find('form:not([data-lightbox-ignore])'); for (var i=0;i<forms.length;i++) { $(forms[i]).on('submit', _formSubmit); } // Select all checkboxes $('#modal').find('.checkbox-select-all').change(function() { $(this).closest('.modal-body').find('.checkbox-select-item').prop('checked', this.checked); }); $('#modal').find('.checkbox-select-item').change(function() { $(this).closest('.modal-body').find('.checkbox-select-all').prop('checked', false); }); }; var _xhr = false; // Public: Handle AJAX in the Lightbox var ajax = function(obj) { if (_xhr !== false) { return; } if (_originalUrl === false) { _originalUrl = obj.url; } // Add lightbox GET parameter if (!obj.url.match(/layout=lightbox/)) { var parts = obj.url.split('#'); obj.url = parts[0].indexOf('?') < 0 ? parts[0]+'?' : parts[0]+'&'; obj.url += 'layout=lightbox&lbreferer='+encodeURIComponent(_currentUrl); obj.url += parts.length < 2 ? '' : '#'+parts[1]; } _xhr = $.ajax(obj); _xhr.always(function() { _xhr = false; }) .done(function(html, status, jq_xhr) { if (jq_xhr.status == 205) { _refreshPage(); return; } // Place Hold error isolation if (obj.url.match(/\/Record/) && (obj.url.match(/Hold\?/) || obj.url.match(/Request\?/))) { var testDiv = $('<div/>').html(html); var error = testDiv.find('.flash-message.alert-danger'); if (error.length && testDiv.find('.record').length) { showAlert(error[0].innerHTML, 'danger'); return false; } } if ( // Close the lightbox after deliberate login obj.method // is a form && ((obj.url.match(/MyResearch/) && !obj.url.match(/Bulk/)) // that matches login/create account || obj.url.match(/catalogLogin/)) // or catalog login for holds && $('<div/>').html(html).find('.flash-message.alert-danger').length == 0 // skip failed logins ) { var eventResult = _emit('VuFind.lightbox.login', { originalUrl: _originalUrl, formUrl: obj.url }); if (_originalUrl.match(/UserLogin/) || obj.url.match(/catalogLogin/)) { if (eventResult) { _refreshPage(); } return false; } else { VuFind.lightbox.refreshOnClose = true; } _currentUrl = _originalUrl; // Now that we're logged in, where were we? } _update(html); }) .fail(function() { showAlert(VuFind.translate('error_occurred'), 'danger'); }); return _xhr; }; var reload = function() { ajax({url:_currentUrl || _originalUrl}); }; /** * Evaluate a callback */ var _evalCallback = function(callback, event, data) { if ('function' === typeof window[callback]) { return window[callback](event, data); } else { return eval('(function(event, data) {' + callback + '}())'); // inline code } }; /** * Modal link data options * * data-lightbox-href = go to this url instead * data-lightbox-ignore = do not open this link in lightbox * data-lightbox-post = post data * data-lightbox-title = Lightbox title (overrides any title the page provides) */ var _constrainLink = function(event) { if (typeof $(this).data('lightboxIgnore') != 'undefined' || this.attributes.href.value.charAt(0) === '#') { return true; } if (this.href.length > 1) { event.preventDefault(); var obj = {url: $(this).data('lightboxHref') || this.href}; if("string" === typeof $(this).data('lightboxPost')) { obj.type = 'POST'; obj.data = $(this).data('lightboxPost'); } _lightboxTitle = $(this).data('lightboxTitle') || ''; ajax(obj); _currentUrl = this.href; VuFind.modal('show'); return false; } }; /** * Handle form submission. * * Form data options: * * data-lightbox-onsubmit = on submit, run named function * data-lightbox-onclose = on close, run named function * data-lightbox-title = Lightbox title (overrides any title the page provides) * * Submit button data options: * * data-lightbox-ignore = do not handle clicking this button in lightbox */ var _formSubmit = function(event) { // Gather data var form = event.target; var data = $(form).serializeArray(); data.push({'name':'layout', 'value':'lightbox'}); // Return in lightbox, please // Add submit button information var submit = $(_clickedButton); _clickedButton = null; var buttonData = {'name':name, 'value':1}; if (submit.length > 0) { if (typeof submit.data('lightbox-ignore') !== 'undefined') { return true; } buttonData.name = submit.attr('name') || 'submit'; buttonData.value = submit.attr('value') || 1; } data.push(buttonData); // Special handlers // On submit behavior if ('string' === typeof $(form).data('lightboxOnsubmit')) { var ret = _evalCallback($(form).data('lightboxOnsubmit'), event, data); // return true or false to send that to the form // return null or anything else to continue to the ajax if (ret === false || ret === true) { return ret; } } // onclose behavior if ('string' === typeof $(form).data('lightboxOnclose')) { document.addEventListener('VuFind.lightbox.closed', function(event) { _evalCallback($(form).data('lightboxOnclose'), event); }, false); } // Loading _modalBody.prepend('<i class="fa fa-spinner fa-spin pull-right"></i>'); // Prevent multiple submission of submit button in lightbox if (submit.closest(_modal).length > 0) { submit.attr('disabled', 'disabled'); } // Store custom title _lightboxTitle = submit.data('lightboxTitle') || $(form).data('lightboxTitle') || ''; // Get Lightbox content ajax({ url: $(form).attr('action') || _currentUrl, method: $(form).attr('method') || 'GET', data: data }); VuFind.modal('show'); return false; }; // Public: Attach listeners to the page var bind = function(target) { if ('undefined' === typeof target) { target = document; } $(target).find('a[data-lightbox]') .unbind('click', _constrainLink) .on('click', _constrainLink); $(target).find('form[data-lightbox]') .unbind('submit', _formSubmit) .on('submit', _formSubmit); // Handle submit buttons attached to a form as well as those in a form. Store // information about which button was clicked here as checking focused button // doesn't work on all browsers and platforms. $('form[data-lightbox] [type=submit]').click(_storeClickedStatus); }; var reset = function() { _html(VuFind.translate('loading') + '...'); _originalUrl = false; _currentUrl = false; _lightboxTitle = ''; }; var init = function() { _modal = $('#modal'); _modalBody = _modal.find('.modal-body'); _modal.on('hide.bs.modal', function() { if (VuFind.lightbox.refreshOnClose) { _refreshPage(); } _emit('VuFind.lightbox.closing'); }); _modal.on('hidden.bs.modal', function() { VuFind.lightbox.reset(); _emit('VuFind.lightbox.closed'); }); VuFind.modal = function(cmd) { _modal.modal(cmd); }; bind(); }; // Reveal return { // Properties refreshOnClose: refreshOnClose, // Methods ajax: ajax, alert: showAlert, bind: bind, flashMessage: flashMessage, reload: reload, // Reset reset: reset, // Init init: init }; });