(function () {
"use strict";
exports.name = "tw5-keyboard-navigation"; exports.after = ["rootwidget"];
exports.startup = function () {
const navigate_up_key = 'k'
const navigate_down_key = 'j'
const bindings = {
'more-tiddler-actions': 'm',
info: null,
'new-here': null,
'new-journal-here': null,
clone: null,
'export-tiddler': null,
edit: 'e',
delete: null,
permalink: null,
permaview: null,
'open-window': null,
'close-others': null,
close: 'c',
'fold-others': null,
fold: null
}
const alternativeLayoutIgnoreKeys = ["ArrowUp"]
const BTN_CLASSNAME_PREFIX = 'tc-btn-%24%3A%2Fcore%2Fui%2FButtons%2F'
const INSTANT_NAVIGATION = false;
const INSTANT_NAVIGATION_BEFORE = 800;
const MARK_CURRENT_TIDDLER = true;
const LIMIT = 50;
var ignoreKeyboardLayout = false;
var cycle = false;
var tiddlers;
var tiddler_index = -1;
var tiddler_title = "";
var timestamp_last_navigation = 0;
var activeElement = document.activeElement;
activeElement.blur();
tiddlers = document.getElementsByClassName("tc-tiddler-frame");
if (tiddlers.length > 0) {
tiddler_index = 0;
if (MARK_CURRENT_TIDDLER) {
tiddlers[tiddler_index].classList.add("activeTiddler");
}
}
$tw.hooks.addHook("th-page-refreshed", function() {
tiddlers = document.getElementsByClassName("tc-tiddler-frame");
if (!MARK_CURRENT_TIDDLER) return;
if (tiddler_index < 0 && tiddlers.length) tiddler_index = 0;
var last_visible_index = tiddlers.length - 1;
while (last_visible_index >= 0 && tiddlers[last_visible_index].style.opacity === "0") {
last_visible_index--;
}
if (tiddler_index > last_visible_index) {
tiddler_index = last_visible_index;
}
if (tiddler_index < 0) return;
var i = tiddler_index;
while (i < tiddlers.length && tiddlers[i].style.opacity === "0") {
i++;
}
if (i == tiddlers.length) {
i = tiddler_index;
while (i >= 0 && tiddlers[i].style.opacity === "0") {
i--;
}
}
tiddler_index = i;
updateActiveTiddlerClass();
});
$tw.hooks.addHook("th-saving-tiddler", function(tiddler) {
tiddler_title = tiddler.getFieldString("title");
return tiddler;
});
$tw.hooks.addHook("th-navigating", function(event) {
tiddler_title = event.navigateTo;
tiddlers = document.getElementsByClassName("tc-tiddler-frame");
for (var i=0; i<tiddlers.length; i++) {
if (tiddlers[i].getAttribute("data-tiddler-title") == tiddler_title) {
if (MARK_CURRENT_TIDDLER) {
if (tiddlers[tiddler_index] !== undefined)
tiddlers[tiddler_index].classList.remove("activeTiddler");
tiddlers[i].classList.add("activeTiddler");
}
tiddler_index = i;
break;
}
}
return event;
});
function updateActiveTiddlerClass() {
for (var i=0; i<tiddlers.length; i++) {
if (i != tiddler_index) {
tiddlers[i].classList.remove("activeTiddler");
}
}
tiddlers[tiddler_index].classList.add("activeTiddler");
}
function getActiveTiddlersTitle() {
tiddler_title = (tiddler_index < 0) ? "" : tiddlers[tiddler_index].getAttribute("data-tiddler-title");
}
function isInViewport(el) {
var rect = el.getBoundingClientRect();
return (
rect.bottom > 0 &&
rect.top < (window.innerHeight || document.documentElement.clientHeight)
);
}
function isElementCloseToTop(el) {
var rect = el.getBoundingClientRect();
return (rect.top >= -LIMIT && rect.top <= LIMIT);
}
function findTopmostTiddler(tiddlers) {
var i = 0;
while (i < tiddlers.length && tiddlers[i].getBoundingClientRect().top <= -LIMIT) {
i++;
}
return i;
}
function switchTiddler(scrollTo) {
if (scrollTo) {
if (INSTANT_NAVIGATION ||
Date.now() - timestamp_last_navigation < INSTANT_NAVIGATION_BEFORE) {
tiddlers[tiddler_index].scrollIntoView();
} else {
tiddlers[tiddler_index].scrollIntoView(
{behavior: "smooth", block: "start", inline: "nearest"});
}
}
timestamp_last_navigation = Date.now();
if (MARK_CURRENT_TIDDLER) {
updateActiveTiddlerClass();
}
getActiveTiddlersTitle();
}
document.onclick = function(e) {
var elem = e.target;
while (elem && !elem.classList.contains("tc-tiddler-frame")) {
elem = elem.parentElement;
}
if (!elem) return;
const titleOfClickedTiddler = elem.getAttribute("data-tiddler-title");
tiddlers = document.getElementsByClassName("tc-tiddler-frame");
for (var i=0; i<tiddlers.length; i++) {
if (tiddlers[i].getAttribute("data-tiddler-title") == titleOfClickedTiddler) {
tiddler_index = i;
switchTiddler(false);
return;
}
}
}
document.onkeydown = function(e) {
if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) return;
var activeElement = document.activeElement;
if (activeElement && activeElement.tagName.toLowerCase() == "textarea") return;
if (activeElement && activeElement.tagName.toLowerCase() == "input") {
if (activeElement.type == "search" && activeElement.classList.contains("tc-popup-handle")) {
if (e.key == "Tab" || e.key == "Enter") {
var searchItems = document.querySelector("div.tc-search-results div.tc-menu-list-item")
if (searchItems) {
searchItems.children[0].focus();
if (e.key == "Enter") {
searchItems.children[0].click();
}
e.preventDefault();
}
}
}
if (e.code == "Escape") {
var dropdown = document.querySelector("div.tc-search-drop-down");
if (dropdown) {
var elem = dropdown.parentNode.parentNode;
elem.querySelector("svg.tc-image-close-button");
if (elem && elem.parentNode) {
elem.parentNode.click();
}
}
activeElement.blur();
}
return;
}
if (e.code == "Escape") {
var menu = document.querySelector("div.tc-drop-down");
if (menu) {
menu.parentElement.previousSibling.click();
}
}
tiddlers = document.getElementsByClassName("tc-tiddler-frame");
if (tiddlers.length == 0) return;
var tiddlerIgnoreKeyboardLayout = $tw.wiki.getTiddler("$:/config/IgnoreKeyboardLayout");
ignoreKeyboardLayout = (tiddlerIgnoreKeyboardLayout.fields.text.trim() == "1");
var tiddlerCycle = $tw.wiki.getTiddler("$:/config/Cycle");
cycle = (tiddlerCycle.fields.text.trim() == "1");
var down_key_released = !e.isComposing && ignoreKeyboardLayout ? (e.code == "Key" + navigate_down_key.toUpperCase())
: (e.key == navigate_down_key);
var up_key_released = !e.isComposing && ignoreKeyboardLayout ? (e.code == "Key" + navigate_up_key.toUpperCase())
: (e.key == navigate_up_key);
var other_bind_used = Object.keys(bindings).find(k => {
if (e.isComposing) return false;
if (!bindings[k]) return false;
if (ignoreKeyboardLayout)
return e.code == ("Key" + bindings[k].toUpperCase()) &&
!alternativeLayoutIgnoreKeys.find(ak => ak == e.key);
return e.key == bindings[k];
})
console.log(e);
if (tiddler_title) {
if (tiddler_index < 0 || tiddler_index >= tiddlers.length ||
tiddlers[tiddler_index].getAttribute("data-tiddler-title") != tiddler_title) {
tiddler_index = -1;
for (var i=0; i<tiddlers.length; i++) {
if (tiddlers[i].getAttribute("data-tiddler-title") == tiddler_title) {
tiddler_index = i;
break;
}
}
}
}
if (down_key_released || up_key_released || other_bind_used) {
if (tiddler_index < 0 || (!isInViewport(tiddlers[tiddler_index]) &&
!isElementCloseToTop(tiddlers[tiddler_index]))) {
tiddler_index = findTopmostTiddler(tiddlers);
if (down_key_released) tiddler_index -= 1;
}
if (down_key_released || up_key_released) {
if (down_key_released) {
tiddler_index += 1;
if (tiddler_index >= tiddlers.length)
tiddler_index = cycle ? 0 : tiddlers.length - 1;
} else if (up_key_released) {
tiddler_index -= 1;
if (tiddler_index < 0)
tiddler_index = cycle ? tiddlers.length - 1 : 0;
}
switchTiddler(true);
} else {
if (tiddlers[tiddler_index] === undefined) return;
if (!isInViewport(tiddlers[tiddler_index])) return;
var button = tiddlers[tiddler_index].
getElementsByClassName(BTN_CLASSNAME_PREFIX+other_bind_used)[0];
if (!button) return;
button.click();
if (other_bind_used == 'close' || other_bind_used == 'delete') {
tiddlers[tiddler_index].classList.remove("activeTiddler");
if (tiddlers.length == 1) {
tiddler_index = -1;
} else {
if (tiddler_index >= tiddlers.length - 1) {
tiddler_index = tiddlers.length - 2;
if (MARK_CURRENT_TIDDLER)
tiddlers[tiddler_index].classList.add("activeTiddler");
} else {
if (MARK_CURRENT_TIDDLER)
tiddlers[tiddler_index+1].classList.add("activeTiddler");
}
}
}
getActiveTiddlersTitle();
}
}
}
};
})();