OpenXE/classes/Widgets/SuperSearch/www/js/supersearch.js

643 lines
22 KiB
JavaScript
Raw Normal View History

2021-05-21 08:49:41 +02:00
var SuperSearch = (function ($) {
'use strict';
var me = {
config: {
inputBuffer: 300 // in milliseconds
},
storage: {
$input: null,
$overlay: null,
$details: null,
$results: null,
$lastUpdate: null,
debounceBuffer: null,
hasResults: false,
isOpen: false
},
init: function () {
me.storage.$input = $('#supersearch-input');
if (me.storage.$input.length === 0) {
return;
}
me.registerEvents();
},
registerEvents: function () {
me.storage.$input.on('keyup.SuperSearch', me.onKeyUpSearchInput);
// Overlay anzeigen bei Focus in das Such-Eingabefeld; nur wenn es schon mal geöffnet war
me.storage.$input.on('focus.SuperSearch', me.onFocusSearchInput);
// Overlay mit ESC schließen
$(document).bind('keydown', function(e) {
if (me.storage.$overlay === null) {
return;
}
if (me.storage.isOpen !== true) {
return;
}
// ESC
if (e.keyCode === 27) {
me.hideOverlay();
}
});
},
/**
* @return {jQuery}
*/
getOverlay: function () {
if (typeof me.storage.$overlay === 'undefined' || me.storage.$overlay === null) {
me.storage.$overlay = me.createOverlay();
me.storage.$details = me.storage.$overlay.find('section.detail');
me.storage.$results = me.storage.$overlay.find('section.result');
me.storage.$lastUpdate = me.storage.$overlay.find('section.last-update');
}
return me.storage.$overlay;
},
showOverlay: function () {
var $overlay = me.getOverlay();
$overlay.show();
me.storage.isOpen = true;
me.showDetails();
},
hideOverlay: function () {
me.getOverlay().hide();
me.storage.isOpen = false;
},
/**
* @return {jQuery}
*/
createOverlay: function () {
var overlaySelector = '#supersearch-overlay';
if ($(overlaySelector).length > 0) {
return $(overlaySelector);
}
var overlayTemplate =
'<span id="supersearch-icon-close" class="icon icon-close"></span>' +
'<div class="result-wrapper">' +
'<section class="empty-message">Keine Suchergebnisse gefunden</section>' +
'<section class="error-message"></section>' +
'<section class="result"></section>' +
'<section class="last-update"></section>' +
'</div>' +
'<div class="detail-wrapper">' +
'<section class="detail"></section>' +
'</div>';
var overlayIdAttr = overlaySelector.substr(1);
var $overlay = $('<div>').attr('id', overlayIdAttr).addClass('supersearch-overlay').html(overlayTemplate);
$overlay.off('click.SuperSearch', '#supersearch-icon-close');
$overlay.on('click.SuperSearch', '#supersearch-icon-close', function (event) {
event.preventDefault();
me.hideOverlay();
});
$overlay.hide();
$overlay.appendTo('#header');
me.storage.isOpen = false;
return $overlay;
},
/**
* @param {Event} event
*/
onKeyUpSearchInput: function (event) {
event.preventDefault();
var controlKeyCodes = [
9, // Tab
13, // Enter
16, // Shift
17, // Strg
18, // Alt
20, // Caps lock
27, // ESC
37, // Cursor Left
38, // Cursor Up
39, // Cursor Right
40 // Cursor Down
];
if ($.inArray(event.keyCode, controlKeyCodes) !== -1) {
return;
}
var that = this;
me.debounce(function () {
var searchQuery = $(that).val();
me.fetchSearchResults(searchQuery).then(me.renderSearchResults);
}, me.config.inputBuffer);
},
/**
* Overlay anzeigen bei Focus in das Such-Eingabefeld; nur wenn es schon mal geöffnet war
*
* @param {Event} event
*/
onFocusSearchInput: function (event) {
event.preventDefault();
if (me.storage.$overlay === null) {
return;
}
if (me.storage.hasResults === false) {
return;
}
me.showOverlay();
},
/**
* @param {string} searchQuery
*
* @return {jqXHR}
*/
fetchSearchResults: function (searchQuery) {
if (typeof searchQuery !== 'string') {
searchQuery = '';
}
return $.ajax({
url: 'index.php?module=supersearch&action=ajax&cmd=search',
method: 'post',
dataType: 'json',
data: {
search_query: searchQuery
},
error: function (jqXHR, textStatus, errorThrown) {
var errorMessage = 'SuperSearch - Unbekannter Fehler #31: ' + errorThrown;
// PHP-Skript hat Fehler geliefert (z.b. 404)
if (textStatus === 'error') {
errorMessage = 'SuperSearch - Unbekannter Server-Fehler beim Laden der Such-Ergebnisse: ';
errorMessage += errorThrown;
}
// PHP-Skript liefert JSON-Error-Response
if (jqXHR.hasOwnProperty('responseJSON') && jqXHR.responseJSON.hasOwnProperty('error')) {
errorMessage = 'SuperSearch - Server-Fehler beim Laden der Such-Ergebnisse: ';
errorMessage += jqXHR.responseJSON.error;
if (jqXHR.responseJSON.hasOwnProperty('data') &&
jqXHR.responseJSON.data === 'index-empty') {
me.showErrorMessage('Fehler: ' + jqXHR.responseJSON.error);
return;
}
}
alert(errorMessage);
}
});
},
/**
* @param {Array} rawResult
*/
renderSearchResults: function (rawResult) {
var $overlay = me.getOverlay();
var $resultContainer = $overlay.find('section.result');
$resultContainer.html('');
if (rawResult.length === 0 || !rawResult.hasOwnProperty('data')) {
$resultContainer.html('Fehler: Suche hat fehlerhaftes Ergebnis geliefert.');
me.storage.hasResults = false;
me.hideResults();
return;
}
// Overlay ausblenden, wenn Suchbegriff zu kurz
if (rawResult.data === null) {
me.storage.hasResults = false;
me.hideOverlay();
return;
}
// Anzeigen wann der Such-Index das letzte Mal aktualisiert wurde
if (rawResult.data.hasOwnProperty('last_index_update_formatted')) {
if (rawResult.data.last_index_update_formatted !== null) {
var lastIndexUpdate = rawResult.data.last_index_update_formatted;
me.storage.$lastUpdate.text('Such-Index vom ' + lastIndexUpdate).show();
} else {
me.storage.$lastUpdate.text('').hide();
}
}
var resultCount = rawResult.data.count;
var searchResults = rawResult.data.results;
if (resultCount === 0) {
me.storage.hasResults = false;
me.showEmptyResults();
return;
}
me.storage.$details.html('');
Object.keys(searchResults).forEach(function (group) {
var groupResult = searchResults[group];
var $groupHtml = me.buildGroupResult(groupResult.key, groupResult.title, groupResult.items);
$resultContainer.append($groupHtml);
});
me.storage.hasResults = true;
me.showResults();
me.showOverlay();
},
/**
* @param {string} groupKey
* @param {string} groupTitle
* @param {array} items
*
* @return {jQuery}
*/
buildGroupResult: function (groupKey, groupTitle, items) {
if (items.length === 0) {
return;
}
if (typeof groupTitle === 'undefined') {
groupTitle = 'Ergebnis';
}
var $resultWrapper = $('<div class="result-group">');
var $resultList = $('<ul class="result-list">');
var $listHead = $('<li class="result-head">').html(groupTitle);
$resultList.append($listHead);
items.forEach(function (item) {
item.group = groupKey;
var itemType = item.type !== null ? item.type : 'default';
var $listItem;
switch (itemType) {
case 'default':
default:
$listItem = me.buildDefaultItemResult(item);
break;
}
$resultList.append($listItem);
});
$resultWrapper.append($resultList);
return $resultWrapper;
},
/**
* @param {object} item
*
* @return {jQuery}
*/
buildDefaultItemResult: function (item) {
var hasSubtitle = item.hasOwnProperty('subtitle') && typeof item.subtitle === 'string';
var hasAdditionalInfos =
item.hasOwnProperty('additionalInfos') &&
typeof item.additionalInfos === 'object' &&
item.additionalInfos !== null;
var mainTitle = '<span class="title-main">' + item.title + '</span>';
var subTitle = hasSubtitle ? '<span class="title-sub">' + item.subtitle + '</span>' : '';
var titleString = '<span class="title">' + mainTitle + subTitle + '</span>';
if (hasAdditionalInfos) {
titleString += '<span class="caption">';
$.each(item.additionalInfos, function (index, additionalInfo) {
titleString += '<span class="additional">' + additionalInfo + '</span>';
});
titleString += '</span>';
}
var $listItem = $('<li>').addClass('result-item');
var $itemLink = $('<a>').attr('href', item.link).html(titleString);
$itemLink.appendTo($listItem);
$itemLink.on('click', function (e) {
e.preventDefault();
me.renderItemDetails(item);
});
return $listItem;
},
/**
* Rendert Ergebnisdetails
*
* @param {object} item
*/
renderItemDetails: function (item) {
// Per AJAX ausführliche Inhalte nachladen
me.fetchItemDetailsDynamicContent(item).then(
function (data) {
me.renderItemDetailsDynamicContent(data, item);
},
function (jqXhr) {
var error =
typeof jqXhr.responseJSON !== 'undefined' &&
typeof jqXhr.responseJSON.error !== 'undefined'
? jqXhr.responseJSON.error
: 'Unbekannter Fehler';
alert('Fehler beim Laden der Detail-Informationen: ' + error);
}
);
},
/**
* @param {object} detailResult
* @param {object} listItem Originales Item-Objekt aus Suchergebnis-Liste
*
* @return {void}
*/
renderItemDetailsDynamicContent: function (detailResult, listItem) {
if (!detailResult.hasOwnProperty('data') || detailResult.data === false) {
// Es wurde kein Detail-Result gefunden
// Link aus Suchergebnis-Item aufrufen
me.hideDetails();
window.location.href = listItem.link;
return;
}
var detail = detailResult.data;
var $details = me.storage.$details;
// Überschrift rendern
var $headline = $('<h1>').html(detail.title);
$details.html('').append($headline);
// Attachments (z.B. Buttons) rendern
if (detail.hasOwnProperty('attachments')) {
var $attachments = me.generateDetailAttachments(detail.attachments);
$details.append($attachments);
}
me.showDetails();
},
/**
* @param {object} item
*
* @return {jqXHR}
*/
fetchItemDetailsDynamicContent: function (item) {
return $.ajax({
url: 'index.php?module=supersearch&action=ajax&cmd=detail',
method: 'post',
dataType: 'json',
data: {
detail_group: item.group,
detail_identifier: item.identifier
},
error: function (jqXHR, textStatus, errorThrown) {
var errorMessage = 'SuperSearch - Unbekannter Fehler #32: ' + errorThrown;
// PHP-Skript hat Fehler geliefert (z.b. 404)
if (textStatus === 'error') {
errorMessage = 'SuperSearch - Unbekannter Server-Fehler beim Laden des Detail-Ergebnisses: ';
errorMessage += errorThrown;
}
// PHP-Skript liefer JSON-Error-Response
if (jqXHR.hasOwnProperty('responseJSON') && jqXHR.responseJSON.hasOwnProperty('error')) {
errorMessage = 'SuperSearch - Server-Fehler beim Laden des Detail-Ergebnisses: ';
errorMessage += jqXHR.responseJSON.error;
}
alert(errorMessage);
}
});
},
/**
* @param {Array} attachments
*
* @return {jQuery} jQuery-Element
*/
generateDetailAttachments: function (attachments) {
var $attachments = $('<div>');
$.each(attachments, function (index, attachment) {
if (!attachment.hasOwnProperty('type')) {
console.error('Attachment ungültig. "type"-Property fehlt.');
return;
}
if (!attachment.hasOwnProperty('data')) {
console.error('Attachment ungültig. "data"-Property fehlt.');
return;
}
if (attachment.type === 'button_block') {
var $buttonBlock = me.generateDetailAttachmentTypeButtonBlock(attachment.data);
$attachments.append($buttonBlock);
}
if (attachment.type === 'content_static') {
var $contentStatic = me.generateDetailAttachmentTypeStaticContent(attachment.data);
$attachments.append($contentStatic);
}
if (attachment.type === 'content_dynamic') {
var $contentDynamic = me.generateDetailAttachmentTypeDynamicContent(attachment.data);
$attachments.append($contentDynamic);
}
});
return $attachments;
},
/**
* @param {Array} items
*
* @return {jQuery} jQuery-Element
*/
generateDetailAttachmentTypeButtonBlock: function (items) {
var $buttonBlock = $('<div>');
$.each(items, function (index, item) {
var $button = $('<a>').text(item.title).addClass('button');
if (item.hasOwnProperty('attributes')) {
// Button-Attribute verarbeiten
$.each(item.attributes, function (attrName, attrValue) {
if (attrName === 'class') {
$button.addClass(attrValue);
return;
}
if (attrName === 'data-icon') {
var iconUrl = '';
switch (attrValue) {
case 'help':
iconUrl = './themes/new/images/help.svg';
break;
case 'settings':
iconUrl = './themes/new/images/settings.svg';
break;
}
if (iconUrl !== '') {
$button.addClass('icon');
$button.addClass('icon-' + attrValue);
var $iconElem = $('<img alt="Handbuch">').attr('src', iconUrl);
var $iconWrapper = $('<span class="icon">').append($iconElem);
$button.prepend($iconWrapper);
}
}
$button.attr(attrName, attrValue);
});
}
$button.appendTo($buttonBlock);
});
return $buttonBlock;
},
/**
* @param {Object} data
*
* @return {jQuery} jQuery-Element
*/
generateDetailAttachmentTypeStaticContent: function (data) {
return $('<p>').html(data.content);
},
/**
* @param {Object} data
*
* @return {jQuery} jQuery-Element
*/
generateDetailAttachmentTypeDynamicContent: function (data) {
var $dynamicContent = $('<div>').addClass('minidetail');
if (data.hasOwnProperty('url') && data.url !== null) {
me.fetchMiniDetailContent(data.url, data.params)
.then(
function (htmlContent) {
$dynamicContent.html(htmlContent);
me.storage.$details.append($dynamicContent);
},
function (jqXhr) {
var message = 'Fehler beim Laden der Mini-Details: ';
if (jqXhr.hasOwnProperty('responseJSON') && jqXhr.responseJSON.hasOwnProperty('error')) {
message += jqXhr.responseJSON.error;
} else {
message += jqXhr.status + ' ' + jqXhr.statusText;
}
$('<div class="error"></div>').text(message).appendTo(me.storage.$details);
}
);
}
return $dynamicContent;
},
/**
* @param {string} miniDetailUrl
* @param {Object} miniDetailParams Zusätzliche POST-Parameter
*
* @return {jqXHR}
*/
fetchMiniDetailContent: function (miniDetailUrl, miniDetailParams) {
if (miniDetailUrl.substr(0, 10) !== 'index.php?') {
alert('Mini-Detail-URL ist ungültig: ' + miniDetailUrl);
throw 'Mini-Detail-URL ist ungültig: ' + miniDetailUrl;
}
if (typeof miniDetailParams !== 'object') {
miniDetailParams = {};
}
return $.ajax({
url: miniDetailUrl,
data: miniDetailParams,
method: 'post',
dataType: 'html'
});
},
/**
* Suchergebnisse einblenden
*/
showResults: function () {
me.getOverlay().addClass('has-result');
me.getOverlay().find('section.empty-message').hide();
me.getOverlay().find('section.error-message').hide();
},
/**
* Suchergebnisse ausblenden
*/
hideResults: function () {
me.getOverlay().removeClass('has-result');
me.getOverlay().find('section.empty-message').hide();
me.getOverlay().find('section.error-message').hide();
me.getOverlay().find('section.last-update').hide();
},
/**
* Details einblenden
*/
showDetails: function () {
me.getOverlay().addClass('has-detail');
me.getOverlay().find('.detail-wrapper').scrollTop(0);
},
/**
* Details einblenden
*/
hideDetails: function () {
me.getOverlay().removeClass('has-detail');
},
/**
* Hinweis anzeigen das keine Ergebnisse gefunden wurden
*/
showEmptyResults: function () {
me.showOverlay();
me.hideDetails();
me.getOverlay().removeClass('has-result');
me.getOverlay().find('section.empty-message').show();
me.getOverlay().find('section.error-message').hide();
},
/**
* @param {string} errorMessage
*/
showErrorMessage: function (errorMessage) {
me.showOverlay();
me.hideDetails();
me.getOverlay().find('section.empty-message').hide();
me.getOverlay().find('section.error-message').html(errorMessage).show();
},
/**
* Puffer-Funktion um Events erst nach einer bestimmten Zeit auszuführen
*
* @param {function} callback
* @param {number} delay
* @param {object|null} contextParam
*/
debounce: function (callback, delay, contextParam) {
var context = typeof contextParam !== 'undefined' && contextParam !== null ? contextParam : this;
var args = arguments;
window.clearTimeout(me.storage.debounceBuffer);
me.storage.debounceBuffer = window.setTimeout(function () {
callback.apply(context, args);
}, delay || 250);
}
};
return {
init: me.init
};
})(jQuery);
$(function () {
SuperSearch.init();
});