Greasy Fork

Greasy Fork is available in English.

Weidian to Agent

Adds an order directly from Weidian to your agent

目前为 2021-06-28 提交的版本,查看 最新版本

// ==UserScript==
// @name         Weidian to Agent
// @namespace    https://www.reddit.com/user/RobotOilInc
// @version      2.2.2
// @description  Adds an order directly from Weidian to your agent
// @author       RobotOilInc
// @match        https://weidian.com/item.html*
// @match        https://*.weidian.com/item.html*
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @license      MIT
// @homepageURL  http://greasyfork.icu/en/scripts/427774-weidian-to-agent
// @supportURL   http://greasyfork.icu/en/scripts/427774-weidian-to-agent
// @require      https://unpkg.com/[email protected]/src/logger.min.js
// @require      https://unpkg.com/[email protected]/dist/jquery.min.js
// @require      http://greasyfork.icu/scripts/401399-gm-xhr/code/GM%20XHR.js?version=938754
// @require      http://greasyfork.icu/scripts/11562-gm-config-8/code/GM_config%208+.js?version=66657
// @connect      basetao.com
// @connect      cssbuy.com
// @connect      superbuy.com
// @connect      ytaopal.com
// @connect      wegobuy.com
// @run-at       document-end
// @icon         https://assets.geilicdn.com/fxxxx/favicon.ico
// ==/UserScript==

class Enum {
  constructor() {
    this._model = ['型号', '模型', '模型', 'model', 'type'];
    this._colors = ['颜色', '彩色', '色', '色彩', '配色', '配色方案', 'color', 'colour', 'color scheme'];
    this._sizing = ['尺寸', '尺码', '型号尺寸', '大小', '浆液', '码数', '码', 'size', 'sizing'];
  }

  _arrayContains(array, query) {
    return array.filter((item) => query.toLowerCase().indexOf(item.toLowerCase()) !== -1).length !== 0;
  }

  isModel(item) {
    return this._arrayContains(this._model, item);
  }

  isColor(item) {
    return this._arrayContains(this._colors, item);
  }

  isSize(item) {
    return this._arrayContains(this._sizing, item);
  }
}

class Item {
  /**
  * @param id {string|null}
  * @param name {string|null}
  * @param imageUrl {string|null}
  * @param model {string|null}
  * @param color {string|null}
  * @param size {string|null}
  * @param others {Array}
  */
  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 id() {
    return this._id;
  }

  get name() {
    return this._name;
  }

  get imageUrl() {
    return this._imageUrl;
  }

  get model() {
    return this._model;
  }

  get color() {
    return this._color;
  }

  get size() {
    return this._size;
  }

  /**
  * @return {string}
  */
  get other() {
    return this._others.join(', ');
  }
}

class Order {
  /**
  * @param shop {Shop}
  * @param item {Item}
  * @param price {Number}
  * @param shipping {Number}
  */
  constructor(shop, item, price, shipping) {
    this._shop = shop;
    this._item = item;
    this._price = price;
    this._shipping = shipping;
  }

  get shop() {
    return this._shop;
  }

  get item() {
    return this._item;
  }

  get price() {
    return this._price;
  }

  get shipping() {
    return this._shipping;
  }
}

class Shop {
  /**
  * @param id {null|string}
  * @param name {null|string}
  * @param url {null|string}
  */
  constructor(id, name, url) {
    this._shopId = id;
    this._shopName = name;
    this._shopUrl = url;
  }

  /**
  * @returns {null|string}
  */
  get id() {
    return this._shopId;
  }

  /**
  * @returns {null|string}
  */
  get name() {
    return this._shopName;
  }

  /**
  * @returns {null|string}
  */
  get url() {
    return this._shopUrl;
  }
}

/**
 * Creates a SKU toast, which is shown because of Weidians CSS
 *
 * @param toast {string}
 */
const Snackbar = function (toast) {
  const $toast = $(`<div class="sku-toast">${toast}</div>`).css('font-size', '20px');

  // Append the toast to the body
  $('.sku-body').append($toast);

  // Set a timeout to remove it
  setTimeout(() => $toast.fadeOut('slow', () => { $toast.remove(); }), 2000);
};

/**
 * @param s {string|undefined}
 * @returns {string}
 */
const capitalize = (s) => (s && s[0].toUpperCase() + s.slice(1)) || '';

