Stores to Agent

Adds an order directly from stores to your agent

Install this script?
Author's suggested script

You may also like FR:Reborn - Agents extension.

Install this script
// ==UserScript==
// @name          Stores to Agent
// @namespace     https://www.reddit.com/user/RobotOilInc
// @version       5.2.3
// @author        RobotOilInc
// @source        https://gitlab.com/robotoilinc/stores-to-agents/
// @license       MIT
// @require       https://unpkg.com/@trim21/gm-fetch@0.1.15/dist/gm_fetch.js#sha384-P0KSVCS+YC1OEOII7FniE0zZ0+xpXAOHHT5aY++HbYNTnJbp8R933m5CVq4XJv4O
// @require       https://unpkg.com/gm-storage@2.0.3/dist/index.umd.min.js#sha384-IforrfCAVNj1KYlzIXNAyX2RADvDkkufuRfcM0qT57QMNdEonuvBRK2WfL8Co3JV
// @require       https://unpkg.com/gm4-polyfill@1.0.1/gm4-polyfill.js#sha384-nDSbBzN1Jn1VgjQjt5u2BxaPO1pbMS9Gxyi5+yIsKYWzqkYOEh11iQdomqywYPaN
// @require       https://unpkg.com/jquery@3.7.0/dist/jquery.min.js#sha384-NXgwF8Kv9SSAr+jemKKcbvQsz+teULH/a5UNJvZc6kP47hZgl62M1vGnw6gHQhb1
// @require       https://unpkg.com/js-logger@1.6.1/src/logger.min.js#sha384-CGmI56C3Kvs2e+Ftr3UpFkkMgOAXBUkLKS/KVkxDEuGSUYF8qki7CzwWWBz4QM60
// @require       https://greasyfork.org/scripts/11562-gm-config-8/code/GM_config%208+.js?version=66657#sha256-229668ef83cd26ac207e9d780e2bba6658e1506ac0b23fb29dc94ae531dd31fb
// @description   Adds an order directly from stores to your agent
// @homepageURL   https://greasyfork.org/en/scripts/427774-stores-to-agent
// @supportURL    https://greasyfork.org/en/scripts/427774-stores-to-agent
// @match         https://detail.1688.com/offer/*
// @match         https://*.taobao.com/item.htm*
// @match         https://*.v.weidian.com/?userid=*
// @match         https://*.weidian.com/item.html*
// @match         https://*.yupoo.com/albums/*
// @match         https://detail.tmall.com/item.htm*
// @match         https://weidian.com/*itemID=*
// @match         https://weidian.com/?userid=*
// @match         https://weidian.com/item.html*
// @match         https://*.pandabuy.com/*
// @match         https://www.pandabuy.com/*
// @grant         GM_addStyle
// @grant         GM_getResourceText
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_registerMenuCommand
// @grant         GM_webRequest
// @grant         GM_xmlhttpRequest
// @grant         GM_deleteValue
// @grant         GM_listValues
// @connect       basetao.com
// @connect       cssbuy.com
// @connect       superbuy.com
// @connect       ytaopal.com
// @connect       wegobuy.com
// @connect       pandabuy.com
// @webRequest    [{ "selector": "*thor.weidian.com/stardust/*", "action": "cancel" }]
// @icon          https://i.imgur.com/2lQXuqv.png
// @run-at        document-end
// ==/UserScript==

