FANDOM


/*

ThreadTools

A series of addons for the Message Wall and Article Comments

project page | reference | Stylesheet

Namespace ModuleEdit

*/
 
(function (ns) { // namespace module
 
    var threads = wgCanonicalNamespace == "Thread" || wgCanonicalNamespace == "Message_Wall";
 
    ns.isMessageWall = function () {
        return threads;
    };
 
    ns.escapeName = function (name) {
        return encodeURIComponent(name.replace(/ /, '_'));
    };
 
    ns.unescapeName = function (name) {
        return decodeURIComponent(name.replace(/_/, ' '));
    };
 
}(window.ThreadTools = window.ThreadTools || {}));
 
/*

Module "Storage"Edit

*/
 
(function (ns) { // module "Storage"
 
    var staleEntries = (new Date(Date.now() - 6 * 30 * 24 * 60 * 60 * 1000)).valueOf(); // six months
    //var staleEntries = (new Date(Date.now() - 2 * 60 * 1000)).valueOf(); // 2 minutes (for testing purposes)
 
    var users = $.extend([], {
 
        save: function () {
            if (this.length) {
                localStorage.setItem('ThreadTools_users', $.toJSON(this));
            } else {
                localStorage.removeItem('ThreadTools_users');
            }
        },
 
        load: function () {
            if (!this.length) {
                var users = $.evalJSON(localStorage.getItem('ThreadTools_users')) || [];
                for (var i = 0; i < users.length; i++) {
                    if (users[i].date > staleEntries) {
                        this.push(users[i]);
                    }
                }
                this.save();
            }
            return this;
        },
 
        add: function (user) {
            user = ns.unescapeName(user);
            for (var i = 0; i < this.length; i++) {
                if (user == this[i].user) {
                    this[i].date = Date.now().valueOf();
                    this.save();
                    return;
                }
            }
            this.push({ user: user, date: Date.now().valueOf() });
            this.save();
        },
 
        rem: function (user) {
            user = ns.unescapeName(user);
            for (var i = 0; i < this.length; i++) {
                if (user == this[i].user) {
                    this.splice(i, 1);
                    break;
                }
            }
            this.save();
        },
 
        getList: function () {
            var users = [];
            for (var i = 0; i < this.length; i++) {
                users.push(this[i].user);
            }
            return users;
        }
 
    }).load();
 
    var lastVisit;
 
    var pages = $.extend([], {
 
        save: function () {
            localStorage.setItem('ThreadTools_pages', $.toJSON(this));
        },
 
        load: function () {
            var pages = $.evalJSON(localStorage.getItem('ThreadTools_pages')) || [];
            var pageId = ns.isMessageWall() ? wgTitle : wgArticleId;
            var currentPage = false;
 
            for (var i = 0; i < pages.length; i++) {
                if (pages[i].id == pageId) {
                    currentPage = pages[i];
                    lastVisit = currentPage.date;
                    if (!currentPage.posts) currentPage.posts = [];
                    if (!currentPage.threads) currentPage.threads = [];
                    currentPage.date = Date.now().valueOf();
                    this.push(pages[i]);
                } else if (pages[i].date > staleEntries) {
                    if (pages[i].posts && !pages[i].posts.length) delete pages[i].posts;
                    if (pages[i].threads && !pages[i].threads.length) delete pages[i].threads;
                    this.push(pages[i]);
                }
            }
 
            if (!currentPage) {
                currentPage = {
                    id: pageId,
                    date: Date.now().valueOf(),
                    isWall: ns.isMessageWall(),
                    posts: [],
                    threads: []
                };
                lastVisit = Date.now().valueOf();
                this.push(currentPage);
            }
 
            this.getPage = function () {
                return currentPage;
            };
 
            var extendList = function (pages) {
                return {
 
                    toString: function (item) {
                        if (!this.length) return '';
                        if (ns.isMessageWall()) {
                            return 'li[data-id="' + this.join('"], li[data-id="') + '"]';
                        } else {
                            return 'li#comm-' + this.join(', li#comm-');
                        }
                    },
 
                    add: function (item) {
                        item = item.toString().replace(/\D+/, '');
                        var i = this.indexOf(item);
                        if (-1 < i) return;
                        this.push(item);
                        pages.save();
                    },
 
                    rem: function (item) {
                        var i = this.indexOf(item.toString().replace(/\D+/, ''));
                        if (-1 == i) return;
                        this.splice(i, 1);
                        pages.save();
                    }
                };
            }
            $.extend(currentPage.posts, extendList(this));
            $.extend(currentPage.threads, extendList(this));
 
            this.save();
            return this;
        }
 
    }).load();
 
    function getUnreadPosts () {
        var unread = $('');
        if (ns.isMessageWall()) {
            $('.timeago', 'ul.comments').each(function () {
                if (Date.parse($(this).attr('title')) > lastVisit) {
                    unread = unread.add($(this).parents('li.SpeechBubble:first'));
                }
            });
        } else {
            $('.permalink', 'ul.comments').each(function () {
                var m = $(this).attr('href').match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\?/);
                if (m) {
                    m.splice(0, 1);
                    m[1]--;  // months are zero based in JavaScript
                    if (new Date(Date.UTC.apply(this, m)) > lastVisit) {
                        unread = unread.add($(this).parents('li.SpeechBubble:first'));
                    }
                }
            });
        }
        return unread;
    }
 
    function getPlonkedPosts () {
        var collapsed = $('');
        var plonked = users.getList();
        if (ns.isMessageWall()) {
            $('div.edited-by a', 'ul.comments').each(function () {
                if (-1 < plonked.indexOf($(this).text())) {
                    collapsed = collapsed.add($(this).parents('li:first'));
                }
            });
        } else {
            $('li.SpeechBubble').each(function () {
                if (-1 < plonked.indexOf($(this).attr('data-user'))) {
                    collapsed = collapsed.add($(this));
                }
            });
        }
        return collapsed;
    }
 
    function getPlonkedUsers () {
        return users.getList();
    }
 
    function getCollapsedPosts () {
        return $(pages.getPage().posts.toString());
    }
 
    function getCollapsedThreads () {
        return $(pages.getPage().threads.toString());
    }
 
    ns.Storage = {
 
        addPost:   function (post) { pages.getPage().posts.add(post); },
        remPost:   function (post) { pages.getPage().posts.rem(post); },
 
        addThread: function (thread) { pages.getPage().threads.add(thread); },
        remThread: function (thread) { pages.getPage().threads.rem(thread); },
 
        addUser:   function (user) { users.add(user); },
        remUser:   function (user) { users.rem(user); },
 
        getUnreadPosts:      getUnreadPosts,
        getPlonkedPosts:     getPlonkedPosts,
        getPlonkedUsers:     getPlonkedUsers,
        getCollapsedPosts:   getCollapsedPosts,
        getCollapsedThreads: getCollapsedThreads
 
        ,users: users, pages: pages        
    };
 
}(window.ThreadTools = window.ThreadTools || {}));
 
