Greasy Fork is available in English.

Pawoo+

Enhance your Pawoo experience with features from Google+

/*

Following code belongs to Pawoo+.
Copyright (C) 2018 Jackson Tan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

// ==UserScript==
// @id             PawooPlus
// @name           Pawoo+
// @namespace      pawoo.plus
// @version        0.1.2
// @description    Enhance your Pawoo experience with features from Google+
// @author         Jackson Tan
// @match          https://pawoo.net/*
// @include        https://pawoo.net/*
// @require        https://greasyfork.org/scripts/22406-moment-js/code/Momentjs.js?version=142481
// @run-at         document-end
// @grant          GM_xmlhttpRequest
// ==/UserScript==

'use strict';

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

var styles = '.detailed-comments-container {padding-top: 12px} .detailed-comments a {color: rgba(255,255,255,0.6); text-decoration: none; font-weight: bold;} .status__action-bar-button {display: flex;} .detailed-status__favorites, .detailed-status__reblogs, .detailed-status__replies {display: inline-block;font-weight: 500;font-size: 16px;margin-left: 6px;} .status .icon-button {width: 36.67147px!important;}';

function onLoad() {
    var host = 'https://pawoo.net';
    var statusApi = '/api/v1/statuses/';
    var contextApi = '/context';
    var favoriteApi = '/favourite';
    var unfavoriteApi = '/unfavourite';
    var reblogApi = '/reblog';
    var unreblogApi = '/unreblog';
    var authentication = JSON.parse(document.getElementById('initial-state').innerHTML).meta.access_token;

    var streamColClass = "item-list";
    var streamColSelector = ".item-list";
    var postTagName = 'ARTICLE';
    var dataIdAttr = 'data-id';
    var statusContentSelector = ".status__content";

    var autoLoadIntervalVal = 30000;

    moment.locale(document.documentElement.lang);

    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            var nodes = Array.prototype.slice.call(mutation.addedNodes);
            nodes.forEach(function (node) {
                if (node.parentElement && node.parentElement.className === streamColClass) {
                    batchReplace(node);
                } else if (node.parentElement && node.parentElement.tagName === postTagName){
                    batchReplace(node.parentNode);
                }
            });
        });
    });
    observer.observe(document.body.querySelectorAll(streamColSelector)[0], {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false,
    });

    var autoLoadInterval = setInterval(function () {
        document.getElementsByClassName(streamColClass)[0].childNodes.forEach(function (item) {
            batchReplace(item);
        })
    }, autoLoadIntervalVal);

    GM_addStyle(styles);

    function batchReplace(node) {
        if (node) {
            var dataId = node.getAttribute(dataIdAttr);
            getPostStatus(dataId, function (reblogs, favorites) {
                if (reblogs !== null && node.querySelector('.fa-retweet')) {
                    if (node.querySelector('.detailed-status__reblogs__count')) {
                        var elem = node.querySelector('.detailed-status__reblogs__count');
                        removeElement(elem);
                    }
                    var element = document.createElement('div');
                    element.setAttribute('class', 'detailed-status__reblogs__count');
                    element.innerHTML = '<span class="detailed-status__reblogs"><span>' + reblogs + '</span></span>';
                    node.querySelector('.status__prepend') ? node.querySelectorAll('.fa-retweet')[1].parentNode.appendChild(element) : node.querySelector('.fa-retweet').parentNode.appendChild(element);
                }
                if (favorites !== null && node.querySelector('.fa-star')) {
                    if (node.querySelector('.detailed-status__favorites__count')) {
                        var elem2 = node.querySelector('.detailed-status__favorites__count');
                        removeElement(elem2);
                    }
                    var element2 = document.createElement('div');
                    element2.setAttribute('class', 'detailed-status__favorites__count');
                    element2.innerHTML = '<span class="detailed-status__favorites"><span>' + favorites + '</span></span>';
                    node.querySelector('.fa-star').parentNode.appendChild(element2);
                }
            }, function (replies) {
                if (replies !== null && (node.querySelector('.fa-reply') || node.querySelector('.fa-reply-all'))) {
                    if (node.querySelector('.detailed-status__replies__count')) {
                        var elem = node.querySelector('.detailed-status__replies__count');
                        removeElement(elem);
                    }
                    var element = document.createElement('div');
                    element.setAttribute('class', 'detailed-status__replies__count');
                    element.innerHTML = '<span class="detailed-status__replies"><span>' + replies + '</span></span>';
                    node.querySelector('.fa-reply') ? node.querySelector('.fa-reply').parentNode.appendChild(element) : node.querySelector('.fa-reply-all').parentNode.appendChild(element);
                }
            }, function (posts) {
                if (posts) {
                    var html = '<div class="detailed-comments">';
                    posts.forEach(function (item, index) {
                        var favoriteCountHTML = item.favourites_count > 0 ? '<div style="padding-left: 16px;"><span style="color: #db4437;font-weight: bold;">+' + item.favourites_count + '</span></div>' : '';
                        var postUrl = item.url;
                        var commentTimeHTML = '<div style="padding-left: 16px;display: flex;flex-direction: column;justify-content: center;"><span style="color: #606984;"><a href=' + postUrl + '>' + moment(item.created_at).fromNow(true) + '</a></span></div>';
                        var element = document.createElement('div');
                        element.innerHTML = item.content;
                        var contentHTML = processDeleteLine(element.childNodes[0], true);
                        var postId = item.id;
                        var favorited = item.favourited;
                        var favoriteHTML = favorited ? '<div class="detailed-comment-unfavorite-button" style="padding-left: 16px; cursor: pointer;"><span style="color: #db4437;font-weight: bold;">+1</span></div>' : '<div class="detailed-comment-favorite-button" style="padding-left: 16px; cursor: pointer;"><span style="color: rgba(255,255,255,0.6);font-weight: bold;">+1</span></div>';
                        var reblogged = item.reblogged;
                        var reblogHTML = reblogged ? '<div class="detailed-comment-unreblog-button" style="padding-left: 16px; cursor: pointer;"><span style="color: #2b90d9;font-weight: bold;">Reshared</span></div>' : '<div class="detailed-comment-reblog-button" style="padding-left: 16px; cursor: pointer;"><span style="color: rgba(255,255,255,0.6);font-weight: bold;">Reshare</span></div>';
                        html += '<div class="detailed-comment" data-id="' + postId + '"><div style="display: flex;"><div><img src="' + item.account.avatar + '" height=24 width=24 style="border-radius: 50%;"></div><div style="display: flex;flex-direction: row;justify-content: center;padding-left: 6px;"><span style="display: flex;flex-direction: column;justify-content: center;"><a href="' + item.account.url + '">' + item.account.display_name + '</a></span><span class="detailed-comment-username" style="display: flex;flex-direction: column;justify-content: center;color: #606984;padding-left: 4px;">@' + item.account.username + '</span>' + commentTimeHTML + '</div></div><div style="padding-left: 28px;display:flex;"><div>' + contentHTML + '</div>' + favoriteCountHTML + '</div><div style="display: flex; padding-top: 2px;"><span class="reply_inline_comment_button" style="cursor: pointer; padding-left: 28px; color: rgba(255,255,255,0.6);">Reply</span>' + reblogHTML + favoriteHTML + '</div><div class="reply_inline_comment_edit_content" style="display: none; padding-left: 28px; flex-direction: column;"><textarea class="reply_inline_comment_textarea" style="resize: none; background: transparent; color: #FFF;" placeholder="コメントを追加..." aria-label="コメントを追加..." role="textbox"></textarea><div style="display: flex; flex-direction: row;"><div class="reply_inline_comment_submit_button" style="padding: 8px; cursor: pointer;">Submit</div><div class="reply_inline_comment_cancel_button" style="padding: 8px; cursor: pointer;">Cancel</div></div></div></div>'
                    })
                    html += '</div>'
                    if (node.querySelector('.status')) {
                        if (node.querySelector('.detailed-comments-container')) {
                            var elem = node.querySelector('.detailed-comments-container');
                            removeElement(elem);
                        }
                        var element = document.createElement('div');
                        element.setAttribute('class', 'detailed-comments-container');
                        element.innerHTML = html;
                        element.querySelectorAll('.detailed-comment') !== null && element.querySelectorAll('.detailed-comment').forEach(function (item, index) {
                            item.querySelector('.reply_inline_comment_button') !== null && item.querySelector('.reply_inline_comment_button').addEventListener('click', function (e) {
                                var username = e.target.parentNode.parentNode.querySelector('.detailed-comment-username') !== null ? e.target.parentNode.parentNode.querySelector('.detailed-comment-username').textContent + ' ' : '';
                                if (e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea') !== null) {
                                    e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea').value = username;
                                }
                                clearInterval(autoLoadInterval);
                                if (e.target.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content') !== null) {
                                    e.target.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content').style.display = 'flex';
                                }
                            })
                            item.querySelector('.reply_inline_comment_submit_button') !== null && item.querySelector('.reply_inline_comment_submit_button').addEventListener('click', function (e) {
                                var status = e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea').value;
                                var replyToId = e.target.parentNode.parentNode.parentNode.getAttribute('data-id');
                                if (status.trim() !== '') {
                                    replyPost(status, replyToId, batchReplace, node);
                                    if (e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea') !== null) {
                                        e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea').value = '';
                                    }
                                    if (e.target.parentNode.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content') !== null) {
                                        e.target.parentNode.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content').style.display = 'none';
                                    }
                                    setNewInterval();
                                }
                            })
                            item.querySelector('.reply_inline_comment_cancel_button') !== null && item.querySelector('.reply_inline_comment_cancel_button').addEventListener('click', function (e) {
                                if (e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea') !== null) {
                                    e.target.parentNode.parentNode.querySelector('.reply_inline_comment_textarea').value = '';
                                }
                                if (e.target.parentNode.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content') !== null) {
                                    e.target.parentNode.parentNode.parentNode.querySelector('.reply_inline_comment_edit_content').style.display = 'none';
                                }
                                setNewInterval();
                            })
                            item.querySelector('.detailed-comment-favorite-button') !== null && item.querySelector('.detailed-comment-favorite-button').addEventListener('click', function (e) {
                                var replyToId = e.target.parentNode.parentNode.parentNode.getAttribute('data-id');
                                favoritePost(replyToId, batchReplace, node);
                            })
                            item.querySelector('.detailed-comment-unfavorite-button') !== null && item.querySelector('.detailed-comment-unfavorite-button').addEventListener('click', function (e) {
                                var replyToId = e.target.parentNode.parentNode.parentNode.getAttribute('data-id');
                                unfavoritePost(replyToId, batchReplace, node);
                            })
                            item.querySelector('.detailed-comment-reblog-button') !== null && item.querySelector('.detailed-comment-reblog-button').addEventListener('click', function (e) {
                                var replyToId = e.target.parentNode.parentNode.parentNode.getAttribute('data-id');
                                reblogPost(replyToId, batchReplace, node);
                            })
                            item.querySelector('.detailed-comment-unreblog-button') !== null && item.querySelector('.detailed-comment-unreblog-button').addEventListener('click', function (e) {
                                var replyToId = e.target.parentNode.parentNode.parentNode.getAttribute('data-id');
                                unreblogPost(replyToId, batchReplace, node);
                            })
                        })
                        node.querySelector('.status').appendChild(element);
                    }
                }
            })
            processDeleteLine(node, false);
        }
    }

    function processDeleteLine(node, isComment) {
        var stack = [];
        var result = [];
        var commentHTML = '';
        var textNode = isComment ? node.childNodes : (node.querySelector(statusContentSelector) !== null ? node.querySelector(statusContentSelector).childNodes : null);
        textNode !== null && textNode.forEach(function (item, index) {
            var matches = item.textContent.match(/(-)/mgi);
            if (matches && matches.length > 1) {
                var indicies = charLocations('-', item.textContent);
                if (indicies.length > 1) {
                    var newText = item.textContent;
                    var charsToReplace = indicies.length % 2 === 0 ? indicies.length : indicies.length - 1;
                    for (var i = 0; i < charsToReplace; i++) {
                        newText = i % 2 === 0 ? newText.replace('-', '<del>') : newText.replace('-', '</del>');
                    }
                    var element = document.createElement('span');
                    element.innerHTML = newText;
                    if (isComment) {
                        commentHTML += newText;
                    } else {
                        item.parentNode.insertBefore(element, item);
                        removeElement(item);
                    }
                }
            }
            else {
                if (item.nodeType === 3 && node.textContent.match('-') !== null) {
                    if (stack.length > 0) {
                        var start = stack.pop();
                        result.push({ start, end: index });
                    } else {
                        stack.push(index)
                    }
                }
                if (item.tagName === 'BR') {
                    stack = [];
                } else if (isComment) {
                    commentHTML += item.outerHTML ? item.outerHTML : item.textContent
                }
            }
        })

        result.length > 0 && result.forEach(function (item, index) {
            var startIndex = item.start;
            var endIndex = item.end;
            for (var i = startIndex; i <= endIndex; i++) {
                var currentNode = isComment ? node.childNodes[i] : node.querySelector(statusContentSelector).childNodes[i];
                if (currentNode.nodeType === 3) {
                    var element = document.createElement('span');
                    var newText;
                    if (i === endIndex) {
                        newText = currentNode.textContent.replace('-', '</del>');
                        element.innerHTML = '<del>' + newText;
                    } else {
                        newText = currentNode.textContent.replace('-', '<del>');
                        element.innerHTML = newText + '</del>';
                    }
                    if (isComment) {
                        commentHTML += newText;
                    } else {
                        currentNode.parentNode.insertBefore(element, currentNode);
                        removeElement(currentNode);
                    }
                } else {
                    var element2 = document.createElement('span');
                    element2.innerHTML = '<del>' + currentNode.innerHTML + '</del>';
                    if (isComment) {
                        commentHTML += newText;
                    } else {
                        currentNode.parentNode.insertBefore(element2, currentNode);
                        removeElement(currentNode);
                    }
                }
            }
        })
        if (isComment) {
            return commentHTML !== '' ? commentHTML : node.innerHTML;
        }
    }

    function charLocations(substring, string) {
        var a = [], i = -1;
        while ((i = string.indexOf(substring, i + 1)) >= 0) a.push(i);
        return a;
    }

    function removeElement(element) {
        element.parentNode.removeChild(element);
    }

    function uuidv4() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    function setNewInterval() {
        autoLoadInterval = setInterval(function () {
            document.getElementsByClassName(streamColClass)[0].childNodes.forEach(function (item) {
                batchReplace(item);
            })
        }, autoLoadIntervalVal);
    }

    function getPostStatus(id, statsProcessor0, statsProcessor1, postProcessor) {
        GM_xmlhttpRequest({
            method: "GET",
            url: host + statusApi + id,
            headers: { "Accept": "application/json" },
            onload: function (response) {
                var data = JSON.parse(response.responseText);
                var reblogs = data.reblogs_count ? data.reblogs_count : 0;
                var favorites = data.favourites_count ? data.favourites_count : 0;
                statsProcessor0(reblogs, favorites);
            },
        });
        GM_xmlhttpRequest({
            method: "GET",
            url: host + statusApi + id + contextApi,
            headers: { "Accept": "application/json" },
            onload: function (response) {
                var data = JSON.parse(response.responseText);
                var replies = data.descendants ? data.descendants.length : 0;
                var posts = data.descendants;
                statsProcessor1(replies);
                postProcessor(posts);
            },
        });
    }

    function replyPost(status, replyToId, cb, node) {
        var statusData = new FormData();
        statusData.append("status", status);
        statusData.append("in_reply_to_id", replyToId);
        statusData.append("media_ids", []);
        statusData.append("sensitive", false);
        statusData.append("spoiler_text", "");
        statusData.append("visibility", "public");

        GM_xmlhttpRequest({
            method: "POST",
            data: statusData,
            url: host + statusApi,
            headers: { "idempotency-key": uuidv4, "authorization": "Bearer " + authentication },
            onload: function (res) {
                cb(node);
            }
        });
    }

    function favoritePost(postId, cb, node) {
        GM_xmlhttpRequest({
            method: "POST",
            url: host + statusApi + postId + favoriteApi,
            headers: { "authorization": "Bearer " + authentication },
            onload: function (res) {
                cb(node);
            }
        });
    }

    function unfavoritePost(postId, cb, node) {
        GM_xmlhttpRequest({
            method: "POST",
            url: host + statusApi + postId + unfavoriteApi,
            headers: { "authorization": "Bearer " + authentication },
            onload: function (res) {
                cb(node);
            }
        });
    }

    function reblogPost(postId, cb, node) {
        GM_xmlhttpRequest({
            method: "POST",
            url: host + statusApi + postId + reblogApi,
            headers: { "authorization": "Bearer " + authentication },
            onload: function (res) {
                cb(node);
            }
        });
    }

    function unreblogPost(postId, cb, node) {
        GM_xmlhttpRequest({
            method: "POST",
            url: host + statusApi + postId + unreblogApi,
            headers: { "authorization": "Bearer " + authentication },
            onload: function (res) {
                cb(node);
            }
        });
    }
    window.onpopstate = history.onpushstate = function(e) {
        observer.observe(document.body.querySelectorAll(streamColSelector)[0], {
            childList: true,
            subtree: true,
            attributes: false,
            characterData: false,
        });
    }
}

window.addEventListener('load', onLoad, false);