Page Read Mode

[ALT+R] Content reader on any page, selecting the text area automatically or manually.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name                Page Read Mode
// @name:zh-CN          网页阅读模式
// @name:zh-TW          網頁閱讀模式
// @description         [ALT+R] Content reader on any page, selecting the text area automatically or manually.
// @description:zh-CN   [ALT+R] 将任何一个网页中影响您阅读的图片,视频,广告等无关内容过滤,仅查看最关注的那一部分内容。特别适合各种内容阅读型网页。同时提供将所选区域的 HTML 代码导出的功能。
// @description:zh-TW   [ALT+R] 將任何一個網頁中影響您閱讀的圖片,視頻,廣告等無關內容過濾,僅查看最關注的那一部分內容。特別適合各種內容閱讀型網頁。同時提供將所選區域的 HTML 代碼導出的功能。

// @author              Moshel
// @namespace           https://hzy.pw
// @homepageURL         https://hzy.pw/
// @supportURL          https://github.com/h2y/link-fix
// @icon                https://wiki.greasespot.net/images/f/f3/Book.png
// @license             GPL-3.0

// @include             *
// @grant               GM_setClipboard
// *run-at              context-menu
// @require             https://cdn.staticfile.org/keymaster/1.6.1/keymaster.min.js
// @resource            useageIMG https://github.com/h2y/link-fix/raw/master/read_mode/useage.png

// @date                12/17/2015
// @modified            01/23/2016
// @version             1.1.2
// ==/UserScript==


/*
    global var
 */
let mode = 0,        //状态标记
    topNode = null,  //顶层节点
    styleNode = null,
    butNodes = null,
    useageNode = null;


/*
    Tool functions
 */
function isNodeShow(node) {
    const styles = window.getComputedStyle(node);

    if(styles.display=='none' || styles.visibility=='hidden')
        return false;

    if(!parseInt(styles.height) || !parseInt(styles.height))
        return false;

    return true;
}


/*
    main functions
 */
function enterCliping(e) {
    mode = 1;
    e.preventDefault();

    //add style
    if(!styleNode) {
        styleNode = document.createElement('style');
        styleNode.innerHTML = `.cliper-top-node {
            box-shadow: 0 0 20px #777 !important;
            border:     3px solid red !important;
        } .read-mode-reading {
            position:   fixed   !important;
            z-index:    9999970 !important;
            top:        0       !important;
            left:       0       !important;
            height:     100%    !important;
            width: 		100%    !important;
            background-color: white   !important;
            overflow:         scroll  !important;
            padding:          0       !important;
            border:           0       !important;
            margin:           0       !important;
        } .read-mode-buts {
            position:   fixed;
            z-index:    9999985;
            top: 2rem;  right: 1rem;
        } .read-mode-button {
            width:      54px;
            height:     54px;
            margin:     0 .5rem;
            padding:    10px 15px;
            color:      #fff;
            opacity:    .5;
            transition: 500ms;
            border-radius:      5px;
            background-color:   black;
        } .read-mode-button:hover {
            background-color:   white;
            border-radius:      0;
            box-shadow:         0 0 10px #000;
            color:              #000;
        } img.read-mode-useage {
            position:   fixed;
            right:      3rem;
            bottom:     2rem;
            z-index:    9999975;
            opacity:    .7;
        }`;
        //styleNode.id = 'read_mode';
        document.body.appendChild(styleNode);
    }

    // useage image
    if(!useageNode) {
        useageNode = document.createElement('img');
        useageNode.src = 'https://github.com/h2y/link-fix/raw/master/read_mode/useage.png';
        useageNode.className = 'read-mode-useage';
        document.body.appendChild(useageNode);
    }

    useageNode.style.display = '';

    //choose the init node
    topNode = document.body;
    let preNode = null;

    do {
        preNode = topNode;
        onDown(e);
    }while(preNode!=topNode && preNode.clientHeight*0.9 < topNode.clientHeight);
}

function quitCliping(e) {
    mode = 0;
    e.preventDefault();

    useageNode.style.display = 'none';

    changeTopNode(null);

    if(butNodes)
        butNodes.style.display = 'none';

    topNode.classList.remove('read-mode-reading');
}


