Stores to Agent

Adds an order directly from stores to your agent

当前为 2023-06-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          Stores to Agent
// @namespace     https://www.reddit.com/user/RobotOilInc
// @version       5.0.4
// @author        RobotOilInc
// @source        https://gitlab.com/robotoilinc/stores-to-agents/
// @license       MIT
// @require       https://unpkg.com/@trim21/[email protected]/dist/gm_fetch.js#sha384-ADP6WAmhiF1sZuixRWK1+r6ltcdzzbHTxIOAJR8Adnd0rSb+m6W4b0Zqi8QizJYw
// @require       https://unpkg.com/[email protected]/dist/index.umd.min.js#sha384-IforrfCAVNj1KYlzIXNAyX2RADvDkkufuRfcM0qT57QMNdEonuvBRK2WfL8Co3JV
// @require       https://unpkg.com/[email protected]/gm4-polyfill.js#sha384-nDSbBzN1Jn1VgjQjt5u2BxaPO1pbMS9Gxyi5+yIsKYWzqkYOEh11iQdomqywYPaN
// @require       https://unpkg.com/[email protected]/dist/jquery.min.js#sha384-i61gTtaoovXtAbKjo903+O55Jkn2+RtzHtvNez+yI49HAASvznhe9sZyjaSHTau9
// @require       https://unpkg.com/[email protected]/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.0.4 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(res) {
    const r = new Response(res.response, {
        statusText: res.statusText,
        status: res.status,
        headers: parseRawHeaders(res.responseHeaders),
    });
    Object.defineProperty(r, "url", {
        value: res.finalUrl,
    });
    return r;
}

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(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, purchaseData);

    // 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_csrf_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_csrf_token]");
    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_csrf_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_csrf_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 => $.trim(string).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) {
    const button = this._buildButton($document, localWindow);
    if (button === null) {
      return;
    }
    $document.find("#detail .tb-property-x .tb-key .tb-action").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", "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" style="margin-top: 20px"></div>').append($button);
  }
  _buildShop(window) {
    const id = window.g_config.idata.shop.id;
    const name = window.g_config.shopName;
    const url = new URL(window.g_config.idata.shop.url, window.location).toString();
    return new Shop(id, name, url);
  }
  _buildItem($document, window) {
    // Build item information
    const id = window.g_config.idata.item.id;
    const name = window.g_config.idata.item.title;

    // Build image information
    const imageUrl = new URL(window.g_config.idata.item.pic, window.location).toString();

    // Retrieve the dynamic selected item
    const {
      model,
      color,
      size,
      others
    } = retrieveDynamicInformation($document, ".tb-skin > .tb-prop", ".tb-property-type", ".tb-selected");
    return new Item(id, name, imageUrl, model, color, size, others);
  }
  _buildPrice($document) {
    const promoPrice = this._buildPromoPrice($document);
    if (promoPrice !== null) {
      return promoPrice;
    }
    return Number(removeWhitespaces($document.find("#J_StrPrice > .tb-rmb-num").text()));
  }
  _buildPromoPrice($document) {
    const promoPrice = $document.find("#J_PromoPriceNum.tb-rmb-num").text();
    if (promoPrice.length === 0) {
      return null;
    }
    const promoPrices = promoPrice.split(" ");
    if (promoPrices.length !== 0) {
      return Number(promoPrices.shift());
    }
    return Number(promoPrice);
  }
  _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(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 => {
      // Only add the button if it doesn't exist
      if ($("#agent-button").length !== 0) {
        return;
      }

      // Add the agent button, if we have one
      const button = this._attachButton($document, window);
      if (button === null) {
        return;
      }
      $(element).before(button);
    });
  }
  _attachFooterBuyNow($document, window) {
    // Attach button the footer (buy now)
    elementReady(".login_plugin_wrapper").then(element => {
      // Only add the button if it doesn't exist
      if ($("#agent-button").length !== 0) {
        return;
      }

      // Add the agent button, if we have one
      const button = this._attachButton($document, window);
      if (button === null) {
        return;
      }
      $(element).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 = $(`<button id="agent-button" class="button showalbumheader__copy">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 $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];
    }
    return Number(removeWhitespaces(currentPrice).replace(/(\D+)/, ""));
  }
  _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");
})();
})();

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