Melonbooks: wishlist: better remove button

Replace wishlist remove button. Compatible with AutoPagerize.

// ==UserScript==
// @name         Melonbooks: wishlist: better remove button
// @namespace    http://darkfader.net/
// @version      0.1
// @description  Replace wishlist remove button. Compatible with AutoPagerize.
// @author       Rafael Vuijk
// @match        https://www.melonbooks.co.jp/mypage/favorite.php
// @grant        none
// ==/UserScript==

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

HTMLElement.prototype.remove = function() { this.parentNode.removeChild(this); return this; }

String.prototype.rsplit = function(sep, maxsplit) {
    var split = this.split(sep);
    return maxsplit ? [ split.slice(0, -maxsplit).join(sep) ].concat(split.slice(-maxsplit)) : split;
}

let transactionid = null;

// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
async function delete_favorite(product_id) {
    let formData = new FormData();
    formData.append('mode', 'delete_favorite');
    formData.append('product_id', product_id);
    formData.append('transactionid', transactionid);

    const response = await fetch("https://www.melonbooks.co.jp/mypage/favorite.php?", {
        method: 'POST',
        body: formData,
    });
    return await response;
}

function onReady() {

    // get CSRF token
    transactionid = document.body.querySelector('input[name="transactionid"]').getAttribute('value');

    // var xpath = "//div[contains(@class, 'item')]";
    const xpath = "//div[@class='wishlist']//tr";

    function process(node) {
        let results = document.evaluate(xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

        for (let i = 0; i < results.snapshotLength; i++) {
            let e = results.snapshotItem(i);

            if (e.hasAttribute('proc')) {    // skip already processed elements. required?
                continue;
            }
            e.setAttribute('proc', true);

            try {
                // console.log(e);

                let item = e.querySelector('td.item');
                let title = item.querySelector('p.title');
                let action = e.querySelector('td.action');

                // get product id
                let href = title.querySelector('a').getAttribute('href');
                const product_id = href.rsplit('=', 1).slice(-1)[0];

                // remove old button
                action.querySelector('a[onclick*="delete_favorite"]').remove();

                /*
                // uhh.. we don't actually need to create a form...
                var f = document.createElement('form');
                f.setAttribute('target', "_blank");
                f.setAttribute('method', "post");
                f.setAttribute('action', "https://www.melonbooks.co.jp/mypage/favorite.php?");

                let ff = document.createElement('input');
                ff.setAttribute('type', "hidden");
                ff.setAttribute('name', "mode");
                ff.setAttribute('value', "delete_favorite");
                f.appendChild(ff);

                ff = document.createElement('input');
                ff.setAttribute('type', "hidden");
                ff.setAttribute('name', "product_id");
                ff.setAttribute('value', product_id);
                f.appendChild(ff);

                ff = document.createElement('input');
                ff.setAttribute('type', "hidden");
                ff.setAttribute('name', "transactionid");
                ff.setAttribute('value', transactionid);
                f.appendChild(ff);

                // product_class_id: 483710
                // quantity: 1
*/
                let ff = document.createElement('input');
//                ff.setAttribute('type', "submit");
                ff.setAttribute('type', "button");
                ff.setAttribute('value', 'remove from wishlist');
                ff.setAttribute('class', 'input_btn br_5'); // doesn't work

                // anchor had these styles...
                ff.setAttribute('style', "\
                display: block;\
                font-size: 12px;\
                text-indent: 0px;\
                border: 1px solid #a09d9e;\
                background: -webkit-linear-gradient( 90deg, rgb(230,230,230) 0%, rgb(255,255,255) 100%);\
                color: #333333;\
                line-height: 24px;");

                action.appendChild(ff);
                //action.appendChild(f);

                ff.addEventListener("click", () => {
                    console.log('click');
                    delete_favorite(product_id).then(response => {
                        if (response.ok) {
                            console.log('yep');
                            e.remove();
                        }
                    }).catch((error) => {
                        console.error('Error:', error);
                    });
                });

                //f.appendChild(ff);

            } catch (error) {
                console.log(error);
            }
        }
    }

    process(document.body);

    var config = { attributes: false, childList: true, characterData: false, subtree: true };

    (document.body.observer = new MutationObserver(function processMutations(mutations) {
        for (var m of mutations) {
            if (m.type == 'childList') {
                if (m.removedNodes.length > 0) {
                    return;// replaced?????
                }
                // TODO: order table, form, div. take highest order.
                [].forEach.call(m.addedNodes, function(v,i,a) {
//                    console.log("type " + type(v));
                    if (v instanceof HTMLTableElement || v instanceof HTMLFormElement || v instanceof HTMLDivElement || v instanceof HTMLSpanElement) {
                        process(v);
                    }
                });
            }
        }
    })).observe(document.body, config);
}

// start the onReady...
if (document.readyState !== "loading") {
    setTimeout(onReady, 0);
} else {
    document.addEventListener("DOMContentLoaded", onReady);
}