/*! @robotoilinc/stores-to-agents v5.2.3 has been created by RobotOilInc. All rights reserved. */
/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ({

/***/ "./node_modules/object-to-formdata/src/index.js":
/***/ ((module) => {

function isUndefined(value) {
  return value === undefined;
}
function isNull(value) {
  return value === null;
}
function isBoolean(value) {
  return typeof value === 'boolean';
}
function isObject(value) {
  return value === Object(value);
}
function isArray(value) {
  return Array.isArray(value);
}
function isDate(value) {
  return value instanceof Date;
}
function isBlob(value, isReactNative) {
  return isReactNative ? isObject(value) && !isUndefined(value.uri) : isObject(value) && typeof value.size === 'number' && typeof value.type === 'string' && typeof value.slice === 'function';
}
function isFile(value, isReactNative) {
  return isBlob(value, isReactNative) && typeof value.name === 'string' && (isObject(value.lastModifiedDate) || typeof value.lastModified === 'number');
}
function initCfg(value) {
  return isUndefined(value) ? false : value;
}
function serialize(obj, cfg, fd, pre) {
  cfg = cfg || {};
  fd = fd || new FormData();
  cfg.indices = initCfg(cfg.indices);
  cfg.nullsAsUndefineds = initCfg(cfg.nullsAsUndefineds);
  cfg.booleansAsIntegers = initCfg(cfg.booleansAsIntegers);
  cfg.allowEmptyArrays = initCfg(cfg.allowEmptyArrays);
  cfg.noAttributesWithArrayNotation = initCfg(cfg.noAttributesWithArrayNotation);
  cfg.noFilesWithArrayNotation = initCfg(cfg.noFilesWithArrayNotation);
  cfg.dotsForObjectNotation = initCfg(cfg.dotsForObjectNotation);
  const isReactNative = typeof fd.getParts === 'function';
  if (isUndefined(obj)) {
    return fd;
  } else if (isNull(obj)) {
    if (!cfg.nullsAsUndefineds) {
      fd.append(pre, '');
    }
  } else if (isBoolean(obj)) {
    if (cfg.booleansAsIntegers) {
      fd.append(pre, obj ? 1 : 0);
    } else {
      fd.append(pre, obj);
    }
  } else if (isArray(obj)) {
    if (obj.length) {
      obj.forEach((value, index) => {
        let key = pre + '[' + (cfg.indices ? index : '') + ']';
        if (cfg.noAttributesWithArrayNotation || cfg.noFilesWithArrayNotation && isFile(value, isReactNative)) {
          key = pre;
        }
        serialize(value, cfg, fd, key);
      });
    } else if (cfg.allowEmptyArrays) {
      fd.append(cfg.noAttributesWithArrayNotation ? pre : pre + '[]', '');
    }
  } else if (isDate(obj)) {
    fd.append(pre, obj.toISOString());
  } else if (isObject(obj) && !isBlob(obj, isReactNative)) {
    Object.keys(obj).forEach(prop => {
      const value = obj[prop];
      if (isArray(value)) {
        while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) {
          prop = prop.substring(0, prop.length - 2);
        }
      }
      const key = pre ? cfg.dotsForObjectNotation ? pre + '.' + prop : pre + '[' + prop + ']' : prop;
      serialize(value, cfg, fd, key);
    });
  } else {
    fd.append(pre, obj);
  }
  return fd;
}
module.exports = {
  serialize
};

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/compat get default export */
/******/ 	(() => {
/******/ 		// getDefaultExport function for compatibility with non-harmony modules
/******/ 		__webpack_require__.n = (module) => {
/******/ 			var getter = module && module.__esModule ?
/******/ 				() => (module['default']) :
/******/ 				() => (module);
/******/ 			__webpack_require__.d(getter, { a: getter });
/******/ 			return getter;
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";

;// CONCATENATED MODULE: external "Logger"
const external_Logger_namespaceObject = Logger;
var external_Logger_default = /*#__PURE__*/__webpack_require__.n(external_Logger_namespaceObject);
;// CONCATENATED MODULE: external "GMStorage"
const external_GMStorage_namespaceObject = GMStorage;
var external_GMStorage_default = /*#__PURE__*/__webpack_require__.n(external_GMStorage_namespaceObject);
;// CONCATENATED MODULE: ./src/Constants.ts
const PandaBuyToken = "PANDABUY_TOKEN";
const PandaBuyUserInfo = "PANDABUY_USERINFO";
;// CONCATENATED MODULE: ./src/exceptions/PandaBuyError.ts
class PandaBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = "PandaBuyError";
  }
}
;// CONCATENATED MODULE: ./src/helpers/StorageHelper.ts
class LocalStorage {
  constructor() {
    this.localStorageSupported = typeof window["localStorage"] != "undefined" && window["localStorage"] != null;
  }

  // add value to storage
  add(key, item) {
    if (this.localStorageSupported) {
      localStorage.setItem(key, item);
    }
  }

  // get one item by key from storage
  get(key) {
    if (this.localStorageSupported) {
      return localStorage.getItem(key);
    }
    return null;
  }

  // remove value from storage
  remove(key) {
    if (this.localStorageSupported) {
      localStorage.removeItem(key);
    }
  }

  // clear storage (remove all items from it)
  clear() {
    if (this.localStorageSupported) {
      localStorage.clear();
    }
  }
}
;// CONCATENATED MODULE: ./src/agents/login/Pandabuy.ts





class PandaBuyLogin {
  constructor() {
    this.store = new (external_GMStorage_default())();
    this.localStorage = new LocalStorage();
  }
  supports(hostname) {
    return hostname.includes("pandabuy.com");
  }
  process() {
    // If we already have a token, don't bother
    const currentToken = this.store.get(PandaBuyToken, null);
    if (currentToken !== null && currentToken.length !== 0) {
      return;
    }

    // Don't bother with getting the token, if we aren't loggeed in yet
    const userInfo = this.localStorage.get(PandaBuyUserInfo);
    if (userInfo === null || userInfo.length === 0) {
      return;
    }

    // The token should now exist
    const updatedToken = this.localStorage.get(PandaBuyToken);
    if (updatedToken === null || updatedToken.length === 0) {
      throw new PandaBuyError("Could not retrieve token");
    }

    // Store it internally
    this.store.set(PandaBuyToken, updatedToken);
    external_Logger_default().info("Updated the PandaBuy Authorization Token");
  }
}
;// CONCATENATED MODULE: ./src/agents/login/Logins.ts

function getLogin(hostname) {
  const agents = [new PandaBuyLogin()];
  let agent = null;
  Object.values(agents).forEach(value => {
    if (value.supports(hostname)) {
      agent = value;
    }
  });
  return agent;
}
// EXTERNAL MODULE: ./node_modules/object-to-formdata/src/index.js
var src = __webpack_require__("./node_modules/object-to-formdata/src/index.js");
;// CONCATENATED MODULE: ./src/exceptions/BaseTaoError.ts
class BaseTaoError extends Error {
  constructor(message) {
    super(message);
    this.name = "BaseTaoError";
  }
}
;// CONCATENATED MODULE: ./src/helpers/DetermineStoreSource.ts
/**
 * Determines on website we are buying something. Used for BaseTao and Pandabuy.
 */
const determineStoreSource = function () {
  if (window.location.hostname.includes("1688.com")) {
    return "1688";
  }

  // Check more specific TaoBao pages first
  if (window.location.hostname.includes("market.m.taobao.com") || window.location.hostname.includes("2.taobao.com")) {
    return "xianyu";
  }
  if (window.location.hostname.includes("taobao.com")) {
    return "taobao";
  }
  if (window.location.hostname.includes("weidian.com") || window.location.hostname.includes("koudai.com")) {
    return "wd";
  }
  if (window.location.hostname.includes("yupoo.com")) {
    return "yupoo";
  }
  if (window.location.hostname.includes("detail.tmall.com")) {
    return "tmall";
  }
  throw new Error(`Could not determine store source ${window.location.hostname}`);
};
;// CONCATENATED MODULE: ./node_modules/@trim21/gm-fetch/dist/index.mjs
function parseRawHeaders(h) {
    const s = h.trim();
    if (!s) {
        return new Headers();
    }
    const array = s.split("\r\n").map((value) => {
        let s = value.split(":");
        return [s[0].trim(), s[1].trim()];
    });
    return new Headers(array);
}
function parseGMResponse(req, res) {
    return new ResImpl(res.response, {
        statusCode: res.status,
        statusText: res.statusText,
        headers: parseRawHeaders(res.responseHeaders),
        finalUrl: res.finalUrl,
        redirected: res.finalUrl === req.url,
    });
}
class ResImpl {
    constructor(body, init) {
        this.rawBody = body;
        this.init = init;
        this.body = toReadableStream(body);
        const { headers, statusCode, statusText, finalUrl, redirected } = init;
        this.headers = headers;
        this.status = statusCode;
        this.statusText = statusText;
        this.url = finalUrl;
        this.type = "basic";
        this.redirected = redirected;
        this._bodyUsed = false;
    }
    get bodyUsed() {
        return this._bodyUsed;
    }
    get ok() {
        return this.status < 300;
    }
    arrayBuffer() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'arrayBuffer' on 'Response': body stream already read");
        }
        this._bodyUsed = true;
        return this.rawBody.arrayBuffer();
    }
    blob() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'blob' on 'Response': body stream already read");
        }
        this._bodyUsed = true;
        // `slice` will use empty string as default value, so need to pass all arguments.
        return Promise.resolve(this.rawBody.slice(0, this.rawBody.size, this.rawBody.type));
    }
    clone() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'clone' on 'Response': body stream already read");
        }
        return new ResImpl(this.rawBody, this.init);
    }
    formData() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'formData' on 'Response': body stream already read");
        }
        this._bodyUsed = true;
        return this.rawBody.text().then(decode);
    }
    async json() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'json' on 'Response': body stream already read");
        }
        this._bodyUsed = true;
        return JSON.parse(await this.rawBody.text());
    }
    text() {
        if (this.bodyUsed) {
            throw new TypeError("Failed to execute 'text' on 'Response': body stream already read");
        }
        this._bodyUsed = true;
        return this.rawBody.text();
    }
}
function decode(body) {
    const form = new FormData();
    body
        .trim()
        .split("&")
        .forEach(function (bytes) {
        if (bytes) {
            const split = bytes.split("=");
            const name = split.shift()?.replace(/\+/g, " ");
            const value = split.join("=").replace(/\+/g, " ");
            form.append(decodeURIComponent(name), decodeURIComponent(value));
        }
    });
    return form;
}
function toReadableStream(value) {
    return new ReadableStream({
        start(controller) {
            controller.enqueue(value);
            controller.close();
        },
    });
}

