MediaWiki:Common.js
Erscheinungsbild
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/* === PREPRINT START === */
/* ============================================
PREPRINT v7.17 - JS v260213r1
ARCHITECTURE:
- Platform Adaptation Layer (PAL): isolates Minerva workarounds
- Model Layer: dimensional intent, linkage fabric, projection
- Feature Layer: BookNav, TOC, Snippets
============================================ */
(function() {
'use strict';
// Prevent repeated initialization
if (window.preprintInitialized) {
console.log('Preprint: Already initialized, skipping');
return;
}
window.preprintInitialized = true;
/* ============================================
PLATFORM ADAPTATION LAYER (PAL)
Contracts:
- PAL.ready(): Promise that resolves when platform is stable
- PAL.getDeviceWidth(): returns actual device width in CSS pixels
- PAL.isNarrow(): returns true if width < 768
- PAL.onProjectionChange(callback): fires when orientation stable
============================================ */
var PAL = (function() {
var isMinerva = mw.config.get('skin') === 'minerva';
var viewportSource = 'unknown';
var domStabilized = false;
var imagesStabilized = false;
// Debug logging with source tracking
function log(adapter, message) {
console.log('Preprint PAL.' + adapter + ': ' + message);
}
/* --- G.2.1 DOM Readiness Adapter --- */
function waitForDOMStability() {
return new Promise(function(resolve) {
if (!isMinerva) {
// Desktop: DOM is ready immediately after document.ready
domStabilized = true;
log('DOM', 'Desktop mode, DOM ready');
resolve();
return;
}
// Minerva: observe mutations, unwrap sections as they appear
var debounceTimer = null;
var timeoutTimer = null;
var observer = null;
function cleanup() {
if (observer) observer.disconnect();
clearTimeout(debounceTimer);
clearTimeout(timeoutTimer);
}
function done(reason) {
cleanup();
disableMinervaCollapse();
domStabilized = true;
log('DOM', reason);
resolve();
}
function unwrapSections() {
var sections = $('.mw-parser-output > section');
if (sections.length > 0) {
sections.each(function() {
$(this).replaceWith($(this).contents());
});
log('DOM', 'Unwrapped ' + sections.length + ' sections');
return true;
}
return false;
}
function resetDebounce() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function() {
done('Stable (100ms silence)');
}, 100);
}
var parserOutput = document.querySelector('.mw-parser-output');
if (!parserOutput) {
done('No .mw-parser-output found');
return;
}
observer = new MutationObserver(function() {
unwrapSections();
resetDebounce();
});
observer.observe(parserOutput, { childList: true, subtree: true });
// Unwrap any sections already present
unwrapSections();
// Start silence timer
resetDebounce();
// Timeout fallback: 1000ms
timeoutTimer = setTimeout(function() {
done('Timeout (1000ms fallback)');
}, 1000);
});
}
function disableMinervaCollapse() {
function sweep() {
$('.mw-parser-output section').removeClass('collapsible-block');
$('[hidden]').removeAttr('hidden');
$('.collapsible-heading').removeClass('collapsible-heading');
$('.section-heading').removeClass('section-heading');
$('.mf-section-hidden').removeClass('mf-section-hidden');
$('.toggle-indicator, .mf-icon').remove();
}
sweep();
// Minerva re-creates collapse after PAL cleanup; CSS hides
// instantly (no flash), delayed sweep cleans DOM structure
setTimeout(sweep, 2000);
log('DOM', 'Minerva collapse disabled (CSS+delayed sweep)');
}
/* --- G.2.2 Viewport Adapter --- */
function initViewport() {
if (!isMinerva) {
viewportSource = 'standard';
log('Viewport', 'Using window.innerWidth (standard)');
return;
}
// Check if Minerva has set problematic viewport
var viewportMeta = $('meta[name="viewport"]');
var content = viewportMeta.attr('content') || '';
if (content.indexOf('width=1120') !== -1 ||
content.indexOf('width=980') !== -1) {
// Override to device-width
viewportMeta.attr('content', 'width=device-width, initial-scale=1');
viewportSource = 'minerva-override';
log('Viewport', 'Overrode Minerva viewport meta');
} else {
viewportSource = 'minerva-native';
log('Viewport', 'Minerva viewport already sane');
}
// Add data attribute for debugging
document.body.setAttribute('data-viewport-source', viewportSource);
}
function getDeviceWidth() {
if (isMinerva) {
// Minerva: always use screen.width (viewport meta unreliable)
return screen.width;
}
// Desktop: use innerWidth if sane, fallback to screen.width
if (window.innerWidth > 0 && window.innerWidth < screen.width * 1.5) {
return window.innerWidth;
}
return screen.width;
}
function isNarrow() {
return getDeviceWidth() < 768;
}
/* --- G.2.3 Image Stability Adapter --- */
function ensureImageStability() {
return new Promise(function(resolve) {
if (!isMinerva) {
// Desktop: images load normally
imagesStabilized = true;
log('Image', 'Desktop mode, no lazy placeholders');
resolve();
return;
}
var placeholders = $('.mw-parser-output .lazy-image-placeholder');
if (placeholders.length === 0) {
imagesStabilized = true;
log('Image', 'No lazy placeholders found');
resolve();
return;
}
var total = placeholders.length;
var loaded = 0;
var failed = 0;
var timeout = null;
function checkComplete() {
if (loaded + failed >= total) {
clearTimeout(timeout);
imagesStabilized = true;
log('Image', 'Complete: ' + loaded + ' loaded, ' + failed + ' failed');
resolve();
}
}
placeholders.each(function() {
var $placeholder = $(this);
var src = $placeholder.attr('data-mw-src');
var width = $placeholder.attr('data-width');
var height = $placeholder.attr('data-height');
var imgClass = $placeholder.attr('data-class') || '';
if (!src) {
failed++;
checkComplete();
return;
}
var $img = $('<img>')
.attr('width', width)
.attr('height', height)
.addClass(imgClass)
.on('load', function() {
loaded++;
checkComplete();
})
.on('error', function() {
failed++;
$(this).addClass('preprint-image-failed');
checkComplete();
})
.attr('src', src); // Set src last to trigger load
$placeholder.replaceWith($img);
});
// Timeout fallback: 2000ms
timeout = setTimeout(function() {
log('Image', 'Timeout: ' + loaded + ' loaded, ' + (total - loaded) + ' pending');
imagesStabilized = true;
resolve();
}, 2000);
});
}
/* --- G.2.5 TOC Stability Adapter (Minerva) --- */
function waitForTOCStability() {
return new Promise(function(resolve) {
if (!isMinerva) {
log('TOC', 'Desktop mode, no wait needed');
resolve();
return;
}
var attempts = 0;
var maxAttempts = 20; // 20 * 50ms = 1000ms max
function poll() {
attempts++;
if ($('.toc').length > 0) {
log('TOC', 'Found after ' + attempts + ' polls');
resolve();
} else if (attempts >= maxAttempts) {
log('TOC', 'Timeout, no TOC found (OK for pages without headings)');
resolve();
} else {
setTimeout(poll, 50);
}
}
poll();
});
}
/* --- G.2.4 Orientation Adapter --- */
var orientationCallbacks = [];
var preChangeCallbacks = [];
var currentOrientation = null;
var orientationLocked = false;
function initOrientationHandler() {
currentOrientation = isNarrow() ? 'portrait' : 'landscape';
setOrientationClass(currentOrientation);
var resizeTimer = null;
var transitionGeneration = 0;
var resizeTimer = null;
var transitionGeneration = 0;
$(window).on('resize orientationchange', function() {
var targetOrientation = isNarrow() ? 'portrait' : 'landscape';
if (targetOrientation === currentOrientation) {
// Cancel any in-flight transition (timer or promise)
if (orientationLocked) {
clearTimeout(resizeTimer);
transitionGeneration++;
setOrientationClass(currentOrientation);
orientationLocked = false;
// Restore layout visibility (pre-change hid it)
var el = document.querySelector('.preprint-layout');
if (el) { el.style.transition = 'opacity 0.15s'; el.style.opacity = '1'; }
log('Orientation', 'Cancelled pending transition, staying ' + currentOrientation);
}
return;
}
// Fire pre-change callbacks IMMEDIATELY (anchor capture)
preChangeCallbacks.forEach(function(cb) {
try {
cb({ from: currentOrientation, to: targetOrientation });
} catch (e) {
console.error('Preprint: Pre-change callback error', e);
}
});
// Lock during transition, invalidate any in-flight promise
orientationLocked = true;
clearTimeout(resizeTimer);
var myGeneration = ++transitionGeneration;
resizeTimer = setTimeout(function() {
setOrientationClass(targetOrientation);
// Force-load images if going to landscape and not yet loaded
var imagePromise = (targetOrientation === 'landscape' && !imagesStabilized)
? ensureImageStability()
: Promise.resolve();
Promise.all([waitForLayoutStability(targetOrientation), imagePromise]).then(function() {
// Stale transition: a newer one superseded us
if (myGeneration !== transitionGeneration) {
log('Orientation', 'Dropped stale transition (gen ' + myGeneration + ', current ' + transitionGeneration + ')');
return;
}
var previousOrientation = currentOrientation;
currentOrientation = targetOrientation;
// Fire callbacks
var event = {
from: previousOrientation,
to: targetOrientation
};
orientationCallbacks.forEach(function(cb) {
try {
cb(event);
} catch (e) {
console.error('Preprint: Orientation callback error', e);
}
});
orientationLocked = false;
});
}, 150);
});
// Wake-detect: device sleep doesn't fire resize/orientationchange
document.addEventListener('visibilitychange', function() {
if (document.visibilityState !== 'visible') return;
var targetOrientation = isNarrow() ? 'portrait' : 'landscape';
if (targetOrientation !== currentOrientation) {
log('Orientation', 'Wake: ' + currentOrientation + ' -> ' + targetOrientation);
$(window).trigger('resize');
}
});
}
function waitForLayoutStability(targetOrientation) {
return new Promise(function(resolve) {
// Set class first so CSS applies
setOrientationClass(targetOrientation);
var layout = document.querySelector('.preprint-layout');
if (!layout) {
resolve();
return;
}
var lastWidth = -1;
var lastHeight = -1;
var stableCount = 0;
var attempts = 0;
var maxAttempts = 20; // 20 * 50ms = 1000ms max
function poll() {
attempts++;
var rect = layout.getBoundingClientRect();
if (rect.width === lastWidth && rect.height === lastHeight) {
stableCount++;
if (stableCount >= 2) {
// Stable for 100ms
log('Orientation', 'Layout stable after ' + attempts + ' polls');
resolve();
return;
}
} else {
stableCount = 0;
lastWidth = rect.width;
lastHeight = rect.height;
}
if (attempts >= maxAttempts) {
log('Orientation', 'Timeout after ' + attempts + ' polls (fallback)');
resolve();
return;
}
setTimeout(poll, 50);
}
// Start after a reflow
requestAnimationFrame(function() {
poll();
});
});
}
function setOrientationClass(orientation) {
if (orientation === 'portrait') {
$('body').addClass('preprint-portrait').removeClass('preprint-landscape');
} else {
$('body').addClass('preprint-landscape').removeClass('preprint-portrait');
}
}
function onProjectionChange(callback) {
orientationCallbacks.push(callback);
}
function onPreProjectionChange(callback) {
preChangeCallbacks.push(callback);
}
function isOrientationLocked() {
return orientationLocked;
}
/* --- PAL.ready() --- */
function ready() {
return new Promise(function(resolve) {
$(document).ready(function() {
// Check activation first
if (!$('#preprint-magic').length) {
log('ready', 'No #preprint-magic, skipping');
resolve({ activated: false });
return;
}
if ($('#mw-content-text').hasClass('preprint-processed')) {
log('ready', 'Already processed, skipping');
resolve({ activated: false });
return;
}
log('ready', 'Starting PAL initialization');
// Sequential initialization
initViewport();
waitForDOMStability()
.then(function() {
if (isNarrow()) {
// Portrait: TOC only, images load progressively
return waitForTOCStability();
}
// Landscape: images + TOC in parallel
return Promise.all([ensureImageStability(), waitForTOCStability()]);
})
.then(function() {
initOrientationHandler();
log('ready', 'PAL ready (Minerva=' + isMinerva + ')');
resolve({
activated: true,
isMinerva: isMinerva,
viewportSource: viewportSource
});
});
});
});
}
// Public API
return {
ready: ready,
getDeviceWidth: getDeviceWidth,
isNarrow: isNarrow,
onProjectionChange: onProjectionChange,
onPreProjectionChange: onPreProjectionChange,
isOrientationLocked: isOrientationLocked,
isMinerva: function() { return isMinerva; },
disableMinervaCollapse: disableMinervaCollapse
};
})();
/* ============================================
MODEL LAYER
Implements: Projection-Invariant Content Linkage
- Dimensional Intent: IC, OC, LH, 2C (from markup)
- Linkage Fabric: markers <-> elements (bidirectional)
- Projection: landscape grid | portrait flow
- User State: element-relative position tracking
============================================ */
var Model = (function() {
var linkage = []; // Array of {marker: $marker, element: $element, type: string}
var layout = null;
var positionAnchor = null; // {element: DOM, type: string, headingId: string}
var returnTarget = null;
/* --- Element Classification (Dimensional Intent) --- */
function classifyElement($el) {
// 2C types
if ($el.hasClass('preprint-2c-wrapper')) return 'wrapper-2c';
if ($el.hasClass('place-2c')) return 'place-2c';
// OC types (will create linkage)
if ($el.hasClass('place-oc')) return 'place-oc';
if ($el.hasClass('place-lh')) return 'place-lh';
if ($el.hasClass('preprint-table-oc-wrapper')) return 'table-oc';
// IC types
if ($el.hasClass('place-ic')) return 'place-ic';
if ($el.hasClass('preprint-table-ic-wrapper')) return 'table-ic';
// Structure
if ($el.hasClass('book-nav') || $el.hasClass('book-progress')) return 'book-nav';
if ($el.is('h1, h2, h3, h4, h5, h6')) return 'heading';
// Skip
if ($el.attr('id') === 'preprint-magic') return 'skip';
// Default: content for IC
return 'content';
}
/* --- Linkage Fabric --- */
function createMarker($element, type) {
var $marker = $('<span class="oc-marker"></span>');
$marker.data('preprintTarget', $element);
$marker.data('preprintType', type);
// Reverse link: element knows its marker
$element.data('preprintMarker', $marker);
linkage.push({
marker: $marker,
element: $element,
type: type
});
return $marker;
}
function getLinkage() {
return linkage;
}
/* --- Structure Building --- */
function buildStructure() {
var parserOutput = $('.mw-parser-output');
var elements = parserOutput.children().toArray();
layout = $('<div class="preprint-layout"></div>');
var currentInner = null;
var currentOuter = null;
var pendingOC = [];
var bookNavElements = [];
// Process each element
elements.forEach(function(el) {
var $el = $(el);
var type = classifyElement($el);
switch (type) {
case 'wrapper-2c':
case 'place-2c':
// 2C: flush columns, append at grid root
flushColumns();
layout.append($el);
break;
case 'place-oc':
case 'place-lh':
case 'table-oc':
// OC types: create marker in IC, collect element
ensureColumns();
var marker = createMarker($el, type);
currentInner.append(marker);
pendingOC.push($el);
break;
case 'book-nav':
// Collect for later insertion before layout
bookNavElements.push($el);
break;
case 'heading':
// Headings create section boundaries
flushColumns();
ensureColumns();
currentInner.append($el);
break;
case 'skip':
// Remove magic div
$el.remove();
break;
default:
// Content: append to IC, check for nested OC
ensureColumns();
currentInner.append($el);
// Find nested OC/LH elements
$el.find('.place-oc, .place-lh').each(function() {
var $nested = $(this);
var nestedType = $nested.hasClass('place-lh') ? 'place-lh' : 'place-oc';
var nestedMarker = createMarker($nested, nestedType);
$nested.before(nestedMarker);
pendingOC.push($nested.detach());
});
}
});
// Final flush
flushColumns();
// Replace content
parserOutput.empty().append(layout);
// Prepend BookNav before layout
bookNavElements.reverse().forEach(function($el) {
layout.before($el);
});
// Mark processed
$('#mw-content-text').addClass('preprint-processed');
// Mark LH elements with media
$('.place-lh').each(function() {
var $lh = $(this);
if ($lh.find('img, .lazy-image-placeholder, .mw-file-element').length > 0) {
$lh.addClass('lh-has-media');
}
});
console.log('Preprint Model: Structure built, ' + linkage.length + ' linkages');
// Helper functions (closure)
function ensureColumns() {
if (!currentInner) {
currentInner = $('<div class="inner-column"></div>');
currentOuter = $('<div class="outer-column"></div>');
}
}
function flushColumns() {
if (currentInner && currentInner.children().length > 0) {
// Move pending OC to outer column
pendingOC.forEach(function($el) {
currentOuter.append($el);
});
pendingOC = [];
// Append columns
layout.append(currentInner);
layout.append(currentOuter);
currentInner = null;
currentOuter = null;
}
}
return layout;
}
/* --- Projection --- */
function applyProjection(mode) {
if (mode === 'portrait') {
applyPortraitProjection();
} else {
applyLandscapeProjection();
}
}
function applyPortraitProjection() {
if (layout && layout[0]) layout[0].style.paddingBottom = '';
$('.preprint-oc-spacer').remove();
// Clear inline positioning
linkage.forEach(function(link) {
link.element[0].style.position = '';
link.element[0].style.top = '';
});
// Separate elements by portrait placement
var topElements = [];
var bottomElements = [];
var defaultElements = [];
linkage.forEach(function(link) {
var placement = link.element.attr('data-portrait');
if (placement === 'top') {
topElements.push(link);
} else if (placement === 'bottom') {
bottomElements.push(link);
} else {
defaultElements.push(link);
}
});
// Default: move to marker position
defaultElements.forEach(function(link) {
link.element.detach();
link.marker.after(link.element);
});
// Top: insert before first content in first IC
if (topElements.length > 0) {
var $firstIC = layout.find('.inner-column').first();
if ($firstIC.length) {
var $firstChild = $firstIC.children().first();
topElements.forEach(function(link) {
link.element.detach();
if ($firstChild.length) {
$firstChild.before(link.element);
} else {
$firstIC.prepend(link.element);
}
});
}
}
// Bottom: append after last content in last IC
if (bottomElements.length > 0) {
var $lastIC = layout.find('.inner-column').last();
if ($lastIC.length) {
bottomElements.forEach(function(link) {
link.element.detach();
$lastIC.append(link.element);
});
}
}
scrollToHash();
console.log('Preprint Model: Portrait projection applied');
}
function applyLandscapeProjection() {
// Move elements back to their correct outer columns via linkage
linkage.forEach(function(link) {
var $oc = link.marker.closest('.inner-column').next('.outer-column');
if ($oc.length) {
link.element.detach();
$oc.append(link.element);
}
});
// Reset scroll for accurate offset calculations
var savedBodyScroll = document.body.scrollTop;
var savedHtmlScroll = document.documentElement.scrollTop;
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
// Force reflow then position
if (layout && layout[0]) {
void layout[0].offsetHeight;
}
reconcileLayout();
// Restore scroll
document.body.scrollTop = savedBodyScroll;
document.documentElement.scrollTop = savedHtmlScroll;
scrollToHash();
console.log('Preprint Model: Landscape projection applied');
}
function reconcileLayout() {
if (!document.body.classList.contains('preprint-landscape')) return;
if (reconcileLayout._running) return;
reconcileLayout._running = true;
// Disconnect observer to prevent re-entry from our writes
if (layout._ocObserver) layout._ocObserver.disconnect();
// === PHASE 1: CLEAN + READ ===
// Remove all previous modifications
$('.preprint-oc-spacer').remove();
if (layout && layout[0]) layout[0].style.paddingBottom = '';
// Reset OC positions
linkage.forEach(function(link) {
link.element[0].style.top = '0px';
});
// Force reflow
void document.body.offsetHeight;
window._dbgClean = $('.oc-marker').first().offset().top;
// Read marker positions, OC container offsets, element geometry
var items = [];
linkage.forEach(function(link, i) {
var $oc = link.element.closest('.outer-column');
if (!$oc.length) { items.push(null); return; }
var markerTop = link.marker.offset().top;
var ocTop = $oc.offset().top;
items.push({
index: i,
link: link,
markerTop: markerTop,
ocTop: ocTop,
ocEl: $oc[0],
height: link.element.outerHeight(true),
margin: parseFloat(link.element.css('margin-top')) || 0,
landscapeDir: link.element.attr('data-landscape') || null,
top: markerTop - ocTop,
docTop: markerTop
});
});
// Read first visible IC child offset per OC container (for elevator)
var ocContainers = [];
var ocFirstICTop = [];
$('.outer-column').each(function() {
ocContainers.push(this);
var $ic = $(this).prev('.inner-column');
if (!$ic.length) { ocFirstICTop.push(0); return; }
var ocTop = $(this).offset().top;
var found = false;
$ic.children().each(function() {
if (this.getBoundingClientRect().height > 0) {
ocFirstICTop.push($(this).offset().top - ocTop);
found = true;
return false;
}
});
if (!found) ocFirstICTop.push(0);
});
// Read protruding IC elements
var protrusions = [];
$('.inner-column').each(function() {
var icWidth = this.getBoundingClientRect().width;
$(this).children().each(function() {
if (this.classList.contains('oc-marker')) return;
var elWidth = this.scrollWidth || this.getBoundingClientRect().width;
if (elWidth > icWidth + 1) {
var top = $(this).offset().top;
protrusions.push({
element: this,
top: top,
bottom: top + this.getBoundingClientRect().height
});
}
});
});
// === PHASE 2: COMPUTE (no DOM access) ===
// Elevator adjustments
items.forEach(function(item) {
if (!item || !item.landscapeDir) return;
var siblings = items.filter(function(other) {
return other && other.ocEl === item.ocEl && other.index !== item.index;
}).sort(function(a, b) { return a.top - b.top; });
if (item.landscapeDir === 'top') {
var above = null;
for (var i = siblings.length - 1; i >= 0; i--) {
if (siblings[i].top < item.top) { above = siblings[i]; break; }
}
if (above) {
item.top = above.top + above.height - item.margin;
} else {
var ocIdx = ocContainers.indexOf(item.ocEl);
if (ocIdx >= 0) {
item.top = ocFirstICTop[ocIdx] - item.margin;
}
}
} else if (item.landscapeDir === 'bottom') {
var below = null;
for (var i = 0; i < siblings.length; i++) {
if (siblings[i].top > item.top) { below = siblings[i]; break; }
}
if (below) {
item.top = below.top - item.height;
}
}
item.docTop = item.ocTop + item.top;
});
// Fix overlaps
var valid = items.filter(function(item) { return item !== null; });
valid.sort(function(a, b) { return a.docTop - b.docTop; });
var lastBottom = 0;
valid.forEach(function(item) {
var overlap = lastBottom - item.docTop;
if (overlap > 0) {
item.top += overlap;
item.docTop += overlap;
}
lastBottom = item.docTop + item.height + 16;
});
// Conflict detection: protruding IC vs computed OC positions
var spacers = [];
protrusions.forEach(function(prot) {
var maxConflictBottom = 0;
valid.forEach(function(item) {
var ocBottom = item.docTop + item.height;
if (prot.top < ocBottom && prot.bottom > item.docTop) {
if (ocBottom > maxConflictBottom) maxConflictBottom = ocBottom;
}
});
if (maxConflictBottom > 0) {
var pushdown = maxConflictBottom - prot.top;
if (pushdown > 0) {
spacers.push({ element: prot.element, height: pushdown });
}
}
});
// === PHASE 3: WRITE (single pass) ===
// Set OC positions
valid.forEach(function(item) {
item.link.element.css({
'position': 'absolute',
'top': item.top + 'px'
});
});
// Insert spacers
spacers.forEach(function(s) {
var spacer = document.createElement('div');
spacer.className = 'preprint-oc-spacer';
spacer.style.height = s.height + 'px';
s.element.parentNode.insertBefore(spacer, s.element);
});
// Final measure: layout padding
void document.body.offsetHeight;
var scrollY = window.pageYOffset || 0;
var layoutBottom = layout[0].getBoundingClientRect().bottom + scrollY;
var maxOCBottom = 0;
valid.forEach(function(item) {
var rect = item.link.element[0].getBoundingClientRect();
if (rect.bottom + scrollY > maxOCBottom) maxOCBottom = rect.bottom + scrollY;
});
if (maxOCBottom > layoutBottom) {
layout[0].style.paddingBottom = (maxOCBottom - layoutBottom) + 'px';
}
reconcileLayout._running = false;
// Reconnect or create observer
if (!layout._ocObserver) {
layout._ocObserver = new MutationObserver(function() {
clearTimeout(layout._ocDebounce);
layout._ocDebounce = setTimeout(reconcileLayout, 300);
});
}
layout._ocObserver.observe(layout[0], {
subtree: true, attributes: true, attributeFilter: ['class']
});
}
// Reposition after images load (initial positioning may precede image render)
$('.preprint-layout img').on('load', function() {
if (document.body.classList.contains('preprint-landscape')) {
reconcileLayout();
}
});
$(window).on('resize', (function() {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(function() {
if (PAL.isOrientationLocked()) return;
if (document.body.classList.contains('preprint-landscape')) {
reconcileLayout();
}
}, 200);
};
})());
function scrollToHash() {
// Check return target first (set by Features.initBookNavFooter)
if (returnTarget) {
var el = document.getElementById(returnTarget);
if (el) {
el.scrollIntoView({ block: 'start' });
window.scrollBy(0, -60);
console.log('Preprint Model: Scrolled to return target ' + returnTarget);
}
returnTarget = null;
return;
}
// Existing hash handling
if (!window.location.hash) return;
var target = document.getElementById(window.location.hash.slice(1));
if (target) {
target.scrollIntoView({ block: 'start' });
window.scrollBy(0, -60);
console.log('Preprint Model: Scrolled to hash ' + window.location.hash);
}
}
/* --- User State (Position Tracking) --- */
function captureAnchor() {
if (PAL.isOrientationLocked()) return;
var viewportTop = 70; // Account for fixed headers
var isLandscape = !PAL.isNarrow();
positionAnchor = null;
var headingFallback = null;
// Find fallback heading
var fallbackHeadingId = null;
$('.preprint-layout').find('h2, h3, h4, h5').each(function() {
var rect = this.getBoundingClientRect();
if (rect.top <= viewportTop + 50) {
var id = $(this).find('.mw-headline').attr('id') || this.id;
if (id && id !== 'mw-toc-heading') {
fallbackHeadingId = id;
}
}
});
// Check 2C elements first
var found = false;
$('.preprint-layout > .preprint-2c-wrapper').each(function() {
var rect = this.getBoundingClientRect();
if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
positionAnchor = { element: this, type: '2C', headingId: fallbackHeadingId };
found = true;
return false;
}
});
if (found) return;
// Check IC children (all inner-columns)
$('.preprint-layout .inner-column').each(function() {
if (found) return false;
$(this).children().each(function() {
var $el = $(this);
// Skip OC elements in portrait
if (!isLandscape && ($el.hasClass('place-oc') || $el.hasClass('place-lh') ||
$el.hasClass('preprint-table-oc-wrapper'))) {
return true;
}
// Skip headings (prefer content), but remember as fallback
if ($el.is('h1,h2,h3,h4,h5,h6')) {
if (!headingFallback) {
var hRect = $el[0].getBoundingClientRect();
if (hRect.top < viewportTop + 50 && hRect.bottom > 0 && hRect.height > 10) {
headingFallback = { element: $el[0], type: 'heading', headingId: fallbackHeadingId };
}
}
return true;
}
var rect = this.getBoundingClientRect();
if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
positionAnchor = { element: this, type: this.tagName.toLowerCase(), headingId: fallbackHeadingId };
found = true;
return false;
}
});
});
if (found) return;
// Check OC elements in landscape
if (isLandscape) {
$('.preprint-layout .outer-column').children().each(function() {
var rect = this.getBoundingClientRect();
if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
positionAnchor = { element: this, type: 'oc', headingId: fallbackHeadingId };
return false;
}
});
}
// Use heading if no content element found
if (!positionAnchor && headingFallback) {
positionAnchor = headingFallback;
}
}
function restoreAnchor() {
if (!positionAnchor) return;
if (positionAnchor.element && document.contains(positionAnchor.element)) {
positionAnchor.element.scrollIntoView({ block: 'start' });
window.scrollBy(0, -60);
} else if (positionAnchor.headingId) {
var el = document.getElementById(positionAnchor.headingId);
if (el) {
el.scrollIntoView({ block: 'start' });
window.scrollBy(0, -60);
}
}
}
function getPositionAnchor() {
return positionAnchor;
}
// Public API
return {
classifyElement: classifyElement,
buildStructure: buildStructure,
getLinkage: getLinkage,
applyProjection: applyProjection,
positionOCElements: reconcileLayout,
captureAnchor: captureAnchor,
restoreAnchor: restoreAnchor,
getPositionAnchor: getPositionAnchor,
getLayout: function() { return layout; },
set returnTarget(id) { returnTarget = id; }
};
})();
/* ============================================
FEATURE LAYER
Built on Model, provides user-facing features
============================================ */
var Features = (function() {
/* --- Icon Positioning --- */
var iconMetrics = null; // Calculated once from landscape
function calculateIconMetrics() {
var layout = document.querySelector('.preprint-layout');
if (!layout) return;
var icon = document.querySelector('.book-nav-icon');
if (!icon) return;
var layoutRect = layout.getBoundingClientRect();
var viewportWidth = document.documentElement.clientWidth;
var deadZone = viewportWidth - layoutRect.right;
var iconWidth = icon.getBoundingClientRect().width;
if (deadZone < iconWidth + 4) return; // Doesn't fit
var margin = Math.round((deadZone - iconWidth) / 2);
iconMetrics = { margin: margin };
applyIconMetrics();
}
function applyIconMetrics() {
var icon = document.querySelector('.book-nav-icon');
if (!icon) return;
if (PAL.isNarrow() || !iconMetrics) {
icon.style.right = '';
icon.style.bottom = '';
return;
}
icon.style.right = iconMetrics.margin + 'px';
icon.style.bottom = iconMetrics.margin + 'px';
}
/* --- BookNav Icon --- */
function initBookNavFooter() {
var bookNavData = $('.book-nav-data');
if (!bookNavData.length) return;
var book = bookNavData.data('book');
var rootAnchor = bookNavData.data('root-anchor');
var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
var bookRoot = (book || '').replace(/_/g, ' ');
var isBookRoot = (currentPage === bookRoot);
// Navigation icon: Minerva only
if (PAL.isMinerva()) {
var $icon = $('<a class="book-nav-icon"></a>');
if (isBookRoot) {
$icon.attr('href', '/');
} else {
$icon.attr('href', '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : ''));
}
var logos = mw.config.get('wgLogos') || {};
var iconSrc = logos.svg || logos.icon || '/images/logo/logo.svg';
$icon.append($('<img>').attr('src', iconSrc).attr('alt', ''));
if (isBookRoot) {
$icon.on('click', function(e) {
e.preventDefault();
var saved = sessionStorage.getItem('preprint-position');
if (saved) {
var pos = JSON.parse(saved);
window.location.href = '/wiki/' + pos.article;
} else {
window.location.href = '/';
}
});
} else {
$icon.on('click', function(e) {
e.preventDefault();
Model.captureAnchor();
var anchor = Model.getPositionAnchor();
var pos = {
article: mw.config.get('wgPageName'),
headingId: anchor ? anchor.headingId : null
};
sessionStorage.setItem('preprint-position', JSON.stringify(pos));
window.location.href = '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '');
});
}
$('body').append($icon);
}
if (!isBookRoot && !window.location.hash) {
var saved = sessionStorage.getItem('preprint-position');
if (saved) {
var pos = JSON.parse(saved);
if (pos.article === mw.config.get('wgPageName') && pos.headingId) {
Model.returnTarget = pos.headingId;
sessionStorage.removeItem('preprint-position');
}
}
}
console.log('Preprint Features: BookNav icon (root=' + isBookRoot + ')');
}
/* --- Sidebar Navigation Link (Vector only) --- */
function initSidebarNav() {
if (PAL.isMinerva()) return;
var bookNavData = $('.book-nav-data');
if (!bookNavData.length) return;
var book = bookNavData.data('book');
if (!book) return;
var rootAnchor = bookNavData.data('root-anchor') || '';
var $landmark = $('.vector-page-tools-landmark').first();
if (!$landmark.length) return;
var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
var bookRoot = (book || '').replace(/_/g, ' ');
var isBookRoot = (currentPage === bookRoot);
var lang = mw.config.get('wgContentLanguage') || '';
var $link = $('<a class="book-nav-sidebar"></a>')
.css({ display: 'inline-block', padding: '12px 12px 7px', fontSize: '14px', color: '#36c' });
if (isBookRoot) {
var label = lang.indexOf('de') === 0
? 'Letzte Seite' : 'Last Page';
$link.attr('href', '#').text(label);
var saved = sessionStorage.getItem('preprint-position');
if (!saved) {
$link.css({ color: '#ccc', pointerEvents: 'none' });
}
$link.on('click', function(e) {
e.preventDefault();
var pos = JSON.parse(sessionStorage.getItem('preprint-position') || 'null');
if (pos) {
window.location.href = '/wiki/' + pos.article;
} else {
window.location.href = '/';
}
});
} else {
var label = lang.indexOf('de') === 0
? 'Inhaltsverzeichnis' : 'Table of Contents';
$link.attr('href', '/wiki/' + book).text(label);
$link.on('click', function(e) {
e.preventDefault();
Model.captureAnchor();
var anchor = Model.getPositionAnchor();
var pos = {
article: mw.config.get('wgPageName'),
headingId: anchor ? anchor.headingId : null
};
sessionStorage.setItem('preprint-position', JSON.stringify(pos));
window.location.href = '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '');
});
}
$landmark.before($link);
console.log('Preprint Features: Sidebar nav (root=' + isBookRoot + ')');
}
/* --- Snippet Support --- */
function initSnippetSupport() {
if (!$('.preprint-layout').length) return;
var $button = null;
function createButton() {
if (!$button) {
$button = $('<button class="snippet-button"></button>');
$button.text($('#ca-talk a').text() || 'Diskussion');
$button.hide();
$('body').append($button);
$button.on('click', function() {
var selection = window.getSelection();
var text = selection.toString().trim();
if (text.length < 2) {
$button.hide();
return;
}
// Find nearest heading
var range = selection.getRangeAt(0);
var $container = $(range.startContainer).closest('p, div, li').first();
var $heading = $container.prevAll('h1,h2,h3,h4,h5,h6').first();
if (!$heading.length) {
$heading = $container.parent().prevAll('h1,h2,h3,h4,h5,h6').first();
}
if (!$heading.length) {
$heading = $container.closest('.inner-column').find('h1,h2,h3,h4,h5,h6').last();
}
var anchorId = $heading.find('.mw-headline').attr('id') || $heading.attr('id') || '';
var pageName = mw.config.get('wgPageName').replace(/_/g, ' ');
localStorage.setItem('preprint-snippet', JSON.stringify({
page: pageName,
anchor: anchorId,
text: text,
timestamp: Date.now()
}));
var talkPage = $('#ca-talk a').attr('href');
if (talkPage) {
window.location.href = talkPage + '?action=edit§ion=new';
}
$button.hide();
});
}
return $button;
}
$(document).on('mouseup', function() {
setTimeout(function() {
var selection = window.getSelection();
var text = selection.toString().trim();
if (text.length >= 2) {
var btn = createButton();
var range = selection.getRangeAt(0);
var rect = range.getBoundingClientRect();
btn.css({
position: 'fixed',
top: (rect.bottom + 5) + 'px',
left: (rect.left + rect.width / 2 - 40) + 'px',
zIndex: 10000
});
btn.show();
} else if ($button) {
$button.hide();
}
}, 10);
});
$(document).on('mousedown', function(e) {
if ($button && !$(e.target).is('.snippet-button')) {
$button.hide();
}
});
}
/* --- Snippet Pre-fill (edit pages) --- */
function initSnippetPrefill() {
if (mw.config.get('wgAction') !== 'edit') return;
var data = localStorage.getItem('preprint-snippet');
if (!data) return;
var snippet = JSON.parse(data);
// Only use if recent (5 minutes)
if (Date.now() - snippet.timestamp > 300000) {
localStorage.removeItem('preprint-snippet');
return;
}
var sectionTitle = snippet.anchor ? 'Re: ' + snippet.anchor.replace(/_/g, ' ') : 'Anmerkung';
var text = '== ' + sectionTitle + ' ==\n';
text += '{{Snippet\n';
text += '|page=' + snippet.page + '\n';
if (snippet.anchor) {
text += '|anchor=' + snippet.anchor + '\n';
}
text += '|text=' + snippet.text + '\n';
text += '}}\n\n';
text += '~~' + '~~';
var $textarea = $('#wpTextbox1');
if ($textarea.length && $textarea.val() === '') {
$textarea.val(text);
var cursorPos = text.lastIndexOf('\n~~' + '~~');
$textarea[0].setSelectionRange(cursorPos, cursorPos);
$textarea.focus();
}
localStorage.removeItem('preprint-snippet');
}
/* --- TOC Suppression --- */
function suppressTOC() {
var isPreprintLayout = $('.preprint-layout').length > 0;
var bookNavData = $('.book-nav-data');
var isBookRoot = false;
if (bookNavData.length) {
var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
var bookRoot = (bookNavData.data('book') || '').replace(/_/g, ' ');
isBookRoot = (currentPage === bookRoot);
}
if (isBookRoot && PAL.isMinerva()) {
// Mobile: root page IS the TOC, hide MW-generated one
$('.toc, #toc').hide();
console.log('Preprint Features: TOC suppressed (bookRoot+Minerva)');
} else if (isPreprintLayout && bookNavData.length && PAL.isMinerva()) {
// Mobile book chapters: linear reading, no TOC
$('.toc, #toc').hide();
console.log('Preprint Features: TOC suppressed (chapter+BookNav+Minerva)');
}
// Desktop: TOC always visible (sticky sidebar navigation)
}
// Public API
return {
calculateIconMetrics: calculateIconMetrics,
applyIconMetrics: applyIconMetrics,
initBookNavFooter: initBookNavFooter,
initSidebarNav: initSidebarNav,
suppressTOC: suppressTOC,
initSnippetSupport: initSnippetSupport,
initSnippetPrefill: initSnippetPrefill
};
})();
/* ============================================
INITIALIZATION
============================================ */
// Snippet pre-fill runs on any edit page
$(document).ready(function() {
Features.initSnippetPrefill();
});
// Main initialization
PAL.ready().then(function(status) {
if (!status.activated) {
// Still init BookNav footer on book root pages
if ($('.book-nav-data').length) {
$('body').addClass('preprint-book');
if (PAL.isMinerva()) {
PAL.disableMinervaCollapse();
}
setTimeout(function() {
Features.initBookNavFooter();
if (!PAL.isMinerva()) {
Features.initSidebarNav();
}
Features.suppressTOC();
}, 100);
}
return;
}
console.log('Preprint: PAL ready, building structure');
// Build structure (Model)
Model.buildStructure();
// Apply initial projection after brief delay for layout to settle
var initialMode = PAL.isNarrow() ? 'portrait' : 'landscape';
setTimeout(function() {
Model.applyProjection(initialMode);
$('.preprint-layout').addClass('preprint-ready');
window.preprintReadyAt = performance.now();
}, 50);
// Initialize features
Features.initBookNavFooter();
if (!PAL.isMinerva()) {
Features.initSidebarNav();
}
Features.suppressTOC();
Features.initSnippetSupport();
// Set up anchor tracking
$(document).on('touchend', function() {
setTimeout(function() {
Model.captureAnchor();
}, 400);
});
// Initial anchor capture
setTimeout(function() {
Model.captureAnchor();
}, 500);
// Hide icon early on rotation (before browser reflows)
window.addEventListener('orientationchange', function() {
var icon = document.querySelector('.book-nav-icon');
if (icon) icon.style.visibility = 'hidden';
});
// Calculate icon metrics from landscape
if (!PAL.isNarrow()) {
setTimeout(function() { Features.calculateIconMetrics(); }, 100);
}
// Hide icon early on rotation (before reflow causes visible jump)
PAL.onPreProjectionChange(function() {
var icon = document.querySelector('.book-nav-icon');
if (icon) icon.style.visibility = 'hidden';
var el = document.querySelector('.preprint-layout');
if (el) { el.style.transition = 'none'; el.style.opacity = '0'; }
});
PAL.onProjectionChange(function(event) {
console.log('Preprint: Projection change ' + event.from + ' -> ' + event.to);
// Icon already hidden by onPreProjectionChange
var icon = document.querySelector('.book-nav-icon');
// Apply new projection
Model.applyProjection(event.to);
// Recalculate icon from landscape geometry, then reveal
if (event.to === 'landscape') {
setTimeout(function() {
Features.calculateIconMetrics();
if (icon) icon.style.visibility = '';
}, 100);
} else {
Features.applyIconMetrics();
setTimeout(function() {
if (icon) icon.style.visibility = '';
}, 150);
}
// Restore position
Model.restoreAnchor();
// Reveal layout after projection complete
var el = document.querySelector('.preprint-layout');
if (el) {
setTimeout(function() {
el.style.transition = 'opacity 0.15s';
el.style.opacity = '1';
}, 30);
}
});
// Expose for debugging and external access
window.Preprint = {
PAL: PAL,
Model: Model,
Features: Features
};
// Also expose positionAnchor for BookNav compatibility
Object.defineProperty(window, 'positionAnchor', {
get: function() { return Model.getPositionAnchor(); }
});
mw.loader.load('/wiki/MediaWiki:Common.js/debug.js?action=raw&ctype=text/javascript');
console.log('Preprint: Initialization complete (v7.17)');
});
})();
/* === PREPRINT END === */
/* Unified Consent System JavaScript v250911r1
* Single file for both EN and DE versions
* Automatically detects language based on domain
*/
/* Common JavaScript - Keep only consent system if needed */
// Language detection helper
function getCurrentLanguage() {
var hostname = window.location.hostname;
return (hostname === 'de.transformal.com' ||
hostname === 'de.olaflangmack.info' ||
hostname === 'de.mediawiki.transformal.com') ? 'de' : 'en';
}
// Localized strings
var strings = {
en: {
journalingEnabled: 'Journaling enabled',
journalingDisabled: 'Journaling disabled',
proofOfWorkEnabled: 'Proof-of-work enabled',
proofOfWorkDisabled: 'Proof-of-work disabled',
preferencesSaved: 'Your preferences are being saved',
checkboxLabel: 'Enabled'
},
de: {
journalingEnabled: 'Aufzeichnungen zugelassen',
journalingDisabled: 'Aufzeichnungen nicht zugelassen',
proofOfWorkEnabled: 'Proof-of-work zugelassen',
proofOfWorkDisabled: 'Proof-of-work nicht zugelassen',
preferencesSaved: 'Ihre Einstellungen werden gespeichert',
checkboxLabel: 'Erlaubt'
}
};
function getString(key) {
var lang = getCurrentLanguage();
return strings[lang][key] || strings.en[key];
}
// Handle consent acquisition box - 90-day reminder for everyone
$(document).ready(function() {
var consentBox = document.getElementById('consent-acquisition-box');
if (!consentBox) return;
// Check if dismissed within last 90 days
var dismissedUntil = localStorage.getItem('consent-acquisition-dismissed-until');
var now = Date.now();
if (dismissedUntil && parseInt(dismissedUntil) > now) {
// Still within 90-day dismissal period
consentBox.style.display = 'none';
} else {
// Show box - dismissal expired or never dismissed
consentBox.style.display = 'block';
consentBox.classList.add('show');
}
consentBox.addEventListener('click', function() {
sessionStorage.setItem('consent-acquisition-dismissed', 'true');
// ADD THIS LINE:
localStorage.setItem('consent-acquisition-dismissed-until', Date.now() + (90 * 24 * 60 * 60 * 1000));
var targetPage = getCurrentLanguage() === 'de' ?
'/wiki/Transformal_GmbH:Einstellungen' :
'/wiki/Transformal_GmbH:Settings';
window.location.href = targetPage;
});
});
// Convert template placeholders to actual checkboxes
$(function() {
$('.consent-option').each(function() {
var $option = $(this);
if ($option.find('.consent-checkbox').length > 0) return;
var consentId = $option.find('.consent-data').text().trim();
var status = $option.find('.consent-status-data').text().trim();
var $checkboxRow = $option.find('.checkbox-row');
if (!$checkboxRow.length) return;
var $container = $('<span class="checkbox-container"></span>');
var $checkbox = $('<input>', {
type: 'checkbox',
id: consentId,
class: 'consent-checkbox'
});
if (status === 'disabled') {
$checkbox.prop('disabled', true);
}
var label = $option.find('.checkbox-placeholder').attr('data-label') || getString('checkboxLabel');
var $label = $('<label>', {
'for': consentId,
text: ' ' + label,
style: 'cursor: pointer;'
});
$container.append($checkbox).append($label);
$checkboxRow.append($container);
$checkboxRow.find('.checkbox-placeholder').remove();
var saved = localStorage.getItem(consentId) === 'true';
$checkbox.prop('checked', saved);
$checkbox.on('change', function() {
localStorage.setItem(consentId, this.checked);
if (typeof handleConsentChange === 'function') {
handleConsentChange(consentId, this.checked);
}
if (typeof showConsentFeedback === 'function') {
showConsentFeedback(consentId, this.checked);
}
});
});
});
// Handle consent changes for different features
function handleConsentChange(feature, enabled) {
if (feature === 'matomo-consent') {
if (enabled) {
if (window._paq) {
// Enable enhanced tracking
_paq.push(['setCookieConsentGiven']);
_paq.push(['setConsentGiven']);
// Track this page view again with full details
_paq.push(['trackPageView']);
_paq.push(['enableHeartBeatTimer']);
_paq.push(['trackVisibleContentImpressions']);
}
} else {
if (window._paq) {
// Revert to anonymous tracking only
_paq.push(['forgetCookieConsentGiven']);
_paq.push(['forgetConsentGiven']);
// Note: Already tracked pages remain in this session
}
}
}
}
// Show feedback when consent changes
function showConsentFeedback(feature, enabled) {
var feedbackDiv = document.getElementById('consent-feedback');
if (!feedbackDiv) return;
var message = '';
if (feature === 'matomo-consent') {
message = enabled ? getString('journalingEnabled') : getString('journalingDisabled');
} else if (feature === 'altcha-consent') {
message = enabled ? getString('proofOfWorkEnabled') : getString('proofOfWorkDisabled');
}
if (message) {
feedbackDiv.innerHTML = message + ' – ' + getString('preferencesSaved');
feedbackDiv.style.display = 'block';
setTimeout(function() {
feedbackDiv.style.display = 'none';
}, 3000);
}
}
/* Template 'ConditionalContent' - v250925r8 */
/* Two templates for alternative running text (ConditionalContent) and alternative sections (ConditionalSection) */
mw.loader.using('mediawiki.user').then(function() {
if (mw.user.isAnon()) {
// Inline content
$('.logged-out-only').each(function() {
if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
$(this).css('display', 'none');
} else {
$(this).css('display', 'inline');
}
});
$('.logged-in-only').css('display', 'none');
// Block content
$('.logged-out-section').each(function() {
if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
$(this).css('display', 'none');
} else {
$(this).css('display', 'block');
}
});
$('.logged-in-section').css('display', 'none');
} else {
// Inline content
$('.logged-in-only').each(function() {
if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
$(this).css('display', 'none');
} else {
$(this).css('display', 'inline');
}
});
$('.logged-out-only').css('display', 'none');
// Block content
$('.logged-in-section').each(function() {
if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
$(this).css('display', 'none');
} else {
$(this).css('display', 'block');
}
});
$('.logged-out-section').css('display', 'none');
}
// Clean TOC
var toc = $('.vector-toc')[0] || $('#toc')[0] || $('.toc')[0];
if (toc) {
$('.logged-in-section:hidden, .logged-out-section:hidden').each(function() {
$(this).find('h1, h2, h3, h4, h5, h6').each(function() {
var id = this.id || $(this).find('.mw-headline').attr('id');
if (id) {
$(toc).find('a[href="#' + id + '"]').closest('li').remove();
}
});
});
}
});