TOC TouchButton

Add Touchfriendly TOC and next and previous link variant

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// https://greasyfork.org/scripts/380720-toc-touchbutton
// @name TOC TouchButton
// @namespace Touchfriendly TOC Variant
// @match https://*.wordpress.com/*
// @version     0.8
// @description Add Touchfriendly TOC and next and previous link variant
// @grant none
// ==/UserScript==

//Known Bug: look for a way to detect readerview/android page reload
//      onreload with active reader view(firefox) -> loads original version with longText link


const SELECTORTOC = 'p > a';
const MEDIAQUERYMAXWIDTH = '480px';

const DEFAULTSHORTTOC = "ToC";
const DEFAULTSHORTPREV = "-1←";
const DEFAULTSHORTNEXT = "→+1";
/**
 * Setting to Pair ["Longtext", "ShortText"]
 * ShortText will be shown as button
 * @param {String} LongText longer String which should be replaced
 * @param {Array} ShortText replacement for longText
 */
const LINKVARIANTS = [
    {
        "shortVersion": DEFAULTSHORTTOC,
        "longVariants": ["Table of Contents", "Index", "ToC"]
    },
    {
        "shortVersion": DEFAULTSHORTPREV,
        "longVariants": ["Previous Chapter", "Prev", "<- Previous Chapter"]
    },
    {
        "shortVersion": DEFAULTSHORTNEXT,
        "longVariants": ["Next Chapter", "Next", "Next Chapter -&gt;"]
    }];
//"<- Previous Chapter" or "&lt;- Previous Chapter" possible
//"Next Chapter ->"    same as before

// ↥ front UI ↥ for adjustment for different sites copy script and adjust SELECTORTOC and LINKVARIANTS and metas(@match, @namespace)
// ↧ script ↧
var nodesArray = [];

function readCssStyle(element, attribute) {
    let style = window.getComputedStyle ? getComputedStyle(element, null) : element.currentStyle;
    return style[attribute]
}

/**
 * toggle Hidden attributes depending on css value (set by mediaquery)
 * 
 * without active javascript attribute toggling ->  the longT Version would get read by Firefox Reader View and maybe other readability plugins
 */
function checkHidden() {
    let shortT = document.querySelectorAll('.shortTOC');
    let longT = document.querySelectorAll('.longTOC');

    //console.log("checkHidden","checkHidden shortT length: "+shortT.length +" # "+ readStyle(shortT[0],"display") );
    //http://john.foliot.ca/aria-hidden/
    for (var i = 0; i < shortT.length; i++) {
        //console.log("checkHidden","checkHidden shortT setAttribute aria-hidden + " + i);
        if (readCssStyle(shortT[i], "display") === "none") {
            //console.log("checkHidden","checkHidden shortT setAttribute aria-hidden display none");
            shortT[i].setAttribute('aria-hidden', 'true');
            shortT[i].setAttribute('hidden', "true");
            shortT[i].setAttribute('role', 'presentation');
        }
        else if (readCssStyle(shortT[i], "display") == "inline-block") {
            shortT[i].setAttribute('aria-hidden', 'false')
            shortT[i].removeAttribute('role');
            shortT[i].removeAttribute('hidden');
        }

    }
    for (let i = 0; i < longT.length; i++) {
        if (readCssStyle(longT[i], "display") == "none") {
            longT[i].setAttribute('aria-hidden', 'true');
            longT[i].setAttribute('hidden', "true");
            longT[i].setAttribute('role', 'presentation');
        }
        else if (readCssStyle(longT[i], "display") == "inline-block") {
            longT[i].setAttribute('aria-hidden', 'false')
            longT[i].removeAttribute('role');
            longT[i].removeAttribute('hidden');
        }
    }
}


