the-glorious-startpage/js/web-menu.js

397 lines
10 KiB
JavaScript

var webMenu = document.getElementById("webMenu");
var webMenuList = document.getElementById("webMenuList");
var webMenuListContainer = document.getElementById("webMenuListContainer");
var webMenuSearchBox = document.getElementById("webMenuSearchBox");
let webMenuVisibility = false;
let webItemFocus;
let webListIndex = 0;
// Disable/Enable inputs
var disableWebMenuInputs = (status) => {
var elems = webMenu.getElementsByTagName('input');
var len = elems.length;
for (var i = 0; i < len; i++) {
elems[i].disabled = status;
}
}
// Create mouse event for passed li
const createCallback = (li, url) => {
// Create a callback property for the passed li
li.callback = () => {
window.location.href = encodeURI(url);
}
}
// Sort list alphabetically
const sortList = () => {
Array.from(webMenuList.getElementsByTagName("li"))
.sort((a, b) => a.textContent.localeCompare(b.textContent))
.forEach(li => webMenuList.appendChild(li));
}
// Populate web menu
const populateWebMenu = () => {
// Generate a list
for (i = 0; i < (webSites.length); i++) {
var site = webSites[i].site;
var icon = webSites[i].icon;
var url = webSites[i].url;
var li = document.createElement('li');
// Add mouseup event
createCallback(li, url);
// Create a href
var aWebLink = document.createElement('a');
aWebLink.className = 'webMenuLink';
aWebLink.href = url;
aWebLink.tabIndex = '-1';
// Create an outer div, child of li
let webItemDiv = document.createElement('div')
webItemDiv.className = 'webItem';
// webItemDiv.tabitemIndex = '1';
webItemDiv.id = "id" + site;
// Create a second div, webItemContainer
var webItemContainer = document.createElement('div');
webItemContainer.className = 'webItemContainer';
// Create the innermost div, contains icon and label
var webItemBody = document.createElement('div');
webItemBody.className = 'webItemBody';
// Create div for webItemIcon
var webItemIconContainer = document.createElement('div');
webItemIconContainer.className = 'webItemIconContainer';
var webItemIcon = document.createElement('div');
webItemIcon.className = 'webItemIcon';
webItemIcon.style.background = "url('assets/webcons/" + icon + ".svg')";
webItemIcon.style.backgroundSize = 'cover';
// Create webItemName
var webItemName = document.createElement('div');
webItemName.className = 'webItemName';
webItemName.innerHTML = site;
// Append divs with heirarchy
webItemDiv.appendChild(webItemContainer);
webItemContainer.appendChild(webItemBody);
webItemIconContainer.appendChild(webItemIcon);
webItemBody.appendChild(webItemIconContainer);
webItemBody.appendChild(webItemName);
aWebLink.appendChild(webItemDiv);
li.appendChild(aWebLink);
webMenuList.appendChild(li);
}
// Call to sort list
sortList();
}
// Fuzzy search
String.prototype.fuzzy = function(term, ratio) {
var string = this.toLowerCase();
var compare = term.toLowerCase();
var matches = 0;
if (string.indexOf(compare) > -1) return true; // covers basic partial matches
for (var i = 0; i < compare.length; i++) {
string.indexOf(compare[i]) > -1 ? matches += 1 : matches -=1;
}
return (matches/this.length >= ratio || term == "");
};
// Search through the list
const filterWebList = () => {
var input, filter, ul, li, a, i, txtValue;
input = webMenuSearchBox;
filter = input.value.toUpperCase();
ul = webMenuList;
li = ul.getElementsByTagName('li');
// Loop through all list items, and focus if matches the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByClassName("webItemName")[0];
txtValue = a.innerHTML || a.textContent || a.innerText;
// If an item match, hightlight it and focus
// if (txtValue.toUpperCase().indexOf(filter) !== -1) {
if (txtValue.toUpperCase().fuzzy(filter, 1) === true) {
// Unselect/Unhightlight old active
var oldWebItemFocus = webItemFocus;
var oldWebItemFocusChild = oldWebItemFocus.querySelector('.webItem');
oldWebItemFocusChild.classList.remove('webItemFocus');
// Update webItemFocus
webItemFocus = li[i];
// Update weblistindex
webListIndex = i;
// Get child
var webItemFocusChild = webItemFocus.querySelector('.webItem');
// Add webItemFocus class to child
webItemFocusChild.classList.add('webItemFocus');
// Scroll focus into active
webItemFocus.scrollIntoView();
}
}
}
// Reset focus on web menu close
const focusReset = () => {
var oldWebItemFocus = webItemFocus;
var oldWebItemFocusChild = oldWebItemFocus.querySelector('.webItem');
oldWebItemFocusChild.classList.remove('webItemFocus');
webListIndex = 0;
}
// Get first item of ul
const getFirstItem = () => {
var ul = webMenuList;
var li = ul.getElementsByTagName('li');
// Focus on first item
webItemFocus = li[0];
// Get child
var webItemFocusChildren = webItemFocus.querySelector('.webItem');
// Add webItemFocus class
webItemFocusChildren.classList.add('webItemFocus');
}
const showWebMenu = () => {
webMenu.classList.add('showWebMenu');
// Enable inputs
disableWebMenuInputs(false);
webMenuVisibility = !webMenuVisibility;
// Focus to input field
webMenuSearchBox.focus();
}
const hideWebMenu = () => {
// Clear input field
webMenuSearchBox.value = '';
// Unfocus input field
webMenuSearchBox.blur();
// Refilter web list
filterWebList();
// Scroll to top
webMenuListContainer.scrollTop = 0;
// Reset focus item
focusReset();
// Get first item
getFirstItem();
webMenu.classList.remove('showWebMenu');
// Disable inputs
disableWebMenuInputs(true);
webMenuVisibility = !webMenuVisibility;
}
const toggleWebMenu = () => {
console.log('toggle web menu');
// If profile anim is still running,
// Return to avoid spam
if (profileAnimRunning) return;
// Rotate profile
rotateProfile();
if (webMenuVisibility) {
// Hide web menu
hideWebMenu();
} else {
// Show Web menu
showWebMenu();
}
// Check if any of these are open, if yes, close it
if (weatherScreen.classList.contains('showWeatherScreen')) {
console.log('weather screen is open, closing...');
hideWeatherScreen();
return;
} else if (searchBoxContainer.classList.contains('showSearchBox')) {
console.log('searchbox is open, closing...');
hideSearchBox();
} else if (dashboard.classList.contains('showRightDashboard')) {
console.log('dashboard is open, closing...');
hideDashboard();
}
// Toggle center box
toggleCenteredBox();
}
// Remove class to focused item
const removeClass = (el, className) => {
// Remove webItemFocus class
var oldWebItemFocus = el.querySelector('.webItem');
oldWebItemFocus.classList.remove('webItemFocus');
};
// Add class to focused item
const addClass = (el, className) => {
var webItemFocusChild = el.querySelector('.webItem');
// Add webItemFocus class to child
webItemFocusChild.classList.add('webItemFocus');
// Scroll focus into active
webItemFocusChild.scrollIntoView();
};
const navigateWithArrows = (key, len) => {
// assign variables to key codes
const [right, left, down, up] = [39, 37, 40, 38];
const getIndexByWindowWidth = () => {
if (window.innerWidth <= 580) { return 1 }
// width of elements in pixels
const menuItemWidth = 138;
const scrollBarWidth = 10;
// viewport width
const vw = (unit) => window.innerWidth * (unit / 100);
// Gets the number of columns by dividing the screen width minus the padding, scroll width and average of menu item width by the menu item width
const containerWindow = ((window.innerWidth - (menuItemWidth / 2) - scrollBarWidth - vw(24)) / menuItemWidth);
// Get rounded result
return Math.round(containerWindow);
}
// Determine the index position by key
const changeWebListIndex = () => {
switch (key) {
case right:
webListIndex++;
break;
case left:
webListIndex--;
break;
case up:
webListIndex = webListIndex - getIndexByWindowWidth();
break;
case down:
webListIndex = webListIndex + getIndexByWindowWidth();
break;
}
}
const changeItemFocus = (condition, overFlowIndex) => {
const next = webMenuList.getElementsByTagName('li')[webListIndex];
if(typeof next !== undefined && condition) {
webItemFocus = next;
} else {
webListIndex = overFlowIndex;
webItemFocus = webMenuList.getElementsByTagName('li')[overFlowIndex];
}
}
const changeItemFocusByKey = () => {
if (key === right) { return changeItemFocus((webListIndex <= len), 0) }
if (key === left) { return changeItemFocus((webListIndex >= 0), len) }
if (key === up) { return changeItemFocus((webListIndex >= 0), len) }
if (key === down) { return changeItemFocus((webListIndex <= len), 0) }
}
// Clear web menu searchbox
webMenuSearchBox.value = '';
changeWebListIndex();
if (webItemFocus) {
removeClass(webItemFocus, 'webItemFocus');
changeItemFocusByKey();
addClass(webItemFocus, 'webItemFocus');
// console.log(webListIndex);
} else {
webListIndex = 0;
webItemFocus = webMenuList.getElementsByTagName('li')[0];
addClass(webItemFocus, 'webItemFocus');
}
}
// Keyboard navigation
webMenu.addEventListener(
'keydown',
(event) => {
var len = webMenuList.getElementsByTagName('li').length - 1;
navigateWithArrows(event.which, len);
},
false
);
// Type event on web mmenu search box
webMenuSearchBox.onkeydown = (event) => {
// Don't hijack keyboard navigation buttons (up, down, left, right)
if ((event.key === 'ArrowRight') || (event.key === 'ArrowDown') ||
(event.key === 'ArrowLeft') || (event.key === 'ArrowUp')) return;
if (event.key === 'Tab') return;
if (event.key === 'Enter' && webItemFocus) {
// Run the focused li's callback
webItemFocus.callback();
// Hide web menu
toggleWebMenu();
} else if (event.key === 'Backspace' && webMenuSearchBox.value.length < 1) {
// Hide web menu if backspace is pressed and searchbox value is 0
toggleWebMenu();
return;
} else if ((event.key === 'Escape') || (event.key === 'Alt')) {
// Ignore escape and alt key
return;
}
// Filter
filterWebList();
}
// Populate and get first child
const initWebMenu = () => {
populateWebMenu();
getFirstItem();
// Disable inputs
disableWebMenuInputs(true);
}
// Initialize web menu
window.onload = initWebMenu();