/**
 * Waits for an element satisfying selector to exist, then resolves promise with the element.
 * Useful for resolving race conditions.
 *
 * @param selector {string}
 * @returns {Promise}
 */
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 });
  });
};

class BaseTaoError extends Error {
  constructor(message) {
    super(message);
    this.name = 'BaseTaoError';
  }
}

/**
 * Trims the input text and removes all inbetween spaces as well.
 *
 * @param string {string}
 */
const removeWhitespaces = (string) => string.trim().replace(/\s(?=\s)/g, '');

class BaseTao {
  get name() {
    return 'BaseTao';
  }

  /**
   * @param order {Order}
   */
  async send(order) {
    // Build the purchase data
    const purchaseData = await this._buildPurchaseData(order);

    Logger.info('Sending order to BaseTao...', purchaseData);

    // Do the actual call
    await $.ajax({
      url: 'https://www.basetao.com/index/Ajax_data/buyonecart',
      data: purchaseData,
      type: 'POST',
      headers: {
        origin: 'https://www.basetao.com',
        referer: 'https://www.basetao.com/index/selfhelporder.html',
        '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',
      },
    }).then((response) => {
      if (removeWhitespaces(response) === '1') {
        return;
      }

      Logger.error('Item could not be added', response);
      throw new BaseTaoError('Item could not be added, make sure you are logged in');
    }).catch((err) => {
      // If the error is our own, just rethrow it
      if (err instanceof BaseTaoError) {
        throw err;
      }

      Logger.error('An error happened when uploading the order', err);
      throw new Error('An error happened when adding the order');
    });
  }

  /**
   * @private
   * @param order {Order}
   */
  async _buildPurchaseData(order) {
    // Get the CSRF token
    const csrf = await this._getCSRF();

    // Build the data we will send
    return {
      csrf_test_name: csrf,
      color: order.item.color,
      size: order.item.size,
      number: 1,
      pric: order.price,
      shipping: order.shipping,
      totalpric: order.price + order.shipping,
      t_title: order.item.name,
      t_seller: order.shop.name,
      t_img: order.item.imageUrl,
      t_href: window.location.href,
      s_url: window.location.href,
      buyyourself: 1,
      note: this._buildRemark(order),
      site: null,
    };
  }

  /**
   * @private
   * @returns {string}
   */
  async _getCSRF() {
    // Grab data from BaseTao
    const data = await $.get('https://www.basetao.com/index/selfhelporder.html');

    // Check if user is actually logged in
    if (data.indexOf('long time no operation ,please sign in again') !== -1) {
      throw new Error('You need to be logged in on BaseTao to use this extension (CSRF).');
    }

    // Convert into jQuery object
    const $data = $(data);

    // Get the username
    const username = $data.find('#dropdownMenu1').text();
    if (typeof username === 'undefined' || username == null || username === '') {
      throw new Error('You need to be logged in on BaseTao to use this extension (CSRF).');
    }

    // Return CSRF
    return $data.find('input[name=csrf_test_name]').first().val();
  }

  /**
   * @private
   * @param order {Order}
   * @returns {string|null}
   */
  _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;
  }
}

class CSSBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'CSSBuyError';
  }
}

class CSSBuy {
  get name() {
    return 'CSSBuy';
  }

  /**
   * @param order {Order}
   */
  async send(order) {
    // Build the purchase data
    const purchaseData = this._buildPurchaseData(order);

    Logger.info('Sending order to CSSBuy...', purchaseData);

    // Do the actual call
    await $.ajax({
      url: 'https://www.cssbuy.com/ajax/fast_ajax.php?action=buyone',
      data: purchaseData,
      dataType: 'json',
      type: 'POST',
      headers: {
        origin: 'https://www.cssbuy.com/item.html',
        referer: 'https://www.cssbuy.com/item.html',
        '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',
      },
    }).then((response) => {
      if (response.ret === 0) {
        return;
      }

      Logger.error('Item could not be added', response);
      throw new CSSBuyError('Item could not be added');
    }).catch((err) => {
      // If the error is our own, just rethrow it
      if (err instanceof CSSBuyError) {
        throw err;
      }

      Logger.error('An error happened when uploading the order', err);
      throw new Error('An error happened when adding the order');
    });
  }