/*

Module "Stylesheet"Edit

*/
 
/**
 * module "Stylesheet"
 *
 * Stylesheet URL: http://pecoes.wikia.com/wiki/ThreadTools.css
 *
 * This module has no properties or methods
 * it's sole purpose is to load the ThreadTools' stylesheet on document.ready
 * Since there's currently only one stylesheet for articles and comments,
 * but also for the different skins, this module also sets a number of classes
 * as triggers for the stylesheet
 */
(function (ns) {
 
    $(function () {
 
        var topUl = $('ul.comments');
 
        /**
         * calculate the "brightness" of a color after this formula:
         * http://24ways.org/2010/calculating-color-contrast
         * this information is needed to decide whether white or black
         * UI elements should be used
         */
        function isBright (color) {
            var m = color.match(/(?:([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2}))|(?:(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3}))/);
            if (!m) return false;
            var rgb = m[1] ? { r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16) }
                           : { r: parseInt(m[4], 10), g: parseInt(m[5], 10), b: parseInt(m[6], 10) };
            return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 >= 128;
        }
 
        /**
         * try to determine the page's background color:
         */
        function getBackColor () {
            var bgColor = $('#WikiaPageBackground').css('background-color');
            if ('transparent' == bgColor) bgColor = $('#WikiaPage').css('background-color');
            if ('transparent' == bgColor) bgColor = $('#EditPageHeader').css('background-color');
            return bgColor;
        }
 
        /**
         * determine whether the page-background is bright or dark
         * and add "bg-bright" or "bg-dark" to topmost ul:
         */
        topUl.addClass(isBright(getBackColor()) ? 'bg-bright' : 'bg-dark')
 
        /**
         * determine whether this is an article page or a thread page 
         * and add classes "article" or "thread" to topmost ul:
         */
        topUl.addClass(ns.isMessageWall() ? 'thread' : 'article');
 
        /**
         * determine skin and add that info to topmost ul as well:
         */
        var skinClass;
        switch (skin) {
            case 'oasis': case 'wikia':
                skinClass = 'oasis';                                            break;
            case 'monobook': case 'wowwiki': case 'lostbook': case 'uncyclopedia':
                skinClass = 'monobook';                                         break;
            case 'monaco': case 'monaco_old': case 'answers':
                skinClass = 'monaco';                                           break;
        }
        topUl.addClass(skinClass);
 
        /**
         * remove the ThreadTools stylesheet if it's alreay loaded (important for
         * testing purposes). Then load the stylesheet and attach it to the document's head
         */
        $('#thread-tools').remove();
        $(document.head).append('<link rel="stylesheet" type="text/css" href="http://pecoes.wikia.com/wiki/ThreadTools.css?action=raw&ctype=text/css">');
    });
 
}(window.ThreadTools = window.ThreadTools || {}));
 
