// ==UserScript==
// @name SteamGifts: Look for Similar
// @namespace lainscripts_sg_look_for_similar
// @version 4.3
// @description Adds buttons to search for similar giveaways and removes redirect from existing links.
// @author lainverse
// @match *://www.steamgifts.com/
// @match *://www.steamgifts.com/giveaway/*
// @match *://www.steamgifts.com/giveaways/*
// @match *://www.steamgifts.com/discussion/*
// @grant none
// ==/UserScript==
// jshint esversion: 8
// jshint unused: true
(function () {
'use strict';
// NodeList iterator polyfill (mostly for Safari)
// https://jakearchibald.com/2014/iterators-gonna-iterate/
if (!NodeList.prototype[Symbol.iterator])
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
(function (st) {
st.id = 'sg_look_for_similar_stylesheet';
document.head.appendChild(st);
st.sheet.insertRule(
'.featured__heading .fa-search {' + (
'font-size: 1.8em'
) + '}', 0);
st.sheet.insertRule(
'.lfsList {' + (
'position: relative;' +
'display: inline-block;' +
'margin-right: -5px'
) + '}', 0);
st.sheet.insertRule(
'.lfsMenu { ' + (
'display: none;' +
'position: absolute;' +
'min-width: max-content;' +
'background-color: #f9f9f9;' +
'box-shadow: 0px 4px 8px -2px #666;' +
'padding: 5px 0px'
) + '}', 0);
st.sheet.insertRule(
'.lfsOpts {' + (
'display: block;' +
'font-weight: bold;' +
'min-width: max-content;' +
'white-space: nowrap;' +
'color: rgb(70, 86, 112) !important;' +
'padding: 5px 15px 5px 5px;' +
'text-decoration: none !important;' +
'font-size: 12px !important'
) + '}', 0);
st.sheet.insertRule(
'.lfsOpts:hover {' + (
'background-color: #ddd'
) + '}', 0);
st.sheet.insertRule(
'.lfsList:hover > .lfsMenu {' + (
'display: inline-block;' +
'z-index: 100'
) + '}', 0);
st.sheet.insertRule(
'.lfsList > .fa, .lfsList > a {' + (
'display: block;' +
'padding: 2px 5px 0 5px;' +
'margin-left: 0'
) + '}', 0);
st.sheet.insertRule(
'.lfsList:hover .fa {' + (
'color: rgb(70, 86, 112)'
) + '}', 0);
st.sheet.insertRule(
'.lfsList:hover {' + (
'background-color: #f9f9f9;' +
'box-shadow: 0px 4px 8px 0px #666;'
) + '}', 0);
})(document.createElement('style'));
let loc = (location.pathname
.replace(/^\/$/, '/giveaways')
.replace(/^\/giveaway\/.*$/, '/giveaway')
.replace(/\/search$/i, '')
.replace(/^\/discussion\/.*/i, '/discussion')),
type = loc.match(/\/([^/]+)\/?$/i)[1],
locs = {
local: {
title: 'Here',
id: 'lfsInLocal',
loc: loc
},
entered: {
title: 'Entered',
id: 'lfsInEntered',
loc: '/giveaways/entered'
},
giveaways: {
title: 'All Giveaways',
id: 'lfsInGiveaways',
loc: '/giveaways'
}
},
contSearch = {
giveaway: [locs.giveaways, locs.entered],
won: [locs.local, locs.entered],
giveaways: [locs.local, locs.entered],
entered: [locs.local, locs.giveaways],
discussion: [locs.giveaways, locs.entered],
default: [locs.local]
},
clHeader = (
'.giveaway__heading__name,' +
'.featured__heading__medium,' +
'a.table__column__heading,' +
'.markdown td > strong > a[href]'
),
clHeaderIns = [
'giveaway__heading__name',
'table__column__heading'
],
caHeaderSib = [
'giveaway__heading__thin',
'featured__heading__small'
];
if (!(type in contSearch)) type = 'default';
const ofClass = (itm, lst) => lst.some(cls => itm.classList.contains(cls));
const creator = document.createElement('outer');
creator.make = html => {
creator.innerHTML = html;
return creator.removeChild(creator.children[0]);
};
function appendLFSLink(he) {
let heLink = he.pathname ? he : he.querySelector('a'),
heText = he.textContent;
// Do not parse invite-only giveaways, non-giveaway and not-steam links
if (heText == 'Invite Only' ||
(heLink &&
!/^\/giveaway\//i.test(heLink.pathname) &&
!/^store\.steampowered\.com|steamcommunity.com$/i.test(heLink.hostname)))
return;
// Do not add LFS button to a node with already existing LFS button
if (he.parentNode.querySelector('.lfsList'))
return;
// Append LFS buttons
let ovl = creator.make('<div class="lfsList"></div>'), //document.createElement('div'),
mnu = creator.make('<div class="lfsMenu"></div>');
ovl.appendChild(
he.parentNode.querySelector('a[href^="/giveaways/search?"]') ||
creator.make('<i class="giveaway__icon fa fa-search" />')
);
ovl.appendChild(mnu);
heText = '/search?q=' + encodeURIComponent(
heText
.replace(/(\s\(\d+(P|\sCopies)\))+$/i, '')
.replace(/\.\.\.$/, '')
);
for (let el of contSearch[type])
mnu.appendChild(
creator.make(`<a class="lfsOpts" id="${el.id}" href="${el.loc + heText}">${el.title}</a>`)
);
while (he.nextElementSibling && ofClass(he.nextElementSibling, caHeaderSib))
he = he.nextElementSibling;
if (ofClass(he, clHeaderIns))
he.appendChild(ovl);
else
he.parentNode.insertBefore(ovl, he.nextElementSibling);
}
for (let header of document.querySelectorAll(clHeader))
appendLFSLink(header);
(new MutationObserver(function (ms) {
for (let m of ms)
for (let node of m.addedNodes)
if (node.nodeType === Node.ELEMENT_NODE)
for (let header of node.querySelectorAll(clHeader))
appendLFSLink(header);
})).observe(document.body, {
childList: true,
subtree: true
});
document.addEventListener('click', e => {
let lnk = e.target.closest('a');
if (!lnk) return;
if (lnk.pathname === '/redirect' && lnk.search.startsWith('?url='))
lnk.href = decodeURIComponent(lnk.search.slice(5));
});
})();