async function GM_fetch(input, init) {
    const request = new Request(input, init);
    let data;
    if (init?.body) {
        data = await request.text();
    }
    return await XHR(request, init, data);
}
function XHR(request, init, data) {
    return new Promise((resolve, reject) => {
        if (request.signal && request.signal.aborted) {
            return reject(new DOMException("Aborted", "AbortError"));
        }
        GM.xmlHttpRequest({
            url: request.url,
            method: gmXHRMethod(request.method.toUpperCase()),
            headers: Object.fromEntries(new Headers(init?.headers).entries()),
            data: data,
            responseType: "blob",
            onload(res) {
                resolve(parseGMResponse(request, res));
            },
            onabort() {
                reject(new DOMException("Aborted", "AbortError"));
            },
            ontimeout() {
                reject(new TypeError("Network request failed, timeout"));
            },
            onerror(err) {
                reject(new TypeError("Failed to fetch: " + err.finalUrl));
            },
        });
    });
}
const httpMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "TRACE", "OPTIONS", "CONNECT"];
// a ts type helper to narrow type
function includes(array, element) {
    return array.includes(element);
}
function gmXHRMethod(method) {
    if (includes(httpMethods, method)) {
        return method;
    }
    throw new Error(`unsupported http method ${method}`);
}


//# sourceMappingURL=index.mjs.map

;// CONCATENATED MODULE: ./src/helpers/Fetch.ts

function get(url, init) {
  return GM_fetch(url, {
    ...init,
    method: "GET"
  });
}
function post(url, request) {
  return GM_fetch(url, {
    ...request,
    method: "POST"
  });
}
;// CONCATENATED MODULE: ./src/helpers/RemoveEmoji.ts
/**
 * Removes all emojis from the input text.
 *
 * @param string {string}
 */
const removeEmoji = string => string.replace(/[^\p{L}\p{N}\p{P}\p{Z}^$\n]/gu, "");
;// CONCATENATED MODULE: ./src/agents/BaseTao.ts






const CSRF_REQUIRED_ERROR = "You need to be logged in on BaseTao to use this extension (CSRF required).";
class BaseTao {
  constructor() {
    this.parser = new DOMParser();
  }
  get name() {
    return "BaseTao";
  }
  async send(order) {
    // Get proper domain to use
    const properDomain = await this._getDomain();

    // Build the purchase data
    const purchaseData = await this._buildPurchaseData(properDomain, order);
    external_Logger_default().info("Sending order to BaseTao...", properDomain, order);

    // Do the actual call
    const response = await post(`${properDomain}/best-taobao-agent-service/bt_action/add_cart`, {
      body: new URLSearchParams((0,src.serialize)(purchaseData)),
      referrer: `${properDomain}/best-taobao-agent-service/how_make/buy_order.html`,
      headers: {
        origin: `${properDomain}`,
        referrer: `${properDomain}/best-taobao-agent-service/how_make/buy_order.html`,
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36",
        "x-requested-with": "XMLHttpRequest"
      }
    });
    const responseData = await response.json();
    if (responseData.value === "1") {
      return;
    }
    external_Logger_default().error("Item could not be added", response);
    throw new BaseTaoError("Item could not be added, make sure you are logged in");
  }
  async _getDomain() {
    // Try HTTPS (with WWW) first
    let response = await get("https://www.basetao.com/best-taobao-agent-service/how_make/buy_order.html");
    let doc = this.parser.parseFromString(await response.text(), "text/html");
    let csrfToken = doc.querySelector("input[name=bt_sb_token]");
    if (csrfToken && csrfToken.value.length !== 0) {
      return "https://www.basetao.com";
    }

    // Try HTTPS (without WWW) after
    response = await get("https://basetao.com/best-taobao-agent-service/how_make/buy_order.html");
    doc = this.parser.parseFromString(await response.text(), "text/html");
    csrfToken = doc.querySelector("input[name=bt_sb_token]");
    console.log(csrfToken);
    if (csrfToken && csrfToken.value.length !== 0) {
      return "https://basetao.com";
    }

    // User is not logged in/there is an issue
    throw new Error(CSRF_REQUIRED_ERROR);
  }
  async _buildPurchaseData(properDomain, order) {
    // Get the CSRF token
    const csrf = await this._getCSRF(properDomain);

    // Build the data we will send
    const data = {
      addtime: Date.now(),
      goodscolor: order.item.color ?? "-",
      goodsimg: order.item.imageUrl,
      goodsname: removeEmoji(order.item.name),
      goodsnum: 1,
      goodsprice: order.price,
      goodsremark: this._buildRemark(order) ?? "",
      goodsseller: removeEmoji(order.shop.name ?? ""),
      goodssite: determineStoreSource(),
      goodssize: order.item.size ?? "-",
      goodsurl: window.location.href,
      item_id: order.item.id,
      sellerurl: order.shop.url ?? "",
      sendprice: order.shipping,
      siteurl: window.location.hostname,
      sku_id: 0,
      type: 1
    };
    return {
      bt_sb_token: csrf,
      data: JSON.stringify(data)
    };
  }
  async _getCSRF(properDomain) {
    // Grab data from BaseTao
    const response = await get(`${properDomain}/best-taobao-agent-service/how_make/buy_order.html`);

    // Check if user is actually logged in
    const data = await response.text();
    if (data.includes("please sign in again")) {
      throw new Error(CSRF_REQUIRED_ERROR);
    }
    const doc = this.parser.parseFromString(data, "text/html");
    const csrfToken = doc.querySelector("input[name=bt_sb_token]");
    if (csrfToken && csrfToken.value.length !== 0) {
      return csrfToken.value;
    }

    // Return CSRF
    throw new Error(CSRF_REQUIRED_ERROR);
  }
  _buildRemark(order) {
    const descriptionParts = [];
    if (order.item.model !== null) descriptionParts.push(`Model: ${order.item.model}`);
    if (order.item.other.length !== 0) descriptionParts.push(order.item.other);
    let description = null;
    if (descriptionParts.length !== 0) {
      description = descriptionParts.join(" / ");
    }
    return description;
  }
}
;// CONCATENATED MODULE: ./src/exceptions/CSSBuyError.ts
class CSSBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = "CSSBuyError";
  }
}
;// CONCATENATED MODULE: ./src/agents/CSSBuy.ts




class CSSBuy {
  get name() {
    return "CSSBuy";
  }
  async send(order) {
    // Build the purchase data
    const purchaseData = this._buildPurchaseData(order);
    external_Logger_default().info("Sending order to CSSBuy...", purchaseData);

    // Do the actual call
    const response = await post("https://www.cssbuy.com/ajax/fast_ajax.php?action=buyone", {
      body: new URLSearchParams((0,src.serialize)(purchaseData)),
      referrer: `https://www.cssbuy.com/?go=item&url=${encodeURIComponent(purchaseData.data.href)}`,
      referrerPolicy: "strict-origin-when-cross-origin",
      headers: {
        "accept": "application/json, text/javascript, */*; q=0.01",
        "accept-language": "nl,en-US;q=0.9,en;q=0.8,de;q=0.7,und;q=0.6",
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "referrer": `https://www.cssbuy.com/?go=item&url=${encodeURIComponent(purchaseData.data.href)}`,
        "referrerPolicy": "strict-origin-when-cross-origin",
        "x-requested-with": "XMLHttpRequest"
      }
    });
    if ((await response.json()).ret === 0) {
      return;
    }
    external_Logger_default().error("Item could not be added", response);
    throw new CSSBuyError("Item could not be added");
  }
  _buildPurchaseData(order) {
    // Build the description
    const description = this._buildRemark(order);

    // Create the purchasing data
    return {
      data: {
        buynum: 1,
        shopid: order.shop.id,
        picture: order.item.imageUrl,
        defaultimg: order.item.imageUrl,
        freight: order.shipping,
        price: order.price,
        color: order.item.color,
        size: order.item.size,
        total: order.price + order.shipping,
        buyyourself: 0,
        seller: order.shop.name,
        href: window.location.href,
        title: order.item.name,
        note: description,
        option: description
      }
    };
  }
  _buildRemark(order) {
    const descriptionParts = [];
    if (order.item.model !== null) descriptionParts.push(`Model: ${order.item.model}`);
    if (order.item.color !== null) descriptionParts.push(`Color: ${order.item.color}`);
    if (order.item.size !== null) descriptionParts.push(`Size: ${order.item.size}`);
    if (order.item.other.length !== 0) descriptionParts.push(`${order.item.other}`);
    let description = null;
    if (descriptionParts.length !== 0) {
      description = descriptionParts.join(" / ");
    }
    return description;
  }
}
;// CONCATENATED MODULE: ./src/agents/PandaBuy.ts






