blob: 13904ec599186f4a47e820f6812047b92ccc7f0c [file] [log] [blame]
/*
* Initialize The JS frontend part and decorate
* the static HTML data.
*
* @author Nils Diewald, Helge Stallkamp, Uyen-Nhu Tran
*
* TODO: Create lazy loading of objects including
* - obj.hint()
* - obj.alertify()
* - obj.session()
* - obj.tutorial()
* - obj.vc() // toggle
* - obj.matchCreate() (using webpack)
* - obj.koral() (show result, parse for errors ...)
* - obj.alignment() // toggle
*
* TODO: After upgrading to ES 6
* - use optional chaining operator (for example see below)
*/
"use strict";
define([
'match',
'hint',
'vc',
'tutorial',
'lib/domReady',
'vc/array',
'lib/alertify',
'session',
'state/manager',
'selectMenu',
'panel/result',
'panel/query',
'panel/pagination',
'tour/tours',
'plugin/server',
'pipe',
'api',
'mailToChiffre',
'util',
'state'
], function (matchClass,
hintClass,
vcClass,
tutClass,
domReady,
vcArray,
alertifyClass,
sessionClass,
stateManagerClass,
selectMenuClass,
resultPanelClass,
queryPanelClass,
paginationPanelClass,
tourClass,
pluginClass,
pipeClass) {
// Override KorAP.log
window.alertify = alertifyClass;
KorAP.log = function (code, msg, src, type) {
if (src) {
msg += '<code class="src">'+src+'</code>';
};
// Use alertify to log errors
alertifyClass.log(
(code === 0 ? '' : code + ': ') +
msg,
(type ? type : 'error'),
10000
);
};
KorAP.vc = vcClass.create(vcArray);
domReady(function (event) {
const d = document;
// Set base URL
KorAP.URL = d.body.getAttribute('data-korap-url') || "";
// Create suffix if KorAP is run in a subfolder
KorAP.session = sessionClass.create(
KorAP.URL.length > 0 ? 'kalamarJS-' + KorAP.URL.slugify() : 'kalamarJS'
);
// Get koralQuery response
const kqe = d.getElementById('koralQuery');
if (kqe !== null) {
KorAP.koralQuery = JSON.parse(kqe.getAttribute('data-koralquery') || "");
};
let gt = document.getElementsByClassName('link-guided-tour');
if (gt.length != null){
for(let j = 0; j < gt.length; j++){
gt[j].setAttribute('href', '#');
gt[j].addEventListener('click', function(){
tourClass.gTstartSearch().start();
// Close the burger menu by simulating a click on the burger icon
const burgerIcon = document.querySelector('.burger-icon');
if (isBurgerMenuOpen) {
burgerIcon.click();
}
});
}
KorAP.tourshowR = function(){
tourClass.gTshowResults().start();
};
}
var obj = {};
// What should be visible in the beginning?
var show = KorAP.session.get('show') || {};
KorAP.Panel = KorAP.Panel || {}
/**
* Release notifications
*/
d.querySelectorAll('#notifications div.notify').forEach(
function(e) {
let msg = e.textContent;
let src = e.getAttribute('data-src');
if (src) {
msg += '<code class="src">'+src+'</code>';
};
let type = e.getAttribute('data-type') || "error";
alertifyClass.log(msg, type, 10000);
}
);
// Responsive navbar: hide and show burger menu
const burgerIcon = document.querySelector('.burger-icon');
let isBurgerMenuOpen = false;
if (burgerIcon) {
burgerIcon.addEventListener('click', function() {
const navbar = document.querySelector('.navbar');
navbar.classList.toggle('show');
isBurgerMenuOpen = !isBurgerMenuOpen;
if (isBurgerMenuOpen) {
navbar.style.top = '0';
}
});
}
// Fallback solution for login dropdown visibility (if :focus-within is not supported)
document.addEventListener('DOMContentLoaded', function() {
const dropdown = document.querySelector('.dropdown');
const dropdownContent = document.querySelector('.dropdown-content');
dropdown.addEventListener('mouseenter', function() {
dropdownContent.style.display = 'block';
});
dropdown.addEventListener('mouseleave', function() {
// If no input inside the form is focused, then close dropdown content
if (!dropdown.contains(document.activeElement)) {
dropdownContent.style.display = 'none';
}
});
dropdownContent.addEventListener('focusin', function() {
dropdownContent.style.display = 'block';
});
dropdownContent.addEventListener('focusout', function(e) {
// If focus moved outside the dropdown content, then close it
if (!dropdownContent.contains(e.relatedTarget)) {
dropdownContent.style.display = 'none';
}
});
});
/**
* Replace Virtual Corpus field
*/
var vcname, vcchoose;
var input = d.getElementById('cq');
var vc = KorAP.vc;
// Add vc name object
if (input) {
input.style.display = 'none';
vcname = d.createElement('span');
vcname.setAttribute('id', 'vc-choose');
vcname.classList.add('select');
// Load virtual corpus object
// Supports "collection" for legacy reasons
if (KorAP.koralQuery !== undefined && (KorAP.koralQuery["collection"] || KorAP.koralQuery["corpus"])) {
try {
vc.fromJson(KorAP.koralQuery["collection"] || KorAP.koralQuery["corpus"]);
}
catch (e) {
KorAP.log(0,e);
}
};
vcchoose = vcname.addE('span');
vcchoose.addT(vc.getName());
if (vc.wasRewritten()) {
vcchoose.classList.add('rewritten');
};
input.parentNode.insertBefore(vcname, input);
};
/**
* Add actions to match entries
*/
var matchElements = d.querySelectorAll(
'#search > ol > li'
);
matchElements.forEach(function(e) {
// Define class for active elements
if (e.classList.contains('active')) {
if (e._match === undefined) {
// lazyLoad
matchClass.create(e).init();
};
}
// Define class for inactive elements
else {
e.addEventListener('click', function (e) {
if (this._match !== undefined)
this._match.open();
else {
// lazyLoad
matchClass.create(this).open();
};
// This would prevent the sidebar to go back
// e.halt();
});
e.addEventListener('keydown', function (e) {
var code = _codeFromEvent(e);
switch (code) {
case 32:
if (this._match !== undefined)
this._match.toggle();
else {
// lazyLoad
matchClass.create(this).open();
};
e.halt();
break;
};
});
};
});
// Function to toggle the shifted class on elements
function shiftContent() {
// Get elements to perform content shift when sidebar is active
const header = document.querySelector('header');
const main = document.querySelector('main');
const footer = document.querySelector('footer');
const results = document.querySelector('.found');
const aside = document.querySelector('aside');
if (aside && aside.classList.contains('active')) {
header.classList.add('shifted');
if (!results) {
main.classList.add('shifted');
}
footer.classList.add('shifted');
adjustHintPosition();
} else {
header.classList.remove('shifted');
main.classList.remove('shifted');
footer.classList.remove('shifted');
adjustHintPosition();
}
}
// Function to adjust the position of the annotation assistant bar (hint),
// when user types into the searchbar and clicks the sidebar (or anywhere
// outside the searchbar) afterwards
function adjustHintPosition() {
if (KorAP.Hint != undefined) {
KorAP.Hint.inputField().reposition();
KorAP.Hint.update();
};
};
// MutationObserver to detect when #hint is injected into the DOM
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
const hint = document.querySelector('#hint');
if (hint) {
shiftContent();
observer.disconnect();
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Add focus listener to aside
var aside = d.getElementsByTagName('aside')[0];
if (aside && aside.classList.contains('active') == false) {
// Horrible lock to deal with sidebar clicks
var asideClicked = false;
shiftContent();
// Make aside active on focus
aside.addEventListener('focus', function (e) {
this.classList.add('active');
shiftContent();
});
// Deactivate focus when clicking anywhere else
var body = d.getElementsByTagName('body')[0];
if (body !== null) {
body.addEventListener('click', function () {
if (!asideClicked) {
aside.classList.remove('active');
shiftContent();
} else {
asideClicked = false;
}
});
}
/* Stop click event on aside
* (to not trickle down to body)
*/
aside.addEventListener('click', function (e) {
asideClicked = true;
});
}
// Replace QL select menus with KorAP menus
var qlField = d.getElementById('ql-field');
if (qlField !== null) {
KorAP.QLmenu = selectMenuClass.create(
d.getElementById('ql-field').parentNode
).limit(10);
};
var resultInfo = d.getElementById('resultinfo');
/**
* Add result panel
*/
var resultPanel = resultPanelClass.create(show);
if (resultInfo != null) {
// Move buttons to resultinfo
resultInfo.appendChild(resultPanel.actions().element());
// The views are at the top of the search results
var sb = d.getElementById('search');
sb.insertBefore(resultPanel.element(), sb.firstChild);
};
// There is a koralQuery
if (KorAP.koralQuery !== undefined) {
// Add KoralQuery view to result panel
if (resultInfo !== null) {
resultPanel.addKqAction()
};
if (KorAP.koralQuery["errors"]) {
KorAP.koralQuery["errors"].forEach(function(e) {
// Malformed query
if (e[0] === 302 && e[2] !== undefined) {
obj.hint = hintClass.create();
obj.hint.alert(e[2], e[1]);
}
// no query
else if (e[0] === 301) {
obj.hint = hintClass.create();
obj.hint.alert(0, e[1]);
}
});
};
};
/*
* There is more than 0 matches, so allow for
* alignment toggling (left <=> right)
*/
if (matchElements.length > 0)
resultPanel.addAlignAction();
KorAP.Panel['result'] = resultPanel;
/*
* Toggle the Virtual Corpus builder
*/
if (vcname) {
vc.onMinimize = function () {
vcname.classList.remove('active');
delete show['vc'];
};
vc.onOpen = function () {
vcname.classList.add('active');
var view = d.getElementById('vc-view');
if (!view.firstChild)
view.appendChild(this.element());
show['vc'] = true;
};
var vcclick = function () {
if (vc.isOpen()) {
vc.minimize()
}
else {
vc.open();
};
};
vcname.onclick = vcclick;
// Click, if the VC should be shown
if (show['vc']) {
vcclick.apply();
};
};
/**
* Init Tutorial view
*/
if (d.getElementById('view-tutorial')) {
window.tutorial = tutClass.create(
d.getElementById('view-tutorial'),
KorAP.session
);
obj.tutorial = window.tutorial;
}
// Tutorial is in parent
else if (window.parent) {
obj.tutorial = window.parent.tutorial;
};
// Initialize queries for d
if (obj.tutorial) {
obj.tutorial.initQueries(d);
// Initialize documentation links
obj.tutorial.initDocLinks(d);
};
/**
* Add VC creation on submission.
*/
var form = d.getElementById('searchform');
if (form !== null) {
form.addEventListener('submit', function (e) {
var qf = d.getElementById('q-field');
// No query was defined
if (qf.value === undefined || qf.value === '') {
qf.focus();
e.halt();
KorAP.log(700, "No query given");
return;
};
// Store session information
KorAP.session.set("show", show);
if (vc !== undefined) {
input.value = vc.toQuery();
if (input.value == '')
input.removeAttribute('name');
}
else {
input.removeAttribute('value');
input.removeAttribute('name');
};
// This would preferably set the query to be "disabled",
// but in that case the query wouldn't be submitted
// at all.
// Setting the cursor to "progress" fails in current versions
// of webkit.
qf.classList.add("loading");
d.getElementById('qsubmit').classList.add("loading");
});
};
//Starts the guided tour at the next page
if(KorAP.session.get("tour")){
tourClass.gTshowResults().start();
}
/**
* Init hint helper
* has to be final because of
* reposition
*/
// Todo: Pass an element, so this works with
// tutorial pages as well!
if (obj.hint === undefined)
obj.hint = hintClass.create();
// Add the hinthelper to the KorAP object to make it manipulatable globally
KorAP.Hint = obj.hint;
/**
* Add query panel
*/
var queryPanel = queryPanelClass.create();
// Get input field
var vcView = d.getElementById('vc-view')
if (form && vcView) {
// The views are below the query bar
form.insertBefore(queryPanel.element(), vcView);
KorAP.Panel['query'] = queryPanel;
};
/**
* Add pagination panel
*/
const paginationPanel = paginationPanelClass.create();
if (paginationPanel) {
paginationPanel.addRandomPage();
KorAP.Panel['pagination'] = paginationPanel;
};
/**
* Initialize password toggle.
*/
initCopyToClipboard(d);
/**
* Initialize password toggle.
*/
initTogglePwdVisibility(d);
/**
* Initialize Plugin registry.
*/
let pe;
if (pe = d.getElementById("kalamar-plugins")) {
let url = pe.getAttribute('data-plugins');
if (url !== undefined) {
KorAP.API.getPluginList(url, function (json) {
if (json && json.length > 0) {
// Add state manager
form = d.getElementById("searchform");
if (!form) {
return;
};
const input = form.addE("input");
input.setAttribute("type","text");
input.setAttribute("name","state");
const url = new URL(window.location.href);
// Access the query parameters to check for states
const state = new URLSearchParams(url.search).get('state');
if (state != null && state != "") {
input.setAttribute("value", state);
};
KorAP.States = stateManagerClass.create(input);
// Load Plugin Server first
KorAP.Plugin = pluginClass.create();
// Add services container to head
d.head.appendChild(KorAP.Plugin.element());
// Add pipe form
KorAP.Pipe = pipeClass.create();
d.getElementById("searchform").appendChild(KorAP.Pipe.element());
try {
// Register all plugins
json.forEach(i => KorAP.Plugin.register(i));
}
catch (e) {
KorAP.log(0, e);
}
}
});
};
};
window.dispatchEvent(new Event("ui-ready"));
return obj;
});
});