michael-brade/LaTeX.js

View on GitHub
src/js/base.js

Summary

Maintainability
A
2 hrs
Test Coverage
function getVariable(el, propertyName) {
    return String(getComputedStyle(el).getPropertyValue('--' + propertyName)).trim();
};

function processTheElements() {
    var thes = document.querySelectorAll('.the');
    for (var i = 0; i < thes.length; i++) {
        var v = getVariable(thes[i], thes[i].getAttribute('display-var'));
        // only mutate if it actually changed
        if (thes[i].textContent != v) {
            thes[i].textContent = v;
        }
    }
}


function _vertical(el, tb) {
    var doc, docEl, rect, win;

    // return zero for disconnected and hidden (display: none) elements, IE <= 11 only
    // running getBoundingClientRect() on a disconnected node in IE throws an error
    if ( !el.getClientRects().length ) {
        return 0;
    }

    rect = el.getBoundingClientRect();

    doc = el.ownerDocument;
    docEl = doc.documentElement;
    win = doc.defaultView;

    return rect[tb] + win.pageYOffset - docEl.clientTop;
}


function offsetTop(el) {
    return _vertical(el, "top");
}

function offsetBottom(el) {
    return _vertical(el, "bottom");
}

function offsetBaseline(el) {
    var mpbaseline = el.querySelector('.mpbaseline');
    return offsetBottom(mpbaseline);
}

function heightAboveBaseline(el) {
    var baseline = offsetBaseline(el);
    var top = offsetTop(el);
    return baseline - top;
}


function positionMarginpars() {
    var mpars = document.querySelectorAll('.marginpar > div');
    var prevBottom = 0;

    mpars.forEach(function(mpar) {
        var mpref = document.querySelector('.body #marginref-' + mpar.id);

        var baselineref = offsetBottom(mpref);
        var heightAB = heightAboveBaseline(mpar);
        var height = mpar.offsetHeight;

        // round to 1 digit
        var top = Math.round((baselineref - heightAB - prevBottom) * 10) / 10;

        // only mutate if it actually changed
        if (mpar.style.marginTop != Math.max(0, top) + "px") {
            mpar.style.marginTop = Math.max(0, top) + "px";
        }

        // if marginTop would have been negative, the element is now further down by that offset => add it to prevBottom
        prevBottom = baselineref - heightAB + height - Math.min(0, top);
    });
}



// don't call resize event handlers too often
var optimizedResize = (function() {
    var callbacks = [],
        running = false;

    // fired on resize event
    function resize() {
        if (!running) {
            running = true;

            if (window.requestAnimationFrame) {
                window.requestAnimationFrame(runCallbacks);
            } else {
                setTimeout(runCallbacks, 66);
            }
        }
    }

    // run the actual callbacks
    function runCallbacks() {
        callbacks.forEach(function(callback) { callback(); });
        running = false;
    }

    // adds callback to loop
    function addCallback(callback) {
        if (callback) {
            callbacks.push(callback);
        }
    }

    return {
        // public method to add additional callback
        add: function(callback) {
            if (!callbacks.length) {
                window.addEventListener('resize', resize);
            }
            addCallback(callback);
        }
    }
}());


// setup event listeners

function completed() {
    document.removeEventListener("DOMContentLoaded", completed);
    window.removeEventListener("load", positionMarginpars);

    var observer = new MutationObserver(function() {
        processTheElements();
        positionMarginpars();
    });

    observer.observe(document, { attributes: true, childList: true, characterData: true, subtree: true });

    // add resize event listener
    optimizedResize.add(positionMarginpars);

    processTheElements();
    positionMarginpars();
}

document.addEventListener("DOMContentLoaded", completed);
window.addEventListener("load", completed);