Skip to content

Instantly share code, notes, and snippets.

@FelixWolf
Last active February 13, 2020 13:44
Show Gist options
  • Save FelixWolf/6809d7850917e3470dcd to your computer and use it in GitHub Desktop.
Save FelixWolf/6809d7850917e3470dcd to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name SL Marketplace Extender
// @namespace http://tools.softhyena.com
// @updateURL https://gist.githubusercontent.com/FelixWolf/6809d7850917e3470dcd/raw/
// @downloadURL https://gist.githubusercontent.com/FelixWolf/6809d7850917e3470dcd/raw/
// @version 0.3
// @description Adds features to the marketplace which should be there
// @author Chaser Zaks
// @match https://marketplace.secondlife.com/*
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
//LIBRARY FUNCTIONS
var pageHandlers = {};
function addPageController(n, c, e) {
pageHandlers[n] = {
"Condition": c,
"Event": e
};
}
function escapeHtml(a) {
return a.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}
function randStr(len) {
var r = "";
for (var i = 0; i < len; i++) {
var chr = Math.round(Math.random() * 50);
if (chr > 25) chr = chr + 6;
r = r + String.fromCharCode(65 + chr);
}
return r;
}
//END LIBRARY FUNCTIONS
//Product page:
addPageController("keywords", function(l) {
return l.substring(0, 3) == "/p/";
}, function() {
console.log("Keywords executed");
//Add keywords
var elm = document.getElementById("product-details");
if (elm !== null) {
var addInfo = document.createElement("li");
var keywords = document.head.querySelector("meta[name=keywords]");
if (keywords !== null) {
var tmp = keywords.content.split(/,\s?/g);
keywords = "<i>";
for (var i = 0, l = tmp.length - 2; i < tmp.length; i++) {
keywords += "<a href=\"https://marketplace.secondlife.com/products/search?utf8=%E2%9C%93&search%5Bcategory_id%5D=&search%5Bmaturity_level%5D=GMA&search%5Bkeywords%5D=" + escape(tmp[i]) + "\">" + escapeHtml(tmp[i]) + "</a>" + (i > l ? "" : ", ");
}
keywords += "</i>";
} else
keywords = "<i>None</i>";
addInfo.style.wordWrap = "break-word";
addInfo.innerHTML = "<B>Tags:</B><br/>" + keywords + "<br/>";
elm.children[0].appendChild(addInfo);
}
});
//Redeliver button:
addPageController("redeliver", function(l) {
return l.substring(0, 3) == "/p/";
}, function() {
console.log("Keywords executed");
//Add keywords
function getOrdersPageUrl(){
var notices = document.querySelectorAll("#flash_notice a");
for(var notice of notices){
if(notice.href.indexOf("/orders/") !== -1 || notice.href.indexOf("/gifts/") !== -1)
return notice.href;
}
return null;
}
function getRedeliveryUrl(url, pid, callback){
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function() {
var parser = new DOMParser(),
doc = parser.parseFromString(xhr.responseText, "text/html");
if(url.indexOf("/orders/") !== -1){
var elms = doc.querySelectorAll("#order #shopping-cart .line-item-row");
for(var found of elms){
if(found.querySelector(".line-item-gift").children.length == 0){
if(found.querySelector("h4 a").href.split("/").pop() == pid){
var links = found.querySelectorAll("a");
for(var link of links){
if(link.href.indexOf("/redeliver") !== -1){
callback(link.href);
return;
}
}
}
}
}
}else if(url.indexOf("/gifts/") !== -1){
var elms = doc.querySelectorAll("#order #gifts");
for(var found of elms){
if(found.querySelector("h4 a").href.split("/").pop() == pid){
var links = found.querySelectorAll("a");
for(var link of links){
if(link.href.indexOf("/redeliver") !== -1){
callback(link.href);
return;
}
}
}
}
}
callback(null);
};
xhr.send(null);
}
function executeRedelivery(url, callback){
var token_name = document.head.querySelector('meta[name="csrf-param"]'),
token_value = document.head.querySelector('meta[name="csrf-token"]');
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.onload = function() {
callback(null);
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send("_method=post&"+encodeURIComponent(token_name.content)+"="+encodeURIComponent(token_value.content));
}
function isRedeliverable(){
var poos = document.body.querySelectorAll(".cmt");
for(var poo of poos){
if(poo.textContent == "Copy"){
for(var cls of poo.classList)
if(cls == "permitted")
return true;
}
}
return false;
}
var redeliverUrl = getOrdersPageUrl();
if(redeliverUrl !== null && isRedeliverable()){
var btn = document.createElement("button");
btn.setAttribute("class", "button cart-button");
btn.style.backgroundImage = "linear-gradient(#00b7ea,#009ec3)";
btn.textContent = "Redeliver";
btn.addEventListener("click", function(){
btn.textContent = "Working...";
btn.setAttribute("disabled", "disabled");
getRedeliveryUrl(redeliverUrl, document.location.pathname.split("/").pop(), function(url){
executeRedelivery(url, function(){
btn.textContent = "Redeliver";
btn.removeAttribute("disabled");
});
});
});
document.getElementById("add-to-cart-form").appendChild(btn);
}
//getRedeliveryUrl(getOrdersPageUrl(), 15237316, function(a){executeRedelivery(a);});
});
//Add clickable reviewer names
addPageController("reviewerNames", function(l) {
return l.substring(0, 3) == "/p/";
}, function() {
//Fix reviewer names
var rnf = document.getElementsByClassName("review-author");
for (var i = 0; i < rnf.length; i++) {
var tmp = rnf[i].getElementsByTagName("a")[0];
tmp.setAttribute("href", "https://my.secondlife.com/" + (tmp.innerText.replace(/\s/g, ".")));
}
var rnf = document.getElementsByClassName("comment-author");
for (var i = 0; i < rnf.length; i++) {
var tmp = rnf[i].getElementsByTagName("strong")[0];
var who = tmp.innerText,
lnk = document.createElement("a");
tmp.innerText = "";
lnk.innerText = who;
lnk.setAttribute("href", "https://my.secondlife.com/" + (who.replace(/\s/g, ".")));
tmp.appendChild(lnk);
}
});
//Add search parameters
addPageController("searchParams", function(l) {
return true;
}, function() {
var srch = document.getElementById("new_search");
function addParameter(name, label, options) {
var p = document.createElement("p"),
rnd = randStr(16),
lab = document.createElement("label"),
el = null;
if (typeof(options) == "undefined") {
el = document.createElement("input");
el.setAttribute("size", 30);
el.setAttribute("text", "text");
} else if (typeof(options) == "object") {
el = document.createElement("select");
if (Array.isArray(options)) {
for (var i = 0; i < options.length; i++) {
var tmp = document.createElement("option");
tmp.setAttribute("value", options[i]);
tmp.innerText = options[i];
el.appendChild(tmp);
}
} else {
for (var i = 0, x = Object.keys(options); i < x.length; i++) {
var tmp = document.createElement("option");
tmp.setAttribute("value", x[i]);
tmp.innerText = options[x[i]];
el.appendChild(tmp);
}
}
}
lab.innerText = label;
lab.setAttribute("for", rnd);
p.appendChild(lab);
el.setAttribute("name", name);
el.setAttribute("id", rnd);
p.appendChild(el);
srch.appendChild(p);
}
srch.appendChild(document.createElement("br"));
addParameter("search[sort]", "Sort by", {
"sales_rank_asc": "Best Selling",
"relevance_desc": "Relevance",
"created_at_desc": "Age: Newest First",
"created_at_asc": "Age: Oldest First",
"price_asc": "Price: Low to High",
"price_desc": "Price: High to Low",
"name_asc": "Name: A to Z",
"name_desc": "Name: Z to A",
"prim_count_asc": "Prims: Low to High",
"prim_count_desc": "Prims: High to Low",
"average_rating_desc": "Rating: High to Low"
});
addParameter("search[per_page]", "Results", [96, 48, 24, 12, 8, 4, 2]);
//Permission based search
var perms = document.createElement("p");
//Copy
var tmp = document.createElement("input"),
tmpn = randStr(16);
tmp.setAttribute("type", "checkbox");
tmp.setAttribute("id", tmpn);
tmp.style.clear = "initial";
tmp.setAttribute("name", "search[copy_permission]");
perms.appendChild(tmp);
var tmp = document.createElement("label");
tmp.setAttribute("for", tmpn);
tmp.style.clear = "initial";
tmp.innerText = "\xa0Copy\xa0\xa0";
perms.appendChild(tmp);
//Modify
var tmp = document.createElement("input"),
tmpn = randStr(16);
tmp.setAttribute("type", "checkbox");
tmp.setAttribute("id", tmpn);
tmp.style.clear = "initial";
tmp.setAttribute("name", "search[modify_permission]");
perms.appendChild(tmp);
var tmp = document.createElement("label");
tmp.setAttribute("for", tmpn);
tmp.innerText = "\xa0Modify\xa0\xa0";
tmp.style.clear = "initial";
perms.appendChild(tmp);
//Transfer
var tmp = document.createElement("input"),
tmpn = randStr(16);
tmp.setAttribute("type", "checkbox");
tmp.setAttribute("id", tmpn);
tmp.style.clear = "initial";
tmp.setAttribute("name", "search[transfer_permission]");
perms.appendChild(tmp);
var tmp = document.createElement("label");
tmp.setAttribute("for", tmpn);
tmp.style.clear = "initial";
tmp.innerText = "\xa0Transfer";
perms.appendChild(tmp);
//Price sorting
perms.appendChild(document.createElement("br"));
var tmp = document.createElement("input");
tmp.setAttribute("type", "number");
tmp.setAttribute("name", "search[price_low]");
tmp.style.clear = "initial";
tmp.style.width = "55px";
tmp.style.height = "18px";
tmp.style.padding = "0 2px";
perms.appendChild(tmp);
var tmp = document.createElement("label");
tmp.style.clear = "initial";
tmp.innerText = "\xa0to\xa0";
perms.appendChild(tmp);
var tmp = document.createElement("input");
tmp.setAttribute("type", "number");
tmp.setAttribute("name", "search[price_high]");
tmp.style.clear = "initial";
tmp.style.width = "55px";
tmp.style.height = "18px";
tmp.style.padding = "0 2px";
perms.appendChild(tmp);
srch.appendChild(perms);
});
//Auto continue
addPageController("skipContinue", function(l) {
return l == "/orders/checkout/continue";
}, function() {
document.getElementsByClassName("button-to")[0].submit();
});
//Auto continue
addPageController("removeAds", function(l) {
return true;
}, function() {
var elms = document.getElementsByClassName("advertisement");
//Removing a element by .remove() apparently removes it from the array as well
//Work around: loop while elms.length is greater than 0 and remove entry [0]
while (elms.length > 0) {
elms[0].remove();
}
});
//Infinite scroll
addPageController("infiniteScroll", function(l) {
var tmp = document.location.pathname.substring(1).split("/");
if (tmp.pop() == "search") {
return true;
}
return false;
}, function() {
function getProducts(page, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", document.location.href + "&search%5Bpage%5D=" + page, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var parser = new DOMParser(),
doc = parser.parseFromString(xhr.responseText, "text/html");
callback(doc.querySelectorAll(".product-listing")[0].children);
}
};
xhr.send(null);
}
function getPageCount() {
var pagination = document.querySelectorAll(".pagination")[0].children;
if (pagination.length > 2) {
return parseInt(pagination[pagination.length - 2].innerText);
}
return 1;
}
function parseQS(input) {
if (typeof(input) == "undefined") {
input = document.location.search.substring(1).split("&");
} else {
if (input.charAt(0) == "?")
input = input.substring(1);
input = input.split("&");
}
var output = {};
for (var i = 0; i < input.length; i++) {
var tmp = input[i].indexOf("=");
output[decodeURIComponent(input[i].substring(0, tmp))] = decodeURIComponent(input[i].substring(tmp + 1));
}
return output;
}
function getCurrentPage() {
var qs = parseQS();
if (typeof(qs["search[page]"]) != "undefined") {
return parseInt(qs["search[page]"]);
}
return 1;
}
function appendElements(src, dst) {
for (var i = 0; i < src.length; i++) {
dst.appendChild(src[i]);
}
}
var currentPage = getCurrentPage(),
totalPages = getPageCount(),
listings = document.querySelectorAll(".product-listing")[0],
paginationFooter = document.querySelectorAll(".footer-paginate")[0],
isLoadingNewPage = false;
paginationFooter.innerText = "Working...";
document.addEventListener('scroll', function(event) {
if (isLoadingNewPage)
return;
if (document.body.scrollHeight <= window.scrollY + window.innerHeight + 32) {
if (currentPage < totalPages) {
isLoadingNewPage = true;
paginationFooter.innerText = "Loading page " + (currentPage + 1) + "...";
getProducts(++currentPage, function(els) {
appendElements(els, listings);
isLoadingNewPage = false;
paginationFooter.innerText = "Working...";
});
} else if (currentPage == totalPages) {
//Handle end of page
paginationFooter.innerText = "End of results.";
currentPage++;
}
}
});
});
//Initializer
function main() {
var path = location.pathname;
for (var i = 0, x = Object.keys(pageHandlers), l = x.length; i < l; i++) {
if (pageHandlers[x[i]].Condition(path))
pageHandlers[x[i]].Event();
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment