/*global AjaxRequestQueue, VuFind */ VuFind.register('itemStatuses', function ItemStatuses() { var _checkItemHandlers = {}; var _handlerUrls = {}; function formatCallnumbers(callnumber, callnumber_handler) { var cns = callnumber.split(',\t'); for (var i = 0; i < cns.length; i++) { // If the call number has a special delimiter, it indicates a prefix that // should be used for display but not for sorting/searching. var actualCallNumber = cns[i]; var displayCallNumber = cns[i]; var parts = cns[i].split('::::'); if (parts.length > 1) { displayCallNumber = parts[0] + " " + parts[1]; actualCallNumber = parts[1]; } cns[i] = callnumber_handler ? '' + displayCallNumber + '' : displayCallNumber; } return cns.join(',\t'); } function displayItemStatus(result, el) { el.querySelectorAll('.status').forEach((status) => { status.innerHTML = result.availability_message; }); el.querySelectorAll('.ajax-availability').forEach((ajaxAvailability) => { ajaxAvailability.classList.remove('ajax-availability'); ajaxAvailability.classList.remove('hidden'); }); let callnumAndLocations = el.querySelectorAll('.callnumAndLocation'); if (typeof(result.error) != 'undefined' && result.error.length > 0 ) { callnumAndLocations.forEach((callnumAndLocation) => { callnumAndLocation.innerHTML = result.error; callnumAndLocation.classList.add('text-danger'); }); el.querySelectorAll('.callnumber,.hideIfDetailed,.location').forEach((e) => { e.classList.add('hidden'); }); } else if (typeof(result.full_status) != 'undefined' && result.full_status.length > 0 && callnumAndLocations.length > 0 ) { // Full status mode is on -- display the HTML and hide extraneous junk: callnumAndLocations.forEach((callnumAndLocation) => { callnumAndLocation.innerHTML = VuFind.updateCspNonce(result.full_status); }); el.querySelectorAll('.callnumber,.hideIfDetailed,.location,.status').forEach((e) => { e.classList.add('hidden'); }); } else if (typeof(result.missing_data) !== 'undefined' && result.missing_data ) { // No data is available -- hide the entire status area: el.querySelectorAll('.callnumAndLocation,.status').forEach((e) => e.classList.add('hidden')); } else if (result.locationList) { // We have multiple locations -- build appropriate HTML and hide unwanted labels: el.querySelectorAll('.callnumber,.hideIfDetailed,.location').forEach((e) => e.classList.add('hidden')); var locationListHTML = ""; for (var x = 0; x < result.locationList.length; x++) { locationListHTML += '
'; if (result.locationList[x].availability) { locationListHTML += '' + VuFind.icon("status-available") + result.locationList[x].location + ' '; } else if (typeof(result.locationList[x].status_unknown) !== 'undefined' && result.locationList[x].status_unknown ) { if (result.locationList[x].location) { locationListHTML += '' + VuFind.icon("status-unknown") + result.locationList[x].location + ' '; } } else { locationListHTML += '' + VuFind.icon("status-unavailable") + result.locationList[x].location + ' '; } locationListHTML += '
'; locationListHTML += '
'; locationListHTML += (result.locationList[x].callnumbers) ? formatCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; locationListHTML += '
'; } el.querySelectorAll('.locationDetails').forEach((locationDetails) => { locationDetails.classList.remove('hidden'); locationDetails.innerHTML = locationListHTML; }); } else { // Default case -- load call number and location into appropriate containers: el.querySelectorAll('.callnumber').forEach((callnumber) => { callnumber.innerHTML = formatCallnumbers(result.callnumber, result.callnumber_handler) + '
'; }); el.querySelectorAll('.location').forEach((location) => { location.innerHTML = result.reserve === 'true' ? result.reserve_message : result.location; }); } el.classList.add('js-item-done'); el.classList.remove('js-item-pending'); } function itemStatusAjaxSuccess(items, response) { let idMap = {}; // make map of ids to element arrays items.forEach(function mapItemId(item) { if (typeof idMap[item.id] === "undefined") { idMap[item.id] = []; } idMap[item.id].push(item.el); }); // display data response.json().then((body) => { body.data.statuses.forEach(function displayItemStatusResponse(status) { if (typeof idMap[status.id] === "undefined") { return; } idMap[status.id].forEach((el) => displayItemStatus(status, el)); }); VuFind.emit("item-status-done"); }); } function itemStatusAjaxFailure(items, response, textStatus) { if ( textStatus === "error" || textStatus === "abort" ) { VuFind.emit("item-status-done"); return; } response.json().then((body) => { // display the error message on each of the ajax status place holder items.forEach(function displayItemStatusFailure(item) { item.el.querySelectorAll(".callnumAndLocation").forEach((callNumAndLocation) => { callNumAndLocation.classList.add("text-danger"); callNumAndLocation.innerHTML = ""; callNumAndLocation.classList.remove("hidden"); callNumAndLocation.innerHTML = typeof body.data === "string" ? body.data : VuFind.translate("error_occurred"); }); }); }).finally(() => { VuFind.emit("item-status-done"); }); } function getStatusUrl(handlerName) { if (_handlerUrls[handlerName] !== undefined) { return _handlerUrls[handlerName]; } return "/AJAX/JSON?method=getItemStatuses"; } function getItemStatusPromise({ handlerName = "ils", acceptType = "application/json", method = "POST", } = {}) { return function runFetchItem(items) { let body = new URLSearchParams(); items.forEach((item) => { body.append("id[]", item.id); }); body.append("sid", VuFind.getCurrentSearchId()); return fetch( VuFind.path + getStatusUrl(handlerName), { method: method, headers: { 'Accept': acceptType, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: body } ); }; } function makeItemStatusQueue({ handlerName = "ils", delay = 200, } = {}) { return new AjaxRequestQueue({ run: getItemStatusPromise({handlerName: handlerName}), success: itemStatusAjaxSuccess, failure: itemStatusAjaxFailure, delay, }); } function checkItemStatus(el) { const hiddenIdEl = el.querySelector(".hiddenId"); if ( hiddenIdEl === null || el.classList.contains("js-item-pending") || el.classList.contains("js-item-done") ) { return; } // update element to reflect lookup el.classList.add("js-item-pending"); el.classList.remove("hidden"); const callnumAndLocationEl = el.querySelector(".callnumAndLocation"); if (callnumAndLocationEl) { callnumAndLocationEl.classList.remove("hidden"); } el.querySelectorAll(".callnumAndLocation .ajax-availability").forEach( (ajaxEl) => ajaxEl.classList.remove("hidden") ); const statusEl = el.querySelector(".status"); if (statusEl) { statusEl.classList.remove("hidden"); } // get proper handler let handlerName = "ils"; if (el.dataset.handlerName) { handlerName = el.dataset.handlerName; } else { const handlerNameEl = el.querySelector(".handler-name"); if (handlerNameEl !== null) { handlerName = handlerNameEl.value; } } // queue the element into the queue let payload = { el, id: hiddenIdEl.value }; if (VuFind.config.get('item-status:load-batch-wise', true)) { _checkItemHandlers[handlerName].add(payload); } else { let runFunc = getItemStatusPromise({handlerName: handlerName}); runFunc([payload]) .then((...res) => itemStatusAjaxSuccess([payload], ...res)) .catch((...error) => { console.error(...error); itemStatusAjaxFailure([payload], ...error); }); } } function checkAllItemStatuses(container = document) { const records = container.querySelectorAll(".ajaxItem"); if (records.length === 0) { VuFind.emit("item-status-done"); return; } records.forEach(checkItemStatus); } function updateContainer(params) { let container = params.container; if (VuFind.isPrinting() || !(VuFind.config.get('item-status:load-observable-only', true))) { checkAllItemStatuses(container); } else { VuFind.observerManager.createIntersectionObserver( 'itemStatuses', checkItemStatus, container.querySelectorAll('.ajaxItem') ); } } function addHandler(handlerName, handlerUrl) { _checkItemHandlers[handlerName] = makeItemStatusQueue({handlerName: handlerName}); _handlerUrls[handlerName] = handlerUrl; } function init() { _checkItemHandlers = { ils: makeItemStatusQueue() }; addHandler("overdrive", "/Overdrive/getStatus"); updateContainer({container: document}); VuFind.listen('results-init', updateContainer); } return { init: init, addHandler: addHandler, check: checkAllItemStatuses, checkRecord: checkItemStatus }; });