Links block for Lego.com

Adds a block to each product on Lego.com containing links to external brick websites, including Brickset, Bricklink, Rebrickable, Brickowl, and BrickEconomy

// ==UserScript==
// @name         Links block for Lego.com
// @license      MIT
// @version      0.1
// @description  Adds a block to each product on Lego.com containing links to external brick websites, including Brickset, Bricklink, Rebrickable, Brickowl, and BrickEconomy
// @author       azuravian
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @match        https://www.lego.com/*
// @grant        none
// @run-at       document-end
// @namespace    https://greasyfork.org/users/1025348
// ==/UserScript==

waitForKeyElements(
    '[data-test*="product-attributes"]',
    start);

function start() {
  const prodattrdiv = document.querySelector('[data-test*="product-attributes"]');
  const item = prodattrdiv.querySelector('[data-test*="item-value"]');
  const itemnum = item.innerText;
  const last_attr = prodattrdiv.lastChild;
  const links_list = last_attr.cloneNode(true);
  links_list.removeChild(links_list.firstChild);
  links_list.removeChild(links_list.firstChild);
  const myspan = links_list.getElementsByTagName('span')[0]
  const spanclass = myspan.querySelector('span').getAttribute('class');
  myspan.removeChild(myspan.firstChild);
  myspan.setAttribute('style', 'text-align: left');


  const br1 = document.createElement('br');
  const br2 = document.createElement('br');
  const br3 = document.createElement('br');
  const br4 = document.createElement('br');

  var brickset = document.createElement('span');

  brickset.setAttribute('class', spanclass);
  var bricksetimg = document.createElement('img');
  var bricksetlink = document.createElement('a');
  bricksetimg.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAA1VBMVEVHcEymLh20giaXhS/AMyUXXSyoOC1QTzp3Vy/AMB3OMyIgXy6Sfy4ljTmXfTDYMCLVMSOkhSXaMB5pVSbxuwvdrBIvYTgmXzI3cj8qcDYyhD4tijsYXS24OCjUphaqLh4rhDmUMSjdrRiaijAgYjDwMCCdLR8tlj7jrhT4wQbxtiDoLyDOMR00tkoxnEHuQSDYoxoxnUI0r0a9mR7/xwLIoByRLR7gOx7hLh4lfTWwKx1GijhWozoohDkkcjMdaC+iiygwoEBxgyMsiDyqjiYQnUGYoyyIwEmGAAAAI3RSTlMA+irxjPs1CBf9uOOXzkXYzdP8+/3JP5kwkIfa7mV74LKCtt0AJoAAAAEQSURBVCiR1c/bTsJAFIXhSSmlAwihLWo0gFqYQ2dKS8+lBxTU938kt7QYxHiv/93Kl9nJIPT/0hVNP1/K2VINIbKx1tLU4tx66p0QZ0II+diMEeNQ3LzVZ30p8XZLFqrWQ8p86sdh+GpP5wqclKmXSoyx9Dy5mDAeMdCYRdyfoP4qM1IPSjEhie8wZvEo4ix8tjuAq5XIDEwSckQH+FOgBiEQYhZFWb4zx/Ftu8HZWLSYUBd6C0/YGSLU01TwLUnM6lBVdXcfO8y2J8NR+1NwswhcesjrOg/ccv8lTVeUvgSQG6zzXfcbAW7WFJzmuyWk/8A1+PJY9wIfKNimxcHFWXR7d0+POLi5vrTWf5G/0ge5ei2NLgiBHwAAAABJRU5ErkJggg==')
  bricksetlink.setAttribute('target', '_blank')
  bricksetlink.setAttribute('href', `https://brickset.com/sets/${itemnum}`)
  brickset.appendChild(bricksetlink)

  var bricklink = brickset.cloneNode(true)
  var rebrickable = brickset.cloneNode(true)
  var brickowl = brickset.cloneNode(true)
  var brickeconomy = brickset.cloneNode(true)

  var bricklinkimg = document.createElement('img');
  bricklinkimg.setAttribute('style', 'height:26px;width:26px')
  bricklinkimg.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAAe1BMVEVHcEwREREREREREREREREREREICxAREREREREaGBMRERERERERERERERERERERERERERERERH/00wNDhAAAQ3/2VD/zEbvyEr/xUF8Zyz+2nW0lEHjuUf+4oZiVTHt0nv/1WnlwWb/zmA+NRxsWSbDoj2TfDXCqmeCeWF8hizGAAAAEnRSTlMAHo0ILOD97zz+bMhatzHP1ec1JXjSAAABQUlEQVQokW2T67aCIBCFU8tCuxiDkIAKKdr7P+EZsI6X5faH6OfsYTHbw2GhS367H3aV3OMzwDM+7rAoe0CKgjyLtmXXGwHWDRVhcL5dk1UzdGSkclS4Hhh6z62j+AQp65QoUUJ1DKCIJ+8kKwDLhoC8dJUyIHnwvuAeSY9Ia1qWVPtVQwBOxwDTzuHbUg9OCDUISkuquh9kFT6iXN80vcNFKduG/UO08lQrpfFmZL2BSLn/AMvqegODMeecGo/WkH71DmWfcRfKWrb1SLaQh0vK8dO2dm3Lv5JyqKxs5S400lpplbS70Bg7KruAaac4fwUhU8oaMx+fP3g90TfKmPngfyPTP2gXI5uHrT3UYdjneA7SFJPevYTyMQHIlyH7BqzyAcN+8Fgn8IjR9DUYzSyHYhPPOdRJlMXb7C5/h2mnf8FjLS4twHqMAAAAAElFTkSuQmCC')
  var bricklinklink = bricklink.getElementsByTagName('a')[0]
  bricklinklink.appendChild(bricklinkimg)
  bricklinklink.innerHTML = bricklinklink.innerHTML + ' Bricklink'
  bricklinklink.setAttribute('href', `https://www.bricklink.com/v2/catalog/catalogitem.page?S=${itemnum}`)

  var rebrickableimg = document.createElement('img');
  rebrickableimg.setAttribute('style', 'height:26px;width:26px')
  rebrickableimg.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABoVBMVEVHcExKST84PDw5OTk6OjpBSEVwbFJjYkwuLS0pKipAQEAkJCQyMjPbynQcHBs0MzM4OzsVFxopNzccLTUmJSI1P0IAAAELEA5gBgynBgtMEBFzEh81FRQGFBoeHh9vDAvbGSTdKDHJKjI7BAQlAwLEyK0rKiijKC2dFxuSFhsOCwaKBQtwURTy0zLoxyH1zSH01UwVHB7euxrpxh710Rbyzy9PPwichSCzliP93CL41z7BozCLcxsEBAagggXLqR25mhuzlBLWtBDmzFTu2SqcghV5YgLGpA7m2hr/8h5eTAIsIgCOdQqGbQg3KgBMPAB7ZxD++x6ijDNgXUfNrhBIOQA+MQGjiBOEd1ILQbBXRgGsjxuTehQBBBzKqyeGgVKWfyYAECuIiFxmUgKmix4iISCvkx+Mim4aLVlqVwmqjA/EpB7HqCSrmkYxRX6XfAjBoRlxWwRdbH28nyK/nRDSsiPHpiPEpCB3ZhjGqTqWgjrjyG7t1GrlwjPUtkP821sAABAUFBWihxyyp15OQxyQf0RXSSBcTRFoWy9rWhViVB6jt1BHAAAAi3RSTlMANJXOn0IaX///u/+mHP//eP///+z/////////////////////DNP//////9uA484v8P////z///7+WLr+////////Rv////7////////////jvf//////2f///////0K5///////lm//////ja/7////m/P//n9j5////a7D//6Bd2KeLm225s+yihhSvdAAAAulJREFUeAFsTgMWw0AQrTtTxeuNnRr3P1rx1Gjs+ZMeTWfzxXIyQqvZbPp1a0AEHJnYbBG3u8n+YBgAgGa/b+KvDqZpfQcM23Z6EysXvg3b9raEGh4wzsWsM7BEAMPbCqk08e0gjOJYdaG7sPUUD8NIaJrwOCVB0AGa5Wj4QVgIWVooKlbXQk46lLmomooSUimqGkbS4Hg6X67tJ7e0kSRNK6QHVpZNcH88nq99+0adNAErha6JJumHRLJqUBuIovDUhXUJ9Ze6t8F3ieFMgkNSZoI7KQySrLv+7Q7lvJ7v+nV7vA6nz+nbmpu/tr5vb/uZd89evmBm4ty8R3CKki/gDAIQZH1UoXBEisbikOO4LxwjK05HIsmk0rHMIsjmoqwvxDrztGbevbq09OYLU1AD8VhMU+LxH4vgT9GD0hr2hHWdXy1Rf2mJg1BVyuVQqOytAFCt1SPJhkfX9WbA/fpNy2azcbCRRGk/VZt22OnmFNgTcF8yWLjaarVsHIOQDLVmsyn+pcBguEkgEgwsjdjx6xnwhZHlLw2Cm3g0A35NhgQWDEOigFKa+RyXVGVEtCYWp+/phrK5scyNBEkyLb601HrDcUwBqog0MZ5KOzTFTkAbN/CUNiGajtLqEgVUFGmMlXBYk3bfg8oeH3awo2g0zzpc+TmQhA2i9fuSJewugkfFfaLq0ykOK6RH97BqpxlSEPXSzeb44PAIDNbrbCL1RUbJZERBpVU7PXqq4OYjUCeefeMYVLpdxcET3evd3++f2Dk7vUUKqbq3r4mGae6CyrDr95f705PD/f2T0z2GY5gvbreaMMToaCSKXrA4KSpJj8lj18l+OT89O38A7qhQHVuiF+Nm+AKAy24CNQTDsqImzfgQUF2hZE/UtDAhkWu6qWii5zYE05xlnAPgRo2QxN6326v/vz0w0tA0qEtzXswB8G9tHS3sjGA2WEVWoWN0CtDIGJ8EVQZswNouPDzf162zq40NQw4AapnDZhU18fsAAAAASUVORK5CYII=')
  var rebrickablelink = rebrickable.getElementsByTagName('a')[0]
  rebrickablelink.appendChild(rebrickableimg)
  rebrickablelink.innerHTML = rebrickablelink.innerHTML + ' Rebrickable'
  rebrickablelink.setAttribute('href', `https://rebrickable.com/search/?show_printed=on&include_accessory=1&include_gear=1&q=${itemnum}`)

  var brickowlimg = document.createElement('img');
  brickowlimg.setAttribute('style', 'height:26px;width:26px')
  brickowlimg.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAAe1BMVEVHcEzX2NjV19fW2NjNz8+dnZ2trq7V1dXW2dnZ29y7urmUlJSYmJjFxcWamZmenp6jo6OamprU1tanp6aurq3UzsqLi4v19vPe4OF7enn/////4bb/05T/3Kj9sCi/gRVkRA3/vlqcahMAAQI8RlD45cwqHATwjw7zkC196lJXAAAAE3RSTlMAMrPJmlCLFf///5T9/9v//zP6OwrJdwAAARlJREFUeAG10NeCgjAQhWF7Jmyy0cikjEi18P5PuGEo2y89Vz9+Ulcv3Hqz3e7208Fuu90cxGIC0mT2xgcbGKa+I+j3T5RqP4oQ6wnFMEYweyESAk9KCRLmhqHl8bSyfNZZ5VayQG6M5jqqAdEp6YNRASCcHfhwjHpGjJYuxZWCC9JoTp90xHNOZVVXpdeZNdRUdVt6VJYRIxVt193uDTnji6rrupRRM+pIzaNLK0k5auopsxn5l7okl7BN+Ug5Irpwfz66x/PqVWbvZfrjs5gvC1mkS1mWBTkIS+Z2RMyjJ0/kNABOaeZXAZtFp5yz/NlcypgjzAjSIoJEngRECzDjPHymIedPZO0B/kNA/IHwzxjX/+50Wr1mH3nDIvGVBEYJAAAAAElFTkSuQmCC')
  var brickowllink = brickowl.getElementsByTagName('a')[0]
  brickowllink.appendChild(brickowlimg)
  brickowllink.innerHTML = brickowllink.innerHTML + ' Brickowl'
  brickowllink.setAttribute('href', `https://www.brickowl.com/search/catalog?query=${itemnum}`)

  var brickeconomyimg = document.createElement('img');
  brickeconomyimg.setAttribute('style', 'height:26px;width:26px')
  brickeconomyimg.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAARVBMVEVHcEzv7/Dv7/Hv7/Dw8PDw8PHw8PH29vTv7/Dv8PHF0/BakOvZ4vXm7PYAZ+cJb+iQse+0yfI8gemhvfAac+j///96pO4XzhHzAAAACnRSTlMAOIbC7P9g/xffFSzsFQAAAVFJREFUeAFdkwGuhCAMRFXpr1JoAVruf9QPmDXLTmIS8mQy0GF7tR+nA3DnsW+/+nPQhTDk/hZ03RN5Cjh/uK8vx4kiMUn/YGhfGCQWzoV8Jfyi19iWRZSllkimGQERHucb0BdW1ZByQcqVKZeE98wJsQ3E0VoOisxYuLaGI7MLLPqB5jWZSTeX4EYazx0JP9B87dA01NozHRNKiSV0GFVSsUym6uHYzgnZbMLmZexPwh7h3NwCU04Vi5lxBHAbDCgfiGyllbGzQ5iQacBo1CwrUVQNVBFg2nobMIlwCTFY6PbG3XYEKtZh0Oe0hGPFSL4HOh7YWB8JJ6stssR+lH3A7qivWHxiDnMwzve1LmKKHMGNi593u0q4w1mWOxL/sgZwP8Oe81xcAwJcb02wvpFkNGEtEUJjeU7yVGitJgZi4Rbfaq6lRk8el1IvzwHX5/APMcQhL8CmHDEAAAAASUVORK5CYII=')
  var brickeconomylink = brickeconomy.getElementsByTagName('a')[0]
  brickeconomylink.appendChild(brickeconomyimg)
  brickeconomylink.innerHTML = brickeconomylink.innerHTML + ' BrickEconomy'
  brickeconomylink.setAttribute('href', `https://www.brickeconomy.com/search?query=${itemnum}`)

  bricksetimg.setAttribute('style', 'height:26px;width:26px')
  bricksetlink.appendChild(bricksetimg)
  bricksetlink.innerHTML = bricksetlink.innerHTML + ' Brickset';

  myspan.appendChild(brickset);
  myspan.appendChild(br1);
  myspan.appendChild(bricklink);
  myspan.appendChild(br2);
  myspan.appendChild(rebrickable);
  myspan.appendChild(br3);
  myspan.appendChild(brickowl);
  myspan.appendChild(br4);
  myspan.appendChild(brickeconomy);
  links_list.removeChild(links_list.firstChild);
  links_list.appendChild(myspan);
  prodattrdiv.appendChild(links_list);
}


/*--- waitForKeyElements():  A utility function, for Greasemonkey scripts,
    that detects and handles AJAXed content.
    Usage example:
        waitForKeyElements (
            "div.comments"
            , commentCallbackFunction
        );
        //--- Page-specific function to do what we want when the node is found.
        function commentCallbackFunction (jNode) {
            jNode.text ("This comment changed by waitForKeyElements().");
        }
    IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = jQuery(selectorTxt);
    else
        targetNodes     = jQuery(iframeSelector).contents ()
                                           .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        /*--- Found target node(s).  Go through each and act if they
            are new.
        */
        targetNodes.each ( function () {
            var jThis        = jQuery(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = waitForKeyElements.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey]
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    waitForKeyElements (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}