Reddit Enhancer

Extends Reddit and adds some useful Functions. (Requires frisch's UserScript Extender)

As of 2017-02-22. See the latest version.

// ==UserScript==
// @name        Reddit Enhancer
// @namespace   http://null.frisch-live.de/
// @version     1.71
// @description Extends Reddit and adds some useful Functions. (Requires frisch's UserScript Extender)
// @author      frisch
// @include     http://*.reddit.com/*
// @include     https://*.reddit.com/*
// @exclude     https://*.reddit.com/message/*
// @exclude     http://*.reddit.com/message/*
// @exclude     https://*.reddit.com/search?*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_openInTab
// ==/UserScript==
console.log("Initializing Reddit Enhancer...");

var settings = {};

var enumKeys = {
    'NONE': -1,
    'BACKSPACE': 8,
    'TAB': 9,
    'ENTER': 13,
    'SHIFT': 16,
    'CTRL': 17,
    'ALT': 18,
    'PAUSE/BREAK': 19,
    'CAPS LOCK': 20,
    'ESCAPE': 27,
    'SPACE': 32,
    'PAGE UP': 33,
    'PAGE DOWN': 34,
    'END': 35,
    'HOME': 36,
    'LEFT ARROW': 37,
    'UP ARROW': 38,
    'RIGHT ARROW': 39,
    'DOWN ARROW': 40,
    'INSERT': 45,
    'DELETE': 46,
    '0': 48,
    '1': 49,
    '2': 50,
    '3': 51,
    '4': 52,
    '5': 53,
    '6': 54,
    '7': 55,
    '8': 56,
    '9': 57,
    'A': 65,
    'B': 66,
    'C': 67,
    'D': 68,
    'E': 69,
    'F': 70,
    'G': 71,
    'H': 72,
    'I': 73,
    'J': 74,
    'K': 75,
    'L': 76,
    'M': 77,
    'N': 78,
    'O': 79,
    'P': 80,
    'Q': 81,
    'R': 82,
    'S': 83,
    'T': 84,
    'U': 85,
    'V': 86,
    'W': 87,
    'X': 88,
    'Y': 89,
    'Z': 90,
    'LEFT WINDOW KEY': 91,
    'RIGHT WINDOW KEY': 92,
    'SELECT KEY': 93,
    'NUMPAD 0': 96,
    'NUMPAD 1': 97,
    'NUMPAD 2': 98,
    'NUMPAD 3': 99,
    'NUMPAD 4': 100,
    'NUMPAD 5': 101,
    'NUMPAD 6': 102,
    'NUMPAD 7': 103,
    'NUMPAD 8': 104,
    'NUMPAD 9': 105,
    'MULTIPLY': 106,
    'ADD': 107,
    'SUBTRACT': 109,
    'DECIMAL POINT': 110,
    'DIVIDE': 111,
    'F1': 112,
    'F2': 113,
    'F3': 114,
    'F4': 115,
    'F5': 116,
    'F6': 117,
    'F7': 118,
    'F8': 119,
    'F9': 120,
    'F10': 121,
    'F11': 122,
    'F12': 123,
    'NUM LOCK': 144,
    'SCROLL LOCK': 145,
    'SEMI-COLON': 186,
    'EQUAL SIGN': 187,
    'COMMA': 188,
    'DASH': 189,
    'PERIOD': 190,
    'FORWARD SLASH': 191,
    'GRAVE ACCENT': 192,
    'OPEN BRACKET': 219,
    'BACK SLASH': 220,
    'CLOSE BRAKET': 221,
    'SINGLE QUOTE': 222,
};
enumKeys.hasValue = function(val){
    for(var k in this)
        if(this[k] === val) {
            return true;
        }

    return false;
};

// User Script Enhancer Properties and Functions
var jq = document.fExt.jq;
var fExtCreateStyle = document.fExt.createStyle;
var fExtAddSub = document.fExt.ctxMenu.addCtxSub;
var fExtMessage = document.fExt.message;
var fExtAddCtxItem = document.fExt.ctxMenu.addCtxItem;
var fExtAddCtxSeparator = document.fExt.ctxMenu.addSeparator;
var fExtClipboard = document.fExt.clipboard;
var fExtGetSelection = document.fExt.getSelection;
var fExtPopup = document.fExt.popup;
var fExtSetLoading = document.fExt.setLoading;

var typeCheck = Object.prototype.toString;
var numberOfSitesLoaded = 1;
var isHiding = false;

var ctxItemUsrHdr, ctxItemUsrHdrPg, ctxItemUsrUnhdr, ctxItemSbHdr, ctxItemSbUnhdr, ctxItemKeywHdr, ctxItemKeywUnhdr, ctxUndo, ctxRedo, ctxItemCpyHtml, ctxItemSett;
var upVoteLinks, downVoteLinks, rSubRedditLinks, rAuthorLinks, postList;

var loc = location.href;
var isSinglePost = location.href.indexOf("/comments/") > 0 || location.href.indexOf("/submit$") > 0;
var isUserPage = location.href.indexOf("/user/") > 0 || location.href.indexOf("/u/") > 0;
var isUserSubmittedPage = isUserPage && location.href.indexOf("/submitted") > 0;
var isMyMulti = location.href.indexOf("/me/m/") > 0;
var isFriendsPage = location.href.indexOf("/r/friends") > 0;
var isSubmitPage = location.href.indexOf("/submit?") > 0;
var isMySavedPage = location.href.indexOf(settings.accountName + "/saved") > 0;
var hiderElements = [];
var headerHeight = jq("#header").outerHeight(true);
var hideLinks = [];
var userHideLinks = [];
var subHideLinks = [];
var duplicateLinks = [];
var keywordsHideLinks = [];
var rTitleLinks;
var rEnhSub = fExtAddSub("Reddit Enhancer", undefined);
var pstItems = [];
pstItems.onlyVisible = function(){
    return this.filter(function(item){
        return jq("#" + item).is(":visible");
    });
};
pstItems.Next = function(steps){
    return GetFromArray(pstItems.onlyVisible(), pstItems.selected, steps ? steps : 1);
};
pstItems.Previous = function(steps){
    return GetFromArray(pstItems.onlyVisible(), pstItems.selected, steps ? steps * -1 : -1);
};
pstItems.First = function(){
    var visiblePosts = pstItems.onlyVisible();
    return visiblePosts.length > 0 ? visiblePosts[0] : undefined;
};
pstItems.Last = function(){
    var visiblePosts = pstItems.onlyVisible();
    return visiblePosts.length > 0 ? visiblePosts[visiblePosts.length - 1] : undefined;
};

function GetFromArray(array, item, steps) {
    if(array.length === 0)
        return undefined;
    else if(item && jq.inArray(item, array) < 0)
        return undefined;

    var ind = 0;
    if(item)
        ind = array.indexOf(item);

    if(ind < 0)
        ind = 0;

    var stepIndex = (steps < 0 ? steps * -1 : steps);
    var modifier = (steps < 0 ? -1 : 1);

    ind += steps;
    while(ind >= array.length)
        ind -= array.length;

    if(ind < 0)
        ind = array.length - 1;

    return array[ind] === item ? undefined : array[ind];
}

var reloadCancelled = true;
var resourcesLinks = [];
var subColors = {};
var settingTranslation = [
    { key: "accountName", translation: "Your Profilename", hint: "Required to determine some pages (e.g. your saved pages)" },

    { key: "markNSFW", translation: "Mark NSFW posts", hint: "Highlights NSFW posts" },
    { key: "replaceTopNew", translation: "Replace Top with New", hint: "Instead of landing on the Top-Page of a reddit, you'll be redirected to the New-Page instead" },
    { key: "sortByVotes", translation: "Sort by Votes", hint: "Sorts the posts by votes" },
    { key: "autoExpandSelectedPost", translation: "Autoexpand Posts", hint: "Causes selected Posts to automatically collapse/expand" },
    { key: "removeDuplicates", translation: "Remove Duplicates", hint: "Removes duplicate posts by checking the link/source of a post" },
    { key: "maximumNumberOfSitesToLoad", translation: "Autoload # of Pages", hint: "Let's you load more than one page at once. Currently buggy and will be worked on." },

    { key: "animationItemTimeout", translation: "Animation Speed", hint: "" },
    { key: "hideItemTimeout", translation: "Posthiding Timeout", hint: "" },
    { key: "imagePreloadingTimeout", translation: "Preloading timeout", hint: "" },

    { key: "hideUsers", translation: "Hide Users", hint: "If true, users in the 'Users to hide'-list will be hidden" },
    { key: "hideUsersNSFWonly", translation: "Hide Users NSFW posts only", hint: "Only hide NSFW posts by the users" },
    { key: "usersToHide", translation: "Users to hide", hint: "" },

    { key: "hideSubs", translation: "Hide Subs", hint: "If true, subsreddits in the 'Subs to hide'-list will be hidden" },
    { key: "subsToHide", translation: "Subs to hide", hint: "" },

    { key: "hideKeywords", translation: "Hide Keywords", hint: "If true, posts containing any keyword fromt he 'Keywords to hide'-list will be hidden" },
    { key: "keywordsToHide", translation: "Keywords to hide", hint: "" },

    { key: "panelView", translation: "Panelview", hint: "Not yet implemented" },

    { key: "shortcuts", translation: "Keyboard Shortcuts", hint: "" },
    { key: "voteUp", translation: "Upvote selected Post", hint: "" },
    { key: "previousPost", translation: "Select previous Post", hint: "" },
    { key: "voteDown", translation: "Downvote selected Post", hint: "" },
    { key: "toggleSelected", translation: "Expand/Collapse selected Post", hint: "" },
    { key: "hideSelected", translation: "Hide selected Post", hint: "" },
    { key: "nextPost", translation: "Select next Post", hint: "" },
    { key: "hideAll", translation: "Hide all Posts", hint: "Also reloads the page after hiding all posts (can be cancelled)" },

    { key: "undoLast", translation: "Undo last action", hint: "Activates the latest element in the undo list" },
    { key: "redoLast", translation: "Redo last action", hint: "Activates the latest element in the redo list" },

    { key: "previousImage", translation: "Display previous Image", hint: "Used for RES-Albums" },
    { key: "nextImage", translation: "Display next Image", hint: "Used for RES-Albums" },

    { key: "zoomIn", translation: "Zoom in", hint: "Used for Images" },
    { key: "zoomOut", translation: "Zoom out", hint: "Used for Images" },
    { key: "rotateLeft", translation: "Rotate left", hint: "Used for Images" },
    { key: "rotateRight", translation: "Rotate right", hint: "Used for Images" }
];

// Images
var imgBooks = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTMyQTVEQUUyQTIyMTFFMTlGMThGNDk3QjgyMTJCNjUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTMyQTVEQUYyQTIyMTFFMTlGMThGNDk3QjgyMTJCNjUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFMzJBNURBQzJBMjIxMUUxOUYxOEY0OTdCODIxMkI2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFMzJBNURBRDJBMjIxMUUxOUYxOEY0OTdCODIxMkI2NSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgQa4kwAAAGQSURBVHjazFaBjYMwDIQXAzACI4BYoN2AblA2oBO0neD5CWADukG7QAUbPCNkA95+HZKVJyGlIL0lK62d+OLLEfCHYfDYfN/3pixN0x0NFbl6Pp+J96KN9QPTBAIIafgkPxryEY8E3rsAfhiKZDR8W0AuyLeunQUTu2SadgjxbnPyEPkY+VjkOX4EtbdZIBS5j0XJrrTwonXRiLVX8pLiHMsQ8106qgDSkR9G7ie66NBlBPpCw/lW+HvQgcZCewJRmFyQn/UuUCQTwJ6FGaPqzjTZQ6EIMcUbwOJWxHV6CyjVLgZYMRH7QvFGioRAHhpVmZPqLFaDjvH3iekVVDWiS/fnSDcIQ9L1e4ZQYjsH8kpH0hoCUBBPaJiTC9UtBootuR5yVouoczQWRiKUuQkQn9seir1PURqsBKRfRd5WQO0q8nawWXmvKYb/B1RuUD//A0TyPCHxEA9cv3ADvC6hmvUY8Oe+gsQrYHAEueFmV05fQQuNb/XyXTHUDlSVb6uOiuR4lfcTG2CQzrb+R4ABALaxlqMMGvrUAAAAAElFTkSuQmCC";
var imgDuplicate = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDQxOEI0NzM3NDlBMTFFMTk1RDhCRjg3NzZFMkRGMjMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDQxOEI0NzQ3NDlBMTFFMTk1RDhCRjg3NzZFMkRGMjMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpENDE4QjQ3MTc0OUExMUUxOTVEOEJGODc3NkUyREYyMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpENDE4QjQ3Mjc0OUExMUUxOTVEOEJGODc3NkUyREYyMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Ph2iTEwAAAGJSURBVHjatFbBcYMwEBSEAsiXV0ogwydPpwPTgd0BVMCkAruDuAOnA7sBZsgvT//40oFzx6wyZwfQacA7cyMBkvZuTzoRGEKWZWtqdmQvZhp7so+6rjvjiQAkR485Ddm7L1mESOwC7O2XeQAiIdcoyRLSsnRX7tDHYIJktrSRYqJKWnKowNiU7ETPN2ThFAMNjjXSCkcsLFmsIpJwbRL6fr57dUMWOiZ3MzdbT+YVkRJDjqUU1S5akoUUeBb5XVHDVpEVoXkQkLO9z/Z2grxPsbVXkC9nIs4xfTOL5AgkJyTeRvDvgC8hHZ+zHNWgpPZAFg/VujmRHMWB3kKmDdl5ESIhV08Asli8K2cT3ZEc0F5ETWQJm9EcoUpr8LcgIvmEdWMkluiCfqUk4y18wIIlCNlex0isdCWS2ifX7vuJO4o934jEG83VHqIq5yIyF3K0hbjkGufPiSL5V0vg+z8hb2fNgfXNYX9hYmxl1wh8vJqB/Mk1om3bnyRJvqn7NlRaFGpsWfJfAQYApiSqoZCrNjYAAAAASUVORK5CYII=";
var imgDelCat = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAG5SURBVEiJ3ZaxbhRBDIY/r45yOtImokVKc+N5gm0okiIFxUVpUiHlEWgipeENkNIkDQI6CiK60zZ0Z6p7ACQKmvAGic4UzMBmdTmSvRUI/mZ31rP+x/5tayTG+MDdD6qqOnJ3ZQCIiC0Wi5ci8srMrgBG7n4gImfuPgQHAO6uLZ/nAFVVVUeDMXTQ9l0Nla5laPselRczkyFJVPWGFtWQzlfh/yMaLftY8lt06+b7NqzS+e9G1D3ZEBX5T2n0GXgLfDCzj63vx8CzslgnoivgRQjhsZk975BgZichhK2ylhjjjdP3QUpp4u57QBk5JiLvZrPZm7JnaeruClV9yI/pvNMxPXL3p6q6Dxya2bc+Gn0NIWw1TXPdIvkEbAIbec8l8CXbzoHdPhGdNk1znVKatEjqTDTNe+pMNAV2UkqT3n2UNSETbJrZXFXrvH+uqtvZhrvvraNREX4DmKpqbWZzgEwy5Vcq9c9NbxExgBjjk3v+a/l5CdQlXaq6nSOrsw3AZDweH4rI2T0Ijs3sJPfOa35fDFFE9qXHdetneavqe1aXdwQuzGxX1rlmrWjYggtyw65FVHCXEfQdGszctvjNerkAAAAASUVORK5CYII=";
var imgAbort = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowNDZCQkYwMzM2Q0ExMUUyQjY5RUUwNzIwODVEMzBFMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowNDZCQkYwNDM2Q0ExMUUyQjY5RUUwNzIwODVEMzBFMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjA0NkJCRjAxMzZDQTExRTJCNjlFRTA3MjA4NUQzMEUzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjA0NkJCRjAyMzZDQTExRTJCNjlFRTA3MjA4NUQzMEUzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Mya9PgAAAUlJREFUeNpi/P//PwM9ACPdLWJkZMSqwNjYWBJIxQKxOxAbALEQVOodEF8A4p1AvPjs2bPPsemHm4/LIqAFfECqGYgzgZiVgIN/A/F0IK4FWviJaIuAlmgBqc1ArERiCN0DYl+gZdcIWgS15CAQi5AZHW+A2B5mGVaLoMF1ngyfYPOZISgYYeYzoSlopoIlDFAzmrGmOhMTE1DqekhExBMLQAlE/syZM8/RfRRLRUsYoGbFwjjIFrnTIJ/CzWRBEjTAolABiD+SYPB7NL4BNouEsGj8CEw5H4i1BZhq0YWEsAUdTQGyRe9oYP47bEEHKiCd0MMcS3CQAi5g89FOGvhoJzaLFkMzGQMVM+xiDIug9cl0Klo0HbmOQk91tdACkVJwD2oW7hqWLtUELSs+rBkWqtAQiCcRmUB+Q9UaIlsysI2TYdOuAwgwAC/sonL067x5AAAAAElFTkSuQmCC";
var imgDelUser = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWhJREFUeNrEVottgzAQxVEGcDdwN4jEAA0bZAPMBFEmiDJB1AkKE2QEvAAqGzQjMELP1SOyELbPMlKfdLL4nN/5Pg9EEUBZlkdazmR2lWQTmSH7HIbBFAkQARJNy1fAtyGyNouISA60fDP8K+7Jdp77Z2agNfdEPqIT0/+USySZ/jKXaHP8O9HI9B9zibjDaHKJOqZ/t4UyPCLta2hYqy2a4QJtW4O932yidRG9e+kchLeG8CqyJ2rXufIUa28VCcQG0ZNp512F6x7Pg6I6R6k9aXsnu3ueL9HSyRqxQnBFGny4ITV9QomqPQgkasERyRbBpKAWILHRHRgOE6XhjXx+nJqMnu6Uzp5Pe6IHk8SVHLdJLmsfP5RhTq/aR+qxxCtCh+xOm06RT8jfiVIgaVOFZtAL8qAm7lIUGNApGjdroiWqUlQY/xMjuq9gzpERiz8fW68P5PcYE1RMvo4Na1TrOOBq3a8AAwDKrW/Ty1UcbgAAAABJRU5ErkJggg==";
var imgTree = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6REIzNzg4NjJDMjdBMTFFMTgyQTdCNUFCM0MyNUJCMTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6REIzNzg4NjNDMjdBMTFFMTgyQTdCNUFCM0MyNUJCMTYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4REUwQTc2QkMyNzgxMUUxODJBN0I1QUIzQzI1QkIxNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4REUwQTc2Q0MyNzgxMUUxODJBN0I1QUIzQzI1QkIxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqK2+9QAAAEGSURBVHjavJWNDYMgEIWFCewGHcHECRihG+gGdhLdQDdoN6ALkLpBR5AN2iM5EtI09fg5LyFEJH4+jnsnqgLRtu0GU/1ni5VVmaj33osCas4wvfb2iUyIU6JhNGygGIiLnByNCLGUzSJRzQxTh48KlZVVBJAhgPTGmAdBlRWUGoCPnRDiADOuX2F9ov6gpNQAQpoAssRAyEeHEB1A+tgjp+ZIo7LVHVnKBZIRFuMgCtRYTlB2UEEWi1Nzg5SHYbHygCAvLj/+pnUpMEmpaoTdv2BDDEgkWtAYWNBCcZcSpnqBcePsRx5mCTaW3WGfRzQ+f+1Xtsb3o6Vv7CCEvY/yut1a/AgwACqvUiICIj/gAAAAAElFTkSuQmCC";

// Variables - End

// Initialization - Start
GMLoad();

// Initialization - Styles - Start
fExtCreateStyle("#header { z-index: 107; }");
fExtCreateStyle("#siteTable { padding: 0 64px; }");
fExtCreateStyle("body.with-listing-chooser>.content, body.with-listing-chooser .footer-parent { margin-left: 0 !important; }");
fExtCreateStyle("div.listing-chooser { left: 0 !important; }");
fExtCreateStyle("div.side { right: 0; }");
fExtCreateStyle("div.side, div.listing-chooser { z-index: 106 !important; position: fixed !important; height: auto; top: " + headerHeight + "px !important; margin: 0; border: 1px solid black; width: 50px !important; overflow-y: hidden !important; padding: 0 !important; }");
fExtCreateStyle("div.side:hover { width: 500px !important; overflow-y: auto !important; max-height: 100vh; }");
fExtCreateStyle("div.listing-chooser:hover { width: 500px !important; overflow-y: auto !important; max-height: 100vh; }");
fExtCreateStyle("span.domain { font-weight: bold; }");

fExtCreateStyle("div.content { padding-top: 80px  !important; }");
fExtCreateStyle(".tabButton { color: #333333 !important; background-color: rgb(252, 255, 215) !important; border: 1px solid black; }");
fExtCreateStyle(".spacerLi { width: 50px; }");
fExtCreateStyle("#postNavigation { position: fixed !important; top: 27px; width: 500px; margin: 0 auto; display: block; z-index: 108; /*font-size: 12px; font-weight: bold; */ left: 0; right: 0; } ");
fExtCreateStyle("#postNavigation a { background-color: #ffffff;  border: 1px solid black;  padding: 8px 14px;  margin: 0 1px; }");
fExtCreateStyle("#postNavigation a:hover { background-color: #dfdfdf; }");
fExtCreateStyle(".pstSelected, .pstSelected * { z-index: 105; }");

fExtCreateStyle(".midcol { width: 32px !important; }");
fExtCreateStyle(".RES-keyNav-activeElement, .RES-keyNav-activeElement .md-container { background-color: transparent !important; }");
fExtCreateStyle("div.md { border: none !important; }");

if (isSinglePost) {
    fExtCreateStyle(".livePreview { width: 1600px !important; }");
    fExtCreateStyle(".livePreview p, .livePreview ol { width: 1500px !important; }");
    fExtCreateStyle("div.usertext-edit { max-width: 50%; };");
    fExtCreateStyle("div.usertext-edit div.md { width: 100%; max-width: 100%; }");
    fExtCreateStyle("div.usertext-edit div.md textarea { width: 100%; }");
} else {
    fExtCreateStyle(".md { max-width: none !important; }");
    fExtCreateStyle(".madeVisible *,div.expando * { z-index: 105; }");
    fExtCreateStyle("a.madeVisible img, a.madeVisible video { max-height: 100vh !important; max-width: 95vw !important;}");
    fExtCreateStyle("body { margin-bottom: 650px !important; }");
    fExtCreateStyle("img.RESImage { border-bottom: #000 solid 3px !important; padding: 0 !important; }");
    fExtCreateStyle("div.thing { min-height: 80px; padding: 5px 0; margin: 0 0 8px 0; border: 1px solid black; }");
    fExtCreateStyle("div.thing.pstSelected div.entry>div:not(.expando-button) { background-color: #fff !important; padding: 10px 20px; border: 1px solid black; }");
    fExtCreateStyle("div.thing div.entry img, div.thing div.entry video { display: none !important; }");
    fExtCreateStyle("div.thing.pstSelected div.entry img, div.thing.pstSelected div.entry video { display: block !important; }");
    if (settings.markNSFW) {
        fExtCreateStyle(".NsfwPost { background-color: rgb(255, 81, 81) !important; }");
        fExtCreateStyle(".NsfwItem .madeVisible { display: none; }");
        fExtCreateStyle(".pstSelected .NsfwItem .madeVisible { display: initial !important; }");
        fExtCreateStyle(".pstSelected .NsfwItem .madeVisible div { width: auto !important; }");
        fExtCreateStyle(".pstSelected .NsfwItem * { z-index: 104 !important; }");
    }
    fExtCreateStyle(".entry .buttons li { line-height: inherit !important; width: 100px; }");
    fExtCreateStyle(".thingUserSubInfo { padding: 4px 8px; height: 26px; display: block; }");
    fExtCreateStyle(".thingUserSubInfo>* { float: left; clear: none; border: 1px solid black; margin: 0 2px; }");
    fExtCreateStyle(".thingUserSubInfo * { font-size: 12px !important; }");
    fExtCreateStyle(".thingUserSubInfo .nsfw-stamp { display: inline !important; }");
    fExtCreateStyle(".thingUserSubInfo p.tagline { width: 450px; }");
    fExtCreateStyle(".thingUserSubInfo a.subreddit { width: 160px; }");
    fExtCreateStyle("ul.flat-list.buttons { float: left; clear: none; padding: 0; position: absolute; left: 870px; }");
    fExtCreateStyle("ul.flat-list.buttons>li { cursor: pointer; }");
    fExtCreateStyle("ul.flat-list.buttons>li, .thingUserSubInfo p.tagline, .thingUserSubInfo a.subreddit { padding: 6px !important; background-color: #eeeeee !important;  text-align: center; }");
    fExtCreateStyle("ul.flat-list.buttons>li:hover, .thingUserSubInfo p.tagline:hover, .thingUserSubInfo a.subreddit:hover { background-color: #cee3f8 !important; }");
    //fExtCreateStyle("a.subreddit { float: left; background-color: white !important; color: #888; font-weight: bold; padding: 0 4px !important; }");
    // fExtCreateStyle("p.tagline { float: left; position: relative; background-color: white; color: #888; font-weight: bold; padding: 0 1px; }");
    fExtCreateStyle("p.title { color: #000000 !important; float: left; clear: left; margin: 0; font-weight: bold; font-size: 15px !important; }");
    fExtCreateStyle("p.title a { color: #000000 !important; }");
    //fExtCreateStyle("ul.flat-list.buttons { padding-bottom: 40px; width: 800px; margin: 20px auto 0 auto; font-size: larger; text-align: center; left: 0; right: 0; clear: both; }");
    //fExtCreateStyle("ul.flat-list.buttons li { padding: 4px; background-color: #fff; margin: 2px; border: 1px solid #999; }");
    fExtCreateStyle(".usertext * { z-index: 1; }");
    fExtCreateStyle("div.thing div.entry div.expando:not(.expando-uninitialized) { display: block !important; }");
    fExtCreateStyle("div.thing div.entry div.expando span { display: none; }");
}
fExtCreateStyle(".toggleHidden, div.thing.hidden { background: repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(255,255,255,.5) 35px, rgba(255,255,255,.5) 70px); }");

if(settings.hideUsers)
    fExtCreateStyle(".userHidden .author { text-decoration: line-through; }");
if(settings.hideSubs)
    fExtCreateStyle(".subsHidden .subreddit { text-decoration: line-through; }");

fExtCreateStyle(".userHidden a.title, .subsHidden a.title, .duplicateHidden a.title { background-position: 0 !important; background-repeat: no-repeat !important; padding: 8px 8px 8px 26px; }");
fExtCreateStyle(".userHidden a.title { background-image: url('" + imgDelUser + "') !important; }");
fExtCreateStyle(".subsHidden a.title { background-image: url('" + imgDelCat + "') !important; }");
fExtCreateStyle(".duplicateHidden a.title { background-image: url('" + imgDuplicate + "') !important; }");

fExtCreateStyle("ul.tabmenu .disabled { background-color: #dfdfdf !important; }");

jq("body").css("margin-bottom", "0");
// Initialization - Styles - End

// Initialization - Controls - Start
var rPostNavigation = jq('<div id="postNavigation"><a href="#" id="topPost">Top</a><a href="#" id="frPost">&lt;&lt;</a><a href="#" id="prevPost">&lt;</a><a href="#" id="nextPost">&gt;</a><a href="#" id="ffPost">&gt;&gt;</a><a href="#" id="botPost">Bottom</a></div>');
rPostNavigation.appendTo("body");

var rTabMenu = jq("ul.tabmenu");
jq('<li class="spacerLi"></li>').appendTo(rTabMenu);

jq('<li><span class="">All Posts:</span></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" title="Updvotes all displayed posts" id="upvoter">Upvote</a></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" title="Downvotes all displayed posts" id="downvoter">Downvote</a></li>').appendTo(rTabMenu);

jq('<li><a href="#" class="choice tabButton collapsed" title="Expands all posts" id="expander">Expand</a></li>').appendTo(rTabMenu);

jq('<li class="spacerLi"></li>').appendTo(rTabMenu);
jq('<li><span class="">Hide:</span></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="background-color: rgb(255, 138, 138) !important;" title="Hides all displayed posts which prevents them from showing up again" id="hideAll">All (<span>?</span>)</a></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="display: none; background-color: rgb(255, 138, 138) !important;" title="Hides all displayed posts which prevents them from showing up again" id="hideSelected">Selected</a></li>').appendTo(rTabMenu);

jq('<li class="spacerLi"></li>').appendTo(rTabMenu);
jq('<li><span class="">Toggle hidden:</span></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="background-color: rgb(175, 255, 171) !important;" title="Toggles the hidden User posts" id="toggleUserHidden">Users (<span>?</span>)</a></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="background-color: rgb(175, 255, 171) !important;" title="Toggles the hidden Sub posts" id="toggleSubsHidden">Subs (<span>?</span>)</a></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="background-color: rgb(175, 255, 171) !important;" title="Toggles the hidden Keyword posts" id="toggleKeywordsHidden">Keywords (<span>?</span>)</a></li>').appendTo(rTabMenu);
jq('<li><a href="#" class="choice tabButton" style="background-color: rgb(175, 255, 171) !important;" title="Toggles the duplicate posts" id="toggleDuplicateHidden">Duplicates (<span>?</span>)</a></li>').appendTo(rTabMenu);

jq('<li class="spacerLi"></li>').appendTo(rTabMenu);
if (loc.indexOf("show=all") < 0) {
    jq('<li><a href="' + loc.replace('#', '') + '?show=all" title="Show all posts of this sub, including previously hidden posts" class="choice tabButton" style="background-color: rgb(187, 187, 255) !important;" id="showAll">Show All</a></li>').appendTo(rTabMenu);
}

if (isSinglePost)
    jq("#upvoter,#downvoter,#expander,#hideAll,#hideSelected,#toggleUserHidden,#toggleSubsHidden,#toggleKeywordsHidden,#toggleDuplicateHidden").addClass("disabled");

fExtCreateStyle("#rEnhSettings { position: fixed; height: 750px; top: 50px; border: 1px solid black; width: 750px; padding: 12px; z-index: 1000; background-color: #FEFEFE; right: 0; left: 0; margin: 0 auto; }");
fExtCreateStyle("#rEnhSettings table { }");
fExtCreateStyle("#rEnhSettings table td.label, #rEnhSettings table td.value { }");
fExtCreateStyle("#rEnhSettings table td.label { float: left; clear: right; font-size: larger; }");
fExtCreateStyle("#rEnhSettings table td.value { float: right; clear: left; }");
fExtCreateStyle("#rEnhSettings table thead { font-size: large; }");
fExtCreateStyle("#rEnhSettings div.tableContainer { overflow-y: scroll; height: 700px; }");
fExtCreateStyle("#rEnhSettings div.settingsButtons { height: 50px;  }");
fExtCreateStyle("#rEnhSettings * { z-index: 1000; margin: 4px; }");
fExtCreateStyle("#rEnhSettings td.label {  }");
fExtCreateStyle("#rEnhSettings a { padding: 4px; border: 1px solid grey; float: right; }");

// Initialization - Controls - Settings Dialog
var settingsDialog = '<div style="display:none" id="rEnhSettings"><div class="tableContainer"><table cellspacing="0" cellpadding="0" border="0" width="700px"><colgroup><col width="200px" /><col width="auto" /></colgroup>';
settingsDialog += '<thead><tr><th class="label">Setting</th><th class="setting">Value</th></tr></thead>';

function BuildSettings(settingsObject) {
    for (var key in settingsObject) {
        var varType = typeof settingsObject[key];

        if (varType === "object") {
            if (typeCheck.call(settingsObject[key]) === '[object Array]')
                AddSettingToDialog(key, "list");
            else {
                settingsDialog += '<tr><td class="label" colspan="2"><b>' + key + '</b></td></tr>';

                if (key === 'shortcuts') {
                    for (var shortcut in settingsObject[key]) {
                        AddSettingToDialog(shortcut, 'key');
                    }
                } else BuildSettings(settingsObject[key]);
            }
        } else AddSettingToDialog(key, varType);
    }
}

function getTranslationObject(key) {
    for (var i in settingTranslation) {
        if (settingTranslation[i].key === key) {
            return settingTranslation[i];
        }
    }

    return { key: key, translation: key, hint: "" };
}

function AddSettingToDialog(key, varType) {
    var transObject = getTranslationObject(key);
    settingsDialog += '<tr title="' + transObject.hint + '">';
    if (varType === "list") {
        settingsDialog += '<td class="label" colspan="2">' + transObject.translation + '</td>';
        settingsDialog += '</tr>';
        settingsDialog += '<tr title="' + transObject.hint + '">';
        settingsDialog += '<td class="setting" colspan="2"><textarea name="' + key + '" class="value" cols="80" rows="7" title="Separate entries by using comma"></textarea></td>';
    } else {
        settingsDialog += '<td class="label">' + transObject.translation + '</td>';
        if (varType === "key") {
            settingsDialog += '<td><select class="value" name="' + key + '">';
            for (var knownKey in enumKeys) {
                settingsDialog += '<option value="' + enumKeys[knownKey] + '">' + knownKey + '</option>';
            }
            settingsDialog += '</select><input type="Button" class="DetectShortcut" value="Assign" /></td>';
        } else {
            var inputType;
            switch (varType) {
                case "boolean":
                    inputType = "checkbox";
                    break;
                case "number":
                    inputType = "number";
                    break;
                case "string":
                    inputType = "text";
                    break;
                default:
                    console.log("Cannot add setting " + key + " to settings dialog because unkown type " + varType);
                    return;
            }

            settingsDialog += '<td class="setting">' + '<input type="' + inputType + '" class="value" name="' + key + '"></td>';
        }
    }
    settingsDialog += '</tr>';
}

jq(document).on("click", "input.DetectShortcut", function(e) {
    fExtMessage("Press the key to assign or press ESC to unassign.");
    jq(document).bind("keydown", { sender: this }, AssignShortcut);
});

function AssignShortcut(e){
    var sender = e.data.sender;
    var jqSender = jq(sender);

    e.preventDefault();

    var kc = e.which;

    if(kc != enumKeys.ESCAPE) {
        if(!enumKeys.hasValue(kc)){
            fExtMessage("Invalid Key! Choose a different key or press ESC to unassign.");
            return false;
        }
        else
            jqSender.parent().find("select").val(kc);
    }
    else
        jqSender.parent().find("select").val(enumKeys.NONE);


    fExtMessage(undefined);

    jq(document).unbind("keydown", AssignShortcut);
    return false;
}

BuildSettings(settings);

settingsDialog += '</table></div>';
settingsDialog += '<div class="settingsButtons"><a href="#" id="renhSettingsClose">Close</a><a href="#" id="renhSettingsSave">Save</a></div>';
settingsDialog += '</div>';
jq("body").append(settingsDialog);
// Initialization - Controls - End

// Moving the navigation buttons
var jqNavButtons = jq("div.nav-buttons");
if (jqNavButtons) {
    if (jqNavButtons.length === 1 && settings.maximumNumberOfSitesToLoad > 1) {
        GrabContent(jq("body").html());
    } else MainInitialization();
} else MainInitialization();

function MainInitialization() {
    // Removal of sponsor divs
    jq("[class^='sponsor'").remove();

    upVoteLinks = jq(".up:not(.archived)");
    downVoteLinks = jq(".down:not(.archived)");
    rSubRedditLinks = jq("a.subreddit");
    rAuthorLinks = jq("a.author");
    postList = jq("#siteTable");

    // Replacing images on imgur from those faggots who can't link properly
    /*
    var replaceAttr = ["href", "data-href-url", "data-outbound-url"];
    jq("a.thumbnail, a.imgScanned, a.title").each(function(index, item) {
        var jqItem = jq(item);
        for(var attrInd in replaceAttr) {
            var tmpAttr = jqItem.attr(replaceAttr[attrInd]);
            if(tmpAttr === undefined)
                continue;
            tmpAttr = tmpAttr.replace("gallery/", "");
            tmpAttr = tmpAttr.replace("/new","");
            tmpAttr = tmpAttr.replace("https","http");
            tmpAttr = tmpAttr.replace("http://i.","http://");
            jqItem.attr(replaceAttr[attrInd], tmpAttr);
        }
    });
    */

    // Holy crap that new outbound url remapping is annoying af... here let's fix this shit
    jq("a[data-href-url]").each(function() {
        var jqThis = jq(this);
        var currentDataHref = this.getAttribute("data-href-url");
        this.setAttribute("data-outbound-url", currentDataHref);
        this.setAttribute("href", currentDataHref);
    });

    if (settings.replaceTopNew) {
        jq("a.subreddit").each(function(index, item) {
            var hrefLink = item.getAttribute("href");
            if (!hrefLink.match(".*/$")) {
                hrefLink = hrefLink + "/";
            }
            hrefLink = hrefLink + "new";
            item.setAttribute("href", hrefLink);
        });
    }

    jq("span.nsfw-stamp").each(function(index, item) {
        var jqItem = jq(item).parent("li");
        var parent = jqItem.parents("ul.flat-list.buttons");
        if (settings.markNSFW) {
            jqItem.parents("div.entry:first").addClass("NsfwItem");
        }
        jqItem.detach().appendTo(parent);
        jqItem.parents("div.thing").addClass("NsfwPost");
    });
    jq("span.spoiler-stamp").each(function(index, item) {
        var jqItem = jq(item).parent("li");
        var parent = jqItem.parents("ul.flat-list.buttons");
        jqItem.detach().appendTo(parent);
        jqItem.parents("div.thing").addClass("SpoilerPost");
    });

    jq("a.comments.empty").each(function(index, item) {
        Mark(jq(item), "uncommented");
    });

    jq("span.domain a").each(function(index, item) {
        var jqItem = jq(item);
        var parentItem = jqItem.parents("div.entry");
        var txt = jqItem.text();

        if (txt.indexOf("youtu") >= 0)
            Mark(parentItem, "video");
        else if (txt.indexOf("imgur") >= 0 || txt.indexOf("gfycat") >= 0)
            Mark(parentItem, "image");
    });

    // remove Gild and Share button
    jq("li.give-gold-button, li.share").remove();
    jq("li a:contains('promoted')").parent().remove();
    jq("li a:contains('gilded')").parent().remove();
    jq("li a:contains('rising')").parent().remove();
    jq("li a:contains('controversial')").parent().remove();

    // highlight hide/unhide
    var hideSubsFromLocation = false;
    var hideUsersFromLocation = false;

    if (!isMyMulti && !isMySavedPage && !isSinglePost && !isFriendsPage) {
        hideSubsFromLocation = true;
        hideUsersFromLocation = true;
    }

    if (!isSinglePost) {
        // Marks Rating of Posts
        jq("div.thing div.midcol div.unvoted").each(function(index, item) {
            var jqItem = jq(item);
            var unvotedVal = jqItem.text();
            var multiplier = unvotedVal.indexOf("k") >= 0 ? 1000 : 1;
            var pts = parseFloat(unvotedVal.replace("k",""));
            if(isNaN(pts))
                pts = 0;
            else
                pts = pts * multiplier;
            if (pts > 100) jqItem.parent().css("background-color", "#99FF99");
            else if (pts > 0) jqItem.parent().css("background-color", "#CCFFCC");
            else if (pts < -10) jqItem.parent().css("background-color", "#FF9999");
            else if (pts < 0) jqItem.parent().css("background-color", "#FFCCCC");
            jqItem.parents("div.thing").data("VoteValue",pts);
        });


        if (!isUserPage) {
            jq("div.deleted").fadeOut();

            if(settings.sortByVotes){
                var pstThings = postList.children("#siteTable div.thing");
                pstThings.sort(function(a, b) {
                    var scoreA = parseInt(jq(a).data("VoteValue"));
                    var scoreB = parseInt(jq(b).data("VoteValue"));

                    if(isNaN(scoreA))
                        scoreA = -1;
                    if(isNaN(scoreB))
                        scoreB = -1;

                    if (scoreA > scoreB)
                        return -1;
                    else if (scoreA < scoreB)
                        return 1;
                    else
                        return 0;
                });

                pstThings.detach();
                pstThings.appendTo(postList);
            }
        }

        postList.children("#siteTable div.thing").each(function(index, item) {
            pstItems.push(item.getAttribute("id"));
        });

        postList.find("a.title").each(function() {
            var text = this.text;
            if (text.length > 160) {
                var spacerInd = text.substring(0, 140).lastIndexOf(" ");
                this.setAttribute("title", "..." + text.substring(spacerInd));
                this.text = text.substring(0, spacerInd) + "... (more)";
            }
        });

        for(var i = 0; i < pstItems.length; i++){
            var jqThing = jq("#" + pstItems[i]);
            var tagline = jqThing.find("p.tagline");
            tagline.detach();
            var subReddit = tagline.find("a.subreddit");
            subReddit.detach();
            var first = jqThing.find("div.entry p:first");
            var jqDiv = jq("<span class='thingUserSubInfo'></span>");
            jqDiv.insertBefore(first);
            var flatlistButtons = jqThing.find("ul.flat-list.buttons");
            flatlistButtons.detach();

            tagline.appendTo(jqDiv);
            subReddit.appendTo(jqDiv);
            flatlistButtons.appendTo(jqDiv);


            if (settings.removeDuplicates) {
                var resource = jqThing.find("a.thumbnail").get(0);
                if(resource) {
                    if (resourcesLinks.indexOf(resource.href) >= 0) {
                        if (!jqThing.hasClass("duplicateHidden"))
                            jqThing.addClass("duplicateHidden");

                        AddToHideLinks(jqThing);
                        duplicateLinks.push(jqThing);
                    } else
                        resourcesLinks.push(resource.href);
                }
            }
        }

        jq("div.thing.promotedlink").each(function() {
            AddToHideLinks(jq(this));
        });

        if (!isFriendsPage && !isUserSubmittedPage) {
            rAuthorLinks.each(function(index, item) {
                var jqItem = jq(item);
                var userName = jqItem.text().replace("/r/", "").replace("r/","");
                var userIndex = jq.inArray(userName, settings.usersToHide);
                var itemThing = jqItem.parents("div.thing");
                if (userIndex >= 0) {
                    var hideThisPost = settings.hideUsers &&
                        !isUserPage &&
                        (settings.hideUsersNSFWonly && jqItem.parents("div.entry").hasClass("NsfwItem") || !settings.hideUsersNSFWonly);

                    if (hideThisPost)
                        AddToHideLinks(itemThing);

                    userHideLinks.push(itemThing);
                    itemThing.addClass("userHidden");
                }
            });
        }

        var hideThisPost = settings.hideKeywords && !isUserPage;
        for (var iKW = 0; iKW < settings.keywordsToHide.length; iKW++) {
            var kwItem = settings.keywordsToHide[iKW];
            if (kwItem) {
                jq("a.title:contains(" + kwItem + ")").each(function(jndex, jtem) {
                    var thing = jq(jtem).parents("div.thing");

                    if (hideThisPost)
                        AddToHideLinks(thing);

                    keywordsHideLinks.push(thing);
                    if (!thing.hasClass("keywordsHidden"))
                        thing.addClass("keywordsHidden");
                });
            }
        }
    }

    if (loc.indexOf("/r/all") >= 0) {
        rSubRedditLinks.each(function(index, item) {
            var subName = item.text.replace("/r/", "").replace("r/","");
            var subIndex = jq.inArray(subName, settings.subsToHide);
            if (subIndex >= 0) {
                var itemThing = jq(item).parents("div.thing");
                if (hideSubsFromLocation)
                    AddToHideLinks(itemThing);
                subHideLinks.push(itemThing);
                itemThing.addClass("subsHidden");
            }
        });
    }

    rAuthorLinks.each(function(index, item) {
        var href = item.getAttribute("href");
        if (href.indexOf("/submitted") === -1)
            item.setAttribute("href", href + "/submitted/");
    });

    jq("#toggleSubsHidden span").text(subHideLinks.length);
    jq("#toggleUserHidden span").text(userHideLinks.length);
    jq("#toggleKeywordsHidden span").text(keywordsHideLinks.length);
    jq("#toggleDuplicateHidden span").text(duplicateLinks.length);

    InitializeContextMenu();

    HideAndAction(hideLinks, 0, null, "enhance");

    if (!isSinglePost) {
        if (!isSubmitPage) {
            if (document.hasFocus())
                MakeActivePost(undefined, pstItems.First());
            else {
                focusFirstPost = function() {
                    setTimeout(function() {
                        MakeActivePost(undefined, pstItems.First());
                    }, settings.animationItemTimeout);
                    window.removeEventListener("focus", focusFirstPost, true);
                };
                window.addEventListener("focus", focusFirstPost, true);
            }

        }

        var subs = jq("a.subreddit");
        if (subs.length > 0) {
            subs.each(function(index, item) {
                var jqItem = jq(item);
                var subText = jqItem.text().replace("/r/", "").replace("r/","");
                var subColor = subColors[subText];
                var eleParent = jqItem.parent().parent();

                if (!subColor) {
                    subColor = getRandomColor();
                    subColors[subText] = subColor;
                }

                //jqItem.detach().prependTo(eleParent);
                jqItem.text(subText);

                jqItem.parents("div.thing").css("background-color", subColor);
            });
        } else {
            jq("span.domain a").each(function(index, item) {
                var jqItem = jq(item);
                var subText = jqItem.text().replace("/r/", "").replace("r/","");
                var subColor = subColors[subText];
                var eleParent = jqItem.parent().parent();

                if (!subColor) {
                    subColor = getRandomColor();
                    subColors[subText] = subColor;
                }

                jqItem.parents("div.thing").css("background-color", subColor);
            });
        }
    }

    jq(".last-clicked").removeClass("last-clicked");
    jq(".promotedlink").remove();

    if (userHideLinks.length === 0) {
        jq("#toggleUserHidden").attr("style", "background-color: #808080 !important; color: #CFCFCF; border: 1px solid black;");
    } else {
        jq("#toggleUserHidden").click(function(e) {
            ToggleVisibility(e, this, ".userHidden");
            return false;
        });
    }
    if (subHideLinks.length === 0) {
        jq("#toggleSubsHidden").attr("style", "background-color: #808080 !important; color: #CFCFCF; border: 1px solid black;");
    } else {
        jq("#toggleSubsHidden").click(function(e) {
            ToggleVisibility(e, this, ".subsHidden");
            return false;
        });
    }
    if (duplicateLinks.length === 0) {
        jq("#toggleDuplicateHidden").attr("style", "background-color: #808080 !important; color: #CFCFCF; border: 1px solid black;");
    } else {
        jq("#toggleDuplicateHidden").click(function(e) {
            ToggleVisibility(e, this, ".duplicateHidden");
            return false;
        });
    }
    if (keywordsHideLinks.length === 0) {
        jq("#toggleKeywordsHidden").attr("style", "background-color: #808080 !important; color: #CFCFCF; border: 1px solid black;");
    } else {
        jq("#toggleKeywordsHidden").click(function(e) {
            ToggleVisibility(e, this, ".keywordsHidden");
            return false;
        });
    }

    var navButton = jq("div.nav-buttons .next-button a");
    if (navButton.length === 1) {
        navButton.prepend().appendTo("#postNavigation");
        var navButtonPrev = jq("div.nav-buttons .prev-button a");
        if (navButtonPrev)
            navButtonPrev.prepend().appendTo("#postNavigation");
    }

    jq("div.nav-buttons").remove();

    InitializeEventHandlers();

    fExtMessage(undefined);
}

// Initialization - End

// Events - Start
function HideAllClick(e) {
    if(e)
        e.preventDefault();

    isHiding = true;

    var siteTable = jq("#siteTable");
    siteTable.css("display", "block");
    postList.css("display", "block");
    siteTable.css("height", siteTable.outerHeight(true) + "px");

    jq("div.thing").each(function(index, thing) {
        var jqThing = jq(thing);
        var offs = jqThing.offset();
        var top = parseInt(offs.top);
        var width = jqThing.width();
        var height = jqThing.height();

        setTimeout(function() {
            jqThing.css({
                top: top,
                left: 0,
                width: width,
                height: height,
                position: "absolute"
            });
        }, settings.animationItemTimeout);
    });


    reloadCancelled = false;
    setTimeout(function() {
        if(pstItems.selected)
            pstItems.jqSelected.animate({ scrollTop: pstItems.jqSelected.offset().top }, 0);

        HideAndAction(hiderElements, 0, null, "reload");
    }, settings.hideItemTimeout);
    jq(this).hide();

    return false;
}

function HideSelectedClick(e) {
    if (pstItems.selected) {
        var selPst = pstItems.selected;
        jq("#nextPost").click();
        Hide(jq("#" + selPst), false, false);
    }
    else
        MakeActivePost(undefined, pstItems.Next());

    if(e)
        e.preventDefault();
    return false;
}

// Kontext-Menü
jq("#fExtContextMenu").on("fExtContextMenuOpening", function(event, actor) {
    var txt = actor.text();
    var usrIndex = jq.inArray(txt, settings.usersToHide);
    var usrTxt = 'Select a User';
    var subIndex = jq.inArray(txt, settings.subsToHide);
    var subTxt = 'Select a Subreddit';
    var bCanHide = actor.hasClass("author") && usrIndex < 0;
    var bCanUnhide = !bCanHide && actor.hasClass("author") && usrIndex >= 0;

    ctxItemUsrHdr.Toggle(bCanHide);
    ctxItemUsrHdrPg.Toggle(bCanHide);
    ctxItemUsrUnhdr.Toggle(bCanUnhide);
    if (actor.hasClass("author"))
        usrTxt = txt;

    ctxItemUsrHdr.ItemText("Hide User - " + usrTxt);
    ctxItemUsrHdrPg.ItemText("Hide User and open Page - " + usrTxt);
    ctxItemUsrUnhdr.ItemText("Unhide User - " + usrTxt);

    bCanHide = actor.hasClass("subreddit") && subIndex < 0;
    bCanUnhide = !bCanHide && actor.hasClass("subreddit") && subIndex >= 0;

    ctxItemSbHdr.Toggle(bCanHide);
    ctxItemSbUnhdr.Toggle(bCanUnhide);
    if (actor.hasClass("subreddit"))
        subTxt = txt;

    ctxItemSbHdr.ItemText("Hide Sub - " + subTxt);
    ctxItemSbUnhdr.ItemText("Unhide Sub - " + subTxt);

    txt = fExtGetSelection();
    var keywIndex = jq.inArray(txt, settings.keywordsToHide);
    var keywTxt = 'Select word(s)';
    bCanHide = txt !== "" && keywIndex < 0;
    bCanUnhide = !bCanHide && txt !== "" && keywIndex >= 0;

    ctxItemKeywHdr.Toggle(bCanHide);
    ctxItemKeywUnhdr.Toggle(bCanUnhide);
    ctxItemKeywHdr.ItemText("Hide Keywords - '" + (txt || keywTxt) + "'");
    ctxItemKeywUnhdr.ItemText("Unhide Keywords - '" + (txt || keywTxt) + "'");

    ctxRedo.Toggle(ctxRedo.find("li.ctxItem").length > 0);
    ctxUndo.Toggle(ctxUndo.find("li.ctxItem").length > 0);
});

if (!isSinglePost && !isSubmitPage) {
    jq(document).on("click", "div.thing", function(e) {
        if(e.target.nodeName === "A" || e.target.nodeName === "LI")
            return true;

        var jqThing;
        if(e.target.nodeName === "DIV" && jq.inArray("thing", e.target.classList) >= 0)
            jqThing = jq(e.target);
        else if(jq(e.target).parents("div.thing:first").length === 1 && (e.target.nodeName === "SPAN" || (e.target.nodeName === "DIV" && jq.inArray("entry", e.target.classList) >= 0)))
            jqThing = jq(e.target).parents("div.thing:first");
        else
            console.log("Need to make active post? Nodename: " + e.target.nodeName + " Classes: " + e.target.classList);

        if (jqThing && jqThing.is(":visible") && jqThing.attr("id") != pstItems.selected) {
            return MakeActivePost(pstItems.selected, jqThing.attr("id"));
        }
        else return true;
    });

    jq(document).bind("keydown", handleShortcut);
    jq(document).on("focusin", "input, textarea, select", function(e) {
        jq(document).unbind("keydown", handleShortcut);
    });
    jq(document).on("focusout", "input, textarea, select", function(e) {
        jq(document).bind("keydown", handleShortcut);
    });

    jq("#topPost").click(function() {
        MakeActivePost(pstItems.selected, pstItems.First());
        return false;
    });

    jq("#botPost").click(function() {
        MakeActivePost(pstItems.selected, pstItems.Last());
        return false;
    });

    jq("#prevPost").click(function() {
        MakeActivePost(pstItems.selected, pstItems.Previous());
        return false;
    });

    jq("#nextPost").click(function() {
        MakeActivePost(pstItems.selected, pstItems.Next());
        return false;
    });

    jq("#frPost").click(function() {
        var pst = pstItems.selected;
        var pstNew = pst;
        pstNew = pstItems.Previous(5);

        MakeActivePost(pst, pstNew);
        return false;
    });

    jq("#ffPost").click(function() {
        var pst = pstItems.selected;
        var pstNew = pst;

        MakeActivePost(pstItems.selected, pstItems.Next(5));
        return false;
    });
}

// Events - Settings
function SetSettingsValues(settingsObject) {
    for (var key in settingsObject) {
        var varType = typeof settingsObject[key];

        SetSettingValue(key, varType, settingsObject[key]);
    }
}

function SetSettingValue(key, varType, value) {
    var settingsElement = jq("#rEnhSettings").find("[name='" + key + "']");

    switch (varType) {
        case "boolean":
            if (value)
                settingsElement.attr("checked", "true");
            else
                settingsElement.removeAttr("checked");
            break;
        case "number":
            settingsElement.val(value);
            break;
        case "string":
            settingsElement.val(value);
            break;
        case "object":
            if (typeCheck.call(value) === '[object Array]')
                settingsElement.val(value.toString());
            else
                SetSettingsValues(value);
            break;
        default:
            console.log("Cannot get setting " + key + " to settings dialog because unkown type " + varType);
            break;
    }
}

function ShowSettings() {
    SetSettingsValues(settings);

    jq("#rEnhSettings").show();
    return false;
}

function SaveSettingsDialog(e) {
    e.preventDefault();

    jq("#rEnhSettings").find("input, textarea, select").each(function() {
        var val;
        var key = this.name;
        var tag = this.tagName;

        switch (tag) {
            case "INPUT":
                var type = this.type;
                switch (type) {
                    case "checkbox":
                        val = Boolean(this.checked);
                        break;
                    case "number":
                        val = parseFloat(this.value);
                        break;
                    case "text":
                        val = this.value;
                        break;
                    case "button":
                        break;
                    default:
                        console.log("unhandled input type " + type + " for key " + key);
                        break;
                }
                break;
            case "TEXTAREA":
                val = this.value.split(",");
                break;
            case "SELECT":
                val = parseInt(this.value);
                break;
            default:
                break;
        }

        if (val !== undefined) {
            if (key in settings)
                settings[key] = val;
            else if (key in settings.shortcuts)
                settings.shortcuts[key] = val;
        }
    });
    GMSave();

    fExtPopup("Settings saved!");

    return false;
}
// Events - End

// Functions - Start
function handleShortcut(e) {
    switch (e.which) {
        case settings.shortcuts.voteUp:
            Vote(pstItems.selected, "up");
            break;
        case settings.shortcuts.previousPost:
            jq("#prevPost").click();
            break;
        case settings.shortcuts.voteDown:
            Vote(pstItems.selected, "down");
            break;
        case settings.shortcuts.nextImage:
            var imgActorNext = jq(".pstSelected div.res-gallery-individual-controls div.res-gallery-next");
            if(imgActorNext.length > 0)
                imgActorNext.get(0).click();
            break;
        case settings.shortcuts.toggleSelected:
            if (pstItems.selected)
                pstItems.jqSelected.find(".expando-button").get(0).click();
            break;
        case settings.shortcuts.previousImage:
            var imgActorPrev = jq(".pstSelected div.res-gallery-individual-controls div.res-gallery-previous");
            if(imgActorPrev.length > 0)
                imgActorPrev.get(0).click();
            break;
        case settings.shortcuts.hideSelected:
            HideSelectedClick();
            break;
        case settings.shortcuts.nextPost:
            jq("#nextPost").click();
            break;
        case settings.shortcuts.hideAll:
            if (reloadCancelled)
                HideAllClick();
            else
                reloadCancelled = true;
            break;
        case settings.shortcuts.zoomIn:
            if (pstItems.selected)
                pstItems.jqSelected.find("img, video, iframe").each(function() { document.fExt.zoomIn(this, 20); });
            break;
        case settings.shortcuts.zoomOut:
            if (pstItems.selected)
                pstItems.jqSelected.find("img, video, iframe").each(function() { document.fExt.zoomOut(this, 20); });
            break;
        case settings.shortcuts.rotateLeft:
            if (pstItems.selected)
                pstItems.jqSelected.find("img, video, iframe").each(function() { document.fExt.rotate(this, -90); });
            break;
        case settings.shortcuts.rotateRight:
            if (pstItems.selected)
                pstItems.jqSelected.find("img, video, iframe").each(function() { document.fExt.rotate(this, 90); });
            break;
        case settings.shortcuts.undoLast:
            if(ctxUndo.Items.length > 0)
                ctxUndo.Items[ctxUndo.Items.length - 1].Trigger();
            break;
        case settings.shortcuts.redoLast:
            if(ctxRedo.Items.length > 0)
                ctxRedo.Items[ctxRedo.Items.length - 1].Trigger();
            break;
        default:
            return true;
    }
    return false;
}

function InitializeHider() {
    jq("form.hide-button").each(function(index, item) {
        var jqItem = jq(item);
        var jqThing = jqItem.parents("div.thing");
        jqThing.find("form.hide-button, form.hide-button a").attr("thing-id", jqThing.attr("id"));
        hiderElements.push(jqThing);
    });

    if (hiderElements.length === 0)
        jq("#hideAll").attr("style", "background-color: #808080 !important; color: #CFCFCF; border: 1px solid black;");

    jq("#hideAll").text("All (" + hiderElements.length + ")");
}

function VoteAndNext(sender, voteLinks, ind) {
    var nextVoteItem = voteLinks[ind];
    jq(nextVoteItem).click();

    ind++;
    if (voteLinks.length > ind) {
        animateControl(sender);

        setTimeout(function() {
            VoteAndNext(sender, voteLinks, ind);
        }, settings.animationItemTimeout);
    } else {
        jq("#downvoter").text("downvote");
        jq("#upvoter").text("upvote");
    }
}

function ExpandAndNext(expandLinks, ind) {
    var jqExpander = jq("#expander");
    var expandItem = expandLinks[ind];
    jq(expandItem).click();

    if (expandLinks.length > ind) {
        animateControl(jqExpander);
        setTimeout(function() {
            ExpandAndNext(expandLinks, ind + 1);
        }, settings.animationItemTimeout);
    }
    else  {
        if(jqExpander.hasClass("collapsed")) {
            jqExpander.text("Collapse");
            jqExpander.removeClass("collapsed");
            jqExpander.addClass("expanded");
        }
        else {
            jqExpander.text("Expand");
            jqExpander.addClass("collapsed");
            jqExpander.removeClass("expanded");
        }
    }
}

function animateControl(sender) {
    var txt = jq(sender).text();
    if (txt.length >= 3)
        txt = ".";
    else
        txt = txt + ".";

    jq(sender).text(txt);
}

function HideAndAction(hideLinks, index, previousLink, action) {
    var jqLink = jq(previousLink);
    var item = hideLinks[index];
    if (jqLink.find("form.hide-button").length === 0) {
        if (hideLinks.length > index) {
            var perc = index * 100 / hideLinks.length;
            fExtSetLoading(perc);
            if (action !== "hideObject")
                fExtMessage(action + " (" + index + "/" + hideLinks.length + ") ...");

            Hide(item, action === "reload" || (action == "enhance" && jq(item).find("a.author.friend").length > 0 && duplicateLinks.indexOf(item) < 0));

            var actArgs = [hideLinks, index + 1, previousLink, action];
            ActionWhenElementIsInvisible(item, HideAndAction, actArgs);
        } else {
            isHiding = false;
            fExtSetLoading(100);
            fExtMessage(undefined);
            switch (action) {
                case "reload":
                    ReloadLooper(5);
                    break;
                case "enhance":
                    if (pstItems.onlyVisible().length === 0)
                        ReloadLooper(5);
                    else
                        InitializeHider();
                    break;
                case "nextPost":
                    if (pstItems.onlyVisible().length === 0)
                        ReloadLooper(5);
                    else
                        jq("#nextPost").click();
                    break;
                default:
                    break;
            }
        }
    } else setTimeout(function() { HideAndAction(hideLinks, index, previousLink, action); }, settings.hideItemTimeout);
}

function ReloadLooper(loops) {
    if (reloadCancelled) {
        fExtMessage();
        return;
    } else {
        if (loops === 0) {
            jq("html, body").animate({ scrollTop: 0 }, 0);

            fExtMessage("Reloading...");
            setTimeout(function() {
                location.reload();
            }, 1000);
        } else {
            var msg = "Reloading in " + loops + " seconds... (click to cancel)";
            fExtMessage(msg);
            document.title = msg;
            setTimeout(function() {
                ReloadLooper(loops - 1);
            }, 1000);
        }
    }
}

function Hide(item, displayAfter, handled) {
    var jqHideButton = item.find("form.hide-button");

    if (!isHiding && pstItems.selected === item.attr("id")) {
        MakeActivePost(item.attr("id"), pstItems.Next());
    }

    if(!handled) {
        jqHideButton.unbind("click", handleHideClick);

        jqHideButton.find("a:first").click();

        var args = [item, displayAfter, true];
        AddUndo(Unhide, args, Hide, args, item.find("p.title").text());

        jqHideButton.bind("click", handleHideClick);
    }

    item.hide();

    if (displayAfter) {
        ActionWhenElementIsInvisible(item, item.show, undefined);
    }

    if(pstItems.onlyVisible().length === 0) {
        reloadCancelled = false;
        ReloadLooper(5);
    }

    return true;
}

function Unhide(item, displayAfter, handled) {
    jq(item).show();

    if(!handled){
        var args = [item, displayAfter, true];
        AddUndo(Hide, args, Unhide, args, item.find("p.title").text());
    }

    return true;
}

function Mark(item, itemType) {
    item.addClass("markedItem");
    item.addClass(itemType);
}

function MakeActivePost(oldId, newId) {
    if (oldId === undefined || newId === undefined || oldId !== newId) {
        if (newId) {
            if (newId !== pstItems.selected) {
                pstItems.selected = newId;
                pstItems.jqSelected = jq("#" + newId);
                if (!pstItems.jqSelected.hasClass("pstSelected"))
                    pstItems.jqSelected.addClass("pstSelected");

                if (settings.autoExpandSelectedPost) {
                    var jsExpander = pstItems.jqSelected.find(".expando-button.collapsed").get(0);
                    if (jsExpander)
                        jsExpander.click();
                }
            }
        }
        else {
            pstItems.selected = undefined;
            pstItems.jqSelected = undefined;
        }

        if (oldId) {
            var jqOldPost = jq("#" + oldId);
            jqOldPost.removeClass("pstSelected");

            if (settings.autoExpandSelectedPost) {
                var jsCollapser = jqOldPost.find(".expando-button.expanded").get(0);
                if (jsCollapser)
                    jsCollapser.click();
            }
        }
    }

    if(pstItems.jqSelected)
        scrollTo(pstItems.jqSelected);

    return false;
}

var preloadComplete = 0;
var preloadingImages = jq("a.title[href*='imgur']:not(.thumbnail)");
preloadingImages.each(function(index, item) {
    var src = item.href;
    if (src.indexOf("https://") < 0) {
        src = src.replace("http://", "https://");
        item.setAttribute("href", src);
    }
    preload(index, preloadingImages.length, src);
});

function preload(index, count, src) {
    var imageNumber = index + 1;
    var msg = "Loading Image " + imageNumber + " of " + count + ": ";
    //console.log(msg + src);
    var image = new Image();
    image.onload = function() {
        image.remove();
        preloadComplete++;
        //console.log(preloadComplete + " of " + count + " images completed.");
    };
    image.onerror = function() {
        if (image.src.indexOf("https://") >= 0) {
            image.src = image.src.replace("https://", "http://");
        } else {
            image.remove();
            preloadComplete++;
        }
        //console.log(preloadComplete + " of " + count + " images completed.");
    };
    image.src = src;
}

function scrollTo(item) {
    var jqItem = jq(item);
    var offs = jqItem.offset();
    var topPos = offs.top - headerHeight;

    jq("html, body").animate({ scrollTop: topPos }, settings.animationItemTimeout);
}

function AddToHideLinks(elm) {
    if (!elm.hasClass("toggleHidden"))
        elm.addClass("toggleHidden");

    if (jq.inArray(elm, hideLinks) < 0)
        hideLinks.push(elm);
}

function Vote(id, direction) {
    if (id !== undefined) {
        var element;

        switch (direction) {
            case "up":
                element = jq("#" + id).find(".arrow.up, .arrow.upmod");
                break;
            case "down":
                element = jq("#" + id).find(".arrow.down, .arrow.downmod");
                break;
            default:
                break;
        }

        if (element.length > 0) {
            element.click();
        }
    }
}

function AddUndo(fnUndo, argsUndo, fnRedo, argsRedo, suffix) {
    if (ctxUndo.Items.length === 10) {
        var removeThis = ctxUndo.Items[0];
        ctxUndo.Items.splice(0, 1);
        removeThis.remove();
    }
    if(!suffix)
        suffix = "";
    else if(suffix.length > 25)
        suffix = suffix.substring(0, 25) + "...";

    var ctxItem = fExtAddCtxItem(suffix && suffix.length > 0 ? fnRedo.name + " - " + suffix : fnRedo.name, ctxUndo);
    ctxItem.fnUndo = fnUndo;
    ctxItem.argsUndo = argsUndo;
    ctxItem.fnRedo = fnRedo;
    ctxItem.argsRedo = argsRedo;
    ctxItem.Action = function(event, sender, actor) {
        sender.fnUndo.apply(this, sender.argsUndo);
        AddRedo(sender.fnUndo, sender.argsUndo, sender.fnRedo, sender.argsRedo, suffix);
        sender.Remove();
    };
}

function AddRedo(fnUndo, argsUndo, fnRedo, argsRedo, suffix) {
    if (ctxRedo.Items.length === 10) {
        ctxRedo.Items[0].remove();
        ctxRedo.Items.splice(0, 1);
    }
    if(!suffix)
        suffix = "";
    else if(suffix.length > 25)
        suffix = suffix.substring(0, 25) + "...";

    var ctxItem = fExtAddCtxItem(suffix && suffix.length > 0 ? fnUndo.name + " - " + suffix : fnUndo.name, ctxRedo);
    ctxItem.fnUndo = fnUndo;
    ctxItem.argsUndo = argsUndo;
    ctxItem.fnRedo = fnRedo;
    ctxItem.argsRedo = argsRedo;
    ctxItem.Action = function(event, sender, actor) {
        sender.fnRedo.apply(this, sender.argsRedo);
        AddUndo(sender.fnUndo, sender.argsUndo, sender.fnRedo, sender.argsRedo, suffix);
        sender.remove();
    };
}

function ToggleVisibility(event, sender, className) {
    event.preventDefault();
    if (jq(sender).data("state") === "shown") {
        jq(sender).data("state","hidden");
        jq(className).hide();
    } else {
        jq(sender).data("state","shown");
        jq(className).show();
    }
}

function HideObject(containingArray, arrayName, text, selector) {
    if (jq.inArray(text, containingArray) < 0) {
        fExtPopup("Adding '" + text + "' from '" + arrayName + "' to hide.");
        containingArray.push(text);
        GMSave();

        var links = [];
        var selectNext = false;

        jq(selector).parents("div.thing").each(function(index, item) {
            var jqItem = jq(item);
            selectNext = selectNext || item.id === pstItems.selected;

            if (jqItem.find("form.hide-button").length > 0)
                links.push(jqItem);
        });

        HideAndAction(links, 0, null, "hideObject");

        return true;
    }
    return false;
}

function UnhideObject(containingArray, arrayName, text, selector) {
    var arrayIndex = jq.inArray(text, containingArray);
    if (arrayIndex >= 0) {
        fExtPopup("Removing '" + text + "' from '" + arrayName + "'.");
        containingArray.splice(arrayIndex, 1);
        GMSave();
        return true;
    }
    return false;
}

function GrabContent(htmlContent) {
    fExtMessage("Loading additional Pages (" + numberOfSitesLoaded + " of max " + settings.maximumNumberOfSitesToLoad + ")");

    var navButtonNext = jq(htmlContent).find("div.nav-buttons .next-button a").get(0);
    if (numberOfSitesLoaded < settings.maximumNumberOfSitesToLoad && navButtonNext) {
        numberOfSitesLoaded += 1;
        var srcUrl = navButtonNext.href;
        //siteTable
        jq.ajax({
            url: srcUrl,
            method: "get",
            success: function(data) {
                jq(data).find("div#siteTable").find("div.thing").each(function() {
                    jq(this).detach().appendTo("div#siteTable");
                });

                GrabContent(data);
            },
            error: function(data) {
                console.log(data);
            }
        });
    } else {
        if (navButtonNext)
            jq(navButtonNext).detach().appendTo("#postNavigation");

        fExtMessage(numberOfSitesLoaded + " Pages loaded, starting initialization...");

        MainInitialization();
    }
}

function InitializeContextMenu() {
    ctxItemUsrHdr = fExtAddCtxItem("Hide User", rEnhSub);
    ctxItemUsrHdr.Action = function(event, sender, actor) {
        var user = actor.text();
        var selector = "a.author:contains(" + user + ")";
        var args = [settings.usersToHide, "Users", user, selector];
        if (HideObject.apply(sender, args))
            AddUndo(UnhideObject, args, HideObject, args, user);
    };

    ctxItemUsrHdrPg = fExtAddCtxItem("Hide User and open Userpage", rEnhSub);
    ctxItemUsrHdrPg.Action = function(event, sender, actor) {
        ctxItemUsrHdr.Action(event, sender, actor);
        GM_openInTab("https://www.reddit.com/u/" + actor.text() + "/submitted/", true);
    };

    ctxItemUsrUnhdr = fExtAddCtxItem("Unhide User", rEnhSub);
    ctxItemUsrUnhdr.Action = function(event, sender, actor) {
        var user = actor.text();
        var selector = "a.author:contains(" + user + ")";
        var args = [settings.usersToHide, "Users", user, selector];
        if (UnhideObject.apply(sender, args)) {
            jq("a.author:contains(" + user + ")").parents("div.thing").each(function(index, item) {
                jq(item).show();
            });
            AddUndo(HideObject, args, UnhideObject, args, user);
        }
    };

    fExtAddCtxSeparator(rEnhSub);

    ctxItemSbHdr = fExtAddCtxItem("Hide Sub", rEnhSub);
    ctxItemSbHdr.Action = function(event, sender, actor) {
        var sub = actor.text().replace("/r/", "").replace("r/","");
        var selector = "a.subreddit:contains(" + sub + ")";
        var args = [settings.subsToHide, "Subs", sub, selector];
        if (HideObject.apply(sender, args))
            AddUndo(UnhideObject, args, HideObject, args, sub);
    };

    ctxItemSbUnhdr = fExtAddCtxItem("Unhide Sub", rEnhSub);
    ctxItemSbUnhdr.Action = function(event, sender, actor) {
        var sub = actor.text().replace("/r/", "").replace("r/","");
        var selector = "a.subreddit:contains(" + sub + ")";
        var args = [settings.subsToHide, "Subs", sub, selector];
        if (UnhideObject.apply(sender, args)) {
            jq(selector).parents("div.thing").each(function(index, item) {
                jq(item).show();
            });
            AddUndo(HideObject, args, UnhideObject, args, sub);
        }
    };

    fExtAddCtxSeparator(rEnhSub);

    ctxItemKeywHdr = fExtAddCtxItem("Hide Keywords", rEnhSub);
    ctxItemKeywHdr.Action = function(event, sender, actor) {
        var keyword = fExtGetSelection().trim();
        var selector = "a.title:contains(" + keyword + ")";
        var args = [settings.keywordsToHide, "Keywords", keyword, selector];
        if (HideObject.apply(sender, args))
            AddUndo(UnhideObject, args, HideObject, args, keyword);
    };

    ctxItemKeywUnhdr = fExtAddCtxItem("Unhide Keywords", rEnhSub);
    ctxItemKeywUnhdr.Action = function(event, sender, actor) {
        var keyword = fExtGetSelection();
        var selector = "a.title:contains(" + keyword + ")";
        var args = [settings.keywordsToHide, "Keywords", keyword, selector];
        if (UnhideObject.apply(sender, args)) {
            jq(selector).parents("div.thing").each(function(index, item) {
                jq(item).show();
            });
            AddUndo(HideObject, args, UnhideObject, args, keyword);
        }
    };

    fExtAddCtxSeparator(rEnhSub);

    // Undo/Redo
    ctxUndo = fExtAddSub("Undo", rEnhSub);
    ctxRedo = fExtAddSub("Redo", rEnhSub);

    fExtAddCtxSeparator(rEnhSub);

    ctxItemCpyHtml = fExtAddCtxItem("Copy HTML", rEnhSub);
    ctxItemCpyHtml.Action = function(event, sender, actor) {
        fExtClipboard("Copy", jq('div.content').html());
    };

    fExtAddCtxSeparator(rEnhSub);

    ctxItemSett = fExtAddCtxItem("REnhancer Settings", rEnhSub);
    ctxItemSett.Action = function() { ShowSettings(); };

    // Copy links from Domain
    fExtAddCtxSeparator(rEnhSub);

    var ctxSubCpyDom = fExtAddSub("Copy Domain Links", rEnhSub);
    var objCpyDom = [];

    jq("span.domain a[href*='/domain/']").each(function() {
        var parentLink = jq(this).parents("div.thing").find("a").get(0);
        var domainName = this.pathname;
        domainName = domainName.replace("/domain/", "");
        domainName = domainName.substring(0, domainName.length - 1);
        var dictCpyDom;
        for (var ind in objCpyDom) {
            var currCpyDom = objCpyDom[ind];
            if (currCpyDom.Domain === domainName) {
                dictCpyDom = currCpyDom;
                break;
            }
        }

        if (dictCpyDom !== undefined) {
            dictCpyDom.Links.push(parentLink.href);
        } else {
            dictCpyDom = {
                Domain: domainName,
                Links: [parentLink.href],
            };
            objCpyDom.push(dictCpyDom);
        }
    });

    objCpyDom.sort(function(a, b) {
        return a.Domain.localeCompare(b.Domain);
    });

    for (var ind in objCpyDom) {
        var objDomain = objCpyDom[ind];
        var ctxCpyDom = fExtAddCtxItem(objDomain.Domain, ctxSubCpyDom);
        ctxCpyDom.Domain = objDomain;
        ctxCpyDom.Action = function() {
            var cpyText = "";
            for (var lnkInd in this.Domain.Links) {
                cpyText += this.Domain.Links[lnkInd];
                cpyText += "\n";
            }
            fExtClipboard("Copy", cpyText);
            fExtPopup("Copied " + this.Domain.Links.length + " links.");
        };
    }
}

function InitializeEventHandlers() {
    jq("#fExtMessage").click(function() { reloadCancelled = true; });
    jq("#hideAll").click(HideAllClick);
    jq("#hideSelected").click(HideSelectedClick);
    jq("#expander").click(function(e) {
        e.preventDefault();
        if(jq("#expander").hasClass("expanded"))
            ExpandAndNext(jq("div.expando-button.expanded:visible"), 0);
        else
            ExpandAndNext(jq("div.expando-button.collapsed:visible"), 0);
        return false;
    });
    jq("#renhSettingsSave").click(SaveSettingsDialog);
    jq("#upvoter").click(function(e) {
        e.preventDefault();
        VoteAndNext(jq(this), upVoteLinks, 0);
        return false;
    });
    jq("#downvoter").click(function(e) {
        e.preventDefault();
        VoteAndNext(jq(this), downVoteLinks, 0);
        return false;
    });
    jq("#renhSettingsClose").click(function(e) {
        jq("#rEnhSettings").hide();
        return false;
    });

    jq("form.hide-button").bind("click", handleHideClick);

    jq("ul.flat-list.buttons>li:not(.report-button)").click(function(e){
        if(e.target.tagName !== "A" && e.target.tagName !== "FORM") {
            var targetItem = jq(e.target).find("a").first().get(0);
            if(targetItem) targetItem.click(e);
            else return true;
        }
        else return true;
    });
}

function handleHideClick(e){
    var jqTarget = jq(e.target);
    var jqThing = jq("#" + e.target.getAttribute("thing-id"));

    var args = [jqThing, false, true];
    AddUndo(Unhide, args, Hide, args, jqThing.find("p.title").text());

    Hide(jqThing, undefined, true);
}

// Functions - Load and Save
function GMLoadValue(valueName) {
    var ret = GM_getValue(valueName);
    if (ret !== undefined)
        return ret.toString().split(",");

    return [];
}

function GMSave() {
    GM_setValue("settings", JSON.stringify(settings));
}

function GMLoad() {
    settings = { // Defaults
        accountName: "enter your account name here, e.g. throwaway001",
        markNSFW: true,
        replaceTopNew: false,
        sortByVotes: false,
        autoExpandSelectedPost: true,
        removeDuplicates: true,
        maximumNumberOfSitesToLoad: 1,

        animationItemTimeout: 500,
        imagePreloadingTimeout: 250,
        hideItemTimeout: 750,

        hideUsers: true,
        hideUsersNSFWonly: true,
        usersToHide: undefined,

        hideSubs: true,
        subsToHide: undefined,

        hideKeywords: true,
        keywordsToHide: undefined,

        shortcuts: {
            nextPost: 98,
            previousPost: 104,
            toggleSelected: 101,
            hideSelected: 99,

            hideAll: 97,

            voteUp: 105,
            voteDown: 103,

            nextImage: 102,
            previousImage: 100,
            zoomIn: 107,
            zoomOut: 109,
            rotateLeft: 111,
            rotateRight: 106,

            undoLast: -1,
            redoLast: -1,
        },

        panelView: false,
    };

    var lSettings = GM_getValue("settings");
    if (lSettings !== undefined) {
        settingsFromLoad(settings, JSON.parse(lSettings.toString()));
    }

    if (!settings.usersToHide)
        settings.usersToHide = GMLoadValue("usersToHide");
    if (!settings.subsToHide)
        settings.subsToHide = GMLoadValue("subsToHide");
    if (!settings.keywordsToHide)
        settings.keywordsToHide = GMLoadValue("keywordsToHide");
}

function settingsFromLoad(setSettings, getSettings){
    for (var k in getSettings) {
        var varType = typeof getSettings[k];
        var varTypeB = typeof setSettings[k];
        switch (varType) {
            case "boolean":
                setSettings[k] = Boolean(getSettings[k]);
                break;
            case "number":
                setSettings[k] = parseInt(getSettings[k]);
                break;
            case "object":
                if(Array.isArray(getSettings[k]))
                    setSettings[k] = getSettings[k];
                else
                    settingsFromLoad(setSettings[k], getSettings[k]);
                break;
            case "string":
            default:
                setSettings[k] = getSettings[k];
                break;
        }
    }
}

function ActionWhenElementIsInvisible(element, action, args) {
    if (element.hasClass("hidden") || element.find("a[text=hidden]").length === 1) {
        action.apply(element, args);
    } else {
        setTimeout(function() {
            ActionWhenElementIsInvisible(element, action, args);
        }, settings.hideItemTimeout);
    }
}

// Functions - Misc
function grabExtension(src) {
    var start, end;

    start = src.lastIndexOf(".") + 1;
    if (src.lastIndexOf("?") >= 0)
        end = src.lastIndexOf("?");
    else
        end = src.length;

    return src.substring(start, end);
}

function colorObject(r, g, b, a){
    var rv = {
        red: 0,
        blue: 0,
        green: 0,
        alpha: 100
    };

    if(r)
        rv.red = parseInt(r);
    if(g)
        rv.green = parseInt(g);
    if(b)
        rv.blue = parseInt(b);
    if(a)
        rv.alpha = parseInt(a);

    rv.getRgba = function(){
        return "rgba(" + this.red + "," + this.green + "," + this.blue + ", " + (this.alpha / 100) + ")";
    };
    rv.getHex = function(){
        var strR = this.red.toString(16);
        var strG = this.blue.toString(16);
        var strB = this.green.toString(16);
        if(strR.length === 1) strR = "0" + strR;
        if(strG.length === 1) strG = "0" + strG;
        if(strB.length === 1) strB = "0" + strB;

        return "#" + strR + strG + strB;
    };

    return rv;
}

function getRandomColor() {
    var color = '#';

    // Neu rgb-style
    var min = 155, max = 255;
    var cO = colorObject(Math.random() * (max - min) + min, Math.random() * (max - min) + min, Math.random() * (max - min) + min);

    if(cO.red > cO.green && cO.red > cO.blue)
        color = getRandomColor();
    else
        color = cO.getHex();

    return color;
}
// Functions - End