Compare commits
6 Commits
234a81b4de
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 02e9932f1e | |||
| 1eb50d8969 | |||
| 2c7137f605 | |||
| 5360f80da4 | |||
| 06b55f5582 | |||
| f4273bafca |
@@ -4,4 +4,5 @@ Dokuwiki template specifically designed for luxtools plugins.
|
||||
This is a fork of the original default dokuwiki template.
|
||||
|
||||
## Changes from the original template
|
||||
- Default font changed to "Perfect DOS VGA 437 Win"
|
||||
- Default font changed to "Perfect DOS VGA 437 Win"
|
||||
- Enhanced header search with DokuWiki quicksearch suggestions, keyboard navigation, and disabled browser autofill
|
||||
@@ -179,6 +179,48 @@
|
||||
text-align: left;
|
||||
display: none;
|
||||
|
||||
&.is-open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
ul.search-suggestions {
|
||||
list-style: none;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
ul.search-suggestions li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
ul.search-suggestions li a {
|
||||
display: block;
|
||||
padding: .15em .25em;
|
||||
}
|
||||
|
||||
ul.search-suggestions li.is-active > a,
|
||||
ul.search-suggestions li a:focus {
|
||||
background-color: __highlight__;
|
||||
color: @ini_text;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
ul.search-suggestions li.is-tabbed > a {
|
||||
box-shadow: inset 0 0 0 1px @ini_border;
|
||||
}
|
||||
|
||||
ul.search-suggestions li.is-loading,
|
||||
ul.search-suggestions li.is-empty {
|
||||
padding: .15em .25em;
|
||||
color: @ini_text_alt;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
margin-bottom: .3em;
|
||||
|
||||
@@ -30,6 +30,7 @@ body {
|
||||
body {
|
||||
font-family: "Iosevka Aile Web", monospace;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ form.search {
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent url(images/search.png) no-repeat 0 0;
|
||||
background: transparent no-repeat 0 0;
|
||||
border-width: 0;
|
||||
width: 19px;
|
||||
height: 14px;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/**
|
||||
* This file provides styles for mobile devices
|
||||
* and smaller screens (up to 480px and 768px width).
|
||||
*
|
||||
* @author Anika Henke <anika@selfthinker.org>
|
||||
*/
|
||||
|
||||
/* for detecting media queries in JavaScript (see script.js): */
|
||||
@@ -222,23 +220,9 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* pagetools status bar on mobile - compact display */
|
||||
#dokuwiki__pagetools.pagetools-statusbar {
|
||||
ul {
|
||||
justify-content: center;
|
||||
gap: 0.15em;
|
||||
|
||||
li a {
|
||||
padding: 0.25em 0.35em;
|
||||
font-size: 0.85em;
|
||||
|
||||
.accesskey {
|
||||
font-size: 0.8em;
|
||||
padding: 0.05em 0.3em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* hide status bar on mobile. It is folded into a submenu */
|
||||
#dokuwiki__pagetools {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* search form */
|
||||
@@ -259,6 +243,10 @@ body {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#dokuwiki__sitetools form.search div.ajax_qsearch.is-open {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* action dropdown is alternative for all hidden tools */
|
||||
#dokuwiki__header .mobileTools {
|
||||
display: block;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
z-index: 100;
|
||||
|
||||
/* Visual styling - status bar appearance */
|
||||
background-color: @ini_background_alt;
|
||||
background-color: @ini_background;
|
||||
border-top: 1px solid @ini_border;
|
||||
padding: 0 0.5em;
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ if (!defined('DOKU_INC')) die();
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
<?php echo hsc(tpl_img_getTag('IPTC.Headline', $IMG))?>
|
||||
[<?php echo strip_tags($conf['title'])?>]
|
||||
</title>
|
||||
<?php tpl_metaheaders()?>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 307 B |
2
main.php
2
main.php
@@ -16,7 +16,7 @@ $showSidebar = $hasSidebar && ($ACT == 'show');
|
||||
<html lang="<?php echo $conf['lang'] ?>" dir="<?php echo $lang['direction'] ?>" class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><?php tpl_pagetitle() ?> [<?php echo strip_tags($conf['title']) ?>]</title>
|
||||
<title><?php tpl_pagetitle() ?></title>
|
||||
<?php tpl_metaheaders() ?>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<?php echo tpl_favicon(['favicon', 'mobile']) ?>
|
||||
|
||||
313
script.js
313
script.js
@@ -86,4 +86,317 @@ jQuery(function(){
|
||||
jQuery('#dokuwiki__pagetools div.tools>ul>li>a').on('click', function(){
|
||||
this.blur();
|
||||
});
|
||||
|
||||
// enhance header search with suggestions and keyboard navigation
|
||||
(function enhanceSearch(){
|
||||
var $input = jQuery('#qsearch__in');
|
||||
var $output = jQuery('#qsearch__out');
|
||||
|
||||
if ($input.length === 0 || $output.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $form = $input.closest('form');
|
||||
var debounceMs = 150;
|
||||
var minChars = 2;
|
||||
var maxSuggestions = 10;
|
||||
var requestTimer = null;
|
||||
var curRequest = null;
|
||||
var items = [];
|
||||
var activeIndex = -1;
|
||||
var lastNavigation = null;
|
||||
var listId = 'qsearch__listbox';
|
||||
|
||||
$form
|
||||
.addClass('search-enhanced')
|
||||
.attr('autocomplete', 'off');
|
||||
|
||||
$input
|
||||
.attr('autocomplete', 'off')
|
||||
.attr('spellcheck', 'false')
|
||||
.attr('aria-autocomplete', 'list')
|
||||
.attr('aria-expanded', 'false')
|
||||
.attr('aria-controls', listId)
|
||||
.attr('aria-haspopup', 'listbox');
|
||||
|
||||
$output
|
||||
.attr('aria-hidden', 'true');
|
||||
|
||||
// remove default quicksearch handlers
|
||||
$input.off('keyup');
|
||||
$output.off('click');
|
||||
|
||||
function clearActive() {
|
||||
activeIndex = -1;
|
||||
lastNavigation = null;
|
||||
$input.removeAttr('aria-activedescendant');
|
||||
}
|
||||
|
||||
function closeList() {
|
||||
if (curRequest) {
|
||||
curRequest.abort();
|
||||
curRequest = null;
|
||||
}
|
||||
if (requestTimer) {
|
||||
window.clearTimeout(requestTimer);
|
||||
requestTimer = null;
|
||||
}
|
||||
|
||||
items = [];
|
||||
clearActive();
|
||||
$output
|
||||
.removeClass('is-open is-loading')
|
||||
.attr('aria-hidden', 'true')
|
||||
.hide()
|
||||
.empty();
|
||||
$form.removeClass('is-searching');
|
||||
$input.attr('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
function openList() {
|
||||
$output
|
||||
.addClass('is-open')
|
||||
.attr('aria-hidden', 'false')
|
||||
.show();
|
||||
$input.attr('aria-expanded', 'true');
|
||||
}
|
||||
|
||||
function renderList(content) {
|
||||
$output.empty().append(content);
|
||||
openList();
|
||||
}
|
||||
|
||||
function renderLoading() {
|
||||
var $list = jQuery('<ul />', {
|
||||
'class': 'search-suggestions',
|
||||
'id': listId,
|
||||
'role': 'listbox',
|
||||
'aria-busy': 'true'
|
||||
});
|
||||
$list.append(jQuery('<li />', {
|
||||
'class': 'is-loading',
|
||||
'role': 'option',
|
||||
'aria-disabled': 'true',
|
||||
'text': 'Loading…'
|
||||
}));
|
||||
$output.addClass('is-loading');
|
||||
renderList($list);
|
||||
}
|
||||
|
||||
function renderNoMatches() {
|
||||
var $list = jQuery('<ul />', {
|
||||
'class': 'search-suggestions',
|
||||
'id': listId,
|
||||
'role': 'listbox',
|
||||
'aria-busy': 'false'
|
||||
});
|
||||
$list.append(jQuery('<li />', {
|
||||
'class': 'is-empty',
|
||||
'role': 'option',
|
||||
'aria-disabled': 'true',
|
||||
'text': 'No Matches'
|
||||
}));
|
||||
$output.removeClass('is-loading');
|
||||
renderList($list);
|
||||
}
|
||||
|
||||
function updateActive() {
|
||||
var $options = $output.find('li[data-index]');
|
||||
$options.removeClass('is-active is-tabbed').attr('aria-selected', 'false');
|
||||
|
||||
if (activeIndex < 0 || activeIndex >= items.length) {
|
||||
clearActive();
|
||||
return;
|
||||
}
|
||||
|
||||
var $active = $options.filter('[data-index="' + activeIndex + '"]');
|
||||
$active.addClass('is-active').attr('aria-selected', 'true');
|
||||
if (lastNavigation === 'tab') {
|
||||
$active.addClass('is-tabbed');
|
||||
}
|
||||
$input.attr('aria-activedescendant', $active.attr('id'));
|
||||
}
|
||||
|
||||
function moveActive(delta, source) {
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
lastNavigation = source || null;
|
||||
if (activeIndex < 0) {
|
||||
activeIndex = delta > 0 ? 0 : items.length - 1;
|
||||
} else {
|
||||
activeIndex = (activeIndex + delta + items.length) % items.length;
|
||||
}
|
||||
updateActive();
|
||||
}
|
||||
|
||||
function selectActive() {
|
||||
if (activeIndex < 0 || activeIndex >= items.length) {
|
||||
return false;
|
||||
}
|
||||
window.location.assign(items[activeIndex].href);
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderResults(results) {
|
||||
var $list = jQuery('<ul />', {
|
||||
'class': 'search-suggestions',
|
||||
'id': listId,
|
||||
'role': 'listbox',
|
||||
'aria-busy': 'false'
|
||||
});
|
||||
|
||||
results.forEach(function (item, index) {
|
||||
var optionId = 'qsearch__option_' + index;
|
||||
var $link = jQuery('<a />', {
|
||||
'href': item.href,
|
||||
'text': item.text
|
||||
});
|
||||
var $option = jQuery('<li />', {
|
||||
'id': optionId,
|
||||
'role': 'option',
|
||||
'data-index': index,
|
||||
'aria-selected': 'false'
|
||||
});
|
||||
$option.append($link);
|
||||
$list.append($option);
|
||||
});
|
||||
|
||||
$output.removeClass('is-loading');
|
||||
renderList($list);
|
||||
clearActive();
|
||||
}
|
||||
|
||||
function handleResults(data) {
|
||||
$form.removeClass('is-searching');
|
||||
curRequest = null;
|
||||
|
||||
var $wrapper = jQuery('<div />').html(data || '');
|
||||
var $links = $wrapper.find('a');
|
||||
items = [];
|
||||
|
||||
$links.each(function () {
|
||||
if (items.length >= maxSuggestions) {
|
||||
return false;
|
||||
}
|
||||
items.push({
|
||||
href: this.href,
|
||||
text: jQuery(this).text()
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!items.length) {
|
||||
renderNoMatches();
|
||||
return;
|
||||
}
|
||||
|
||||
renderResults(items);
|
||||
}
|
||||
|
||||
function scheduleSearch() {
|
||||
if (requestTimer) {
|
||||
window.clearTimeout(requestTimer);
|
||||
requestTimer = null;
|
||||
}
|
||||
requestTimer = window.setTimeout(runSearch, debounceMs);
|
||||
}
|
||||
|
||||
function runSearch() {
|
||||
var term = jQuery.trim($input.val());
|
||||
|
||||
if (term.length < minChars) {
|
||||
closeList();
|
||||
return;
|
||||
}
|
||||
|
||||
if (curRequest) {
|
||||
curRequest.abort();
|
||||
}
|
||||
|
||||
$form.addClass('is-searching');
|
||||
renderLoading();
|
||||
|
||||
var base = (typeof DOKU_BASE !== 'undefined') ? DOKU_BASE : '/';
|
||||
curRequest = jQuery.post(
|
||||
base + 'lib/exe/ajax.php',
|
||||
{
|
||||
call: 'qsearch',
|
||||
q: encodeURI(term)
|
||||
},
|
||||
handleResults,
|
||||
'html'
|
||||
).fail(function () {
|
||||
$form.removeClass('is-searching');
|
||||
curRequest = null;
|
||||
renderNoMatches();
|
||||
});
|
||||
}
|
||||
|
||||
$input.on('input', scheduleSearch);
|
||||
|
||||
$input.on('keydown', function (event) {
|
||||
var key = event.key;
|
||||
var isOpen = $output.hasClass('is-open');
|
||||
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'Escape') {
|
||||
event.preventDefault();
|
||||
closeList();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
moveActive(1, 'arrow');
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
moveActive(-1, 'arrow');
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'Tab') {
|
||||
event.preventDefault();
|
||||
moveActive(event.shiftKey ? -1 : 1, 'tab');
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'Enter') {
|
||||
if (selectActive()) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$output.on('mouseenter', 'li[data-index]', function () {
|
||||
activeIndex = parseInt(jQuery(this).attr('data-index'), 10);
|
||||
lastNavigation = 'mouse';
|
||||
updateActive();
|
||||
});
|
||||
|
||||
$output.on('mouseleave', 'li[data-index]', function () {
|
||||
clearActive();
|
||||
});
|
||||
|
||||
$output.on('mousedown', 'li[data-index] > a', function (event) {
|
||||
event.preventDefault();
|
||||
window.location.assign(this.href);
|
||||
});
|
||||
|
||||
jQuery(document).on('click', function (event) {
|
||||
if (!jQuery(event.target).closest($form).length) {
|
||||
closeList();
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ if (!defined('DOKU_INC')) die();
|
||||
<!-- SITE TOOLS -->
|
||||
<div id="dokuwiki__sitetools">
|
||||
<h3 class="a11y"><?php echo $lang['site_tools']; ?></h3>
|
||||
<?php tpl_searchform(); ?>
|
||||
<?php tpl_searchform(true, false); ?>
|
||||
<div class="mobileTools">
|
||||
<?php echo (new \dokuwiki\Menu\MobileMenu())->getDropdown($lang['tools']); ?>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user