NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name TPT Fixer Upper // @namespace https://github.com/wolfy1339/UserScripts // @description This script fixes some bugs/errors on The Powder Toy's website (http://powdertoy.co.uk) // @icon https://brilliant-minds.github.io/img/powder.png // @author wolfy1339 // @oujs:author wolfy1339 // @copyright 2014-2017, wolfy1339 // @license MIT License // @downloadURL https://openuserjs.org/src/scripts/wolfy1339/TPT_Fixer_Upper.user.js // @updateURL https://openuserjs.org/meta/wolfy1339/TPT_Fixer_Upper.meta.js // @version 2.22 // @grant none // @match *://powdertoy.co.uk/* // @run-at document-idle // ==/UserScript== /* global tptenhance, tinymce */ var currentURL = window.location.pathname; function addCss(cssString) { // Helper function to add CSS if (!jQuery("style").length) { jQuery("<style type=\"text/css\"></style>").append(cssString).appendTo("head"); } else { jQuery("style").eq(0).append(cssString); } } function replacePageHeader() { // Helper function to replace the old page headers with breadcrumbs as used everywhere else. var container = jQuery("<div class=\"container\"></div>"); var currentThreadName, currentGroupID, currentGroupName, currentUserName, breadcrumb; if (currentURL.indexOf("/Admin/") !== -1 || currentURL == "/Groups/Page/Resign.html" || currentURL == "/Groups/Page/Register.html") { var header = jQuery(".Pageheader").find("a"); currentGroupName = header.text(); currentGroupID = header.attr("href").substring(29); if (currentURL.indexOf("/Admin/") !== -1) { currentUserName = jQuery(".OtherF a").text(); } } else { if (jQuery(".Pageheader a:eq(1)").text() != "Groups") { currentGroupName = jQuery(".Pageheader a").eq(1).text(); currentGroupID = tptenhance.groups.currentGroupId(); } else { var header = jQuery(".Pageheader a").eq(2) currentGroupName = header.text(); currentGroupID = header.attr("href").split("Group=")[1].split("&")[0]; } } // Set the page name or thread name depending on the current URL if (currentURL == "/Groups/Thread/View.html") { currentThreadName = jQuery(".TopicTitle").text(); } else if (currentURL == "/Groups/Thread/EditPost.html") { currentThreadName = "Edit Post"; } else if (currentURL == "/Groups/Thread/Create.html") { currentThreadName = "New Thread"; } else if (currentURL == "/Groups/Thread/Moderation.html") { currentThreadName = "Delete"; } else if (currentURL == "/Groups/Admin/Members.html") { currentThreadName = "Members"; container.css({"width": "900px"}); } else if (currentURL == "/Groups/Admin/Edit.html" || currentURL == "/Groups/Admin/MemberElevation.html") { currentThreadName = "Edit"; } else if (currentURL == "/Groups/Admin/MemberRemove.html") { currentThreadName = "Remove"; } else if (currentURL == "/Groups/Page/Resign.html") { currentThreadName = "Resign"; } else if (currentURL == "/Groups/Page/Register.html") { currentThreadName = "Register"; } // Use different formats depending on the curent URL if (currentURL == "/Groups/Admin/MemberElevation.html" || currentURL == "/Groups/Admin/MemberRemove.html") { breadcrumb = jQuery(["<ul class=\"breadcrumb\">", "<li><a href=\"/Groups/Page/Groups.html\">Groups</a><span class=\"divider\">/</span></li>", "<li><a href=\"/Groups/Page/View.html?Group=" + currentGroupID + "\">" + currentGroupName + "</a><span class=\"divider\">/</span></li>", "<li class=\"active\"><a>" + currentUserName + "</a><span class=\"divider\">/</span></li>", "<li class=\"active\"><a>" + currentThreadName + "</a></li>", "</ul>" ].join("")); } else { breadcrumb = jQuery(["<ul class=\"breadcrumb\">", "<li><a href=\"/Groups/Page/Groups.html\">Groups</a><span class=\"divider\">/</span></li>", "<li><a href=\"/Groups/Page/View.html?Group=" + currentGroupID + "\">" + currentGroupName + "</a><span class=\"divider\">/</span></li>", "<li class=\"active\"><a>" + currentThreadName + "</a></li>", "</ul>" ].join("")); } container.append(breadcrumb); jQuery(".Pageheader").remove(); container.insertBefore(".contents"); if (currentURL == "/Groups/Page/Register.html") { jQuery(".breadcrumb").css({"margin-bottom": "7px"}); } } var TPTFixerUpper = function() { if (currentURL.indexOf("/Groups") !== -1) { // Make Groups system better // Overide the currentGroupId function to work with the breadcrumbs and the old page header tptenhance.groups.currentGroupId = function() { if (jQuery(".breadcrumb").length) { return +(jQuery(".breadcrumb a").eq(1).attr("href").split("Group=")[1].split("&")[0]); } else { return +(jQuery(".Pageheader a").eq(1).attr("href").split("Group=")[1].split("&")[0]); } }; } if (currentURL.indexOf("/Groups/Admin/") !== -1 || currentURL.indexOf("/Groups/Thread/") !== -1) { replacePageHeader(); } if (currentURL == "/Search.html") { // Enhancements for the rebuilt search feature jQuery(".search-avatar").css({"margin-right": "10px"}); addCss([".search-thumbnail img {", " border-radius: 3px;", " border: 2px solid #DDD;", "}", ".search-result .details {", " margin-left: 70px;", " margin-right:20px;", "}", ".input-prepend.input-append input:first-child {", " border-radius: 3px 0 0 3px;", "}", ".description {", " word-wrap: break-word;", "}" ].join("\n")); jQuery(".input-append").attr("class", "input-prepend input-append"); jQuery(".posts .search-thumbnail").css({"width": "63px"}); jQuery(".threads .search-thumbnail").css({"width": "63px"}); } else if (currentURL == "/Discussions/Categories/Index.html") { // Fix thread view and post count, if the number is big it won't overflow as much addCss([".TopicList li .Meta span {", " max-height: 14px;", " font-size: 10px;", "}" ].join("\n")); } else if (currentURL == "/Profile/Password.html") { // Stylize submit input jQuery(".Subpage input[type=\"submit\"]").addClass("btn btn-primary"); } else if (currentURL == "/PasswordReset.html") { // Stylize submit input jQuery(".Subpage input[type=\"submit\"]").addClass("btn btn-primary"); } else if (currentURL == "/Profile/Avatar.html") { addCss([".btn-file {", " position: relative;", " overflow: hidden;", " margin-right: -4px!important;", "}", ".btn-file input[type=file] {", " position: absolute;", " top: 0;", " right: 0;", " min-width: 100%;", " min-height: 100%;", " font-size: 100px;", " text-align: right;", " filter: alpha(opacity=0);", " opacity: 0;", " outline: none;", " background: white;", " cursor: inherit;", " display: block;", "}", "input[type=text]:focus {", " box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);", " -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);", " border-color: #ddd;", "}" ].join("\n")); // Stylize file input jQuery(".OtherF").removeClass("OtherF"); jQuery("form div input").replaceWith(["<div class=\"input-prepend\">", " <span class=\"btn btn-file\">Browse...<input type=\"file\" name=\"Avatar\" accept=\"image/*\"></span>", " <input class=\"span8\" id=\"path\" type=\"text\" readonly=\"\">", "</div>" ].join("\n")).css({"width":"255px"}); jQuery(document).on("change", ".btn-file :file", function() { var input = jQuery(this); var label = input.val().replace(/\\/g, "/").replace(/.*\//, ""); input.trigger("fileselect", label); }); jQuery(".btn-file :file").on("fileselect", function(event, label) { var input = jQuery(this).parents(".input-prepend").find(":text"); if (input.length) { input.val(label); } }); } else if (currentURL == "/Groups/Admin/Members.html") { // Make the Admin management page work better by changing it's looks a little bit jQuery(".Pagination").remove(); jQuery(".contents").css({"width": "900px"}); jQuery(".MemberColumn").css({"width": "417.5px"}); jQuery(".MemberName").css({"width": "120px"}); var btns = jQuery(".btn-mini.btn-danger"); btns.text("").append('<i class="icon icon-white icon-remove"></i>'); btns.each(function() { var that = jQuery(this); var user = that.parent().parent().find(".MemberName").text(); that.attr("title", "Remove " + user); }); } else if (currentURL == "/Groups/Admin/MemberElevation.html") { // Stylize submit input jQuery("input[type=\"submit\"]").addClass("btn btn-primary"); } else if (currentURL == "/Groups/Thread/View.html") { addCss([".MessageList .Post .Meta .Author .Gravatar {", " border: 0 none;", " float: left;", " height: 40px;", " margin: 0 10px 0 0;", " overflow: hidden;", " width: 40px;", " background: linear-gradient(to top, rgba(255,255,255,0.1) 0%,rgba(0,0,0,0.1) 100%);", " background: -webkit-linear-gradient(top, rgba(255,255,255,0.1) 0%,rgba(0,0,0,0.1) 100%);", " background: -o-linear-gradient(top, rgba(255,255,255,0.1) 0%,rgba(0,0,0,0.1) 100%);", " background: -ms-linear-gradient(top, rgba(255,255,255,0.1) 0%,rgba(0,0,0,0.1) 100%);", " border-radius: 3px;", " box-shadow: 0 0 5px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.4);", " -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.4);", " -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.4);", " -o-box-shadow: 0 0 5px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.4);", " -ms-box-shadow: 0 0 5px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.4);", "}", ".MessageList .Post .Meta .Author .Gravatar img {", " border-radius: 3px;", " position: relative;", " height: 40px;", " width: 40px;", "}", ".UserInformation {", " border: 1px solid #CCC;", " box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.2);", " background: white;", " width: 300px;", " padding: 7px;", " border-radius: 3px;", " position: absolute;", " z-index: 99;", "}", ".UserInformation .UserInfoRow label {", " display: inline;", " margin-right: 4px;", "}", ".UserInformation span.Author {", " font-size: 17px;", " padding-left: 0;", " font-weight: bold;", " line-height: 3.5;", " padding-left: 10px;", "}", ".UserInformation span.Author .Gravatar {", " border: 0 none;", " float: left;", " height: 64px;", " margin: 0 10px 0 0;", " overflow: hidden;", " width: 64px;", " box-shadow: 0 0 5px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.4);", " border-radius: 3px;", " background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);", " background: linear-gradient(top, rgba(255,255,255,0.1) 0%,rgba(0,0,0,0.1) 100%);", "}", ".UserInformation span.Author .Gravatar img {", " border-radius: 3px;", " position: relative;", " z-index: -1;", " height: 64px;", " width: 64px;", "}", ".Developer .Comment .Meta .Author {", " background-image: url(/Themes/Next/Design/Images/Developer.png);", "}", ".savegame {", " vertical-align: top;", " display: inline-block;", "}", "p code {", " display: inline;", "}" ].join("\n")); // Add the Op class to all of a users posts if they are the author of the topic var pagination = jQuery(".pagination .active").text(); var post = jQuery(".MessageList").children(":first-child"); if (pagination == "11" && !post.hasClass("Mine")) { var posteeData = jQuery(".MessageList").children(":first-child").find(".Author a").text(); localStorage.setItem("postee", posteeData); } else if (pagination == "11" && post.hasClass("Mine")) { localStorage.clear("postee"); } jQuery(".Post").each(function() { var Postee = $(this).find(".Author a").text(); var postee = localStorage.getItem("postee"); if (Postee == postee) { $(this).addClass("Op"); } }); // Fix the permalink on each post jQuery(".Permalink a").each(function() { var that = jQuery(this); var href = that.attr("href").replace("Message=", "Message-"); that.attr("href", href); }); // Fixes to add the overlay when you click on the profile picture, just like in the forums jQuery(".Author").each(function() { var that = jQuery(this); var firstChild = that.children(":first-child"); var href = firstChild.attr("href"); var src = that.find("img").attr("src"); firstChild.replaceWith("<div class=\"Gravatar\"><a href=\"" + href + "\"><img src=\"" + src + "\"></a></div>"); }); /* Taken from the website's javascript (http://powdertoy.co.uk/Applications/Application.Discussions/Javascript/Thread.js), Modified slightly to fit my coding styles */ jQuery(".Author .Gravatar").on("click", function() { var informationForm = jQuery("<div class=\"UserInformation\">Loading…</div>"); jQuery("body").append(informationForm); var pos = jQuery(this).offset(); var link = jQuery(this).parent().children("a").attr("href").replace(/\.html/, ".json"); informationForm.css("top", pos.top-3); informationForm.css("left", pos.left-3); jQuery.getJSON(link).done(function(data) { var user = data.User; var forum = user.Forum; var form = jQuery(["<span class=\"Author\">", "<div class=\"Gravatar\"><img src=\"" + user.Avatar + "\"></div>", "<a href=\"/User.html?Name=" + user.Username + "\">" + user.Username + "</a>", "</span>", "<div class=\"Clear\"></div>", "<div class=\"UserInfoForum\">", "<h1>Forum</h1>", "<div class=\"UserInfoRow\"><label>Reputation:</label>" + forum.Reputation + "</div>", "<div class=\"UserInfoRow\"><label>Posts:</label>" + forum.Replies + "</div>", "<div class=\"UserInfoRow\"><label>Topics:</label>" + forum.Topics + "</div></div>" ].join("")); informationForm.html(form); }); informationForm.mouseleave(function() { informationForm.remove(); }); return false; }); // Prevent ghost talk jQuery(".Message span[style=\"color: white;\"]").removeAttr("style"); jQuery(".Mine.Owner").addClass("Administrator"); jQuery(".Mine.Manager").addClass("Moderator"); jQuery(".Moderator").each(function() { var that = jQuery(this); var a = that.find(".Meta .Author a").text(); if (a == "jacob1" || a == "cracker64" || a == "jacksonmj" || a == "AntB" || a == "Xenocide" || a == "savask" || a == "triclops200") { that.removeClass("Moderator").addClass("Developer"); that.find(".UserTitle").text("Developer"); } else if (a == "Simon") { that.removeClass("Moderator").addClass("Administrator"); that.find(".UserTitle").text("Administrator"); } }); // Replace the embedded savegames with a version that uses the same format as the forums // Wait for all page content (embedded saves) to load jQuery(".fSaveGame").each(function() { var that = jQuery(this); that.find(".fSaveRating").remove(); that.find(".fSaveGameThumb").contents().unwrap(); that.find(".fAuthor").addClass("author").removeClass("fAuthor"); that.find(".fComments").addClass("comments").removeClass("fComments"); that.find(".fSaveDetails").addClass("caption").removeClass("fSaveDetails"); var overlay = jQuery("<div class=\"overlay\"></div>"); var titleEl = jQuery(this).find(".fTitle"); var title = titleEl.attr("title").replace(/[,.\s]+/g, "_"); var href = titleEl.find("a").attr("href"); var pthref = href.substring(21, 28); overlay.append("<a class=\"btn btn-primary\" href=\"" + href + "\">View</a>"); overlay.append("<a class=\"btn btn-inverse\" href=\"ptsave:" + pthref + "#" + title + "\">Open</a>"); overlay.css({"opacity": 0}); overlay.appendTo(this); var title2 = titleEl.attr("title"); var text = titleEl.find(".fTitle a").text(); titleEl.replaceWith("<h5 title=\"" + title2 + "\"><a href=\"" + href + "\">" + text + "</a></h5>"); that.find(".SaveDownloadDo").remove(); that.addClass("savegame").removeClass("fSaveGame"); that.find("a img").attr("width", "153").attr("height", "96"); }); jQuery(".savegame").on("mouseover", function() { jQuery(this).find(".overlay").animate({opacity: 1, top: "3px"}, 150); }); jQuery(".savegame").on("mouseleave", function() { jQuery(this).find(".overlay").animate({opacity: 0, top: "-23px"}, 150); }); } else if (currentURL == "/Groups/Thread/EditPost.html") { // Add last edited count to the post itself // Fetch the username properly if the conversation notification icon is present¸ var user; if (jQuery(".dropdown-toggle").find(".badge.badge-info").length) { user = jQuery(".dropdown-toggle").clone().children().remove().end().text().trim(); } else { user = jQuery(".dropdown-toggle").text().trim(); } var dt = new Date(); var month; // Only prepend a zero if the month is lower than 10 (dt.getUTCMonth() + 1) if (dt.getUTCMonth() < 9) { month = "0" + (dt.getUTCMonth() + 1); } else { month = dt.getUTCMonth() + 1; } var time = dt.getUTCHours() + ":" + dt.getUTCMinutes() + " " + dt.getUTCDate() + "/" + month + "/" + dt.getUTCFullYear(); var lastEdited; setTimeout(function() { var content = tinymce.activeEditor.getContent({format:"text"}); var text; if (content.indexOf("<p><small>Edited") === -1) { lastEdited = "<p><small>Edited once by " + user + ". Last: " + time + "</small></p>"; text = content + lastEdited; } else { var edits; if (content.indexOf("<p><small>Edited once") !== -1) { edits = 2; } else { edits = parseInt(content.split("<p><small>Edited")[1].split(" ")[1], 10) + 1; } if (content.split("<p><small>Edited")[1].split("by")[1].split("Last:")[0].indexOf(user) === -1) { user = content.split("<p><small>Edited")[1].split("by")[1].split("Last:")[0].trim() + ", " + user; } lastEdited = "<p><small>Edited " + edits.toString() + " times by " + user + ". Last: " + time + "</small></p>"; text = content.split("<p><small>")[0] + lastEdited; } tinymce.activeEditor.setContent(text); }, 1000); } else if (currentURL == "/Groups/Page/View.html") { addCss([".breadcrumb {", " margin: 0;", " border-top: none;", " border-right: none;", " border-left: none;", "}" ].join("\n")); jQuery(".Pageheader").addClass("breadcrumb").removeClass("Pageheader"); } else if (currentURL == "/Groups/Page/Resign.html") { replacePageHeader(); jQuery("input[type=\"submit\"]").addClass("btn btn-danger"); } else if (currentURL == "/Groups/Page/Index.html") { // Make the page look like the rest of the website jQuery(".Pageheader").css({"background": "#fff", "border-bottom": "0px", "font-weight": "normal", "padding": "0"}); jQuery(".Page").css({"border": "none"}); jQuery(".contents").css({"padding": "10px","background": "white","border": "1px solid #DDD"}); jQuery(".GroupItem:last-child").css({"border-bottom": "none"}); } else if (currentURL == "/Groups/Page/Groups.html") { // Remove bottom border on the last child of .GroupItem jQuery(".GroupItem:last-child").css({"border-bottom": "none"}); addCss([".breadcrumb {", " border-left: none;", " border-right: none;", " border-bottom: none;", " margin: 0;", " padding: 0;", "}" ].join("\n")); jQuery(".PageFooter").addClass("breadcrumb").removeClass("PageFooter"); } else if (currentURL == "/Groups/Page/Register.html") { replacePageHeader(); // Reduce the group registration page to the basics that are needed jQuery("h1").eq(2).remove(); jQuery("textarea").hide(); jQuery(".OtherF label").remove(); jQuery("input[name=\"Submit\"]").attr("value", "Submit Registration"); replacePageHeader(); } }; // Check if the Powder Toy enhancements script is loaded // My script functions only work if they are ran after that specific script setTimeout(function() { if (typeof tptenhance == "undefined") { var script = jQuery("<script src=\"https://cdn.rawgit.com/jacksonmj/Userscript-TPT-Enhancements/master/Powder_Toy_enhancements.user.js\"></script>"); jQuery("head").append(script); setTimeout(function() { TPTFixerUpper(); }, 10000); } else { TPTFixerUpper(); } }, 2000);