MediaWiki:Common.js: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung Markierung: Zurückgesetzt |
Keine Bearbeitungszusammenfassung |
||
| (46 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
/* === PREPRINT START === */ | /* === PREPRINT START === */ | ||
/* ============================================ | /* ============================================ | ||
PREPRINT v7.17 - JS | PREPRINT v7.17 - JS v260225r1 | ||
============================================ */ | ============================================ */ | ||
| Zeile 14: | Zeile 9: | ||
// Prevent repeated initialization | // Prevent repeated initialization | ||
if (window.preprintInitialized) { | if (window.preprintInitialized) { | ||
return; | return; | ||
} | } | ||
| Zeile 35: | Zeile 29: | ||
var imagesStabilized = false; | var imagesStabilized = false; | ||
// Debug logging with source tracking | // Debug logging with source tracking (silent in production) | ||
function log(adapter, message) { | function log(adapter, message) { | ||
} | } | ||
| Zeile 329: | Zeile 322: | ||
var myGeneration = ++transitionGeneration; | var myGeneration = ++transitionGeneration; | ||
resizeTimer = setTimeout(function() { | resizeTimer = setTimeout(function() { | ||
setOrientationClass(targetOrientation); | setOrientationClass(targetOrientation); | ||
| Zeile 471: | Zeile 463: | ||
initViewport(); | initViewport(); | ||
waitForDOMStability() | waitForDOMStability() | ||
.then(function() { | .then(function() { | ||
| Zeile 573: | Zeile 564: | ||
function getLinkage() { | function getLinkage() { | ||
return linkage; | return linkage; | ||
} | |||
/* --- List Repair --- */ | |||
// MW parser ejects template-generated spans (Place) from list items: | |||
// Pattern A (start): <li class="mw-empty-elt"/> + <p><span>...</span>text</p> | |||
// Pattern B (end): <li>text</li> + <p><span>...</span></p> | |||
// Both followed optionally by a continuation list. | |||
// This repairs the DOM before structure analysis. | |||
function repairListPlacement() { | |||
var $output = $('.mw-parser-output'); | |||
if (!$output.length) return; | |||
var repaired = 0; | |||
function findEmptyItem($list) { | |||
var $li = $list.find('li.mw-empty-elt').last(); | |||
if ($li.length) return $li; | |||
var $dd = $list.find('dd').filter(function() { | |||
return this.childNodes.length === 0; | |||
}).last(); | |||
return $dd.length ? $dd : null; | |||
} | |||
function isNestingWrapper(el) { | |||
if (el.tagName !== 'LI') return false; | |||
if (el.children.length !== 1) return false; | |||
if (!/^(UL|OL|DL)$/.test(el.firstElementChild.tagName)) return false; | |||
for (var i = 0; i < el.childNodes.length; i++) { | |||
if (el.childNodes[i].nodeType === 3 && el.childNodes[i].textContent.trim()) return false; | |||
} | |||
return true; | |||
} | |||
$output.find('p').each(function() { | |||
var p = this; | |||
if (!p.parentNode) return; | |||
var first = p.firstChild; | |||
if (!first || first.nodeType !== 1) return; | |||
if (!first.classList.contains('place-lh') && !first.classList.contains('place-oc')) return; | |||
var $p = $(p); | |||
var $prevList = $p.prev('ul, ol, dl'); | |||
if (!$prevList.length) return; | |||
// Pattern A: empty item (Place at start of list item) | |||
var $target = findEmptyItem($prevList); | |||
var isPatternA = $target !== null; | |||
// Pattern B: no empty item (Place at end of list item) | |||
if (!$target) { | |||
$target = $prevList.find('li').last(); | |||
if (!$target.length) $target = $prevList.find('dd').last(); | |||
if (!$target.length) return; | |||
} | |||
if (isPatternA) $target.removeClass('mw-empty-elt'); | |||
// Move <p> contents into target item | |||
while (p.firstChild) { | |||
$target[0].appendChild(p.firstChild); | |||
} | |||
// Merge continuation list if same type follows | |||
var $next = $p.next(); | |||
if ($next.length && $next[0].tagName === $prevList[0].tagName) { | |||
var targetAtRoot = $target[0].parentNode === $prevList[0]; | |||
var children = $next.children().get(); | |||
for (var i = 0; i < children.length; i++) { | |||
if (isNestingWrapper(children[i])) { | |||
var subList = children[i].firstElementChild; | |||
if (targetAtRoot) { | |||
// Pattern B: sub-list becomes child of target | |||
$target[0].appendChild(subList); | |||
} else { | |||
// Pattern A: sub-list items become siblings | |||
var parentList = $target[0].parentNode; | |||
while (subList.firstChild) { | |||
parentList.appendChild(subList.firstChild); | |||
} | |||
} | |||
} else { | |||
$prevList[0].appendChild(children[i]); | |||
} | |||
} | |||
$next.remove(); | |||
} | |||
$p.remove(); | |||
repaired++; | |||
}); | |||
} | } | ||
| Zeile 578: | Zeile 661: | ||
function buildStructure() { | function buildStructure() { | ||
repairListPlacement(); | |||
var parserOutput = $('.mw-parser-output'); | var parserOutput = $('.mw-parser-output'); | ||
var elements = parserOutput.children().toArray(); | var elements = parserOutput.children().toArray(); | ||
| Zeile 664: | Zeile 748: | ||
} | } | ||
}); | }); | ||
// Helper functions (closure) | // Helper functions (closure) | ||
function ensureColumns() { | function ensureColumns() { | ||
| Zeile 708: | Zeile 790: | ||
if (layout && layout[0]) layout[0].style.paddingBottom = ''; | if (layout && layout[0]) layout[0].style.paddingBottom = ''; | ||
$('.preprint-oc-spacer').remove(); | $('.preprint-oc-spacer').remove(); | ||
// Clear inline positioning | // Clear inline positioning set by landscape projection | ||
linkage.forEach(function(link) { | linkage.forEach(function(link) { | ||
link.element[0].style.top = ''; | |||
link.element[0].style.position = ''; | link.element[0].style.position = ''; | ||
}); | }); | ||
| Zeile 764: | Zeile 846: | ||
scrollToHash(); | scrollToHash(); | ||
} | } | ||
| Zeile 788: | Zeile 869: | ||
} | } | ||
reconcileLayout(); | |||
// Restore scroll | // Restore scroll | ||
| Zeile 795: | Zeile 876: | ||
scrollToHash(); | scrollToHash(); | ||
} | } | ||
function | function reconcileLayout() { | ||
if ( | 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(); | |||
// Save and reset scroll for accurate offset calculations (Minerva scrolls body) | |||
var savedBodyScroll = document.body.scrollTop; | |||
var savedHtmlScroll = document.documentElement.scrollTop; | |||
document.body.scrollTop = 0; | |||
document.documentElement.scrollTop = 0; | |||
// === PHASE 1: CLEAN + READ === | |||
// Remove | // Remove all previous-cycle artifacts | ||
$('.preprint-oc-spacer').remove(); | $('.preprint-oc-spacer').remove(); | ||
if (layout && layout[0]) layout[0].style.paddingBottom = ''; | |||
linkage.forEach(function(link) { | |||
if (link.element[0].style.position !== 'absolute') { | |||
link.element[0].style.position = 'absolute'; | |||
} | link.element[0].style.top = '0px'; | ||
} | |||
}); | |||
// | // Force reflow after cleanup | ||
linkage.forEach(function(link) { | void document.body.offsetHeight; | ||
var $ | |||
// Read marker positions, OC container offsets, element geometry | |||
var items = []; | |||
linkage.forEach(function(link, i) { | |||
if (!$oc.length) | var $oc = link.element.closest('.outer-column'); | ||
if (!$oc.length) { items.push(null); return; } | |||
var | var markerTop = link.marker.offset().top; | ||
var ocTop = $oc.offset().top; | |||
$element. | 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 | 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); | |||
if (! | |||
}); | }); | ||
// | // Read protruding IC elements | ||
var protrusions = []; | |||
var | $('.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; | |||
if ( | |||
var siblings = items.filter(function(other) { | |||
var | 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 (type-pair minimum gaps) | |||
var valid = items.filter(function(item) { return item !== null; }); | |||
valid.sort(function(a, b) { return a.docTop - b.docTop; }); | |||
function elementGap(item) { | |||
if (item.link.type === 'place-lh') return 2; | |||
if (item.height >= 50) return 16; | |||
return 4; | |||
} | |||
var | var lastItem = null; | ||
valid.forEach(function(item) { | |||
if (lastItem) { | |||
var minGap = Math.max(elementGap(item), elementGap(lastItem)); | |||
var requiredTop = lastItem.docTop + lastItem.height + minGap; | |||
var overlap = requiredTop - item.docTop; | |||
if (overlap > 0) { | |||
item.top += overlap; | |||
item.docTop += overlap; | |||
} | |||
} | |||
lastItem = item; | |||
}); | |||
// | // Conflict detection: protruding IC vs computed OC positions | ||
var | var spacers = []; | ||
protrusions.forEach(function(prot) { | |||
var | var maxConflictBottom = 0; | ||
valid.forEach(function(item) { | |||
var ocBottom = item.docTop + item.height; | |||
bottom | 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'; | |||
var | s.element.parentNode.insertBefore(spacer, s.element); | ||
var | }); | ||
var rect = | // Final measure: layout padding | ||
if (rect.bottom > | 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'; | |||
} | |||
// Restore scroll | |||
document.body.scrollTop = savedBodyScroll; | |||
document.documentElement.scrollTop = savedHtmlScroll; | |||
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'] | |||
}); | }); | ||
if ( | // ResizeObserver: catches container width changes not visible to | ||
// window resize (TOC pin/unpin, tool menu toggle, DevTools panel). | |||
// Created once, never disconnected — only fires on actual size change. | |||
if (!layout._resizeObserver && typeof ResizeObserver !== 'undefined') { | |||
layout._resizeObserver = new ResizeObserver(function() { | |||
clearTimeout(layout._resizeDebounce); | |||
layout._resizeDebounce = setTimeout(function() { | |||
if (PAL.isOrientationLocked()) return; | |||
if (document.body.classList.contains('preprint-landscape')) { | |||
reconcileLayout(); | |||
} | |||
}, 200); | |||
}); | |||
layout._resizeObserver.observe(layout[0]); | |||
} | } | ||
} | } | ||
| Zeile 1.049: | Zeile 1.111: | ||
$('.preprint-layout img').on('load', function() { | $('.preprint-layout img').on('load', function() { | ||
if (document.body.classList.contains('preprint-landscape')) { | if (document.body.classList.contains('preprint-landscape')) { | ||
reconcileLayout(); | |||
} | } | ||
}); | }); | ||
| Zeile 1.060: | Zeile 1.122: | ||
if (PAL.isOrientationLocked()) return; | if (PAL.isOrientationLocked()) return; | ||
if (document.body.classList.contains('preprint-landscape')) { | if (document.body.classList.contains('preprint-landscape')) { | ||
reconcileLayout(); | |||
} | } | ||
}, 200); | }, 200); | ||
| Zeile 1.073: | Zeile 1.135: | ||
el.scrollIntoView({ block: 'start' }); | el.scrollIntoView({ block: 'start' }); | ||
window.scrollBy(0, -60); | window.scrollBy(0, -60); | ||
} | } | ||
returnTarget = null; | returnTarget = null; | ||
| Zeile 1.085: | Zeile 1.146: | ||
target.scrollIntoView({ block: 'start' }); | target.scrollIntoView({ block: 'start' }); | ||
window.scrollBy(0, -60); | window.scrollBy(0, -60); | ||
} | } | ||
} | } | ||
| Zeile 1.198: | Zeile 1.258: | ||
getLinkage: getLinkage, | getLinkage: getLinkage, | ||
applyProjection: applyProjection, | applyProjection: applyProjection, | ||
positionOCElements: | positionOCElements: reconcileLayout, | ||
captureAnchor: captureAnchor, | captureAnchor: captureAnchor, | ||
restoreAnchor: restoreAnchor, | restoreAnchor: restoreAnchor, | ||
getPositionAnchor: getPositionAnchor, | getPositionAnchor: getPositionAnchor, | ||
getLayout: function() { return layout; }, | getLayout: function() { return layout; }, | ||
set returnTarget(id) { returnTarget = id; } | set returnTarget(id) { returnTarget = id; }, | ||
pauseObserver: function() { | |||
if (layout && layout._ocObserver) layout._ocObserver.disconnect(); | |||
}, | |||
resumeObserver: function() { | |||
if (layout && layout[0] && layout._ocObserver) { | |||
layout._ocObserver.observe(layout[0], { | |||
subtree: true, attributes: true, attributeFilter: ['class'] | |||
}); | |||
} | |||
} | |||
}; | }; | ||
})(); | })(); | ||
| Zeile 1.268: | Zeile 1.338: | ||
var $icon = $('<a class="book-nav-icon"></a>'); | var $icon = $('<a class="book-nav-icon"></a>'); | ||
if (isBookRoot) { | if (isBookRoot) { | ||
$icon.attr('href', '/'); | var saved = sessionStorage.getItem('preprint-position'); | ||
if (saved) { | |||
var pos = JSON.parse(saved); | |||
$icon.attr('href', '/wiki/' + pos.article); | |||
} else { | |||
$icon.attr('href', '#'); | |||
$icon.addClass('book-nav-icon-disabled'); | |||
} | |||
} else { | } else { | ||
$icon.attr('href', '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '')); | $icon.attr('href', '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '')); | ||
| Zeile 1.283: | Zeile 1.360: | ||
var pos = JSON.parse(saved); | var pos = JSON.parse(saved); | ||
window.location.href = '/wiki/' + pos.article; | window.location.href = '/wiki/' + pos.article; | ||
} | } | ||
// No action when disabled — no homepage fallback | |||
}); | }); | ||
} else { | } else { | ||
| Zeile 1.314: | Zeile 1.390: | ||
} | } | ||
} | } | ||
} | } | ||
| Zeile 1.381: | Zeile 1.455: | ||
$landmark.before($link); | $landmark.before($link); | ||
if (mw.config.get('wgUserName')) { | |||
var style = document.createElement('style'); | |||
style.textContent = '@media(max-width:790px){.book-nav-sidebar{display:none!important}}'; | |||
document.head.appendChild(style); | |||
} | |||
} | } | ||
| Zeile 1.523: | Zeile 1.601: | ||
// Mobile: root page IS the TOC, hide MW-generated one | // Mobile: root page IS the TOC, hide MW-generated one | ||
$('.toc, #toc').hide(); | $('.toc, #toc').hide(); | ||
} else if (isPreprintLayout && bookNavData.length && PAL.isMinerva()) { | } else if (isPreprintLayout && bookNavData.length && PAL.isMinerva()) { | ||
// Mobile book chapters: linear reading, no TOC | // Mobile book chapters: linear reading, no TOC | ||
$('.toc, #toc').hide(); | $('.toc, #toc').hide(); | ||
} | } | ||
// Desktop: TOC always visible (sticky sidebar navigation) | // Desktop: TOC always visible (sticky sidebar navigation) | ||
| Zeile 1.572: | Zeile 1.648: | ||
return; | return; | ||
} | } | ||
// Build structure (Model) | // Build structure (Model) | ||
Model.buildStructure(); | Model.buildStructure(); | ||
| Zeile 1.582: | Zeile 1.656: | ||
setTimeout(function() { | setTimeout(function() { | ||
Model.applyProjection(initialMode); | Model.applyProjection(initialMode); | ||
Model.pauseObserver(); | |||
$('.preprint-layout').addClass('preprint-ready'); | $('.preprint-layout').addClass('preprint-ready'); | ||
Model.resumeObserver(); | |||
window.preprintReadyAt = performance.now(); | window.preprintReadyAt = performance.now(); | ||
}, 50); | }, 50); | ||
| Zeile 1.626: | Zeile 1.702: | ||
PAL.onProjectionChange(function(event) { | PAL.onProjectionChange(function(event) { | ||
// Icon already hidden by onPreProjectionChange | // Icon already hidden by onPreProjectionChange | ||
| Zeile 1.639: | Zeile 1.714: | ||
Features.calculateIconMetrics(); | Features.calculateIconMetrics(); | ||
if (icon) icon.style.visibility = ''; | if (icon) icon.style.visibility = ''; | ||
Model.positionOCElements(); | |||
}, 100); | }, 100); | ||
} else { | } else { | ||
| Zeile 1.672: | Zeile 1.748: | ||
}); | }); | ||
mw.loader.load('/wiki/MediaWiki:Common.js/debug.js?action=raw&ctype=text/javascript'); | // mw.loader.load('/wiki/MediaWiki:Common.js/debug.js?action=raw&ctype=text/javascript'); | ||
}); | }); | ||
| Zeile 1.681: | Zeile 1.756: | ||
/* Unified Consent System JavaScript | |||
/* === CONSENT START === */ | |||
/* Unified Consent System JavaScript v260224r1 | |||
* Single file for both EN and DE versions | * Single file for both EN and DE versions | ||
* Automatically detects language based on domain | * Automatically detects language based on domain | ||
| Zeile 1.720: | Zeile 1.797: | ||
} | } | ||
// Handle consent acquisition box - 90-day reminder for | // Handle consent acquisition box - 90-day reminder for visitors (spec §8.9) | ||
$(document).ready(function() { | $(document).ready(function() { | ||
var consentBox = document.getElementById('consent-acquisition-box'); | var consentBox = document.getElementById('consent-acquisition-box'); | ||
| Zeile 1.742: | Zeile 1.819: | ||
// ADD THIS LINE: | // ADD THIS LINE: | ||
localStorage.setItem('consent-acquisition-dismissed-until', Date.now() + (90 * 24 * 60 * 60 * 1000)); | localStorage.setItem('consent-acquisition-dismissed-until', Date.now() + (90 * 24 * 60 * 60 * 1000)); | ||
var targetPage = getCurrentLanguage() === 'de' ? | if (mw.config.get('wgUserName')) { | ||
window.location.href = '/wiki/Special:Preferences#mw-prefsection-legal'; | |||
} else { | |||
var targetPage = getCurrentLanguage() === 'de' ? | |||
'/wiki/Transformal_GmbH:Einstellungen' : | |||
'/wiki/Transformal_GmbH:Settings'; | |||
window.location.href = targetPage; | |||
} | |||
}); | }); | ||
}); | }); | ||
| Zeile 1.843: | Zeile 1.924: | ||
} | } | ||
} | } | ||
/* === CONSENT END === */ | |||
/* === CONDITIONAL START === */ | |||
/* Template 'ConditionalContent' - v250925r8 */ | /* Template 'ConditionalContent' - v250925r8 */ | ||
| Zeile 1.903: | Zeile 1.988: | ||
} | } | ||
}); | }); | ||
/* === CONDITIONAL END === */ | |||