class PandaBuy {
  constructor() {
    this.store = new (external_GMStorage_default())();
  }
  get name() {
    return "PandaBuy";
  }
  async send(order) {
    const token = this.store.get(PandaBuyToken, null);
    if (token === null || token.length === 0) {
      throw new PandaBuyError("We do not have your PandaBuy authorization token yet. Please visit PandaBuy (and login if needed)");
    }

    // Build the purchase data
    const purchaseData = this._buildPurchaseData(order);
    external_Logger_default().info("Sending order to PandaBuy...", purchaseData);

    // Do the actual call
    await post("https://www.pandabuy.com/gateway/user/cart/add", {
      credentials: "include",
      mode: "cors",
      referrer: `https://www.pandabuy.com/uniorder?text=${encodeURIComponent(window.location.href)}`,
      referrerPolicy: "strict-origin-when-cross-origin",
      body: JSON.stringify(purchaseData),
      headers: {
        "accept": "application/json, text/plain, */*",
        "accept-language": "nl,en-US;q=0.9,en;q=0.8,de;q=0.7,und;q=0.6",
        "authorization": `Bearer ${token}`,
        "content-type": "application/json;charset=UTF-8",
        "currency": "CNY",
        "device": "pc",
        "lang": "en"
      }
    }).then(async response => {
      const data = await response.json();
      if (response.status === 200 && data.msg === null) {
        return;
      }

      // Our token has expired
      if (response.status === 401) {
        // Reset the current token
        GM_setValue(PandaBuyToken, null);
        external_Logger_default().error("PandaBuy authorization token has expired");
        throw new PandaBuyError("Your PandaBuy authorization token has expired. Please visit PandaBuy (or login at PandaBuy) again");
      }
      external_Logger_default().error("Item could not be added", data.msg);
      throw new PandaBuyError("Item could not be added");
    }).catch(err => {
      // If the error is our own, just rethrow it
      if (err instanceof PandaBuyError) {
        throw err;
      }
      external_Logger_default().error("An error happened when uploading the order", err);
      throw new Error("An error happened when adding the order");
    });
  }
  _buildPurchaseData(order) {
    // Build the description
    const description = this._buildRemark(order);

    // Create the purchasing data
    return {
      storeName: order.shop.name,
      storeId: order.shop.id,
      goodsUrl: window.location.href,
      goodsName: order.item.name,
      goodsNameCn: order.item.name,
      goodsAttr: description,
      goodsAttrCn: description,
      storageNo: 1,
      goodsPrice: order.price,
      goodsNum: 1,
      fare: order.shipping,
      remark: "",
      selected: 1,
      purchaseType: 1,
      goodsImg: order.item.imageUrl,
      servicePrice: "0.00",
      writePrice: order.price,
      actPrice: order.price,
      storeSource: determineStoreSource(),
      goodsId: order.item.id,
      seller: order.shop.name,
      storeUrl: "https://weidian.com/?userid=" + order.shop.id
    };
  }
  _buildRemark(order) {
    const descriptionParts = [];
    if (order.item.model !== null && order.item.model.length !== 0) descriptionParts.push(`Model: ${order.item.model}`);
    if (order.item.color !== null && order.item.color.length !== 0) descriptionParts.push(`Color: ${order.item.color}`);
    if (order.item.size !== null && order.item.size.length !== 0) descriptionParts.push(`Size: ${order.item.size}`);
    if (order.item.other.length !== 0) descriptionParts.push(`${order.item.other}`);
    let description = null;
    if (descriptionParts.length !== 0) {
      description = descriptionParts.join(" / ");
    }
    return description;
  }
}
;// CONCATENATED MODULE: ./src/exceptions/SuperBuyError.ts
class SuperBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = "SuperBuyError";
  }
}
;// CONCATENATED MODULE: ./src/helpers/BuildTaoCarts.ts
class BuildTaoCarts {
  purchaseData(order) {
    // Build the description
    const description = this._buildRemark(order);

    // Generate an SKU based on the description
    let sku = null;
    if (description !== null && description.length !== 0) {
      sku = description.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
    }

    // Create the purchasing data
    return {
      type: 1,
      shopItems: [{
        shopLink: "",
        shopSource: "NOCRAWLER",
        shopNick: "",
        shopId: "",
        goodsItems: [{
          beginCount: 0,
          count: 1,
          desc: description,
          freight: order.shipping,
          freightServiceCharge: 0,
          goodsAddTime: Math.floor(Date.now() / 1000),
          goodsCode: `NOCRAWLER-${sku}`,
          goodsId: window.location.href,
          goodsLink: window.location.href,
          goodsName: order.item.name,
          goodsPrifex: "NOCRAWLER",
          goodsRemark: description,
          guideGoodsId: "",
          is1111Yushou: "no",
          picture: order.item.imageUrl,
          platForm: "pc",
          price: order.price,
          priceNote: "",
          serviceCharge: 0,
          sku: order.item.imageUrl,
          spm: "",
          warehouseId: "1"
        }]
      }]
    };
  }
  _buildRemark(order) {
    const descriptionParts = [];
    if (order.item.model !== null) descriptionParts.push(`Model: ${order.item.model}`);
    if (order.item.color !== null) descriptionParts.push(`Color: ${order.item.color}`);
    if (order.item.size !== null) descriptionParts.push(`Size: ${order.item.size}`);
    if (order.item.other.length !== 0) descriptionParts.push(`${order.item.other}`);
    let description = null;
    if (descriptionParts.length !== 0) {
      description = descriptionParts.join(" / ");
    }
    return description;
  }
}
;// CONCATENATED MODULE: ./src/agents/SuperBuy.ts




class SuperBuy {
  constructor() {
    this._builder = new BuildTaoCarts();
  }
  get name() {
    return "SuperBuy";
  }
  async send(order) {
    // Build the purchase data
    const purchaseData = this._builder.purchaseData(order);
    external_Logger_default().info("Sending order to SuperBuy...", purchaseData);

    // Do the actual call
    const response = await post("https://front.superbuy.com/cart/add-cart", {
      body: JSON.stringify(purchaseData),
      headers: {
        origin: "https://www.superbuy.com",
        referer: "https://www.superbuy.com/",
        "content-type": "application/json;charset=UTF-8",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36"
      }
    });
    const data = await response.json();
    if (data.state === 0 && data.msg === "Success") {
      return;
    }
    external_Logger_default().error("Item could not be added", data.msg);
    throw new SuperBuyError("Item could not be added");
  }
}
;// CONCATENATED MODULE: ./src/exceptions/WeGoBuyError.ts
class WeGoBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = "WeGoBuyError";
  }
}
;// CONCATENATED MODULE: ./src/agents/WeGoBuy.ts




