Enhancement Userscript for LIHKG

An Enhancement Userscript for LIHKG

2021-06-11 일자. 최신 버전을 확인하세요.

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name         Enhancement Userscript for LIHKG
// @version      0.4
// @description  An Enhancement Userscript for LIHKG
// @include        /https?\:\/\/lihkg\.com/
// @icon         https://www.google.com/s2/favicons?domain=lihkg.com
// @grant        GM_addStyle
// @namespace https://greasyfork.org/users/371179
// ==/UserScript==
(function() {
    'use strict';
    GM_addStyle(`
    .EGBBkGyEbfIEpHMLTW84H:not([dragmode]),.EGBBkGyEbfIEpHMLTW84H[dragmode="text"]{position:fixed;left:-9999px;top:-9999px;width:2px;height:2px;}
    div[contenteditable] p>img~br:last-child,div[contenteditable] p>a~br:last-child,div[contenteditable] p>div~br:last-child{content:'';}
    body ._21IQKhlBjN2jlHS_TVgI3l:after {left:0.4rem}
    body ._21IQKhlBjN2jlHS_TVgI3l .vv9keWAXpwoonDah6rSIU ._3D2lzCKDMcdgEkexZrTSUh{margin-left: -6px;width: 16px;}
    body label[for*="-dislike-like"]{display:inline-block !important;}
    body label[for*="-like-like"]{display:inline-block !important;}
    body ._3ExaynSI6tUp5h1U50MHtI ._3imUf8qB9LmLpk_t5PjDm4>div:first-child+div:last-child{margin-left:-6px;}
    span[data-tip="正評"]:not([data-score])::after{content: " ";
    font-size: .6rem;
    font-weight: 400;
    margin-top: .3rem;}
    span[data-tip="負評"]:not([data-score])::after{content: " ";
    font-size: .6rem;
    font-weight: 400;
    margin-top: .3rem;}
    span[data-tip="正評"],span[data-tip="負評"]{padding-top:0px !important;}
    `)
    //fix copy editing


    var isNumCheck = function(n) {
        return n > 0 || n < 0 || n === 0
    }
    var postDetails = {}
    var threadDetails = {}
    var pendingRefreshThread = false;

    var testBlockElm = function(elm) {
        if (elm && elm.nodeType == 1) {
            switch (elm.tagName) {
                case 'DIV':
                case 'P':
                case 'BLOCKQUOTE':
                    return true;

                default:
                    return false;

            }

        }
    }

    var getElementText = function(el) {
        var text = '';
        // Text node (3) or CDATA node (4) - return its text
        if ((el.nodeType === 3) || (el.nodeType === 4)) {
            text = el.nodeValue;
            // If node is an element (1) and an img, input[type=image], or area element, return its alt text
        } else if ((el.nodeType === 1) && (
                (el.tagName.toLowerCase() == 'img') ||
                (el.tagName.toLowerCase() == 'area') ||
                ((el.tagName.toLowerCase() == 'input') && el.getAttribute('type') && (el.getAttribute('type').toLowerCase() == 'image'))
            )) {
            text = el.getAttribute('alt') || '';
            if (el.tagName.toLowerCase() == 'img' && text == '' && el.getAttribute('data-original')) {
                text = '[img]' + el.getAttribute('data-original') + '[/img]';
            } else if (el.tagName.toLowerCase() == 'img' && text == '' && el.getAttribute('src')) {
                text = '[img]' + el.getAttribute('src') + '[/img]';
            }
            // Traverse children unless this is a script or style element
        } else if ((el.nodeType === 1) && (
                (el.tagName.toLowerCase() == 'br')
            )) {
            text = '\n';
            // Traverse children unless this is a script or style element
        } else if ((el.nodeType === 1) && !el.tagName.match(/^(script|style)$/i)) {
            var children = el.childNodes;

            if (el && testBlockElm(el) && el.previousSibling ? testBlockElm(el.previousSibling) : false) {
                text += '\n'
            }
            if (el.tagName.toLowerCase() == 'blockquote') {
                text += '[quote]'
            }
            for (var i = 0, l = children.length; i < l; i++) {
                text += getElementText(children[i]);
            }
            if (el.tagName.toLowerCase() == 'blockquote') {
                text += '[/quote]'
            }

        }
        return text;
    };


    document.cssAll = function() {

        var s = document.querySelectorAll.apply(this, arguments)

        s = Array.prototype.slice.call(s, 0)
        return s
    }

    function urlConvert(url) {
        var src = url.replace(/\w+\:\/\//, '')
        var replacements = [...src.matchAll(/[\w\.]+/g)].filter((t) => /\./.test(t))
        if (replacements.length > 1) {
            replacements.length--;

        }
        replacements.forEach((s) => {
            src = src.replace(s, '')
        })

        src = src.replace(/\/+/g, '/')

        return src;

    }

    var emoji = {};
    setTimeout(function() {
        console.log(emoji)
    }, 1500)

    setInterval(() => {

        document.cssAll('img[src*="lihkg.com"][alt]:not([title])').forEach(function(imgElm) {
            var src = imgElm.getAttribute('src');
            var erc = urlConvert(src)
            var imgAlt = imgElm.getAttribute('alt') || "";
            if (/^[\x20-\x7E]+$/.test(imgAlt) && /\W/.test(imgAlt)) {
                emoji[erc] = imgAlt.trim()
            }

            imgElm.setAttribute('title', imgAlt)

        })


        document.cssAll('a[href*="profile/"]:not([href*="//"]):not([title])').forEach(function(aElm) {
            aElm.setAttribute('title', aElm.getAttribute('href'))
        })

        document.cssAll('[data-ic~="hkgmoji"]:not([title])>img[src*="lihkg.com"]:not([alt])').forEach(function(imgElm) {
            var src = imgElm.getAttribute('src');
            var erc = urlConvert(src)
            var text = emoji[erc] ? emoji[erc] : "[img]" + erc + "[/img]"
            imgElm.parentNode.setAttribute('title', text)
            imgElm.setAttribute('alt', text)


        })

        document.cssAll('a[href*="local.lihkg.com"]>img:not([anchored])').forEach(function(img) {
            img.setAttribute('anchored', 'true')


            var originalSrc = img.getAttribute('src') || img.getAttribute('data-original') || ""
            var newSrc = originalSrc.replace('local.lihkg.com', 'cdn.lihkg.com');

            if (newSrc && originalSrc != newSrc) {

                // console.log(originalSrc, newSrc)

                var fx = function() {
                    if (img.complete == false) return setTimeout(fx, 33);

                    if (img.currentSrc == "") {

                        var b = img.cloneNode(false);
                        b.removeAttribute('data-original');
                        b.removeAttribute('data-src');

                        if (b.getAttribute('alt') == "") b.removeAttribute('alt')
                        if (b.getAttribute('title') == "") b.removeAttribute('title')

                        b.setAttribute('src', newSrc);
                        img.parentNode.replaceChild(b, img)

                        if (b.parentNode.getAttribute('href')) {
                            b.parentNode.setAttribute('href', b.parentNode.getAttribute('href').replace(originalSrc, newSrc));
                            if (b.nextElementSibling && b.nextElementSibling.hasAttribute('data-error')) b.nextElementSibling.parentNode.removeChild(b.nextElementSibling);
                            if (b.nextElementSibling && b.nextElementSibling.outerHTML.toLocaleLowerCase() == '<ins></ins>') b.nextElementSibling.parentNode.removeChild(b.nextElementSibling);



                        }

                        b.removeAttribute('anchored')


                    }

                }
                fx();


            }

        })


        document.cssAll('div[contenteditable] div[data-ic][contenteditable="false"]').forEach((elm) => {

            elm.removeAttribute('contenteditable')
        })


        document.cssAll('img[src]:not([alt]),img[src][alt=""]').forEach((el) => {

            if (el.getAttribute('alt') || el.getAttribute('title')) return;

            var text = '';
            if (el.tagName.toLowerCase() == 'img' && el.getAttribute('data-original')) {
                text = '[img]' + el.getAttribute('data-original') + '[/img]';
            } else if (el.tagName.toLowerCase() == 'img' && el.getAttribute('src')) {
                text = '[img]' + el.getAttribute('src') + '[/img]';
            }
            if (text) el.setAttribute('alt', text)
            if (text) el.setAttribute('title', text)

        })




        document.cssAll('[data-post-id]:not([hacked])').forEach((el) => {

            el.setAttribute('hacked', 'true');
            var post_id = el.getAttribute('data-post-id');
            if (!post_id) return;

            //console.log(post_id, postDetails)
            var post_detail = postDetails[post_id]
            if (post_detail) {
                // console.log(55,post_detail)

            }

        })

        //postDetails
        //data-post-id="5226a9cb7b395fbc182d183a6ee9b35c8adfd2fe"




    }, 33)



    function refreshingThreadEvent(thread_id) {


        console.log(threadDetails[thread_id])
        if (thread_id && threadDetails[thread_id]) {


            document.cssAll('span[data-tip="正評"]').forEach((elm) => {

                elm.setAttribute('data-score', threadDetails[thread_id]["like_count"]);
                elm.style.paddingTop = '0px';
            })


            document.cssAll('span[data-tip="負評"]').forEach((elm) => {

                elm.setAttribute('data-score', threadDetails[thread_id]["dislike_count"]);
                elm.style.paddingTop = '0px';
            })



        }


    }


    var cid_refreshingThread = 0;

    function refreshingThread() {

        if (!cid_refreshingThread) return;


        var titlespan = document.cssAll('a[href^="/category/"]+span');
        if (titlespan.length == 1) {
            var titlespanElm = titlespan[0]

            if (!titlespanElm.querySelector('noscript')) {
                titlespanElm.appendChild(document.createElement('noscript'))


                if (pendingRefreshThread) {

                    var thread_id = pendingRefreshThread === true ? (/thread\/(\d+)\//.exec(location + "") || [null, null])[1] : pendingRefreshThread

                    pendingRefreshThread = false;
                    clearInterval(cid_refreshingThread);
                    cid_refreshingThread = 0;
                    refreshingThreadEvent(thread_id)


                }


            }
        }

    }




    var makePlain = false;
    document.addEventListener('drop', function(evt) {
        // console.log(evt, makePlain, evt.target)

        var p = evt.target;
        var contenteditable = false;
        while (p) {
            if (p.hasAttribute('contenteditable') && p.getAttribute('contenteditable') != 'false') {
                contenteditable = true;
                break;
            }
            p = p.parentNode;

        }

        if (contenteditable) {

            if (makePlain && evt.dataTransfer.getData('text/html')) {

                var text = evt.dataTransfer.getData('text/html');


                setTimeout(function() {
                    var sel = window.getSelection();
                    if (sel.getRangeAt && sel.rangeCount) {


                        var range = sel.getRangeAt(0);

                        var cloneContents = range.cloneContents();


                        var hh = document.createElement('html')
                        hh.innerHTML = text;
                        console.log(hh.cloneNode(true))
                        var hhPlain = getElementText(hh)

                        console.log(hhPlain)
                        hh.innerText = hhPlain


                        if (cloneContents.textContent !== hhPlain) {

                            range.deleteContents();

                            var trueHTML = '<p>' + hh.innerHTML.toLowerCase().replace(/<br\s*\/?>/g, "<br></p><p>") + "</p>"

                            console.log(trueHTML)

                            document.execCommand('insertHTML', false, trueHTML);
                        }

                    }

                }, 10);
            }

        }


    }, true)


    document.addEventListener('dragstart', function(evt) {
        var editable = document.querySelector('div[contenteditable]')
        var dragFrom = evt.target
        if (editable && dragFrom) {

            if (editable == dragFrom || editable.contains(dragFrom)) {

            } else {
                makePlain = true;

            }

        }
        // console.log(evt)
    }, false);


    document.addEventListener('dragend', function(evt) {
        if (Event.prototype._preventDefault) {
            Event.prototype.preventDefault = Event.prototype._preventDefault
            Event.prototype._preventDefault = null

        }
        setTimeout(function() {
            makePlain = false;
        }, 77);
    }, false);

    document.addEventListener('drop', function(evt) {

        setTimeout(function() {
            makePlain = false;
        }, 77);
        if (Event.prototype._preventDefault) {
            Event.prototype.preventDefault = Event.prototype._preventDefault
            Event.prototype._preventDefault = null
        }

        var dropZone = document.querySelector('.EGBBkGyEbfIEpHMLTW84H');
        if (!dropZone) return;
        dropZone.removeAttribute('dragmode');


    }, true)



    document.addEventListener('dragenter', function(evt) {

        var dropZone = document.querySelector('.EGBBkGyEbfIEpHMLTW84H');



        var isFileTransfer = false;
        if (evt.dataTransfer.types) {
            for (var i = 0; i < evt.dataTransfer.types.length; i++) {
                if (evt.dataTransfer.types[i] == "Files") {
                    isFileTransfer = true;
                    break;
                }
            }
        }

        if (!isFileTransfer) {

            if (dropZone)
                dropZone.setAttribute('dragmode', 'text');
            //  evt.dataTransfer.effectAllowed='copy';
            if (!Event.prototype._preventDefault) {
                Event.prototype._preventDefault = Event.prototype.preventDefault;
                Event.prototype.preventDefault = function() {
                    if (this.type == 'dragover') {

                    } else {
                        return this._preventDefault();
                    }
                    //console.log(this)
                };
            }



        } else {
            makePlain = false;
            if (dropZone)
                dropZone.setAttribute('dragmode', 'file');


        }
        //        console.log('dragenter',!isFileTransfer)



    }, false)



    var injection = function() {

        var api_callback = "uleccyqjstui"

        ;
        ((xmlhr, xmlhr_pt) => {
            if (!xmlhr_pt._open) {
                xmlhr_pt._open = xmlhr_pt.open;


                xmlhr_pt.open = function() {
                    console.log('xmlhr_open', arguments)
                    if (/https?\:\/\/[\x20-2E\x30-5B\x5D-\x7E]*lihkg\.com\/[\x20-\x7E]*api[\x20-\x7E]+/.test(arguments[1])) {
                        this._url = arguments[1];

                        console.log('_url', this._url)
                    }
                    this._open.apply(this, arguments)
                }
            }



            if (!xmlhr_pt._send) {
                xmlhr_pt._send = xmlhr_pt.send;


                xmlhr_pt.send = function() {
                    if (this._url) {
                        this.addEventListener('load', function() {
                            var resText = this.responseText;
                            var jsonObj = null;
                            if (resText && typeof resText == 'string') {
                                try {
                                    jsonObj = JSON.parse(resText)
                                } catch (e) {}
                            }

                            if (jsonObj) {
                                //like_count

                                var code_num = 0

                                if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.item_data && jsonObj.response.item_data.length >= 1 && jsonObj.response.item_data[0]["post_id"]) {
                                    code_num = 361
                                } else if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.thread_id) {
                                    code_num = 351
                                }
                                console.log('code', code_num)
                                var event = new CustomEvent(api_callback, {
                                    detail: {
                                        code: code_num,
                                        responseJSON: jsonObj
                                    }
                                });
                                document.dispatchEvent(event);



                                //console.log(jsonObj)
                            }

                        })
                    }
                    console.log('xmlhr_send', arguments)
                    this._send.apply(this, arguments)
                }
            }


        })(XMLHttpRequest, XMLHttpRequest.prototype)

    }

    var jsscript = document.createElement('script');
    jsscript.type = 'text/javascript';
    jsscript.innerHTML = '(' + injection + ')()';
    document.documentElement.appendChild(jsscript)

    var api_callback = "uleccyqjstui"
    //data-post-id="5226a9cb7b395fbc182d183a6ee9b35c8adfd2fe"
    document.addEventListener(api_callback, function(e) {
        if (!e || !e.detail) return;
        console.log(e.detail)
        var jsonObj;
        switch (e.detail.code) {

            case 351:

            case 361:

                jsonObj = e.detail.responseJSON;


                if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.item_data && jsonObj.response.item_data.length >= 1 && jsonObj.response.item_data[0]["post_id"]) {
                    var reply_post_fx = (reply_item) => {
                        if ('dislike_count' in reply_item && 'like_count' in reply_item && reply_item["post_id"]) {

                            var like_count = +reply_item['like_count']
                            var dislike_count = +reply_item['dislike_count']
                            var post_id = reply_item['post_id']

                            if (isNumCheck(like_count) && isNumCheck(dislike_count) && post_id) {
                                postDetails[post_id] = {
                                    'like_count': like_count,
                                    'dislike_count': dislike_count
                                }
                            }

                        }
                    };
                    jsonObj.response.item_data.forEach(reply_post_fx)
                    if (jsonObj.response.pinned_post && jsonObj.response.pinned_post["post_id"]) reply_post_fx(jsonObj.response.pinned_post)

                }



                if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.thread_id) {
                    var thread_fx = (thread_item) => {
                        if ('like_count' in thread_item && 'dislike_count' in thread_item && thread_item["thread_id"]) {

                            var like_count = +thread_item['like_count']
                            var dislike_count = +thread_item['dislike_count']
                            var thread_id = thread_item['thread_id']

                            if (isNumCheck(like_count) && isNumCheck(dislike_count) && thread_id) {
                                threadDetails[thread_id] = {
                                    'like_count': like_count,
                                    'dislike_count': dislike_count
                                }
                                pendingRefreshThread = thread_id;
                                if (!cid_refreshingThread) cid_refreshingThread = setInterval(refreshingThread, 1);
                            }

                        }
                    };
                    thread_fx(jsonObj.response)
                    console.log(99, threadDetails)

                }

                console.log(jsonObj)
                break;


            default:
        }

    });


    // Your code here...
})();