/* DOKUWIKI:include js/skip-link-focus-fix.js */
jQuery(document).ready(function() {
/*
* Click to toggle sidebar.
*/
function toggleSidebar() {
jQuery( '#writr__sidebar' ).on( 'click', '#writr__sidebar-toggle', function( e ) {
e.preventDefault();
jQuery( 'html, body' ).scrollTop( 0 );
jQuery( this ).toggleClass( 'open' );
jQuery( 'body' ).toggleClass( 'sidebar-closed' );
jQuery( '#writr__secondary' ).resize();
} );
}
/**
* Handles toggling the navigation menu for small screens.
*/
function toggleNavigation() {
var $container = jQuery('#writr__site-navigation');
if (!$container.length) return;
var $button = jQuery('.menu-toggle', $container);
if (!$button.length) return;
var $menu = jQuery('ul', $container);
if (!$menu.length) {
$menu.hide();
return;
}
$button.click(function(){
$container.toggleClass('toggled');
});
}
/*
* A function to enable/disable a dropdown submenu.
*/
function toggleSubmenu() {
jQuery( '.main-navigation .node > div > a' ).append( '' );
jQuery( '#writr__site-navigation' ).on( 'click', '.dropdown-icon', function( e ) {
e.preventDefault();
jQuery( this ).toggleClass( 'open' );
if ( jQuery( this ).hasClass( 'open' ) ) {
jQuery( this ).parent().parent().next( 'ul' ).show();
} else {
jQuery( this ).parent().parent().next( 'ul' ).hide();
}
} );
}
/*
* Close TOC by default
*/
function closeToc() {
var $toc = jQuery('#dw__toc .toggle');
if($toc.length) {
$toc[0].setState(-1);
}
}
/*
* Change search submit input to submit button to make it easier to style
* @deprecated since Detritus
*/
function changeSearchInput() {
var $searchForm = jQuery('.search-form > form > div');
var $searchButton = jQuery('input[type="submit"]', $searchForm).detach();
var title = $searchButton.attr('title');
var value = $searchButton.val();
$searchForm.append('');
}
/*
* Enable add new page dropdown
*/
function enableAddNewPage() {
jQuery('.action.AddNewPage').click(function(event) {
event.preventDefault();
const button = jQuery(this);
jQuery('.addnewpage').toggle(0,function(){
// set aria-expanded attribute based on visibility
button.attr('aria-expanded', jQuery(this).is(':visible'));
});
});
jQuery(document).click(function(event) {
if (!jQuery(event.target).closest('.action.AddNewPage, .addnewpage').length) {
jQuery('.addnewpage').hide();
}
});
}
/*
* Enable translation dropdown
*/
function enableTranslation() {
jQuery('.action.Translation').click(function(event) {
event.preventDefault();
const button = jQuery(this);
jQuery('.plugin_translation').toggle(0,function(){
// set aria-expanded attribute based on visibility
button.attr('aria-expanded', jQuery(this).is(':visible'));
});
});
jQuery(document).click(function(event) {
if (!jQuery(event.target).closest('.action.Translation, .plugin_translation').length) {
jQuery('.plugin_translation').hide();
}
});
}
/*
* Enable Toolbar Dropdowns
*/
function enableToolbarDropdowns() {
jQuery('#writr__toolbar .hook .node').each(function() {
const dropdown = jQuery(this);
dropdown.find('div.li').click(function(event) {
const trigger = jQuery(this);
dropdown.find('> ul').toggle(0,function(){
trigger.attr('aria-expanded', jQuery(this).is(':visible'));
});
// Close dropdown when clicking outside
jQuery(document).on('click.dropdown', function(e) {
if (!dropdown.is(e.target) && dropdown.has(e.target).length === 0) {
dropdown.find('> ul').hide();
trigger.attr('aria-expanded', 'false');
jQuery(document).off('click.dropdown');
}
});
});
});
}
/*
* Enable Collapse
*/
function enableCollapse() {
jQuery('[data-toggle="collapse"]').click(function(event){
event.preventDefault();
const trigger = jQuery(this);
const target = jQuery(trigger.attr('data-target'));
target.slideToggle('fast',function(){
// set aria-expanded attribute based on visibility
trigger.attr('aria-expanded', target.is(':visible'));
});
});
}
/*
* Enable Dropdowns
*/
function enableDropdowns() {
jQuery('.dropdown').each(function() {
const dropdown = jQuery(this);
dropdown.find('[data-toggle="dropdown"]').click(function(event) {
event.preventDefault();
const button = jQuery(this);
dropdown.find('.dropdown-menu').toggle(0,function(){
button.attr('aria-expanded', jQuery(this).is(':visible'));
});
// Close dropdown when clicking outside
jQuery(document).on('click.dropdown', function(e) {
if (!dropdown.is(e.target) && dropdown.has(e.target).length === 0) {
dropdown.find('> ul').hide();
button.attr('aria-expanded', 'false');
jQuery(document).off('click.dropdown');
}
});
});
});
}
/*
* Enable Tooltips
*/
function enableTooltips() {
jQuery('body.enableTooltips [title]:not(.media):not(img):not([title=""]), body.enableTooltips [alt]:not(.media):not(img):not([alt=""])').each(function() {
const element = jQuery(this);
const content = element.attr('alt') ? element.attr('alt') : element.attr('title');
element.attr('data-tooltip-content', content);
element.hover(function() {
// Prevent default browser tooltip from showing
const tooltipType = element.attr('alt') ? 'alt' : 'title';
element.removeAttr(tooltipType).attr('data-tooltip-type', tooltipType);
// Create and append the tooltip
const tooltip = jQuery('
' + content + '
');
jQuery('body').append(tooltip);
// Calculate and set the position of the tooltip
const elementOffset = element.offset();
const tooltipWidth = tooltip.outerWidth();
const elementWidth = element.outerWidth();
const topPosition = elementOffset.top + element.outerHeight() + 10; // Adjust +10 for spacing
const leftPosition = elementOffset.left + (elementWidth / 2) - (tooltipWidth / 2);
tooltip.css({
top: topPosition,
left: leftPosition,
display: 'inline-block'
});
}, function() {
// Restore the original attribute and remove the tooltip
element.attr(element.attr('data-tooltip-type'), content);
jQuery('.tooltip').remove();
});
});
}
/*
* Enable Improved File Input
*/
function enableFileInput() {
jQuery('input[type="file"]').on('change', function() {
var input = jQuery(this);
var label = input.prev('span');
var group = input.parent('label');
var fileName = input.val().split('\\').pop() || 'No file chosen';
// Check if a div already exists
if (input.next('div').length > 0) {
input.next('div').remove();
}
// Create a div after the input
var file = jQuery('');
// Update the text of the file-name span
file.prepend(fileName);
// Add a button to clear the field
var button = jQuery('').appendTo(file);
// Add Click event on the button
button.click(function(){
// Clear value
input.val('');
// Remove the file object
file.remove();
// Show input and label
label.show();
input.show();
});
// Insert the div after the input
input.after(file);
// Hide label & input
label.hide();
input.hide();
});
}
/*
* Disable Newlines in Textareas
*/
function disableNewlines() {
// Check if "?do=" is present in the URL
if (window.location.search.indexOf("?do=") === -1) {
jQuery("form").on("submit", function(event) {
// Prevent the form from submitting immediately
event.preventDefault();
// Find the textarea and replace newlines with " \\ "
var textarea = jQuery(this).find('textarea');
textarea.val(textarea.val().replace(/\n/gm, ' \\\\\\ '));
// After running the function, manually trigger the form submission
this.submit();
});
}
}
/*
* Run the functions
*/
jQuery(function(){
toggleSidebar();
toggleNavigation();
toggleSubmenu();
closeToc();
changeSearchInput();
enableAddNewPage();
enableTranslation();
enableToolbarDropdowns();
enableDropdowns();
enableTooltips();
enableCollapse();
enableFileInput();
});
/*
* NSPAGES enable multi-line support
*/
jQuery('.nspagesPicturesModeTitle').each(function() {
if (jQuery(this).prop('scrollHeight') > 32) {
jQuery(this).addClass('multi-line');
}
});
/*
* Disqus iframe minimum height
*
* Disqus sets an inline height with !important (via JS). That height can be a bit
* too small for our visual caps/padding. We therefore enforce:
* effectiveHeight >= (disqusHeight + EXTRA)
*
* Important: we must NOT keep adding EXTRA on our own updates.
*/
(function disqusMinHeightFix(){
const $thread = jQuery('#disqus__thread');
if (!$thread.length) return;
const EXTRA = 72; // additional space needed for our visual caps/spacing
function parseInlinePx(value) {
if (!value) return null;
const n = parseInt(String(value).replace('px', ''), 10);
return Number.isFinite(n) ? n : null;
}
function getInlineHeightPx(iframe) {
// Prefer inline style.height because Disqus writes it.
const h = parseInlinePx(iframe.style && iframe.style.height);
if (h !== null) return h;
// Fallback: jQuery height
const $if = jQuery(iframe);
const jh = parseInlinePx($if.css('height'));
return jh !== null ? jh : null;
}
function applyFloor(iframe) {
if (!iframe) return;
const current = getInlineHeightPx(iframe);
if (current === null) return;
// If this height is exactly what we last applied, ignore (prevents +64 runaway).
const lastApplied = parseInlinePx(iframe.getAttribute('data-writr-last-applied'));
if (lastApplied !== null && current === lastApplied) return;
// Treat the current inline height as Disqus' desired height.
const disqusHeight = current;
const target = disqusHeight + EXTRA;
// Only increase if needed.
if (current < target) {
iframe.style.setProperty('height', target + 'px', 'important');
iframe.setAttribute('data-writr-last-applied', String(target));
}
// Keep a record of the last disqus height we saw.
iframe.setAttribute('data-writr-last-disqus', String(disqusHeight));
}
function attachToIframe(iframe) {
if (!iframe) return;
// Avoid attaching twice.
if (iframe.getAttribute('data-writr-disqus-observer') === '1') {
applyFloor(iframe);
return;
}
iframe.setAttribute('data-writr-disqus-observer', '1');
// Initial apply.
applyFloor(iframe);
// Observe style changes (Disqus updates height via inline styles).
const obs = new MutationObserver(function(mutations){
for (const m of mutations) {
if (m.type === 'attributes' && m.attributeName === 'style') {
applyFloor(iframe);
}
}
});
obs.observe(iframe, { attributes: true, attributeFilter: ['style'] });
// Store observer so we can disconnect if needed.
iframe._writrDisqusObs = obs;
}
function findPrimaryIframe() {
// Disqus uses an iframe id like dsq-app####.
const iframe = $thread.find('iframe[id^="dsq-app"]').get(0);
return iframe || null;
}
// Attach to current iframe.
attachToIframe(findPrimaryIframe());
// In some cases Disqus replaces the iframe node. Watch the container for changes.
const containerObs = new MutationObserver(function(){
const iframe = findPrimaryIframe();
if (iframe) attachToIframe(iframe);
});
containerObs.observe($thread.get(0), { childList: true, subtree: true });
// As a last resort, re-apply on window resize (layout can change Disqus height).
jQuery(window).on('resize', function(){
const iframe = findPrimaryIframe();
if (iframe) applyFloor(iframe);
});
})();
});