Medium: Editor For Programmers

Use `code` for inline code. Automatically fix quotes in code tags. Link to a section of text easily.

Από την 09/07/2016. Δείτε την τελευταία έκδοση.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Medium: Editor For Programmers
// @namespace    https://github.com/Zren/
// @version      4
// @description  Use `code` for inline code. Automatically fix quotes in code tags. Link to a section of text easily.
// @author       Zren
// @icon         https://medium.com/favicon.ico
// @match        https://medium.com/p/*/edit
// @grant        none
// ==/UserScript==

(function(){
    function wrapInlineCode() {
        var tagBefore = '<code class="markup--code"><strong class="markup--strong">';
        var tagAfter = '</strong></code>';

        var sel = window.getSelection();
        if (sel.type === 'Caret' && sel.focusNode.nodeName === "#text") {
            // Note: Does not trigger if first character in text node, since the focus isn't a Text node.
            var str = sel.focusNode.nodeValue;
            var before = str.substr(0, sel.focusOffset - 1);
            var after = str.substr(sel.focusOffset);
            console.log("Before: ", before);
            console.log("After: ", after);
            var index = before.lastIndexOf('`');
            if (index >= 0) {
                // we just typed the right quote
                if (index === before.length-1) {
                    // ``
                    // Ignore
                } else {
                    // `...`
                    var range = document.createRange();
                    range.setStart(sel.focusNode, index);
                    range.setEnd(sel.focusNode, sel.focusOffset);
                    sel.removeAllRanges();
                    sel.addRange(range);
                    var html = range.toString();
                    html = html.substr(1, html.length-2); // trim ``
                    html = tagBefore + html + tagAfter + ' ';
                    document.execCommand('insertHTML', false, html);
                    range.collapse(false); // move cursor to end
                    return;
                }
            }
            var index = after.indexOf('`');
            if (index >= 0) {
                // we just typed the left quote
                if (index == 0) {
                    // ``
                    // Ignore
                } else {
                    var range = document.createRange();
                    range.setStart(sel.focusNode, sel.focusOffset - 1);
                    range.setEnd(sel.focusNode, sel.focusOffset + index + 1);
                    sel.removeAllRanges();
                    sel.addRange(range);
                    var html = range.toString();
                    html = html.substr(1, html.length-2); // trim ``
                    html = tagBefore + html + tagAfter + ' ';
                    document.execCommand('insertHTML', false, html);
                    range.collapse(false); // move cursor to end
                    return;
                }
            }
        }
    }
    
    function alwaysBrInPre(e) {
        var sel = window.getSelection();
        if (sel.type === 'Caret' && sel.focusNode.nodeName === "#text") {
            console.log(sel.focusNode.nodeValue.length, sel.focusOffset, sel.focusNode.nodeValue.substr(sel.focusOffset), sel.focusNode);
            if (sel.focusOffset !== 0) return; // End of line = selecting start of next line.

            // Focused on start of line.
            var el = sel.focusNode;
            while (el) {
                if (!el.parentNode) break; // Don't run on #document element since it doesn't have el.hasAttribute
                if (el.classList && el.classList.contains('section-inner')) break; // Ignore everything outside the post itself.
                if (el != el.parentNode.firstChild) break; // Only match end of line = start of next line.
                if (el.parentNode.tagName == 'PRE') {
                    // Insert linebreak <br>
                    
                    var secondPre = el.parentNode;
                    var firstPre = secondPre.previousSibling;
                    
                    firstPre.appendChild(document.createElement('br')); // <br> removed during split
                    firstPre.appendChild(document.createTextNode(''));
                    var newFocusLine = document.createElement('br');
                    firstPre.appendChild(newFocusLine); // The actual <br> we wanted to enter.
                    
                    // Move all elements back into the first pre.
                    while (secondPre.firstChild) {
                        firstPre.appendChild(secondPre.firstChild);
                    }
                    
                    // Delete the second <pre>
                    // We can't remove it since it will break the entire editor...
                    secondPre.appendChild(document.createElement('br'));
                    //secondPre.remove();
                    //document.execCommand('delete');
                    
                    // Move cursor to new line.
                    var range = document.createRange();
                    range.setStart(newFocusLine, 0);
                    range.collapse(true);
                    sel.removeAllRanges();
                    sel.addRange(range);
                    
                    e.preventDefault();
                }
                el = el.parentNode;
            }
        }
    }

    function onKeyDown(e) {
        if (e.key === '`') {
            setTimeout(wrapInlineCode, 100); // Wait for ` to be written so we can replace it
        } else if (e.keyCode == 9) { // Tab
            e.preventDefault();
        } else if (e.keyCode == 13) { // Enter
            alwaysBrInPre(e);
        } else if (e.key == '6' && e.ctrlKey && e.altKey) {
            console.log('CTRL+ALT+6', e);
        } else {
            console.log('Key:', e.key, e.ctrlKey, e.altKey);
        }
    }
    
    function fixQuotes() {
        // Fix quotes in <pre> and <code> tags.
        setInterval(function(){
            var codeTags = document.querySelectorAll('pre, code');
            for (var tag of codeTags) {
                if (tag.innerHTML.indexOf('“') >= 0 || tag.innerHTML.indexOf('”') >= 0) {
                    tag.innerHTML = tag.innerHTML.replace('“', '"').replace('”', '"');
                }
            }
        }, 1000);
    }
    
    function showPermalink() {
        // Setup (temporary) permalink to line.
        var tag = document.createElement('a');
        tag.style.position = 'absolute';
        tag.style.display = 'block';
        tag.style.top = '-9999px';
        tag.style.left = 0;
        tag.style.color = '#888';
        tag.innerHTML = '¶'; //'[link]';
        document.body.appendChild(tag);

        function onMouseOver(e) {
            var el = e.relatedTarget || e.target;
            while (el) {
                if (!el.parentNode) break; // Don't run on #document element since it doesn't have el.hasAttribute
                if (el.classList.contains('section-inner')) break; // Ignore everything outside the post itself.
                if (el.hasAttribute('name')) {
                    showTag(el)
                    break;
                }
                el = el.parentNode;
            }
        }
        function showTag(el) {
            var rect = el.getBoundingClientRect();
            var url = document.querySelector('link[rel="canonical"]').href;
            url = url.substr(0, url.length - '/edit'.length);
            url += '#' + el.getAttribute('name');
            tag.href = 'javascript:window.history.replaceState({}, "", "' + url + '")';
            var tagGuide = document.querySelector('.section-inner');
            var tagGuideRect = tagGuide.getBoundingClientRect();
            var tagRect = tag.getBoundingClientRect();
            tag.style.left = '' + (tagGuideRect.left + window.scrollX - tagRect.width - 60) + 'px';
            tag.style.top = '' + (rect.top + window.scrollY) + 'px';
        }

        var taggedElements = document.querySelectorAll('.postArticle-content');
        for (var el of taggedElements) {
            //el.addEventListener('mouseover', onMouseOver, true);
            el.addEventListener('click', onMouseOver, true);
        }
    }
    
    function onLoad() {
        // Bind keys
        var main = document.querySelector('main[contenteditable="true"]');
        main.addEventListener('keydown', onKeyDown, true);
        
        fixQuotes();
        showPermalink();
    }

    function waitForLoad() {
        var main = document.querySelector('main[contenteditable="true"]');
        if (main) {
            onLoad();
            console.log('[Medium: Markdown] Loaded');
        } else {
            setTimeout(waitForLoad, 100);
        }
    }
    setTimeout(waitForLoad, 100);
})();