//https://stackoverflow.com/questions/23683439/gm-addstyle-equivalent-in-tampermonkey
function addGlobalStyle(css) {
    let head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

//https://stackoverflow.com/questions/1686571/greasemonkey-how-to-apply-a-css-rule-only-for-media-print
//https://stackoverflow.com/questions/8624210/getting-jquery-and-gm-addstyle-to-work-in-a-chrome-userscript-based-off-of-a-wor
//multiline for better readability add at end of line -> \
//https://stackoverflow.com/questions/23608346/how-to-style-a-div-like-the-button-element
addGlobalStyle('\
.shortTOC{  display:none;visiblity:hidden;}\
.longTOC{ display:inline-block;visiblity:visible;} \
@media (max-width: '+ MEDIAQUERYMAXWIDTH + ') { \
    .shortTOC { display: inline-block;visiblity:visible;\
        width: 23%;\
        text-align: center;\
        line-height:3.5em;\
        font-size:6vw;\
        -ms-touch-action: manipulation;\
        touch-action: manipulation;\
        cursor: pointer;\
        -webkit-user-select: none;\
        -moz-user-select: none;\
        -ms-user-select: none;\
        user-select: none;\
        background-image: none;\
        border: 1px solid transparent;\
        border-radius: 4px;\
        border-color: #ccc;\
    }\
     .longTOC { display:none;visiblity:hidden;}\
 }\
 ');

/**
 * results has only Nodes which match filterString
 * @param {Array} Nodes 
 * @param {String} filterString 
 */
function getNodes(Nodes, filterString) {
    //filter both HTML entity character and pure text
    //innerHTML:characters as is (example %nbsp; kept as space) ; textContent: example &nbsp; converted to space character
    let TOCFilter = Nodes.filter(e => (e.innerHTML == filterString || e.textContent == filterString));
    /*
    let TOCNodes = TOCFilter.map((e, i) => {
        //console.log("elementcontent: " +e + " at index: "+i +" nodeValueOuterhtml: " + e.outerHTML + " nodeValueinnerhtml: " + e.innerHTML );        
        return e
    });
    */
    return TOCFilter
}
/**
 * add custom classes to link text
 * @param {array} Nodes linkarray
 * @param {string} shortText 
 */
function setClasses(Nodes, shortText) {
    Nodes.forEach(element => {
        let TOCButton = document.createElement("a")
        TOCButton.href = element;
        TOCButton.setAttribute("class", "shortTOC")
        TOCButton.textContent = shortText
        element.setAttribute("class", "longTOC")
        element.parentNode.insertBefore(TOCButton, element.nextSibling);
    });
}
/**
 * Compare long link text and replace with shortText version
 * @param {Array} nodes link array 
 * @param {String} longText longer String which should be replaced and is used as filter for the linkarray
 * @param {String} shortText replacement for longText
 */
function addTouchfriendlyVariant(nodes, longText, shortText) {
    let TOCNodes = getNodes(nodes, longText)
    setClasses(TOCNodes, shortText);
    //console.log('Toc element: ' + TOCNodes);
}


function main() {
    let getSelectedLinks = function () {
        let links = Array.from(
            document.querySelectorAll(SELECTORTOC)
        );

        return links;
    };
    const links = getSelectedLinks();

    /*
    addTouchfriendlyVariant(links, "Table of Contents", "ToC");
    addTouchfriendlyVariant(links, "Previous Chapter", "-1←");
    addTouchfriendlyVariant(links, "Next Chapter", '→+1');
    */

    LINKVARIANTS.forEach(key => {
        key.longVariants.forEach(variant => {
            //console.log("LINKVARIANTS: " + key.shortVersion + " # " + variant);
            addTouchfriendlyVariant(links, variant, key.shortVersion);
        });
    });


}
main()

//https://www.sitepoint.com/jquery-document-ready-plain-javascript/
var callback = function () {
    // Handler when the DOM is fully loaded
    checkHidden();
};

//https://stackoverflow.com/questions/2720658/how-to-detect-when-a-tab-is-focused-or-not-in-chrome-with-javascript
var focusCheckCallback = function () {
    //console.log("onvisibilityState reaction: " + document.visibilityState);
    if (document.visibilityState == "visible") //hidden -> readerview rebuild?
        checkHidden();
}

if (
    document.readyState === "complete" ||
    (document.readyState !== "loading" && !document.documentElement.doScroll)
) {
    callback();
} else {
    document.addEventListener("DOMContentLoaded", callback);
}

window.addEventListener("resize", function () {
    checkHidden();
});
window.addEventListener("visibilitychange", focusCheckCallback);
//window.addEventListener("blur",focusCheckCallback);