function buildButNodes() {
    butNodes = document.createElement('div');
    butNodes.className = 'read-mode-buts';

    let buts = [
        {
            text:    "Exit read mode",
            handler: quitCliping,
            icon:    '✘'
        }, {
            text:    "Save HTML data",
            handler: onSaveHTML,
            icon:    '❖'
        }
    ];

    for(let but of buts) {
        let newBut = document.createElement('a');
        newBut.className = 'read-mode-button';
        newBut.innerHTML = but.icon;
        newBut.title     = but.text;
        newBut.onclick   = but.handler;
        butNodes.appendChild(newBut);
    }

    document.body.appendChild(butNodes);
}


function changeTopNode(newNode) {
    if(topNode)
        topNode.classList.remove('cliper-top-node');

    if(newNode)
        newNode.classList.add('cliper-top-node');
    else
        return;

    topNode = newNode;

    //scroll
    var winH = window.screen.availHeight,
        winY = window.scrollY,
        domH = topNode.clientHeight,
        domY = topNode.getBoundingClientRect().top + winY;
    //console.log(winH,winY,domH,domY);

    if(domH>winH)
        window.scrollTo(0, domY - 50 );
    else
        window.scrollTo(0, domY - (winH-domH)/2 );
}


/*
    Event handler
 */
function onSaveHTML(e) {
    let htmlStr = '';

    htmlStr += topNode.outerHTML.split('\n').join('')
                    .replace(/(id|class)=(\'.*?\'|\".*?\")/ig, '')
                    .replace(/<!--.*?-->/g, '')
                    .replace(/>[\t ]+?</g, '><')
                    .replace(/<(link|meta).*?>/ig, '')
                    .replace(/<style.*?>.*?<\/style>/ig, '')
                    .replace(/<script.*?>.*?<\/script>/ig, '');

    GM_setClipboard(htmlStr);

    alert('Copied into clipboard.');
}


function onUp(e) {
    if(!mode) return;
    e.preventDefault();

    if(topNode.parentElement)
        changeTopNode(topNode.parentNode);
}

function onDown(e) {
    if(!mode) return;
    e.preventDefault();

    if(!topNode.childElementCount)
        return;

    var scanNodes = topNode.children,
        maxNode = null;
    var maxHeight = -1;

    for(let node of scanNodes)
        if(isNodeShow(node) && node.clientHeight > maxHeight) {
            maxHeight = node.clientHeight;
            maxNode = node;
        }

    if(maxNode)
        changeTopNode(maxNode);
}

function onLeft(e) {
    if(!mode) return;
    e.preventDefault();

    let nowNode = topNode;
    for(let node=nowNode; node.previousElementSibling;) {
        node = node.previousElementSibling;
        if(isNodeShow(node)) {
            nowNode = node;
            break;
        }
    }

    if(nowNode!=topNode)
        changeTopNode(nowNode);
    //else: up
    else if (topNode.parentNode) {
        let bakNode = nowNode = topNode;

        onUp(e);
        nowNode = topNode;

        onLeft(e);
        if(nowNode==topNode)
            changeTopNode(bakNode);
        else
            onDown(e);
    }
}

function onRight(e) {
    if(!mode) return;
    e.preventDefault();

    let nowNode = topNode;
    for(let node=nowNode; node.nextElementSibling;) {
        node = node.nextElementSibling;
        if(isNodeShow(node)) {
            nowNode = node;
            break;
        }
    }

    if(nowNode!=topNode)
        changeTopNode(nowNode);
    //else: up
    else if (topNode.parentNode) {
        let bakNode = nowNode = topNode;

        onUp(e);
        nowNode = topNode;

        onRight(e);
        if(nowNode==topNode)
            changeTopNode(bakNode);
        else
            onDown(e);
    }
}


function onEnter(e) {
    if(!mode) return;
    e.preventDefault();

    quitCliping(e);

    topNode.classList.add('read-mode-reading');

    //buttons
    if(butNodes)
        butNodes.style.display = '';
    else
        buildButNodes();
}


/*
    Main
 */
key('alt+r', function(){
	if(mode)
		quitCliping(new MouseEvent("main"));
	else
		enterCliping(new MouseEvent("main"));
});


/*
    bind action
 */
key('up',    onUp);
key('down',  onDown);
key('left',  onLeft);
key('right', onRight);

key('enter', onEnter);
key('esc',   quitCliping);