/*

Module "ToggleButtons"Edit

*/
 
(function (ns) { // module "ToggleButtons"
 
    /*
     * Function:    getPostsByUser
     * Description: returns all <li> elements for posts made by user
     * Parameters:  username -- user whose posts to return
     */
    function getPostsByUser(username) {
	    if (ns.isMessageWall()) {
	    	var postsByUser = $();
	    	$('li.SpeechBubble').each(function () {
	    	    if ($(this).find('.edited-by a:first').text() === username) {
	    	        postsByUser = postsByUser.add($(this));
	    	    }
	    	});
	    	return postsByUser;
	    } else {
	        return $('li[data-user="' + username + '"]');
	    }
    }
 
    /*
     * Function:    toggleThread (Article Comments only!)
     * Description: Shows or hides a single thread, record action in localStorage
     * Parameters:  thread -- jQuery object around <li id="comm-2115"> (comments)
     *              show   -- boolean, true: show thread, false: hide thread
     *              store  -- boolean, true: add/rem to storage, false: don't affect Storage
     */
    function toggleThread (thread, show, store) {
        var displayValue = show ? 'block' : 'none';
        var threadContent = thread.find('div.article-comm-text');
        var threadReplies = thread.next('ul.sub-comments');
        var threadID = thread.attr('id');
 
        // Physically show or hide the thread
        threadContent.css('display', displayValue);
        threadReplies.css('display', displayValue);
 
        if (store) {  // Add or remove thread from storage
            show ? ns.Storage.remThread(threadID) : ns.Storage.addThread(threadID);
        }
    };
 
    /*
     * Function:    toggleUser
     * Description: Shows or hides all posts by a single user, record action in localStorage
     * Parameters:  username -- string, name of a user to plonk
     *              show     -- boolean, true: show posts, false: hide posts
     *              store    -- boolean, true: add/rem to storage, false: don't affect Storage
     */
    function toggleUser (username, show, store) {
        var displayValue = show ? 'block' : 'none';
        var userPosts = getPostsByUser(username);
 
        if (ns.isMessageWall()) {
            userPosts.find('div.editarea').css('display', displayValue);
        } else {
            userPosts.find('div.article-comm-text').css('display', displayValue);
        }
 
        if (store) {  // Add or remove user from storage
            show ? ns.Storage.remUser(username) : ns.Storage.addUser(username);
        }
    };
 
    /*
     * Function:    togglePost
     * Description: Shows or hides a single post, record action in localStorage
     * Parameters:  post  -- jQuery object around <li id="comm-2115"> (comments)
     *                       jQuery object around <li data-id="2123"> (message wall)
     *              show  -- boolean, true: show the post, false: hide the post
     *              store -- boolean, true: add/rem to storage, false: don't affect Storage
     */ 
    function togglePost (post, show, store) {
    	var displayValue = show ? 'block' : 'none';
 
        var postContent = ns.isMessageWall() ?
                post.children('blockquote').children('div.MiniEditorWrapper').children('div.editarea') :
                post.find('div.article-comm-text');
 
        var postID = ns.isMessageWall() ?
                post.attr('data-id') :
                post.attr('id');
 
        // Physically show or hide the posts
        postContent.css('display', displayValue);
 
        if (store) {  // Add or remove post from Storage
            show ? ns.Storage.remPost(postID) : ns.Storage.addPost(postID);
        }
    };
 
    // Text displayed for the various thread buttons
    var buttonTexts = {
        toggleComment: {
            true: 'Hide Post', false: 'Show Post'
        },
        toggleUser: {
            true: 'Hide User', false: 'Show User'
        },
        toggleThread: {
            true: 'Hide Thread', false: 'Show Thread'
        }
    };
 
    // create HTML string required to create a button
    function createButton (className) {
        return '<button class="threadButton ' + className + ' wikia-button secondary" type="button">'
            + buttonTexts[className][true] +
            '</button>';
    }
 
    function add () {
 
        $('.threadButton').remove();
 
        // Add thread buttons to the DOM
        if (ns.isMessageWall()) {
            $('li.message > .speech-bubble-message')
            .prepend(
                createButton('toggleComment') + createButton('toggleUser')
            );
        } else {
            $('ul.sub-comments')
            .prev('li.SpeechBubble')
            .find('.speech-bubble-message')
            .prepend(
                createButton('toggleThread')
            );
            $('.speech-bubble-message')
            .prepend(
                createButton('toggleComment') + createButton('toggleUser')
            );
        }        
 
        $('.threadButton')
        .data({
            on: true,
        })
        .click(function () {
            var on = !$(this).data('on');
            $(this).data('on', on)
            .text(function () {
                for (var i in buttonTexts) {
                    if ($(this).hasClass(i)) {
                        return buttonTexts[i][on];
                    }
                }
            })
            .trigger('toggleButton');
        });
 
        // Toggle Thread handler
        $('.toggleThread')
        .on('toggleButton', function () {
            var thread = $(this).parents('li');
            var show = $(this).data('on');
            var threadID = thread.attr(ns.isMessageWall() ? 'data-id' : 'id');
 
            $(this).siblings('.threadButton')  // show or hide other buttons
            .css('visibility', show ? 'visible' : 'hidden');
 
            toggleThread(thread, show, true);  // show or hide this post
 
            console.log(  // log result to console
                ($(this).data('on') ? 'showing thread: ' : 'hiding thread: ') + threadID
            );
        });
 
        // Toggle User handler
        $('.toggleUser')
        .on('toggleButton', function () {
            // update state of all other .toggleUser buttons
            $('.toggleUser').data('on', $(this).data('on'));
            $('.toggleUser').text($(this).text());
 
            var username = ns.isMessageWall() ?
                    $(this).siblings('.MiniEditorWrapper').find('.edited-by a:first').text() :
                    $(this).parents('li').attr('data-user');
            var show = $(this).data('on');
 
            getPostsByUser(username)  // show or hide other buttons
            .children('.speech-bubble-message').children('.toggleUser').siblings('.threadButton')
            .css('visibility', show ? 'visible' : 'hidden');
 
            toggleUser(username, show, true); // show or hide this user
 
            console.log(  // log result to console
                ($(this).data('on') ? 'showing user: ': 'hiding user: ') + username
            );
        });
 
        // Toggle Comment handler
        $('.toggleComment')
        .on('toggleButton', function () {
            var post = $(this).parent('blockquote').parent('li');
            var show = $(this).data('on');
            var postID = post.attr(ns.isMessageWall() ? 'data-id' : 'id');
 
            $(this).siblings('.threadButton')  // show or hide other buttons
            .css('visibility', show ? 'visible' : 'hidden');
 
            togglePost(post, show, true);  // show or hide this post
 
            console.log(  // log result to console
                ($(this).data('on') ? 'showing post: ' : 'hiding post: ') + postID
            );
        });
 
        // Collapse the posts that are supposed to be collapsed upon pageload
        var postsToHide = ns.Storage.getCollapsedPosts();
        togglePost(postsToHide, false, false);
        var toggleCommentButton = postsToHide.children('.speech-bubble-message').children('.toggleComment');
        toggleCommentButton.text(buttonTexts["toggleComment"][false]);
        toggleCommentButton.data('on',false);
        toggleCommentButton.siblings('.threadButton').css('visibility', 'hidden');
 
        // Plonk users that are supposed to be plonked upon pageload
        var usersToHide = ns.Storage.getPlonkedUsers();
        for (var i = 0; i < usersToHide.length; i++) {
            toggleUser(usersToHide[i], false, false);
        }
        var toggleUserButton = ns.Storage.getPlonkedPosts()
            .children('.speech-bubble-message').children('.toggleUser');
        toggleUserButton.text(buttonTexts["toggleUser"][false]);
        toggleUserButton.data('on',false);
        toggleUserButton.siblings('.threadButton').css('visibility', 'hidden');
 
        // Collapse threads that are supposed to be collapsed upon pageload
        var threadsToHide = ns.Storage.getCollapsedThreads();
        toggleThread(threadsToHide, false, false);
        var toggleThreadButton = threadsToHide.children('.speech-bubble-message').children('.toggleThread');
        toggleThreadButton.text(buttonTexts["toggleThread"][false]);
        toggleThreadButton.data('on',false);
        toggleThreadButton.siblings('.threadButton').css('visibility', 'hidden');
    }
 
    ns.ToggleButtons = {
        add:          add,
        buttonTexts:  buttonTexts,
        togglePost:   togglePost,
        toggleUser:   toggleUser,
        toggleThread: toggleThread
    };
 
    $(function () { add(); });
 
}(window.ThreadTools = window.ThreadTools || {}));
 
