Skip to content

Instantly share code, notes, and snippets.

@theseanl
Last active January 6, 2021 18:33
Show Gist options
  • Save theseanl/95d91f7efc361b8b80b672b06c810904 to your computer and use it in GitHub Desktop.
Save theseanl/95d91f7efc361b8b80b672b06c810904 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Coupang price trends
// @namespace seanl
// @description Attaches buttons that links to price trends page on coupang websites
// @version 1.0.1
// @license MIT
// @match https://www.coupang.com/*
// @grant unsafeWindow
// @run-at document-end
// ==/UserScript==
const processed = new WeakSet();
const ANCHOR_CLASS = 'trend__anchor';
const searchPageStyle = `a.${ANCHOR_CLASS} { position: absolute; bottom: 10px; right: 10px; }`;
const productPageStyle = `a.${ANCHOR_CLASS} { vertical-align: bottom; }`;
function linkTemplate(href) {
return `<a class="${ANCHOR_CLASS}" href="${href}" target="_blank" style="vertical-align:bottom"><img width=20 height=20 src="http://alltimeprice.com/favicon.ico"></img></a>`
}
let hasStyle = false;
function ensureStyleAdded(css) {
if (hasStyle) return;
const style = document.createElement('style');
style.textContent = css;
document.body.appendChild(style);
hasStyle = true;
}
//function insertLink(el, href) {
function insertLink(el, pid, itemId) {
if (processed.has(el)) return;
processElement(el, pid, itemId);
processed.add(el);
}
function processElement(el, pid, itemId) {
el.insertAdjacentHTML('beforeend', linkTemplate(`https://alltimeprice.com/product/?pid=${pid}-${itemId}`));
}
const reProductId = /^\/vp\/products\/([\d]*)$/
function onProductLink(anchor) {
let rocketBadge = anchor.getElementsByClassName("rocket badge");
if (rocketBadge === null || rocketBadge.length === 0) return;
const {href} = anchor;
const url = new URL(href);
let match = reProductId.exec(url.pathname);
if (match === null) return;
const pid = match[1];
const itemId = url.searchParams.get("vendorItemId");
ensureStyleAdded(searchPageStyle);
insertLink(anchor, pid, itemId);
}
function findProductLinkSubtree(node) {
if (node.tagName === "A" && node.href.startsWith("/vp/products")) {
onProductLink(node);
} else {
let anchors = node.querySelectorAll("a[href^='/vp/products/");
if (!anchors) return;
for (let anchor of anchors) {
onProductLink(anchor);
}
}
}
function onSalePrice(div) {
const pid = unsafeWindow["sdp"]["productId"];
const itemId = unsafeWindow["sdp"]["vendorItemId"]
if (!pid || !itemId) return;
ensureStyleAdded(productPageStyle);
insertLink(div, pid, itemId);
}
function findSalePriceSubtree(node) {
if (node.classList.contains("prod-sale-price")) {
onSalePrice(node);
} else {
let nodes = node.getElementsByClassName("prod-sale-price");
if (!nodes) return;
for (let node of nodes) {
onSalePrice(node);
}
}
}
function findElements(finder) {
finder(document.documentElement);
new MutationObserver((mutations) => {
for (let mutation of mutations) {
let { addedNodes } = mutation;
if (!addedNodes) continue;
for (let addedNode of addedNodes) {
if (addedNode.nodeType !== Node.ELEMENT_NODE) continue;
finder(addedNode);
}
}
}).observe(document.documentElement, {
subtree: true,
childList: true
});
}
if (location.pathname.startsWith("/np/search")) {
findElements(findProductLinkSubtree);
} else if (location.pathname.startsWith("/vp/products/")) {
findElements(findSalePriceSubtree);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment