// ==UserScript==
// @name SteamGifts: Open in pop-in
// @namespace lainscripts_steamgifts_steam_popin
// @description Opens various links in pop-in window. This includes Giveaway links, links to Steam store and new messages.
// @include *://www.steamgifts.com/*
// @include *://steamdb.info/*
// @version 4.0
// @grant none
// @run-at document-start
// ==/UserScript==
/* jshint esnext: true */
(()=>{
'use strict';
var empty = [],
truncSubdomains = (s) => (s.match(/[^.]+\.[^.]+$/i) || empty)[0],
domains = {
SteamGifts : 'steamgifts.com',
SteamDB : 'steamdb.info'
},
inIframe = function() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
},
runCodeFor = {
[domains.SteamGifts]: function() {
var isSteamGifts = (s) => truncSubdomains(s) === domains.SteamGifts,
isKnown = /^\/giveaways\/|\/(about|account|discussions?|giveaway|go\/comment|legal|messages|roles|support|user)(\/|$)/i,
isAboutLegal = /^\/(about|legal)\//i,
isDiscussion = /^\/(discussion|go\/comment)\//i,
isSearch = /^\/giveaways\/search/i,
isSteamStore = /^store\.steampowered\.com$/i,
isSteamPackage = /\/(app|sub)\//i,
firstLevel = /^\/[^/]+/i;
var markAsOutdatedClasses = ['form__sync-default','table__remove-default'],
ofClass = (c) => { return this.classList && this.classList.contains(c); },
getFirstLevel = (pathname) => (firstLevel.exec(pathname) || empty)[0];
// attach styles before document displayed
(function(style) {
style.id = 'popinStyles';
style.type = 'text/css';
document.head.appendChild(style);
style.sheet.insertRule('@keyframes spinnerFrames {0% {transform:rotate(0deg)} 100% {transform:rotate(-360deg)}}',0);
style.sheet.insertRule('#popinSpinner svg {'+
['animation: spinnerFrames linear 1s',
'animation-iteration-count: infinite',
'transform-origin: 50% 50%'].join(';')+'}', 0);
style.sheet.insertRule('#popinBackground {'+
['box-sizing: border-box',
'position: fixed',
'top: 0',
'left: 0',
'padding: 35px 40px',
'width: 100%',
'height: 100%',
'background-color: rgba(0,0,0,0.85)',
'z-index: 1000000',
'display: none'].join(';')+'}', 0);
style.sheet.insertRule('#popinFrame {'+
['position: relative',
'width: 100%',
'height: 100%',
'border: #aaa 2px solid',
'border-radius: 3px',
'background-color: rgba(0,0,0,0.33)',
'z-index: 1'].join(';')+'}', 0);
style.sheet.insertRule('#popinSpinner {'+
['position: fixed',
'top: 50%',
'left: 50%',
'margin-top: -50px',
'margin-left: -50px',
'width: 100px',
'height: 100px',
'z-index: 2'].join(';')+'}', 0);
// modify navigation elements when various pages are loaded in the pop-in
if (inIframe()) {
// make page cover the entire window height and reset padding with margin if someone changed them
style.sheet.insertRule('html, body {'+['height: 100%',
'padding: 0!important',
'margin: 0!important'].join(';')+'}', 0);
// hide some useless for pop-in blocks
style.sheet.insertRule(['.offer__outer-wrap',
'.footer__outer-wrap'].join(',')+' {display: none}', 0);
// make content as heigh as the page + fix for the giveaway pages (they have 1 extra block)
if (getFirstLevel(document.location.pathname) === '/giveaway') {
style.sheet.insertRule('.page__outer-wrap {min-height: calc(100% - 50px - 208px)}', 0);
} else {
style.sheet.insertRule('.page__outer-wrap {min-height: calc(100% - 50px)}', 0);
}
// display floating account button in the top-right corner (except for the account page)
if (getFirstLevel(document.location.pathname) === '/account') {
style.sheet.insertRule('header {display: none!important}', 0);
} else {
style.sheet.insertRule(['.nav__left-container',
'.nav__button-container--notification'].join(',')+' {display: none}', 0);
style.sheet.insertRule('header > nav {padding: 0!important}', 0);
style.sheet.insertRule('.sidebar {top: 25px!important}', 0);
style.sheet.insertRule('header {'+
['top: -5px!important',
'right: 0',
'padding: 0',
'position: fixed',
'width: auto!important',
'background: none',
'z-index: 100'].join(';')+'}', 0);
}
// hide Browse category in frames on pages with giveaways lists
if (getFirstLevel(document.location.pathname) === '/giveaways')
style.sheet.insertRule('.sidebar__heading:first-of-type, .sidebar__navigation:first-of-type {display: none}', 0);
// hide sidebar and padding at the left side for discussions
if (getFirstLevel(document.location.pathname) === '/discussion') {
style.sheet.insertRule('.sidebar {display: none}', 0);
style.sheet.insertRule('.widget-container>div:not(:first-child) {'+
['padding-left: 0!important',
'margin-left: 0!important',
'border-left: none!important'].join(';')+'}', 0);
style.sheet.insertRule('.page__heading__breadcrumbs a {pointer-events: none}');
}
}
})(document.createElement('style'));
function createSVGSpinner(out_radius, in_radius, width, number_of_lines, period, direction) {
var num = number_of_lines, shift = out_radius + width / 2, step = 0;
var nameSpace = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(nameSpace, 'svg');
svg.setAttribute('version', '1.1');
svg.setAttribute('x', '0px');
svg.setAttribute('y', '0px');
svg.setAttribute('width', (2 * shift) + 'px');
svg.setAttribute('height', (2 * shift) + 'px');
svg.setAttribute('style', ['stroke: #fff',
'stroke-width: ' + width + 'px',
'stroke-linecap: round'].join(';'));
var g = document.createElementNS(nameSpace, 'g');
svg.appendChild(g);
var line;
while (num--) {
if (direction >= 0) step = num; else step = number_of_lines - num;
line = document.createElementNS(nameSpace, 'line');
line.setAttribute('x1', shift + Math.cos(2 * Math.PI / number_of_lines * num) * in_radius);
line.setAttribute('y1', shift + Math.sin(2 * Math.PI / number_of_lines * num) * in_radius);
line.setAttribute('x2', shift + Math.cos(2 * Math.PI / number_of_lines * num) * out_radius);
line.setAttribute('y2', shift + Math.sin(2 * Math.PI / number_of_lines * num) * out_radius);
line.setAttribute('stroke-opacity', 1 / (num + 1));
g.appendChild(line);
}
return svg;
}
function popinObj() {
/* jshint validthis: true */
var _self = this;
_self.childPopin = null;
var spinner = document.createElement('div');
spinner.appendChild(createSVGSpinner(45, 25, 10, 10, 1.8, -1));
spinner.id = 'popinSpinner';
var popin = document.createElement('div');
popin.id = 'popinBackground';
var ifrm = null;
var openFrame = function (a) {
var url = a.href;
var ifr = document.createElement('iframe');
ifr.id = 'popinFrame';
ifr.onload = function() {
spinner.style.zIndex = 0;
if (isSteamGifts(a.hostname))
_self.childPopin = this.contentDocument.popin;
};
// Hide spinner if page keeps loading after a 5 seconds
setTimeout(function() {
if (popin.style.display === 'block')
spinner.style.zIndex = 0;
}, 5000);
url = url.replace('http:','https:').replace('/store.steampowered.com/','/steamdb.info/');
ifr.src = url;
return ifr;
};
popin.appendChild(spinner);
document.body.appendChild(popin);
_self.show = function (a) {
document.body.style.overflowY = 'hidden';
ifrm = openFrame(a);
popin.appendChild(ifrm);
popin.style.display = 'block';
};
_self.hide = function () {
var onUpdateParent = false;
if (ifrm.src.search('/www.steamgifts.com/') > -1) {
var ifDoc = ifrm.contentDocument;
// propagate outdated state of parent document and reload it if applicable
var deepPopin = _self.childPopin;
while(deepPopin && deepPopin.childPopin && !deepPopin.isOutdated())
deepPopin = deepPopin.childPopin;
if (deepPopin && deepPopin.isOutdated()) {
document.popin.setIsOutdated();
// only pages with active giveaways should be reloaded
if (document.location.pathname === '/' || isSearch.test(document.location.pathname)) {
location.reload();
onUpdateParent = true;
}
} else
(()=>{
// update points and notifications in parent window
var points = '.nav__points';
var notifs = '.nav__button[href^="/giveaways/"],.nav__button[href="/messages"]';
((pin, pout) => {
if (!pin || !pout.length) return;
var i = pout.length;
while (i--)
pout[i].textContent = pin.textContent;
})(ifDoc.querySelector(points), document.querySelectorAll(points));
((pin, pout) => {
if (!pin.length || !pout.length) return;
var replaceNavNotification = function(pTo, to, from) {
if (to && from)
to.textContent = from.textContent;
if (!to && from)
pTo.appendChild(from.cloneNode(true));
if (to && !from)
pTo.removeChild(to);
};
var i = pout.length, j, nnSelector = '.nav__notification';
while (i--) {
j = pin.length;
while (j--)
if (pout[i].href === pin[j].href) {
pout[i].parentNode.className = pin[j].parentNode.className;
replaceNavNotification(pout[j],
pout[j].querySelector(nnSelector),
pin[j].querySelector(nnSelector));
}
}
})(ifDoc.querySelectorAll(notifs), document.querySelectorAll(notifs));
})();
}
if (!onUpdateParent) {
document.body.style.overflowY = 'auto';
popin.removeAttribute('style');
spinner.removeAttribute('style');
}
_self.childPopin = null;
ifrm.parentNode.removeChild(ifrm);
ifrm = null;
};
popin.onclick = _self.hide;
// parent document state checker (in some cases it's only logical to refresh parent page when popin closed)
var isParentOutdated = false;
_self.isOutdated = function() {
return isParentOutdated;
};
_self.setIsOutdated = function() {
isParentOutdated = true;
};
}
document.addEventListener ("DOMContentLoaded", function() {
document.popin = new popinObj();
document.body.addEventListener('click', function(e) {
var t = e.target;
// handle only LMB clicks
if (e.which !== 1)
return;
if (markAsOutdatedClasses.some(ofClass, t) ||
markAsOutdatedClasses.some(ofClass, t.parentNode))
document.popin.setIsOutdated();
// try to drill up to an A element if present
while (t.parentNode && !t.href)
t = t.parentNode;
// if user clicked a link - try to handle it properly
if (t.href) {
// handle known links - specific categories on SteamGifts and links to packages on Steam
if ((isSteamGifts(t.hostname) && isKnown.test(t.pathname) && !isSearch.test(t.pathname)) ||
(isSteamStore.test(t.hostname) && isSteamPackage.test(t.pathname))) {
// do not handle links to pages of the same category as the current one
if ((document.location.pathname !== '/giveaways' &&
(getFirstLevel(t.pathname) === getFirstLevel(document.location.pathname))) ||
(isAboutLegal.test(t.pathname) && isAboutLegal.test(document.location.pathname)) ||
(isDiscussion.test(t.pathname) && isDiscussion.test(document.location.pathname)))
return;
// Workaround for EasySteamGifts: do not handle join button if present
if (t.parentNode.classList.contains('esg-join'))
return;
// if we are still here, then
document.popin.show(t);
e.preventDefault();
} else {
// handle unknown links when page is loaded in a frame
if (inIframe())
t.target = '_blank';
}
}
});
});
},
[domains.SteamDB] : function(){
if (!inIframe())
return;
var isSteamDB = (s) => truncSubdomains(s) === domains.SteamDB;
document.addEventListener ("DOMContentLoaded", function() {
document.body.addEventListener('click', function(e) {
var t = e.target;
// handle only LMB clicks
if (e.which !== 1)
return;
// try to drill up to an A element if present
while (t.parentNode && !t.href)
t = t.parentNode;
// open all third-party domains in a new tab
if (t.href && !isSteamDB(t.hostname))
t.target = '_blank';
});
});
}
};
runCodeFor[truncSubdomains(document.location.hostname)]();
})();