  /**
   * @private
   * @param order {Order}
   * @return {object}
   */
  _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,
        colorProp: null,
        size: order.item.size,
        sizeProp: null,
        usd_price: null,
        usd_freight: null,
        usd_total_price: null,
        total: order.price + order.shipping,
        buyyourself: 0,
        seller: order.shop.name,
        href: window.location.href,
        title: order.item.name,
        note: description,
        expressno: null,
        promotionCode: null,
        option: description,
      },
    };
  }

  /**
   * @private
   * @param order {Order}
   * @returns {string|null}
   */
  _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;
  }
}

class SuperBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'SuperBuyError';
  }
}

class TaoCartsBuilder {
  /**
   * @param order {Order}
   */
  purchaseData(order) {
    // Build the description
    const description = this._buildRemark(order);

    // Generate an SKU based on the description
    // eslint-disable-next-line no-bitwise
    const 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',
        }],
      }],
    };
  }

  /**
   * @param order {Order}
   * @returns {string|null}
   */
  _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;
  }
}

class SuperBuy {
  constructor() {
    this._builder = new TaoCartsBuilder();
  }

  get name() {
    return 'SuperBuy';
  }

  /**
   * @param order {Order}
   */
  async send(order) {
    // Build the purchase data
    const purchaseData = this._builder.purchaseData(order);

    Logger.info('Sending order to SuperBuy...', purchaseData);

    // Do the actual call
    await $.ajax({
      url: 'https://front.superbuy.com/cart/add-cart',
      data: JSON.stringify(purchaseData),
      dataType: 'json',
      type: 'POST',
      headers: {
        origin: 'https://www.superbuy.com',
        referer: 'https://www.superbuy.com/',
        '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',
      },
    }).then((response) => {
      if (response.state === 0 && response.msg === 'Success') {
        return;
      }

      Logger.error('Item could not be added', response.msg);
      throw new SuperBuyError('Item could not be added');
    }).catch((err) => {
      // If the error is our own, just rethrow it
      if (err instanceof SuperBuyError) {
        throw err;
      }

      Logger.error('An error happened when uploading the order', err);
      throw new Error('An error happened when adding the order');
    });
  }
}

class WeGoBuyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'WeGoBuyError';
  }
}

class WeGoBuy {
  constructor() {
    this._builder = new TaoCartsBuilder();
  }

  get name() {
    return 'WeGoBuy';
  }

  /**
   * @param order {Order}
   */
  async send(order) {
    // Build the purchase data
    const purchaseData = this._builder.purchaseData(order);

    Logger.info('Sending order to WeGoBuy...', purchaseData);

    // Do the actual call
    await $.ajax({
      url: 'https://front.wegobuy.com/cart/add-cart',
      data: JSON.stringify(purchaseData),
      dataType: 'json',
      type: 'POST',
      headers: {
        origin: 'https://www.wegobuy.com',
        referer: 'https://www.wegobuy.com/',
        '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',
      },
    }).then((response) => {
      if (response.state === 0 && response.msg === 'Success') {
        return;
      }

      Logger.error('Item could not be added', response.msg);
      throw new WeGoBuyError('Item could not be added');
    }).catch((err) => {
      // If the error is our own, just rethrow it
      if (err instanceof WeGoBuyError) {
        throw err;
      }

      Logger.error('An error happened when uploading the order', err);
      throw new Error('An error happened when adding the order');
    });
  }
}

class YtaopalError extends Error {
  constructor(message) {
    super(message);
    this.name = 'YtaopalError';
  }
}

class Ytaopal {
  get name() {
    return 'Ytaopal';
  }

  /**
   * @param order {Order}
   */
  async send(order) {
    // Build the purchase data
    const purchaseData = this._buildPurchaseData(order);

    Logger.info('Sending order to Ytaopal...', purchaseData);

    // Do the actual call
    await $.ajax({
      url: 'https://www.ytaopal.com/Cart/Add',
      data: purchaseData,
      dataType: 'json',
      type: 'POST',
      headers: {
        origin: 'https://www.ytaopal.com/Cart/Add',
        referer: 'https://www.ytaopal.com/Cart/Add',
        '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',
      },
    }).then((response) => {
      if (response.status !== 0) {
        return;
      }

      Logger.error('Item could not be added', response);
      throw new YtaopalError(response.info);
    }).catch((err) => {
      // If the error is our own, just rethrow it
      if (err instanceof YtaopalError) {
        throw err;
      }

      Logger.error('An error happened when uploading the order', err);
      throw new Error('An error happened when adding the order');
    });
  }