class WeGoBuy {
  constructor() {
    this._builder = new BuildTaoCarts();
  }
  get name() {
    return "WeGoBuy";
  }
  async send(order) {
    // Build the purchase data
    const purchaseData = this._builder.purchaseData(order);
    external_Logger_default().info("Sending order to WeGoBuy...", purchaseData);

    // Do the actual call
    const response = await post("https://front.wegobuy.com/cart/add-cart", {
      body: JSON.stringify(purchaseData),
      headers: {
        origin: "https://www.superbuy.com",
        referer: "https://www.superbuy.com/",
        "content-type": "application/json;charset=UTF-8",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36"
      }
    });
    const data = await response.json();
    if (data.state === 0 && data.msg === "Success") {
      return;
    }
    external_Logger_default().error("Item could not be added", data.msg);
    throw new WeGoBuyError("Item could not be added");
  }
}
;// CONCATENATED MODULE: ./src/agents/Agents.ts





const getAgent = selection => {
  switch (selection) {
    case "basetao":
      return new BaseTao();
    case "cssbuy":
      return new CSSBuy();
    case "pandabuy":
      return new PandaBuy();
    case "superbuy":
      return new SuperBuy();
    case "wegobuy":
      return new WeGoBuy();
    default:
      throw new Error(`Agent '${selection}' is not implemented`);
  }
};
;// CONCATENATED MODULE: ./src/classes/Item.ts
class Item {
  constructor(id, name, imageUrl, model, color, size, others) {
    this.id = id;
    this.name = name;
    this.imageUrl = imageUrl;
    this.model = model;
    this.color = color;
    this.size = size;
    this.others = others;
  }
  get other() {
    return this.others.join("\n");
  }
}
;// CONCATENATED MODULE: ./src/classes/Order.ts
class Order {
  constructor(shop, item, price, shipping) {
    this.shop = shop;
    this.item = item;
    this.price = price;
    this.shipping = shipping;
  }
}
;// CONCATENATED MODULE: ./src/classes/Shop.ts
class Shop {
  constructor(id, name, url) {
    this.id = id;
    this.name = name;
    this.url = url;
  }
}
;// CONCATENATED MODULE: ./src/helpers/ElementReady.ts
/**
 * Waits for an element satisfying selector to exist, then resolves promise with the element.
 * Useful for resolving race conditions.
 */
const elementReady = function (selector) {
  return new Promise(resolve => {
    // Check if the element already exists
    const element = document.querySelector(selector);
    if (element) {
      resolve(element);
    }

    // It doesn't so, so let's make a mutation observer and wait
    new MutationObserver((mutationRecords, observer) => {
      // Query for elements matching the specified selector
      Array.from(document.querySelectorAll(selector)).forEach(foundElement => {
        // Resolve the element that we found
        resolve(foundElement);

        // Once we have resolved we don't need the observer anymore.
        observer.disconnect();
      });
    }).observe(document.documentElement, {
      childList: true,
      subtree: true
    });
  });
};
;// CONCATENATED MODULE: ./src/helpers/RemoveWhitespaces.ts
/**
 * Trims the input text and removes all in between spaces as well.
 *
 * @param string {string}
 */
const removeWhitespaces = string => string.trim().replace(/\s(?=\s)/g, "");
;// CONCATENATED MODULE: ./src/helpers/Snackbar.ts

const Snackbar = function (toast) {
  // Log the snackbar, for ease of debugging
  external_Logger_default().info(toast);

  // Setup toast element
  const $toast = $(`<div style="background-color:#333;border-radius:2px;bottom:50%;color:#fff;display:block;font-size:16px;left:50%;margin-left:-150px;min-width:250px;opacity:1;padding:16px;position:fixed;right:50%;text-align:center;transition:background .2s;width:300px;z-index:2147483647">${toast}</div>`);

  // Append to the body
  $("body").append($toast);

  // Set a timeout to remove the toast
  setTimeout(() => $toast.fadeOut("slow", () => $toast.remove()), 5000);
};
;// CONCATENATED MODULE: ./src/stores/1688.ts








