SteamGifts: Look for similar

Adds a button to search for a similar giveaways.

As of 2016-04-26. See the latest version.

// ==UserScript==
// @name         SteamGifts: Look for similar
// @namespace    lainscripts_sg_look_for_similar
// @version      2.0
// @description  Adds a button to search for a similar giveaways.
// @author       lainverse
// @match        *://www.steamgifts.com/
// @match        *://www.steamgifts.com/giveaway/*
// @match        *://www.steamgifts.com/giveaways/*
// @grant        none
// ==/UserScript==
/* jshint esnext: true */
(function() {
    'use strict';

    (function(st) {
        st.id = 'sg_look_for_similar_stylesheet';
        document.head.appendChild(st);
        st.sheet.insertRule('.featured__heading .fa-search, .featured__heading .lfsList { font-size: 1.5em }', 0);
        st.sheet.insertRule('.lfsList { display: inline-block }',0);
        st.sheet.insertRule('.lfsOpts { display: none; font-weight: bold }',0);
        st.sheet.insertRule('.lfsList:hover > .lfsOpts { display: inline-block }',0);
        st.sheet.insertRule('.lfsList:hover > .fa { display: none }',0);
    })(document.createElement('style'));

    var clHeader = '.giveaway__heading__name,.table__column__heading,.featured__heading__medium',
        clHeaderIns = ['giveaway__heading__name','table__column__heading'],
        caHeaderSib = ['giveaway__heading__thin','featured__heading__small'],
        loc = location.pathname.replace(/\/search$/i, '');

    if (loc === '/') loc = '/giveaways';
    if (/\/giveaway\//.test(loc)) loc = '/giveaway';

    var 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.entered],
                      giveaways: [locs.local, locs.entered],
                      entered  : [locs.local, locs.giveaways],
                      default  : [locs.local]};

    if (!(type in contSearch)) type = 'default';

    function ofClass(itm, lst) {
        var i = lst.length;
        while(i--)
            if (itm.classList.contains(lst[i]))
                return true;
        return false;
    }

    function createLFSLink (href, id, title) {
        console.log(href, id, title);
        var lnk = document.createElement('a'),
            ico = document.createElement('i');
        ico.className = 'giveaway__icon fa fa-search';
        lnk.className = 'lfsOpts';
        lnk.textContent = title;
        lnk.insertBefore(ico, lnk.firstChild);
        lnk.href = href;
        lnk.id = id;
        return lnk;
    }

    function appendLFSLink(he) {
        var heLink = he.pathname ? he : he.querySelector('a'),
            heText = he.textContent;

        // Do not parse invite-only giveaways and non-giveaway links
        if (!he || heText == 'Invite Only' ||
            (heLink && !/^\/giveaway\//.test(heLink.pathname)))
            return;

        // Do not add LFS button to a node with already existing LFS button
        if (he.parentNode.querySelector('.lfsList'))
            return;

        // Append LFS buttons
        var ovi = document.createElement('i'),
            ovl = document.createElement('div');

        ovi.className = 'giveaway__icon fa fa-search';
        ovl.className = 'lfsList';
        ovl.appendChild(ovi);

        heText = '/search?q=' +
            encodeURIComponent(heText
                               .replace(/\s\(\d+\sCopies\)$/i,'')
                               .replace(/\.\.\.$/,''));

        contSearch[type].forEach(function(el) {
            ovl.appendChild(createLFSLink(el.loc + heText, el.id, el.title));
        });

        while (he.nextElementSibling && ofClass(he.nextElementSibling, caHeaderSib))
            he = he.nextElementSibling;
        if (ofClass(he, clHeaderIns))
            he.appendChild(ovl);
        else
            he.parentNode.insertBefore(ovl, he.nextElementSibling);
    }

    (function(he) {
        var i = he.length;
        while (i--)
            appendLFSLink(he[i]);
    })(document.querySelectorAll(clHeader));

    function parseAddedNodes(m, he) {
        var i = m.length;
        while (i--) {
            if (!m[i].querySelector)
                continue;
            var hes = m[i].querySelectorAll(clHeader), j = hes.length;
            while (j--)
                appendLFSLink(hes[j]);
        }
    }

    var obs = new MutationObserver(function(ms){
        var i = ms.length;
        while (i--)
            if (ms[i].addedNodes.length)
                parseAddedNodes(ms[i].addedNodes);
    });

    obs.observe(document.body, {childList: true, subtree: true});
})();