Bước tới nội dung

Khác biệt giữa bản sửa đổi của “MediaWiki:Common.js”

Từ Từ nguyên Tiếng Việt
Không có tóm lược sửa đổi
Thẻ: Đã bị lùi lại
Không có tóm lược sửa đổi
Thẻ: Đã bị lùi lại
Dòng 274: Dòng 274:
         timeout = setTimeout(optimizeColumns, 120);
         timeout = setTimeout(optimizeColumns, 120);
     });
     });
};
 
    // Remove "notheme"
    document.querySelectorAll('.timedmediaplayer').forEach(el => el.classList.remove('notheme'));
 
};  


if (document.readyState === 'loading') {
if (document.readyState === 'loading') {

Phiên bản lúc 09:10, ngày 23 tháng 4 năm 2026

// Optimized & Modernized Version (with your requested behaviors preserved)

const optimizeColumns = () => {
    // Example columns (excol)
    document.querySelectorAll('dl').forEach(dl => {
        let maxWidth = 0;
        const dds = dl.querySelectorAll('dd');
        const containerWidth = dl.clientWidth;

        dds.forEach(dd => {
            dd.style.setProperty('min-width', 'max-content');
            dd.style.setProperty('column-span', 'unset');

            const ddWidth = dd.clientWidth + parseFloat(getComputedStyle(dd).marginLeft || 0);

            if (ddWidth > containerWidth / 2) {
                dd.style.setProperty('min-width', 'calc(100% - 1.6em)');
                dd.style.setProperty('column-span', 'all');
            } else if (maxWidth < ddWidth) {
                maxWidth = ddWidth + 5;
            }
        });

        const count = dds.length;
        if (count >= 6) {
            if (maxWidth * 3 < containerWidth) dl.style.columnCount = 3;
            else if (maxWidth * 2 < containerWidth) dl.style.columnCount = 2;
            else dl.style.columnCount = 1;
        } else {
            dl.style.columnCount = 1;
        }
    });

    // Cognates columns
    document.querySelectorAll('.cognates ul').forEach(ul => {
        const lis = ul.querySelectorAll('li');
        if (!lis.length) return;

        let widestWidth = 0;
        lis.forEach(li => widestWidth = Math.max(widestWidth, li.clientWidth));

        const refText = document.querySelector('.cognates .reference-text');
        if (!refText) return;

        const refOl = refText.querySelector('ol');
        const marginLeft = refOl ? parseFloat(getComputedStyle(refOl).marginLeft || 0) : 0;
        const containerWidth = refText.clientWidth;
        const itemWidth = widestWidth + marginLeft + 10;

        if (lis.length > 7 && containerWidth > itemWidth * 3) {
            ul.style.columnCount = 3;
        } else if (lis.length > 4 && containerWidth > itemWidth * 2) {
            ul.style.columnCount = 2;
        } else {
            ul.style.columnCount = 1;
        }
    });
};

// Wikipedia links
const cleanWikiLinks = () => {
    document.querySelectorAll('.extiw').forEach(link => {
        link.title = link.title.replace('wikipedia:vi:', '');
    });
};

// Keyboard shortcuts
const setupShortcuts = () => {
    document.addEventListener('keydown', e => {
        const isMac = navigator.platform.includes('Mac');
        if (!(isMac ? e.metaKey : e.ctrlKey)) return;

        const map = {
            's': '[value="Save changes"]',
            'p': '[value="Show preview"]',
            'e': 'a[accesskey="e"]',
            'r': 'input[accesskey="r"]',
            'g': 'input[accesskey="g"]'
        };

        const selector = map[e.key];
        if (selector) {
            e.preventDefault();
            var el = document.querySelector(selector);
            if (el) el.click();
        }
    });
};

// Clean citations
const cleanCitations = () => {
    const citerefs = document.querySelectorAll('[id^="cite_ref-"]');
    citerefs.forEach(ref => {
        if (ref.childNodes[0] && ref.childNodes[0].textContent === ' ') {
            ref.childNodes[0].textContent = '';
        }
    });

    if (citerefs.length) {
        const parent = citerefs[0].parentElement;
        if (parent) {
            parent.innerHTML = parent.innerHTML.replaceAll(' <sup id="cite_ref-', '<sup id="cite_ref-');
        }
    }
};

// Line-break arrows - Preserved original fallback entries[0] case
const processEntryArrows = () => {
    if (window.location.href.search('index.php') >= 0 || window.location.href.lastIndexOf(':') !== 5) return;

    var ol = document.querySelector('ol');
    const entries = ol ? ol.querySelectorAll('li') : null;
    if (!entries) return;

    entries.forEach((entryEl, i) => {
        if (!entryEl.innerHTML.includes('> →')) return;

        const parts = entryEl.innerHTML.split('> →');
        let newHTML = parts[0] + 'b>';
        let didFallback = false;  // FIX: flag to track if entries[0] fallback was used

        for (let j = 1; j < parts.length; j++) {
            if (j < parts.length - 1) {
                newHTML += `<br><span style="display:inline-block;margin-left:calc(${j+1}em + 0.5ch);text-indent:calc(-1em - 0.5ch)"><arrow>↳ </arrow>${parts[j]}</span>`;
            } else {
                // Last part - keep original fallback logic
                if (parts[j].search('dl') > -1) {
                    const beforeDl = parts[j].split('<dl')[0];
                    const lastSpaceIdx = beforeDl.lastIndexOf(' ');
                    const textBefore = beforeDl.slice(0, lastSpaceIdx);
                    const lastWord = beforeDl.slice(lastSpaceIdx + 1);

                    newHTML += `<br><span style="display:inline-block;margin-left:calc(${parts.length}em + 0.5ch);text-indent:calc(-1em - 0.5ch)"><arrow>↳ </arrow>${textBefore}&nbsp;${lastWord}</span><dl${parts[j].split('<dl')[1]}`;
                } else if (parts[j].slice(parts[j].lastIndexOf(' ') - 2, parts[j].lastIndexOf(' ')) !== '<a') {
                    const lastSpace = parts[j].lastIndexOf(' ');
                    newHTML += `<br><span style="display:inline-block;margin-left:calc(${parts.length}em + 0.5ch);text-indent:calc(-1em - 0.5ch)"><arrow>↳ </arrow>${parts[j].slice(0, lastSpace)}&nbsp;${parts[j].slice(lastSpace + 1)}</span>`;
                } else {
                    // Original fallback: append to first entry
                    entries[0].innerHTML += `<br><span style="display:inline-block;margin-left:calc(${parts.length}em + 0.5ch);text-indent:calc(-1em - 0.5ch)"><arrow>↳ </arrow>${parts[j]}`;
                    didFallback = true;  // FIX: mark that we used the fallback
                }
            }
        }

        // FIX: only overwrite innerHTML if we didn't use the entries[0] fallback
        if (!didFallback) {
            entryEl.innerHTML = newHTML;
        }
    });
};

// Video zoom
const setupVideoZoom = () => {
    const zoombtns = document.getElementsByClassName('enlarge');
    const videos = document.querySelectorAll('[title="Play video"]');

    const resizeVideo = (index, multiply) => {
        const vid = videos[index];
        if (!vid) return;

        vid.width *= multiply;
        vid.height *= multiply;

        const w = vid.width + 'px';
        const h = vid.height + 'px';

        vid.style.width = w; vid.style.height = h;

        let parent = vid.parentElement;
        while (parent && parent !== document.body) {
            parent.style.width = w;
            parent.style.height = h;
            parent = parent.parentElement;
        }

        const btn = zoombtns[index];
        if (btn) {
            const isEnlarge = multiply > 1;
            btn.onclick = () => resizeVideo(index, isEnlarge ? 0.5 : 2);
            btn.title = isEnlarge ? 'Thu nhỏ' : 'Phóng to';
        }
    };

    Array.from(zoombtns).forEach((btn, i) => {
        btn.onclick = () => resizeVideo(i, 2);
        btn.title = 'Phóng to';
    });
};

// Zoomed text
// FIX: wrap HTMLCollection with Array.from() before calling .forEach()
const setupZoomText = () => {
    const wrapClass = (className) => {
        Array.from(document.getElementsByClassName(className)).forEach(el => {
            const html = el.innerHTML;
            el.innerHTML = `<div class="tttext">${html}<span class="ttzoom"> ${html}</span></div>`;
        });
    };

    wrapClass('Hani');
    wrapClass('notHani');

    document.querySelectorAll('.ttzoom').forEach(span => {
        if (span.getBoundingClientRect().right + 20 > window.innerWidth) {
            span.style.left = '-800%';
        }
    });
};

// Hide empty ruby
const hideEmptyRuby = () => {
    document.querySelectorAll('rt').forEach(rt => {
        if (rt.innerText.trim() === '') rt.style.display = 'none';
    });
};

// Replace textimg - original i++ behavior preserved (skips every other element)
const setTextImgPlaceholders = () => {
    const textimg = document.querySelectorAll('[class*="textimg"]');
    for (let i = 0; i < textimg.length; i++) {
        const img = textimg[i].querySelector('img');
        if (img) img.src = 'https://www.tunguyentiengviet.com/images/4/47/Placeholder.png';
        i++;   // original manual i++ kept exactly as requested
    }
};

// Highlight entry
const highlightEntry = () => {
    if (window.location.href.includes('#entry')) {
        const id = window.location.href.substring(window.location.href.indexOf('#') + 1);
        const el = document.getElementById(id);
        if (el && el.parentElement) {
            el.parentElement.style.background = 'rgba(255,222,100,0.4)';
        }
    }
};

// Main init
const init = () => {
    optimizeColumns();
    cleanWikiLinks();
    setupShortcuts();
    cleanCitations();
    processEntryArrows();
    setupVideoZoom();
    setupZoomText();
    hideEmptyRuby();
    setTextImgPlaceholders();
    highlightEntry();

    // Empty notelistalpha
    const notelist = document.querySelector('.notelistalpha');
    if (notelist && notelist.childElementCount < 2) {
        notelist.style.visibility = 'hidden';
    }

    // Global replaces
    const content = document.getElementById('mw-content-text');
    if (content) {
        if (window.location.href.indexOf('MediaWiki') === -1) {
            content.innerHTML = content.innerHTML.replaceAll('  ', ' ');
        }
        if (window.location.href.indexOf('index') === -1) {
            content.innerHTML = content.innerHTML
                .replaceAll(' &gt; ', '<con> &gt; </con>')
                .replaceAll(' ~ ', '<con> ~ </con>');
        }
    }

    // Debounced resize
    let timeout;
    window.addEventListener('resize', () => {
        clearTimeout(timeout);
        timeout = setTimeout(optimizeColumns, 120);
    });

    // Remove "notheme"
    document.querySelectorAll('.timedmediaplayer').forEach(el => el.classList.remove('notheme'));

}; 

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
} else {
    init();
}