class Store1688 {
  constructor() {
    this.store = new (external_GMStorage_default())();
  }
  attach($document, localWindow) {
    elementReady(".order-button-wrapper > .order-button-children > .order-button-children-list").then(element => {
      const button = this._buildButton($document, localWindow);
      if (button === null) {
        return;
      }
      $(element).prepend(button);
    });
  }
  supports(hostname) {
    return hostname.includes("1688.com");
  }
  _buildButton($document, window) {
    // Force someone to select an agent
    if (this.store.get("agentSelection") === "empty") {
      GM_config.open();
      Snackbar("Please select what agent you use");
      return null;
    }

    // Get the agent related to our config
    const agent = getAgent(this.store.get("agentSelection", ""));

    // Create button
    const $button = $(`<button id="agent-button" class="order-normal-button order-button">Add to ${agent.name}</button>`);
    $button.on("click", async () => {
      // Disable button to prevent double clicks and show clear message
      $button.attr("disabled", "disabled").text("Processing...");

      // Try to build and send the order
      try {
        await agent.send(this._buildOrder($document, window));
      } catch (err) {
        $button.attr("disabled", null).text(`Add to ${agent.name}`);
        return Snackbar(err);
      }
      $button.attr("disabled", null).text(`Add to ${agent.name}`);

      // Success, tell the user
      return Snackbar("Item has been added, be sure to double check it");
    });
    return $('<div class="order-button-tip-wrapper"></div>').append($button);
  }
  _buildShop(window) {
    const id = window.__GLOBAL_DATA.offerBaseInfo.sellerUserId;
    const name = window.__GLOBAL_DATA.offerBaseInfo.sellerLoginId;
    const url = new URL(window.__GLOBAL_DATA.offerBaseInfo.sellerWinportUrl, window.location).toString();
    return new Shop(id, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = window.__GLOBAL_DATA.tempModel.offerId;
    const name = removeWhitespaces(window.__GLOBAL_DATA.tempModel.offerTitle);

    // Build image information
    const imageUrl = new URL(window.__GLOBAL_DATA.images[0].size310x310ImageURI, window.location).toString();

    // Retrieve the dynamic selected item
    const skus = this._processSku($document);
    return new Item(id, name, imageUrl, null, null, null, skus);
  }
  _buildPrice($document) {
    const itemPrice = Number(removeWhitespaces($document.find(".order-price-wrapper .total-price .value").text()));
    if (Number.isNaN(itemPrice)) {
      return 0;
    }
    return itemPrice;
  }
  _buildShipping($document) {
    const shippingPrice = Number(removeWhitespaces($document.find(".logistics-express .logistics-express-price").text()));
    if (Number.isNaN(shippingPrice)) {
      return 0;
    }
    return shippingPrice;
  }
  _buildOrder($document, window) {
    return new Order(this._buildShop(window), this._buildItem($document, window), this._buildPrice($document), this._buildShipping($document));
  }
  _processSku($document) {
    const selectedItems = [];

    // Grab the module that holds the selected data
    const skuData = this._findModule($document.find(".pc-sku-wrapper")[0]).getSkuData();

    // Grab the map we can use to find names
    const skuMap = skuData.skuState.skuSpecIdMap;

    // Parse all the selected items
    const selectedData = skuData.skuPannelInfo.getSubmitData().submitData;

    // Ensure at least one item is selected
    if (typeof selectedData.find(item => item.specId !== null) === "undefined") {
      throw new Error("Make sure to select at least one item");
    }

    // Process all selections
    selectedData.forEach(item => {
      const sku = skuMap[item.specId];

      // Build the proper name
      let name = removeWhitespaces(sku.firstProp);
      if (sku.secondProp != null && sku.secondProp.length !== 0) {
        name = `${name} - ${removeWhitespaces(sku.secondProp)}`;
      }

      // Add it to the list with quantity
      selectedItems.push(`${name}: ${item.quantity}x`);
    });
    return selectedItems;
  }
  _findModule($element) {
    const instanceKey = Object.keys($element).find(key => key.startsWith("__reactInternalInstance$"));
    const internalInstance = $element[instanceKey];
    if (internalInstance == null) return null;
    return internalInstance.return.ref.current;
  }
}
;// CONCATENATED MODULE: ./src/helpers/Capitalize.ts
/**
 * @param s {string|undefined}
 * @returns {string}
 */
const capitalize = s => s && s[0].toUpperCase() + s.slice(1) || "";
;// CONCATENATED MODULE: ./src/Enums.ts
class Enum {
  _model = ["型号", "模型", "模型", "model", "type"];
  _colors = ["颜色", "彩色", "色", "色彩", "配色", "配色方案", "color", "colour", "color scheme"];
  _sizing = ["尺寸", "尺码", "型号尺寸", "大小", "浆液", "码数", "码", "size", "sizing"];
  isModel(item) {
    return this._arrayContains(this._model, item);
  }
  isColor(item) {
    return this._arrayContains(this._colors, item);
  }
  isSize(item) {
    return this._arrayContains(this._sizing, item);
  }
  _arrayContains(array, query) {
    return array.filter(item => query.toLowerCase().indexOf(item.toLowerCase()) !== -1).length !== 0;
  }
}
;// CONCATENATED MODULE: ./src/helpers/RetrieveDynamicInformation.ts



const retrieveDynamicInformation = ($document, rowCss, rowTitleCss, selectedItemCss) => {
  // Create dynamic items
  let model = null;
  let color = null;
  let size = null;
  const others = [];

  // Load dynamic items
  $document.find(rowCss).each((key, value) => {
    const _enum = new Enum();
    const rowTitle = $(value).find(rowTitleCss).text();
    const selectedItem = $(value).find(selectedItemCss);

    // Check if this is model
    if (_enum.isModel(rowTitle)) {
      if (selectedItem.length === 0) {
        throw new Error("Model is missing");
      }
      model = removeWhitespaces(selectedItem.text());
      return;
    }

    // Check if this is color
    if (_enum.isColor(rowTitle)) {
      if (selectedItem.length === 0) {
        throw new Error("Color is missing");
      }
      color = removeWhitespaces(selectedItem.text());
      return;
    }

    // Check if this is size
    if (_enum.isSize(rowTitle)) {
      if (selectedItem.length === 0) {
        throw new Error("Sizing is missing");
      }
      size = removeWhitespaces(selectedItem.text());
      return;
    }
    others.push(`${capitalize(rowTitle)}: ${removeWhitespaces(selectedItem.text())}`);
  });
  return {
    model,
    color,
    size,
    others
  };
};
;// CONCATENATED MODULE: ./src/stores/TaoBao.ts








class TaoBao {
  attach($document, localWindow) {
    // If not logged in, show a snackbar
    elementReady("[class^=SecurityContent--rightTips]").then(() => {
      Snackbar("Please login before you use this script");
    });

    // If logged in, just continue
    elementReady("[class^=Actions--root]").then(element => {
      const button = this._buildButton($document, localWindow);
      if (button === null) {
        return;
      }
      $(element).after(button);
    });
  }
  supports(hostname) {
    return hostname.includes("taobao.com");
  }
  _buildButton($document, window) {
    // Force someone to select an agent
    if (GM_config.get("agentSelection") === "empty") {
      GM_config.open();
      Snackbar("Please select what agent you use");
      return null;
    }

    // Get the agent related to our config
    const agent = getAgent(GM_config.get("agentSelection"));
    const $button = $(`<button id="agent-button">Add to ${agent.name}</button>`).css("width", "288px").css("color", "#FFF").css("border-color", "#F40").css("background", "#F40").css("cursor", "pointer").css("text-align", "center").css("font-family", '"Hiragino Sans GB","microsoft yahei",sans-serif').css("font-size", "16px").css("line-height", "38px").css("border-width", "1px").css("border-style", "solid").css("border-radius", "2px");
    $button.on("click", async () => {
      // Disable button to prevent double clicks and show clear message
      $button.attr("disabled", "disabled").text("Processing...");

      // Try to build and send the order
      try {
        await agent.send(this._buildOrder($document, window));
      } catch (err) {
        $button.attr("disabled", null).text(`Add to ${agent.name}`);
        return Snackbar(err);
      }
      $button.attr("disabled", null).text(`Add to ${agent.name}`);

      // Success, tell the user
      return Snackbar("Item has been added, be sure to double check it");
    });
    return $('<div class="tb-btn-add-agent" style="margin-top: 20px"></div>').append($button);
  }
  _buildShop($document, window) {
    const storeURL = $document.find("a[class^=ShopHeader]").first().attr("href");
    const authorMatches = storeURL.match(/\/\/(.+)\.taobao\.com/);
    const id = authorMatches ? authorMatches[1] : storeURL;
    const name = $document.find("[class*=-shopName-]").text();
    const url = new URL(storeURL, window.location).toString();
    return new Shop(id, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = new URLSearchParams(window.location.search).get("id");
    const name = $document.find("h1[class^=ItemTitle--mainTitle]").text();

    // Build image information
    const picSrc = $document.find("img[class^=PicGallery--mainPic]").first().attr("src");
    const imageUrl = new URL(picSrc, window.location).toString();

    // Retrieve the dynamic selected item
    const {
      model,
      color,
      size,
      others
    } = retrieveDynamicInformation($document, "[class^=SkuContent--skuItem]", "[class^=ItemLabel--labelText]", "[class*=SkuContent--isSelected]");
    return new Item(id, name, imageUrl, model, color, size, others);
  }
  _buildPrice($document) {
    return Number(removeWhitespaces($document.find("[class^=Price--priceText]").text()));
  }
  _buildShipping($document) {
    const postageText = removeWhitespaces($document.find("#J_WlServiceInfo").first().text());

    // Check for free shipping
    if (postageText.includes("快递 免运费")) {
      return 0;
    }

    // Try and get postage from text
    const postageMatches = postageText.match(/([\d.]+)/);

    // If we can't find any numbers, assume free as well, agents will fix it
    return postageMatches !== null ? Number(postageMatches[0]) : 0;
  }
  _buildOrder($document, window) {
    return new Order(this._buildShop($document, window), this._buildItem($document, window), this._buildPrice($document), this._buildShipping($document));
  }
}
;// CONCATENATED MODULE: ./src/stores/Tmall.ts







class Tmall {
  attach($document, localWindow) {
    const button = this._buildButton($document, localWindow);
    if (button === null) {
      return;
    }
    $document.find(".tb-btn-basket.tb-btn-sku").before(button);
  }
  supports(hostname) {
    return hostname === "detail.tmall.com";
  }
  _buildButton($document, window) {
    // Force someone to select an agent
    if (GM_config.get("agentSelection") === "empty") {
      GM_config.open();
      Snackbar("Please select what agent you use");
      return null;
    }

    // Get the agent related to our config
    const agent = getAgent(GM_config.get("agentSelection"));
    const $button = $(`<button id="agent-button">Add to ${agent.name}</button>`).css("width", "180px").css("color", "#FFF").css("border-color", "#F40").css("background", "#F40").css("cursor", "pointer").css("text-align", "center").css("font-family", '"Hiragino Sans GB","microsoft yahei",sans-serif').css("font-size", "16px").css("line-height", "38px").css("border-width", "1px").css("border-style", "solid").css("border-radius", "2px");
    $button.on("click", async () => {
      // Disable button to prevent double clicks and show clear message
      $button.attr("disabled", "disabled").text("Processing...");

      // Try to build and send the order
      try {
        await agent.send(this._buildOrder($document, window));
      } catch (err) {
        $button.attr("disabled", null).text(`Add to ${agent.name}`);
        return Snackbar(err);
      }
      $button.attr("disabled", null).text(`Add to ${agent.name}`);

      // Success, tell the user
      return Snackbar("Item has been added, be sure to double check it");
    });
    return $('<div class="tb-btn-add-agent"></div>').append($button);
  }
  _buildShop(window) {
    const id = window.g_config.shopId;
    const name = window.g_config.sellerNickName;
    const url = new URL(window.g_config.shopUrl, window.location).toString();
    return new Shop(id, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = window.g_config.itemId;
    const name = removeWhitespaces($document.find("#J_DetailMeta > div.tm-clear > div.tb-property > div > div.tb-detail-hd > h1").text());

    // Build image information
    const imageUrl = $document.find("#J_ImgBooth").first().attr("src");

    // Retrieve the dynamic selected item
    const {
      model,
      color,
      size,
      others
    } = retrieveDynamicInformation($document, ".tb-skin > .tb-sku > .tb-prop", ".tb-metatit", ".tb-selected");
    return new Item(id, name, imageUrl, model, color, size, others);
  }
  _buildPrice($document) {
    let price = Number(removeWhitespaces($document.find(".tm-price").first().text()));
    $document.find(".tm-price").each((key, element) => {
      const currentPrice = Number(removeWhitespaces(element.textContent));
      if (price > currentPrice) price = currentPrice;
    });
    return price;
  }
  _buildShipping($document) {
    const postageText = removeWhitespaces($document.find("#J_PostageToggleCont > p > .tm-yen").first().text());

    // Check for free shipping
    if (postageText.includes("快递 免运费")) {
      return 0;
    }

    // Try and get postage from text
    const postageMatches = postageText.match(/([\d.]+)/);

    // If we can't find any numbers, assume free as well, agents will fix it
    return postageMatches !== null ? Number(postageMatches[0]) : 0;
  }
  _buildOrder($document, window) {
    return new Order(this._buildShop(window), this._buildItem($document, window), this._buildPrice($document), this._buildShipping($document));
  }
}
;// CONCATENATED MODULE: ./src/stores/Weidian.ts








class Weidian {
  attach($document, localWindow) {
    $document.find(".footer-btn-container > span").add(".item-container > .sku-button").on("click", () => {
      // Force someone to select an agent
      if (GM_config.get("agentSelection") === "empty") {
        alert("Please select what agent you use");
        GM_config.open();
        return;
      }
      this._attachFooter($document, localWindow);
      this._attachFooterBuyNow($document, localWindow);
    });

    // Setup for storefront
    $document.on("mousedown", "div.base-ct.img-wrapper", () => {
      // Force new tab for shopping cart (must be done using actual window and by overwriting window.API.Bus)
      localWindow.API.Bus.on("onActiveSku", t => localWindow.open(`https://weidian.com/item.html?itemID=${t}&frb=open`).focus());
    });

    // Check if we are a focused screen (because of storefront handler) and open the cart right away
    if (new URLSearchParams(localWindow.location.search).get("frb") === "open") {
      $document.find("[data-spider-action-name='add_Cart']").trigger("click");
    }
  }
  supports(hostname) {
    return hostname.includes("weidian.com");
  }
  _attachFooter($document, window) {
    // Attach button the footer (buy with options or cart)
    elementReady(".sku-footer").then(element => {
      const $element = $(element);

      // Only add the button if it doesn't exist
      if ($element.parent().find("#agent-button").length !== 0) {
        return;
      }

      // Add the agent button, if we have one
      const button = this._attachButton($document, window);
      if (button === null) {
        return;
      }
      $element.after(button);
    });
  }
  _attachFooterBuyNow($document, window) {
    // Attach button the footer (buy now)
    elementReady("#login_quickLogin_wrapper").then(element => {
      const $parent = $(element).parent();

      // Only add the button if it doesn't exist
      if ($parent.parent().find("#agent-button").length !== 0) {
        return;
      }

      // Add the agent button, if we have one
      const button = this._attachButton($document, window);
      if (button === null) {
        return;
      }
      $parent.after(button);
    });
  }
  _attachButton($document, window) {
    // Force someone to select an agent
    if (GM_config.get("agentSelection") === "empty") {
      GM_config.open();
      Snackbar("Please select what agent you use");
      return null;
    }

    // Get the agent related to our config
    const agent = getAgent(GM_config.get("agentSelection"));
    const $button = $(`<button id="agent-button">Add to ${agent.name}</button>`).css("background", "#f29800").css("color", "#FFFFFF").css("font-size", "15px").css("text-align", "center").css("padding", "15px 0").css("width", "100%").css("height", "100%").css("cursor", "pointer");
    $button.on("click", async () => {
      // Disable button to prevent double clicks and show clear message
      $button.attr("disabled", "disabled").text("Processing...");

      // Try to build and send the order
      try {
        await agent.send(this._buildOrder($document, window));
      } catch (err) {
        $button.attr("disabled", null).text(`Add to ${agent.name}`);
        Snackbar(err);
        return;
      }
      $button.attr("disabled", null).text(`Add to ${agent.name}`);

      // Success, tell the user
      Snackbar("Item has been added, be sure to double check it");
      return;
    });
    return $button;
  }
  _buildShop($document, window) {
    // Setup default values for variables
    let id = null;
    let name = null;
    let url = null;

    // Try and fill the variables
    let $shop = $document.find(".shop-toggle-header-name").first();
    if ($shop.length !== 0) {
      name = removeWhitespaces($shop.text());
    }
    $shop = $document.find(".item-header-logo").first();
    if ($shop.length !== 0) {
      url = new URL($shop.attr("href"), window.location.href).toString();
      id = url.replace(/^\D+/g, "");
      name = removeWhitespaces($shop.text());
    }
    $shop = $document.find(".shop-name-str").first();
    if ($shop.length !== 0) {
      url = new URL($shop.parents("a").first().attr("href"), window.location.href).toString();
      id = url.replace(/^\D+/g, "");
      name = removeWhitespaces($shop.text());
    }

    // If no shop name is defined, just set shop ID
    if ((name === null || name.length === 0) && id !== null) {
      name = id;
    }
    return new Shop(id, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = window.location.href.match(/[?&]itemId=(\d+)/i)[1];
    const name = removeWhitespaces($document.find(".item-title").first().text());

    // Build image information
    let $itemImage = $document.find("img#skuPic");
    if ($itemImage.length === 0) $itemImage = $document.find("img.item-img");
    const imageUrl = $itemImage.first().attr("src");
    const {
      model,
      color,
      size,
      others
    } = retrieveDynamicInformation($document, ".sku-content .sku-row", ".row-title", ".sku-item.selected");
    return new Item(id, name, imageUrl, model, color, size, others);
  }
  _buildPrice($document) {
    let $currentPrice = $document.find(".sku-cur-price");
    if ($currentPrice.length === 0) $currentPrice = $document.find(".cur-price");
    return Number(removeWhitespaces($currentPrice.first().text()).replace(/(\D+)/, ""));
  }
  _buildShipping($document) {
    const $postageBlock = $document.find(".postage-block").first();
    const postageMatches = removeWhitespaces($postageBlock.text()).match(/([\d.]+)/);

    // If we can't find any numbers, assume free, agents will fix it
    return postageMatches !== null ? Number(postageMatches[0]) : 0;
  }
  _buildOrder($document, window) {
    return new Order(this._buildShop($document, window), this._buildItem($document, window), this._buildPrice($document), this._buildShipping($document));
  }
}
;// CONCATENATED MODULE: ./src/stores/Yupoo.ts






class Yupoo {
  attach($document, localWindow) {
    // Setup for item page
    const button = this._buildButton($document, localWindow);
    if (button === null) {
      return;
    }
    $document.find(".showalbumheader__tabgroup").prepend(button);
  }
  supports(hostname) {
    return hostname.includes("yupoo.com");
  }
  _buildButton($document, window) {
    // Force someone to select an agent
    if (GM_config.get("agentSelection") === "empty") {
      GM_config.open();
      Snackbar("Please select what agent you use");
      return null;
    }

    // Get the agent related to our config
    const agent = getAgent(GM_config.get("agentSelection"));
    const $button = $(`<a id="agent-button" class="button showalbumheader__copy" style="background: rgb(242, 152, 0); color: rgb(255, 255, 255);">Add to ${agent.name}</a>`);
    $button.on("click", async () => {
      // Disable button to prevent double clicks and show clear message
      $button.attr("disabled", "disabled").text("Processing...");

      // Try to build and send the order
      try {
        await agent.send(this._buildOrder($document, window));
      } catch (err) {
        $button.attr("disabled", null).text(`Add to ${agent.name}`);
        return Snackbar(err);
      }
      $button.attr("disabled", null).text(`Add to ${agent.name}`);

      // Success, tell the user
      return Snackbar("Item has been added, be sure to double check it");
    });
    return $button;
  }
  _buildShop($document, window) {
    // Setup default values for variables
    const author = window.location.hostname.replace(".x.yupoo.com", "");
    const name = $document.find(".showheader__headerTop > h1").first().text();
    const url = `https://${author}.x.yupoo.com/albums`;
    return new Shop(author, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = window.location.href.match(/albums\/(\d+)/i)[1];
    const name = removeWhitespaces($document.find("h2 > .showalbumheader__gallerytitle").first().text());

    // Build image information
    const $itemImage = $document.find(".showalbumheader__gallerycover > img").first();
    const imageUrl = new URL($itemImage.attr("src").replace("photo.yupoo.com/", "cdn.fashionreps.page/yupoo/"), window.location.href).toString();

    // Ask for dynamic information
    let color = prompt("What color (leave blank if not needed)?");
    if (color !== null && removeWhitespaces(color).length === 0) {
      color = null;
    }
    let size = prompt("What size (leave blank if not needed)?");
    if (size !== null && removeWhitespaces(size).length === 0) {
      size = null;
    }
    return new Item(id, name, imageUrl, null, color, size, []);
  }
  _buildPrice($document) {
    const priceHolder = $document.find("h2 > .showalbumheader__gallerytitle");
    let currentPrice = "0";

    // Try and find the price of the item
    const priceMatcher = priceHolder.text().match(/¥?(\d+)¥?/i);
    if (priceHolder && priceMatcher && priceMatcher.length !== 0) {
      currentPrice = priceMatcher[1];
    }
    const predeterminedPrice = Number(removeWhitespaces(currentPrice).replace(/(\D+)/, ""));
    const price = prompt("How much is the item?", String(predeterminedPrice));
    if (price === null || removeWhitespaces(price).length === 0) {
      return predeterminedPrice;
    }
    return Number(removeWhitespaces(price));
  }
  _buildOrder($document, window) {
    return new Order(this._buildShop($document, window), this._buildItem($document, window), this._buildPrice($document), 10);
  }
}
;// CONCATENATED MODULE: ./src/stores/Stores.ts





function getStore(hostname) {
  const agents = [new Store1688(), new TaoBao(), new Tmall(), new Yupoo(), new Weidian()];
  let agent = null;
  Object.values(agents).forEach(value => {
    if (value.supports(hostname)) {
      agent = value;
    }
  });
  return agent;
}
;// CONCATENATED MODULE: ./src/index.ts




// Inject config styling
GM_addStyle("div.config-dialog.config-dialog-ani { z-index: 2147483647; }");

// Setup proper settings menu
GM_config.init("Settings", {
  agentSection: {
    label: "Select your agent",
    type: "section"
  },
  agentSelection: {
    label: "Your agent",
    type: "select",
    default: "empty",
    options: {
      empty: "Select your agent...",
      basetao: "BaseTao",
      cssbuy: "CSSBuy",
      pandabuy: "PandaBuy",
      superbuy: "SuperBuy",
      wegobuy: "WeGoBuy"
    }
  }
});

// Reload page if config changed
GM_config.onclose = saveFlag => {
  if (saveFlag) {
    window.location.reload();
  }
};

// Register menu within GM
GM_registerMenuCommand("Settings", GM_config.open);

// eslint-disable-next-line func-names
(async function () {
  // Setup the logger.
  external_Logger_default().useDefaults();

  // Log the start of the script.
  external_Logger_default().info(`Starting extension '${GM_info.script.name}', version ${GM_info.script.version}`);

  // Check if we are on a store
  const store = getStore(window.location.hostname);
  if (store !== null) {
    // If we have a store handler, attach and start
    store.attach($(window.document), window.unsafeWindow);
    return;
  }

  // Check if we are on a reshipping agent
  const login = getLogin(window.location.hostname);
  if (login !== null) {
    // If we are on one, execute whatever needed for that
    login.process();
    return;
  }
  external_Logger_default().error("Unsupported website");
})();
})();

/******/ })()
;