  /**
   * @private
   * @param order {Order}
   * @return {object}
   */
  _buildPurchaseData(order) {
    // Build the description
    const description = this._buildRemark(order);

    // Create the purchasing data
    return {
      buytype: null,
      cart_price: order.price,
      id: order.item.id,
      ItemID: order.item.id,
      ItemName: order.item.name,
      ItemNameCN: order.item.name,
      ItemNick: '微店', // Weidian
      ItemPic: order.item.imageUrl,
      ItemURL: window.location.href,
      LocalFreight: order.shipping,
      promotionid: null,
      PropID: null,
      quantity: 1,
      remark: description,
      sku_id: null,
      sku_num: null,
    };
  }

  /**
   * @private
   * @param order {Order}
   * @returns {string|null}
   */
  _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;
  }
}

/**
 * @param agentSelection
 * @returns {*}
 */
const getAgent = (agentSelection) => {
  switch (agentSelection) {
    case 'basetao':
      return new BaseTao();
    case 'cssbuy':
      return new CSSBuy();
    case 'superbuy':
      return new SuperBuy();
    case 'wegobuy':
      return new WeGoBuy();
    case 'ytaopal':
      return new Ytaopal();
    default:
      throw new Error(`Agent '${agentSelection}' is not implemented`);
  }
};

class Weidian {
  /**
   * @param $document
   */
  attach($document) {
    // Setup for when someone presses the buy button
    $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;
      }

      // Attach button the the footer
      elementReady('.sku-footer').then((element) => {
        $(element).before(this._attachButton($document));
      });
    });
  }

  /**
   * @private
   * @param $document
   */
  _attachButton($document) {
    // Get the agent related to our config
    const agent = getAgent(GM_config.get('agentSelection'));

    const $button = $(`<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', true).text('Processing...');

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

      $button.attr('disabled', false).text(`Add to ${agent.name}`);

      // Success, tell the user
      return Snackbar('Item has been added, be sure to double check it');
    });

    return $button;
  }

  /**
   * @private
   * @param $document
   * @return {Shop}
   */
  _buildShop($document) {
    // 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 = $shop.attr('href').replace('//weidian.com', 'https://weidian.com');
      id = url.replace(/^\D+/g, '');
      name = removeWhitespaces($shop.text());
    }

    $shop = $document.find('.shop-name-str').first();
    if ($shop.length !== 0) {
      url = $shop.parents('a').first().attr('href').replace('//weidian.com', 'https://weidian.com');
      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);
  }

  /**
   * @private
   * @param $document
   * @return {Item}
   */
  _buildItem($document) {
    const _enum = new Enum();

    // Build item information
    const id = window.location.href.match(/[?&]itemId=(\d+)/i)[1];
    const name = removeWhitespaces($document.find('.item-title').first().text());
    const imageUrl = $document.find('img#skuPic').first().attr('src');

    // Create dynamic items
    let model = null;
    let color = null;
    let size = null;
    const others = [];

    // Load dynamic items
    $document.find('.sku-content .sku-row').each((key, value) => {
      const rowTitle = $(value).find('.row-title').text();
      const selectedItem = $(value).find('.sku-item.selected');

      // 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 new Item(id, name, imageUrl, model, color, size, others);
  }

  /**
   * @private
   * @param $document
   * @return {Order}
   */
  _buildOrder($document) {
    // Build order price
    const $currentPrice = $document.find('.sku-cur-price');
    const price = Number(removeWhitespaces($currentPrice.first().text()).replace(/(\D+)/, ''));

    // Decide on shipping (if we can't find any numbers, assume free)
    const $postageBlock = $document.find('.postage-block').first();
    const postageMatches = removeWhitespaces($postageBlock.text()).match(/([\d.]+)/);
    const shipping = postageMatches !== null ? Number(postageMatches[0]) : 0;

    return new Order(this._buildShop($document), this._buildItem($document), price, shipping);
  }
}

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

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

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

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

// Setup GM_XHR
$.ajaxSetup({ xhr() { return new GM_XHR(); } });

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

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

  // Actually start extension
  (new Weidian()).attach($(window.document));
}());