Greasy Fork

Greasy Fork is available in English.

Image navigation on telegraph

More options for navigating between pictures on telegra.ph

// ==UserScript==
// @name         Image navigation on telegraph
// @namespace    https://github.com/ShedanAlkur/
// @version      0.1.12
// @description  More options for navigating between pictures on telegra.ph
// @author       ShedanAlkur
// @match        https://telegra.ph/*
// @icon         https://telegra.ph/favicon.ico?1
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const COUNTER_FADEOUT_TIME = 2500;
    const SCROLL_APPLY_TIME = 120;

    const backwardLinkStyle =
        `.backward-link{position:absolute;z-index:2;width:calc(40% + 100px);height:100%;left:-100px;border:none!important}.backward-link:hover{background:linear-gradient(90deg,rgba(0,0,0,0.15) 0%,rgba(0,0,0,0.15) 85%,rgba(0,0,0,0) 100%)}`;
    const imageStyle =
        `.figure_wrapper a{border-bottom: none!important;}`;
    const imageCounterStyle =
        `.image-counter{display:inline-block;position:fixed;z-index:999;bottom:0;left:50%;transform:translate(-50%,0);margin-bottom:12px;padding:4px 12px;color:#000;background-color:#fff;font-family:CustomSansSerif,'Lucida Grande',Arial,sans-serif;font-weight:600;font-style:normal;font-size:17px;text-decoration:none;border:2px solid #333;border-radius:16px;text-transform:uppercase;opacity:70%;cursor:default}.image-counter:hover{opacity:30%}.image-counter:empty{display:none}`;
    const addressElement =
        `.address-element input{accent-color:rgb(121,130,139)}.address-element::before{content:'•';padding:0 7px}`;
    const dropdownMenu =
        `.dropdown-menu{position:relative;display:inline-block}.dropdown-content{display:none;position:absolute;background-color:#fff;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);z-index:20}.dropdown-menu:hover .dropdown-content{display:block}.dropdown-content>*{padding:4px;white-space:nowrap;display:block;text-align:left}.dropdown-content>:hover{background-color:#0001}`;
    const buttonTop =
        `.button-top{position:fixed;z-index:9;bottom:0;left:0;margin:12px;opacity:0;visibility:hidden}.button-top.show{opacity:70%;visibility:visible;}`;
    const darkmode =
        `.darkmode body{background-color:#191919}.darkmode .dropdown-content{background-color:#373737}.darkmode .tl_alert_message,.darkmode .tl_report_popup,.darkmode .tl_page_footer{color:rgba(255,255,255,.75);background-color:#373737}.darkmode .tl_textfield{background-color:unset;color:rgba(255,255,255,.75)}.darkmode .tl_article .tl_article_content,.darkmode .tl_article .tl_article_content .ql-editor *,.darkmode .tl_article.tl_article_edit.title_focused [data-label]:after,.darkmode .tl_article.tl_article_edit.title_required h1[data-placeholder]:before,.darkmode .tl_popup a{color:rgba(255,255,255,.75)}.darkmode .tl_article .tl_article_content,.darkmode .tl_article h1,.darkmode .tl_article h2{color:rgba(255,255,255,.8)}.darkmode .button,.darkmode .tl_article .share_button,.darkmode .tl_article_edit .publish_button,.darkmode .tl_article_editable .edit_button,.darkmode .tl_article_saving .publish_button{color:rgba(255,255,255,.8);background-color:rgba(255,255,255,.2);border-color:rgba(255,255,255,0)}.darkmode .tl_article .tl_article_content code,.darkmode .tl_article .tl_article_content pre{background-color:rgba(255,255,255,.2)}.darkmode .tl_article .tl_article_content blockquote,.darkmode .tl_article a{border-color:rgba(255,255,255,.75)}.darkmode .account,.darkmode .tl_article address,.darkmode .tl_article address a{color:rgba(255,255,255,.5)}.darkmode .address[data-placeholder].empty:after,.darkmode .tl_article.tl_article_edit [data-placeholder].empty:before,.darkmode .tl_article.tl_article_edit figcaption[data-placeholder].empty:after{color:rgba(255,255,255,.44)}.darkmode .editable_input{background-color:#0000}.darkmode img{filter:brightness(80%)}*{transition:background-color .1s linear}`;
    var _images;
    var _currentIndex = 0, _imageCount = 0, _scrollIndex = 0;
    var _hCounter;
    var _hLabelUseExt, hInputUseExt, _hTopButton;
    var _counterTimeout, _scrollTimeout, _preventScrollIndexTimeout;
    var _preventScrollIndex = false;
    var _useImageNavigationExt = localStorage.getItem('_useImageNavigationExt') == 'true';
    var _useDarkmode = localStorage.getItem('_useDarkmode') == 'true';
    var _useSmoothScroll = localStorage.getItem('_useSmoothScroll') == 'true';

    function addGlobalStyle(css) {
        var head, style;
        head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    }

    function updateCounter(fadeoutTime = COUNTER_FADEOUT_TIME) {
        _hCounter.innerHTML = `${(_scrollIndex | 0) + 1}/${_imageCount}`;
        clearTimeout(_counterTimeout);
        _counterTimeout = setTimeout(() => { _hCounter.innerHTML = '' }, fadeoutTime);
    }

    function disableScrollIndexPrevention() {
        _preventScrollIndex = false;
        history.replaceState(null, null, ' ');
    }

    function handleLinkClick(event) {
        _currentIndex = _scrollIndex = +event.target.dataset.indexto;
        _preventScrollIndex = true;
        clearTimeout(_preventScrollIndexTimeout);
        _preventScrollIndexTimeout =
            setTimeout(disableScrollIndexPrevention, _scrollTimeout + 1);
        updateCounter();
    }

    function toImage() {
        _scrollIndex = _currentIndex;
        _images[_currentIndex].scrollIntoView();
        _preventScrollIndex = true;
        clearTimeout(_preventScrollIndexTimeout);
        _preventScrollIndexTimeout =
            setTimeout(disableScrollIndexPrevention, SCROLL_APPLY_TIME);
        updateCounter();
    }

    //
    // Event handlers
    //

    function handleKeydown(event) {
        if (event.key === 'ArrowRight') {
            _currentIndex = Math.floor(_currentIndex + 1);
            if (_currentIndex >= _imageCount) _currentIndex = 0;
            toImage();
        } else if (event.key === 'ArrowLeft') {
            _currentIndex = Math.ceil(_currentIndex - 1);
            if (_currentIndex < 0) _currentIndex = _imageCount - 1;
            toImage();
        }
    }

    function handleChange_checkboxWrapImage(event) {
        event.target.disabled = true;
        if (event.target.checked) {
            _useImageNavigationExt = true;
            localStorage.setItem('_useImageNavigationExt', true);
            EnableImageWrapping();
        } else {
            _useImageNavigationExt = false;
            localStorage.setItem('_useImageNavigationExt', false);
            DisableImageWrapping();
        }
        event.target.disabled = false;
        // window.location.reload();
    }

    function handleChange_checkboxDarkmode(event) {
        event.target.disabled = true;
        if (event.target.checked) {
            _useDarkmode = true;
            localStorage.setItem('_useDarkmode', true);
        } else {
            _useDarkmode = false;
            localStorage.setItem('_useDarkmode', false);
        }
        document.querySelector('html').classList.toggle('darkmode');
        event.target.disabled = false;
        // window.location.reload();
    }

    function handleChange_checkboxSmoothScroll(event) {
        event.target.disabled = true;
        if (event.target.checked) {
            _useSmoothScroll = true;
            localStorage.setItem('_useSmoothScroll', true);
            document.querySelector('html').style.scrollBehavior = 'smooth';
        } else {
            _useSmoothScroll = false;
            localStorage.setItem('_useSmoothScroll', false);
            document.querySelector('html').style.removeProperty('scroll-behavior');
        }
        event.target.disabled = false;
        // window.location.reload();
    }

    function handleScroll(event) {
        if (window.scrollY > 250) {
            _hTopButton.classList.add('show');
        } else {
            _hTopButton.classList.remove('show');
        }

        if (_preventScrollIndex) {
            clearTimeout(_preventScrollIndexTimeout);
            _preventScrollIndexTimeout =
                setTimeout(disableScrollIndexPrevention, SCROLL_APPLY_TIME);
            return;
        }

        _scrollIndex = 0;
        var rect;
        var hiddenCounter = true;
        for (; _scrollIndex < _imageCount; _scrollIndex++) {
            rect = _images[_scrollIndex].getBoundingClientRect();
            if (rect.top > window.innerHeight) { // if current image under the view
                _scrollIndex = _scrollIndex - .5; break;
            }
            if ((rect.top + rect.bottom) / 2 >= 0) { // if current image in the view
                hiddenCounter = false;
                break;
            }
        }
        if (rect.bottom < 0) { // if last image above the view
            _scrollIndex = _imageCount - 0.5;
        }

        if (_scrollIndex > _imageCount) {
            _scrollIndex = _imageCount - 1;
            console.log("How I ended up here?!");
        }

        if (hiddenCounter) {
            clearTimeout(_scrollTimeout);
            // _hCounter.innerHTML = '';
            _scrollTimeout = setTimeout(() => {
                _currentIndex = _scrollIndex;
            }, SCROLL_APPLY_TIME);
        } else {
            clearTimeout(_scrollTimeout);
            _scrollTimeout = setTimeout(() => {
                _currentIndex = _scrollIndex;
                updateCounter(COUNTER_FADEOUT_TIME - SCROLL_APPLY_TIME);
            }, SCROLL_APPLY_TIME);

            updateCounter();
        }
    }

    //
    // Wrapping images
    //

    function EnableImageWrapping() {
        _images = document.querySelectorAll('img');

        var index = 0, indexTo = 0, hrefS = '#_img-';
        var linkB, linkF;
        _imageCount = _images.length
        for (; index < _imageCount; index++) {
            indexTo = ((index <= 0) ? _imageCount - 1 : index - 1);
            linkB = document.createElement('a');
            linkB.setAttribute('href', hrefS + indexTo);
            linkB.setAttribute('data-index', index);
            linkB.setAttribute('data-indexto', indexTo);
            linkB.classList.add('backward-link');

            indexTo = ((index < _imageCount - 1) ? index + 1 : 0);
            _images[index].setAttribute('id', '_img-' + index);
            _images[index].setAttribute('data-index', index);
            _images[index].setAttribute('data-indexto', indexTo);
            linkF = document.createElement('a');
            linkF.setAttribute('href', hrefS + indexTo);

            _images[index].parentNode.appendChild(linkB);
            _images[index].parentNode.appendChild(linkF);
            linkF.appendChild(_images[index]);

            linkF.addEventListener('click', handleLinkClick);
            linkB.addEventListener('click', handleLinkClick);
        }

        if (_images) {
            _hCounter = document.createElement('div');
            _hCounter.classList.add('image-counter');
            document.querySelector('body').appendChild(_hCounter);
        }

        window.addEventListener('keydown', handleKeydown);
        window.addEventListener('scroll', handleScroll)
    }

    function DisableImageWrapping() {
        window.removeEventListener('keydown', handleKeydown);
        window.removeEventListener('scroll', handleScroll);

        var figureWrapper;
        _images.forEach(i => {
            figureWrapper = i.parentNode.parentNode;
            figureWrapper.insertBefore(i, figureWrapper.firstChild);
            i.removeAttribute('id');
            i.removeAttribute('data-index');
            i.removeAttribute('data-indexto');
        })
        document.querySelectorAll('[href^="#_img-"]').forEach(x => x.remove());
        _hCounter.remove();
        _images = [];
    }

    //
    // MAIN PART
    //

    window.addEventListener('DOMContentLoaded', function () {
        console.log('%c"Image navigation on telegraph" is running', 'color: yellow;');
        history.replaceState(null, null, ' ');

        //
        // Adding and changing the page style
        //

        if (_useSmoothScroll) {
            document.querySelector('html').style.scrollBehavior = 'smooth';
        }
        addGlobalStyle(buttonTop +
            imageStyle +
            addressElement +
            dropdownMenu +
            backwardLinkStyle +
            imageCounterStyle);
        addGlobalStyle(darkmode);

        //
        // Adding back to top button
        //

        _hTopButton = document.createElement('button');
        _hTopButton.classList.add('button');
        _hTopButton.classList.add('button-top');
        _hTopButton.innerHTML = 'UP';
        _hTopButton.addEventListener('click', (event) => {
            event.preventDefault();
            window.scrollTo({ top: 0, behavior: 'smooth' });
        });

        document.querySelector('body').appendChild(_hTopButton);

        //
        // Adding dropdown menu
        //
        var _hDropdownSpan = document.createElement('span');
        document.querySelector('address').appendChild(_hDropdownSpan);
        _hDropdownSpan.classList.add('dropdown-menu', 'address-element');
        _hDropdownSpan.appendChild(document.createTextNode('Settings'));

        var _hDropdownContent = document.createElement('span');
        _hDropdownContent.classList.add('dropdown-content');
        _hDropdownSpan.appendChild(_hDropdownContent);

        //
        // Adding an extension control checkbox
        //

        _hLabelUseExt = document.createElement('label');
        _hDropdownContent.appendChild(_hLabelUseExt);

        hInputUseExt = document.createElement('input');
        hInputUseExt.setAttribute('type', 'checkbox');
        hInputUseExt.checked = _useImageNavigationExt;
        _hLabelUseExt.appendChild(hInputUseExt)

        _hLabelUseExt.appendChild(document.createTextNode('Image navigation'));

        hInputUseExt.addEventListener('change', handleChange_checkboxWrapImage);

        document.getElementById('_edit_button')
            .addEventListener('click', () => {
                hInputUseExt.checked = false;
                DisableImageWrapping();
            });

        //
        // Adding an darkmode control checkbox
        //

        var _hLabelUseDarkmode = document.createElement('label');
        _hDropdownContent.appendChild(_hLabelUseDarkmode);

        var hInputDarkmode = document.createElement('input');
        hInputDarkmode.setAttribute('type', 'checkbox');
        hInputDarkmode.checked = _useDarkmode;
        _hLabelUseDarkmode.appendChild(hInputDarkmode)

        _hLabelUseDarkmode.appendChild(document.createTextNode('Dark mode'));

        hInputDarkmode.addEventListener('change', handleChange_checkboxDarkmode);

        //
        // Adding an smooth scroll control checkbox
        //

        var _hLabelUseSmoothScroll = document.createElement('label');
        _hDropdownContent.appendChild(_hLabelUseSmoothScroll);

        var hInputSmoothScroll = document.createElement('input');
        hInputSmoothScroll.setAttribute('type', 'checkbox');
        hInputSmoothScroll.checked = _useSmoothScroll;
        _hLabelUseSmoothScroll.appendChild(hInputSmoothScroll)

        _hLabelUseSmoothScroll.appendChild(document.createTextNode('Smooth scroll'));

        hInputSmoothScroll.addEventListener('change', handleChange_checkboxSmoothScroll);

        //
        // Wrapping images
        //

        if (_useImageNavigationExt) EnableImageWrapping();

        if (_useDarkmode) {
            document.querySelector('html').classList.toggle('darkmode');
        }

        console.log('%c"Image navigation on telegraph" has been successfully launched', 'color: green;');
    }, false)

})();