/*

Module "ViewSource"Edit

*/
 
(function (ns) { // module "ViewSource"
 
    function add () {
 
        // Add "Source" button to "More" dropdown (Message Wall only):
        if (ns.isMessageWall()) {
 
            $('a.view-source', '#Wall').remove();
 
            $('.buttonswrapper ul', '#Wall')
            .append('<li><a class="view-source" href="#"> Source </a></li>');
 
            // Log source to console:
            $('a.view-source', '#Wall')
            .click(function (e) {
                e.preventDefault();
                var id = $(this).parents('li.SpeechBubble:first').attr('data-id');
                $.get(wgServer + wgScript + '?curid=' + id + '&action=raw', function (data) {
                    var m; if (m = data.match(/(.+)\<ac_metadata\s+title="([^"]+)"/)) {
                        console.log(m[1]);
                    } else {
                        console.log(data);
                    }
                });
            });
 
        // Add bracket pictogram and "source" link to article commments:
        } else {
 
            $('.view-source', '#article-comments-ul').remove();
 
            console.log('adding view source buttons');
 
            $('.tools', '#article-comments-ul')
            .prepend(
                '<img width="14" height="16" src="data:image/gif;base64,R0lGODlhDgAQAKIHAMfFy1VQZhsXLUU0lxgHaBQITC8fdQAAACH5BAEAAAcALAAAAAAOABAAAANJCKpxtyyQUkxxwVYhi/jOJxQENwlOemRTh6qrUUolHM9EDbOmrvK010/WE6aAOSOGGLQhPU7mxABoOBiUwXTwCgwsJZEoJBYkAAA7" alt="" />' +
                '<a class="view-source" href="#">source</a>'
            );
 
            // Log source to console:
            $('.view-source', '#article-comments-ul')
            .click(function (e) {
                e.preventDefault();
                var m, text = $(this).parents('.speech-bubble-message:first').children('.article-comm-text');
                if (m = text.attr('id').match(/\d+$/)) {
                    $.get(wgServer + wgScript + '?curid=' + m.pop() + '&action=raw', function (data) {
                        console.log(data);
                    });
                }
            });
        }
    }
 
    ns.ViewSource = {
        add: add
    }
 
}(window.ThreadTools = window.ThreadTools || {}));
 
/*

LauncherEdit

*/
 
$(function () { // Launcher
 
    if (window.localStorage && $('ul.comments li')) {
 
        // Reload ThreadTools when article comments paginate:
        if (window.ArticleComments && ArticleComments.addHover) {
            ArticleComments.addHoverOverride = ArticleComments.addHover;
            ArticleComments.addHover = function () {
                ArticleComments.addHoverOverride();
                ThreadTools.ToggleButtons.add();
                ThreadTools.ViewSource.add();
            }
        }
 
        ThreadTools.Stylesheet.add();
        ThreadTools.ToggleButtons.add();
        ThreadTools.ViewSource.add();
    }
});
 
//

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.