diff --git a/module/finc/src/finc/RecordTab/HierarchyTree.php b/module/finc/src/finc/RecordTab/HierarchyTree.php index 315b773894fe50aa0da87b36fdf6c63318eb1ae9..56133533858816cab39174a1e310b1cf762716c5 100644 --- a/module/finc/src/finc/RecordTab/HierarchyTree.php +++ b/module/finc/src/finc/RecordTab/HierarchyTree.php @@ -67,4 +67,15 @@ class HierarchyTree extends \VuFind\RecordTab\HierarchyTree } return false; } + + /** + * Can this tab be loaded via AJAX? + * + * @return bool + */ + public function supportsAjax() + { + // No, special width adjustment needed. + return true; + } } diff --git a/themes/finc-accessibility/js/cart.js b/themes/finc-accessibility/js/cart.js new file mode 100644 index 0000000000000000000000000000000000000000..9fc90e6810a59fd411c5861d382083f1672054af --- /dev/null +++ b/themes/finc-accessibility/js/cart.js @@ -0,0 +1,287 @@ +/*global Cookies, VuFind */ +/*exported cartFormHandler */ + +VuFind.register('cart', function Cart() { + var _COOKIE = 'vufind_cart'; + var _COOKIE_SOURCES = 'vufind_cart_src'; + var _COOKIE_DELIM = "\t"; + var _COOKIE_DOMAIN = false; + var _COOKIE_PATH = '/'; + + function setDomain(domain) { + _COOKIE_DOMAIN = domain; + } + + function setCookiePath(path) { + _COOKIE_PATH = path; + } + + function _uniqueArray(op) { + var ret = []; + for (var i = 0; i < op.length; i++) { + if (ret.indexOf(op[i]) < 0) { + ret.push(op[i]); + } + } + return ret; + } + + function _getItems() { + var items = Cookies.getItem(_COOKIE); + if (items) { + return items.split(_COOKIE_DELIM); + } + return []; + } + function _getSources() { + var items = Cookies.getItem(_COOKIE_SOURCES); + if (items) { + return items.split(_COOKIE_DELIM); + } + return []; + } + function getFullItems() { + var items = _getItems(); + var sources = _getSources(); + var full = []; + if (items.length === 0) { + return []; + } + for (var i = items.length; i--;) { + full[full.length] = sources[items[i].charCodeAt(0) - 65] + '|' + items[i].substr(1); + } + return full; + } + + function hasItem(id, _source) { + var source = _source || VuFind.defaultSearchBackend; + return _getItems().indexOf(String.fromCharCode(65 + _getSources().indexOf(source)) + id) > -1; + } + + function _refreshToggles() { + var $toggleBtns = $('.btn-bookbag-toggle'); + if ($toggleBtns.length > 0) { + $toggleBtns.each(function cartIdEach() { + var $this = $(this); + $this.find('.cart-add,.cart-remove').addClass('hidden'); + if (hasItem($this.data('cart-id'), $this.data('cart-source'))) { + $this.find('.cart-remove').removeClass('hidden'); + } else { + $this.find('.cart-add').removeClass('hidden'); + } + }); + } + } + + function updateCount() { + var items = VuFind.cart.getFullItems(); + $('#cartItems strong').html(items.length); + if (items.length === parseInt(VuFind.translate('bookbagMax'), 10)) { + $('#cartItems .full').removeClass('hidden'); + } else { + $('#cartItems .full').addClass('hidden'); + } + _refreshToggles(); + } + + function addItem(id, _source) { + var source = _source || VuFind.defaultSearchBackend; + var cartItems = _getItems(); + var cartSources = _getSources(); + if (cartItems.length >= parseInt(VuFind.translate('bookbagMax'), 10)) { + return false; + } + var sIndex = cartSources.indexOf(source); + if (sIndex < 0) { + // Add source to source cookie + cartItems[cartItems.length] = String.fromCharCode(65 + cartSources.length) + id; + cartSources[cartSources.length] = source; + Cookies.setItem(_COOKIE_SOURCES, cartSources.join(_COOKIE_DELIM), false, _COOKIE_PATH, _COOKIE_DOMAIN); + } else { + cartItems[cartItems.length] = String.fromCharCode(65 + sIndex) + id; + } + Cookies.setItem(_COOKIE, _uniqueArray(cartItems).join(_COOKIE_DELIM), false, _COOKIE_PATH, _COOKIE_DOMAIN); + updateCount(); + return true; + } + function removeItem(id, source) { + var cartItems = _getItems(); + var cartSources = _getSources(); + // Find + var cartIndex = cartItems.indexOf(String.fromCharCode(65 + cartSources.indexOf(source)) + id); + if (cartIndex > -1) { + var sourceIndex = cartItems[cartIndex].charCodeAt(0) - 65; + var saveSource = false; + for (var i = cartItems.length; i--;) { + if (i === cartIndex) { + continue; + } + // If this source is shared by another, keep it + if (cartItems[i].charCodeAt(0) - 65 === sourceIndex) { + saveSource = true; + break; + } + } + cartItems.splice(cartIndex, 1); + // Remove unused sources + if (!saveSource) { + var oldSources = cartSources.slice(0); + cartSources.splice(sourceIndex, 1); + // Adjust source index characters + for (var j = cartItems.length; j--;) { + var si = cartItems[j].charCodeAt(0) - 65; + var ni = cartSources.indexOf(oldSources[si]); + cartItems[j] = String.fromCharCode(65 + ni) + cartItems[j].substring(1); + } + } + if (cartItems.length > 0) { + Cookies.setItem(_COOKIE, _uniqueArray(cartItems).join(_COOKIE_DELIM), false, _COOKIE_PATH, _COOKIE_DOMAIN); + Cookies.setItem(_COOKIE_SOURCES, _uniqueArray(cartSources).join(_COOKIE_DELIM), false, _COOKIE_PATH, _COOKIE_DOMAIN); + } else { + Cookies.removeItem(_COOKIE, _COOKIE_PATH, _COOKIE_DOMAIN); + Cookies.removeItem(_COOKIE_SOURCES, _COOKIE_PATH, _COOKIE_DOMAIN); + } + updateCount(); + return true; + } + return false; + } + + var _cartNotificationTimeout = false; + function _registerUpdate(_form) { + var $form = typeof _form === 'undefined' + ? $('form[name="bulkActionForm"]') + : $(_form); + $("#updateCart, #bottom_updateCart").unbind('click').click(function cartUpdate() { + var elId = this.id; + var selected = []; + var addToSelected = function processCartFormValues() { + if (-1 === selected.indexOf(this.value)) { + selected.push(this.value); + } + }; + var selectedInForm = $form.find('input[name="ids[]"]:checked'); + var selectedFormAttr = $('input[form="' + $form.attr('id') + '"][name="ids[]"]:checked'); + $(selectedInForm).each(addToSelected); + $(selectedFormAttr).each(addToSelected); + if (selected.length > 0) { + var msg = ""; + var orig = getFullItems(); + $(selected).each(function cartCheckedItemsAdd() { + var data = this.split('|'); + addItem(data[1], data[0]); + }); + var updated = getFullItems(); + var added = updated.length - orig.length; + var inCart = selected.length - added; + msg += VuFind.translate('itemsAddBag', {'%%count%%': added}); + if (updated.length >= parseInt(VuFind.translate('bookbagMax'), 10)) { + msg += "<br/>" + VuFind.translate('bookbagFull'); + } + if (inCart > 0 && orig.length > 0) { + msg += "<br/>" + VuFind.translate('itemsInBag', {'%%count%%': inCart}); + } + $('#' + elId).data('bs.popover').options.content = msg; + $('#cartItems strong').html(updated.length); + } else { + $('#' + elId).data('bs.popover').options.content = VuFind.translate('bulk_noitems_advice'); + } + $('#' + elId).popover('show'); + if (_cartNotificationTimeout !== false) { + clearTimeout(_cartNotificationTimeout); + } + _cartNotificationTimeout = setTimeout(function notificationHide() { + $('#' + elId).popover('hide'); + }, 5000); + return false; + }); + } + + function _registerToggles() { + var $toggleBtns = $('.btn-bookbag-toggle'); + if ($toggleBtns.length > 0) { + $toggleBtns.each(function cartIdEach() { + var $this = $(this); + var currentId = $this.data('cart-id'); + var currentSource = $this.data('cart-source'); + $this.find('.correct').removeClass('correct hidden'); + $this.find('.cart-add').click(function cartAddClick(e) { + e.preventDefault(); + if (addItem(currentId, currentSource)) { + $this.find('.cart-add').addClass('hidden'); + /* #20374 set focus */ + $this.find('.cart-remove').removeClass('hidden').focus(); + /* #20374 set focus - END*/ + } else { + $this.popover({content: VuFind.translate('bookbagFull')}); + setTimeout(function recordCartFullHide() { + $this.popover('hide'); + }, 5000); + } + }); + $this.find('.cart-remove').click(function cartRemoveClick(e) { + e.preventDefault(); + removeItem(currentId, currentSource); + /* #20374 set focus */ + $this.find('.cart-add').removeClass('hidden').focus(); + /* #20374 set focus - END */ + $this.find('.cart-remove').addClass('hidden'); + }); + }); + } + } + + function init() { + // Record buttons + _registerToggles(); + // Search results + _registerUpdate(); + $("#updateCart, #bottom_updateCart").popover({ + content: '', + html: true, + trigger: 'manual', + placement: $(document.body).hasClass('rtl') ? 'left' : 'right' + }); + /* #18034 accessibility: inform screen reader about changes, eventually intregrate by PR into bootstrap - RL */ + var cart = document.getElementById("cartSummary"); + if (cart !== null && cart !== undefined) { + cart.setAttribute("aria-live", "polite"); + cart.setAttribute("aria-atomic", "true"); + } + /* #18034 - END */ + updateCount(); + } + + // Reveal + return { + // Methods + addItem: addItem, + getFullItems: getFullItems, + hasItem: hasItem, + removeItem: removeItem, + setCookiePath: setCookiePath, + setDomain: setDomain, + updateCount: updateCount, + // Init + init: init + }; +}); + +// Building an array and checking indexes prevents a race situation +// We want to prioritize empty over printing +function cartFormHandler(event, data) { + var keys = []; + for (var i in data) { + if (Object.prototype.hasOwnProperty.call(data, i)) { + keys.push(data[i].name); + } + } + if (keys.indexOf('ids[]') === -1) { + return null; + } + if (keys.indexOf('print') > -1) { + return true; + } +} + +document.addEventListener('VuFind.lightbox.closed', VuFind.cart.updateCount, false); diff --git a/themes/finc-accessibility/js/record.js b/themes/finc-accessibility/js/record.js index 9766867212c7524e3ed39a0a6eac7c250fc27d76..26da27f6f5f484bf429a4eab12162fc12bf15de8 100644 --- a/themes/finc-accessibility/js/record.js +++ b/themes/finc-accessibility/js/record.js @@ -274,6 +274,8 @@ function recordDocReady() { // accessibility: mark tab controls as selected $top.find('.record-tab.active').find('a').attr('aria-selected', 'false'); + // accessibility: set information about connection between tab control and tab content + $(this).attr('aria-controls', tabid); $('#' + tabid + '-tabselector').attr('aria-selected', 'true').attr('aria-controls', tabid); // accessibility: set aria-hidden for content panes diff --git a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-de.min.js b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-de.min.js index 0b23dca9f44e502f5c348a865c348630a554e075..9462f550c7f0d71ac6de52ed7987fe5831e5d3fe 100644 --- a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-de.min.js +++ b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-de.min.js @@ -327,4 +327,9 @@ $(document).on('keyup', '.dropdown-abort', function(e) { toggle.dropdown("toggle"); } } +}); + +/* restore focus after closing of lightbox */ +$(document).on('click', '.bulkActionButtons input', function(e) { + VuFind.lightbox.setOrigin(e.target); }); \ No newline at end of file diff --git a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js index 11bef8e1ba5fde3b9db5fe1c6b18a7239e3b23a1..7717b77ecd987717df53a4ae4de3c44e81629e1d 100644 --- a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js +++ b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js @@ -327,4 +327,9 @@ $(document).on('keyup', '.dropdown-abort', function(e) { toggle.dropdown("toggle"); } } +}); + +/* restore focus after closing of lightbox */ +$(document).on('click', '.bulkActionButtons input', function(e) { + VuFind.lightbox.setOrigin(e.target); }); \ No newline at end of file diff --git a/themes/finc/js/cart-finc.js b/themes/finc/js/cart-finc.js deleted file mode 100644 index 0fbaf1015f44cd429d87a2f1db9a0f5745c5c320..0000000000000000000000000000000000000000 --- a/themes/finc/js/cart-finc.js +++ /dev/null @@ -1,8 +0,0 @@ -/* #18034 - inform screen reader about changes, eventually intregrate by PR into bootstrap cart.js - RL */ -$(document).ready(function() { - var cart = document.getElementById("cartSummary"); - if (cart != null) { - cart.setAttribute("aria-live", "polite"); - cart.setAttribute("aria-atomic", "true"); - } -}); diff --git a/themes/finc/templates/ajax/resolverLinks.phtml b/themes/finc/templates/ajax/resolverLinks.phtml index cdc5ac85facdc8c80928de7ad8b60646db73edb8..4b2e0a9ed2b4ee8050d643752941eb0adb76f175 100644 --- a/themes/finc/templates/ajax/resolverLinks.phtml +++ b/themes/finc/templates/ajax/resolverLinks.phtml @@ -20,7 +20,14 @@ <span class="last"></span> </div> <?php /* finc-specific change #7986 - END */ ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <br /> + <?= $this->externalLink( + $this->escapeHtmlAttr($link['href']), + $link['title'] ?? '', + [ + 'title' => $link['service_type'] ?? '', + 'class' => !empty($link['access']) ? 'access-' . $link['access'] : '' + ] + ) ?> <br /> <?php /* finc-specific change #5334 - CK */ ?> <small> <?= isset($link['coverage']) ? $this->escapeHtml($link['coverage']) : '' ?> diff --git a/themes/finc/templates/record/view.phtml b/themes/finc/templates/record/view.phtml index 3749c2121c6fc81e1f807f1b38c5e40014cb3a48..076ef6fb512970578a5aac9a2f531bdacdc6a6c8 100644 --- a/themes/finc/templates/record/view.phtml +++ b/themes/finc/templates/record/view.phtml @@ -71,7 +71,7 @@ <a href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav" <?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)): ?> data-background<?php endif ?> aria-selected="<?= $isActiveTab ? "true" : "false" ?>" - aria-controls="<?= $tabName ?>" + <?php if($isActiveTab): ?>aria-controls="<?= $tabName ?>"<?php endif; ?> id="<?= $tabName ?>-tabselector"> <?= $this->transEsc($desc) ?> <span class="sr-only load-tab-content"><?= $this->transEsc('load_tab_content_hint') ?></span></a> diff --git a/themes/finc/theme.config.php b/themes/finc/theme.config.php index c20700c8971b8c09e40fc3d0cd8e4d267cf4477d..652545a3d02d4d550bb1be2fc57cace7edd3cac8 100644 --- a/themes/finc/theme.config.php +++ b/themes/finc/theme.config.php @@ -5,8 +5,7 @@ return [ 'check_item_statuses.js', 'lightbox_form_cache.js', 'covers.js', - 'common-finc.js', - 'cart-finc.js' + 'common-finc.js' ], 'helpers' => [ 'aliases' => [