pntan / CÔNG CỤ HỖ TRỢ V4

// ==UserScript==
// @name         CÔNG CỤ HỖ TRỢ V4
// @version      0.0.4
// @namespace    tanphan.toolv3
// @icon         https://www.google.com/s2/favicons?sz=64&domain=http://anonymouse.org/
// @description  Một số công cụ hỗ trợ công việc
// @license      MIT
// @author       TânPhan
// @copyright    2025, TanPhan (nhattanphan2014@gmail.com)
// @match        *://*/*
// @grant        none
// @updateURL    https://openuserjs.org/meta/pntan/CÔNG_CỤ_HỖ_TRỢ_V4.meta.js
// @downloadURL  https://openuserjs.org/install/pntan/CÔNG_CỤ_HỖ_TRỢ_V4.user.js
// @require      https://code.jquery.com/jquery-3.7.1.min.js
// @require      https://code.jquery.com/ui/1.13.2/jquery-ui.min.js
// ==/UserScript==
(function() {
  'use strict';

  const VERSION = '0.0.1';
  const X_LIMIT = 5;

  // --- ĐÃ CẬP NHẬT: Thêm nút action-btn vào gia_duoi_layout ---
  const HTML_UI = `<style>.tp-success-bg{background:#45d87d}</style><div class="tp-container tp-toast"></div><style>.tp-container.tp-toast{width:fit-content;height:auto;position:fixed;margin-left:50%;top:5%;transform:translate(-50%);z-index:999999999;display:flex;flex-direction:column;flex-wrap:wrap;gap:2vh}.tp-container.tp-toast .toast{padding:1vh 1vw;background:#fff;border-radius:10px;color:#fff;text-shadow:0 0 1px #121212,0 0 1px #121212,0 0 1px #121212,0 0 1px #121212,0 0 1px #121212}.tp-container.tp-toast .toast.info{background:rgba(80,220,245,.7)}.tp-container.tp-toast .toast.success{background:rgba(111,255,155,.7)}.tp-container.tp-toast .toast.error{background:rgba(245,80,80,.7)}.tp-container.tp-toast .toast.warning{background:rgba(245,229,80,.7)}</style><div class="tp-container tp-main"><div class="header"><div class="time">00:00:00</div><div class="help">Hướng Dẫn</div><div class="theme-switcher"><button class="btn-theme light-mode active"data-theme="light">☀️</button> <button class="btn-theme dark-mode"data-theme="dark">🌙</button></div></div><div class="list-screen"><div class="box-screen setting"data-screen="setting"><p>⚙️</p></div><div class="box-screen main"data-screen="main"><p>🏡</p></div><div class="box-screen online"data-screen="online"><p>🖥️</p></div></div><div class="content-screen"><div class="screen screen-setting"><p>Setting Screen</p></div><div class="screen screen-main active"><div class="list-function active"><div class="box-function"><p>Function 1</p></div><div class="box-function"><p>Function 2</p></div><div class="box-function"><p>Function 3</p></div><div class="box-function"><p>Function 4</p></div><div class="box-function"><p>Function 5</p></div><div class="box-function"><p>Function 6</p></div><div class="box-function"><p>Function 7</p></div></div><div class="layout-function"><div class="back">Trở Lại</div><div class="box gia_duoi show"id="gia_duoi_layout"><p>GIÁ ĐUÔI</p><button class="action-btn"data-action="gia_duoi">THỰC HIỆN SỬA GIÁ</button></div></div></div><div class="screen screen-online"><p>Online Screen</p></div></div><style>.tp-container{padding:0;margin:0;border:none;box-sizing:border-box}.tp-container *{padding:0;margin:0;border:0;box-sizing:border-box;font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;font-weight:700;user-select:none}.tp-container ::-webkit-scrollbar{height:6px;width:6px}.tp-container ::-webkit-scrollbar-track{border-radius:20px;background-color:#000}.tp-container ::-webkit-scrollbar-track:hover{background-color:#5a5e5f}.tp-container ::-webkit-scrollbar-track:active{background-color:#ff9e9e}.tp-container ::-webkit-scrollbar-thumb{border-radius:20px;background-color:#eaeaea}.tp-container ::-webkit-scrollbar-thumb:hover{background-color:#a36f6f}.tp-container ::-webkit-scrollbar-thumb:active{background-color:#888bce}.tp-container .action-btn{margin-top:10px;padding:5px 10px;background:#90ee90;border-radius:5px;cursor:pointer;color:#121212}.tp-container.tp-main{top:0;position:fixed;background:rgba(223,223,223,.5);backdrop-filter:blur(10px);width:0;padding:0;height:109%;color:#fff;z-index:999999998;transition:.5s}.tp-container.tp-main.active,.tp-container.tp-main:hover{width:60vw;height:100%;padding:2vh 2vw}.tp-container.tp-main .header{display:flex;justify-content:space-between;align-items:center;width:100%;height:3vh;color:#000;overflow:hidden}.tp-container.tp-main .header .time{font-size:1.5vh;letter-spacing:1rcap}.tp-container.tp-main .header .help{color:#a79dff;cursor:help}.tp-container.tp-main .header .theme-switcher{position:relative;width:auto;height:100%;aspect-ratio:1/1}.tp-container.tp-main .header .theme-switcher .btn-theme{position:absolute;height:100%;border-radius:50%;font-size:2vh;cursor:pointer;background:0 0;transition:.5s}.tp-container.tp-main .header .theme-switcher .btn-theme.active{top:0!important;left:0}.tp-container.tp-main .header .theme-switcher .btn-theme.light-mode{top:-100%;left:0}.tp-container.tp-main .header .theme-switcher .btn-theme.dark-mode{top:100%;left:0}.tp-container.tp-main .list-screen{margin-top:2vh;display:flex;flex-direction:row;justify-content:flex-start;align-items:center;width:100%;height:4vh;overflow-y:auto}.tp-container.tp-main .list-screen .box-screen{width:100%;height:4vh;background:rgba(0,0,0,.1);backdrop-filter:blur(5px);display:flex;justify-content:center;align-items:center;font-size:2vh;color:#000;cursor:pointer}.tp-container.tp-main .list-screen .box-screen.active{background:rgba(0,0,0,.3);backdrop-filter:blur(10px)}.tp-container.tp-main .list-screen .box-screen:hover p{transform:scale(1.3);transition:.3s}.tp-container.tp-main .list-screen .box-screen:first-child{border-top-left-radius:20px;border-bottom-left-radius:20px}.tp-container.tp-main .list-screen .box-screen:last-child{border-top-right-radius:20px;border-bottom-right-radius:20px}.tp-container.tp-main .content-screen{margin-top:2vh;width:100%;height:calc(100% - 15vh);background:rgba(0,0,0,.1);backdrop-filter:blur(5px);border-radius:20px;overflow:hidden;position:relative}.tp-container.tp-main .content-screen .screen{width:100%;height:100%;color:#000;transition:.5s;position:absolute;padding:2vh 2vw}.tp-container.tp-main .content-screen .screen.screen-setting{top:0;left:-100%}.tp-container.tp-main .content-screen .screen.screen-main{top:100%;left:0;position:relative;width:100%}.tp-container.tp-main .content-screen .screen.screen-main .list-function{width:0;height:100%;margin:0 auto;overflow-y:scroll;overflow:hidden;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-around;align-items:flex-start;align-content:flex-start;gap:2vw;transition:.5s}.tp-container.tp-main .content-screen .screen.screen-main .list-function.active{width:100%}.tp-container.tp-main .content-screen .screen.screen-main .list-function .box-function{width:auto;height:4vh;line-height:auto;background:#fff;display:flex;flex-direction:row;justify-content:center;align-items:center;border-radius:10px;padding:2vh 2vw;word-break:keep-all}.tp-container.tp-main .content-screen .screen.screen-main .layout-function{position:absolute;top:2vh;left:2vw;width:100%;height:0;transition:.5s;overflow:hidden}.tp-container.tp-main .content-screen .screen.screen-main .layout-function.active{height:100%}.tp-container.tp-main .content-screen .screen.screen-main .layout-function .back{width:100%;height:4vh;line-height:4vh;font-weight:bolder;cursor:pointer}.tp-container.tp-main .content-screen .screen.screen-main .layout-function .box{width:0;height:0;opacity:0;transition:.5s;background:rgba(0,0,0,.1);backdrop-filter:blur(5px);border-radius:10px;padding:10px;margin-bottom:10px}.tp-container.tp-main .content-screen .screen.screen-main .layout-function .box.show{width:100%;height:auto;opacity:1}.tp-container.tp-main .content-screen .screen.screen-online{top:0;left:100%}.tp-container.tp-main .content-screen .screen.active{top:0;left:0}</style></div>`;

  // Khởi tạo biến toàn cục
  var INFO_PAGE = null;
  // --- KHU VỰC ĐỊNH NGHĨA CÁC HÀM CHỨC NĂNG --- (Đã chuyển lên trên func_list)
  // var funcTest = () => {
  //     boxAlert("Hàm thử nghiệm ĐÃ CHẠY", "success");
  // }

  /**
   * @func gia_duoi
   * @description 'Sửa giá khuyễn mãi bằng giá đuôi'
   */
  var gia_duoi = () => {
    boxAlert("SỬA GIÁ THEO GIÁ ĐUÔI", "info");
    if (INFO_PAGE.url.host.split(".").includes("shopee"))
      shopee();

    async function shopee() {
      var box = $(".discount-items .discount-item-component");
      if (box.length == 1) {
        boxAlert("Không tìm thấy sản phẩm");
        boxToast("Không tìm thấy sản phẩm", "error");
        return;
      }

      var indexBox = 0;

      async function nextBox() {
        if (indexBox > box.length) {
          boxAlert("Đã hoàn tất cập nhật giá");
          boxToast("Đã hoàn tất cập nhật giá");
          return;
        }

        var varianty = box.eq(indexBox).find(".discount-edit-item-model-component");

        var indexVarianty = 0;

        async function nextVarianty() {
          if (indexVarianty > varianty.length) {
            return;
          }

          var variant_name = varianty.eq(indexVarianty).find(".item-content.item-variation");
          console.log(variant_name.text());
          var variant_current_price = varianty.eq(indexVarianty).find(".item-content.item-price");
          var variant_discount_price = varianty.eq(indexVarianty).find(".eds-input.currency-input input");
          var variant_discount_percent = varianty.eq(indexVarianty).find(".eds-input.discount-input input");

          var variant_switch = varianty.eq(indexVarianty).find(".item-content.item-enable-disable");

          if(variant_switch.find(".eds-switch--disabled").length == 0){
            if(variant_switch.find(".eds-switch--close").length > 0){
              simulateReactEvent(variant_switch.find(".eds-switch--close"), "click");
            }
          }else{
            indexVarianty++;
            nextVarianty();
            return;
          }

          await delay(500);

          var giaDuoi = tachGia(variant_current_price.text()).giaDuoi;

          console.log(giaDuoi);


          if (parseInt(giaDuoi) == 0) {
            giaDuoi = Math.round(parseInt(flatPrice(variant_current_price.text())) - 1000);
            boxToast(`Giá đuôi đã được điều chỉnh = ${giaDuoi} do không tìm thấy giá đuôi`);
          } else if (parseInt(giaDuoi) < parseInt(flatPrice(variant_current_price.text())) / 2) {
            giaDuoi = Math.round((parseInt(flatPrice(variant_current_price.text())) / 2) - 1000);
            boxToast(`Giá đuôi đã được điều chỉnh = ${giaDuoi} do giảm quá 50%`, "warning");
          }

          variant_discount_price.val(giaDuoi);
          simulateReactEvent(variant_discount_price, "input");

          varianty.eq(indexVarianty).addClass("tp-success-bg");

          indexVarianty++;
          await nextVarianty();
        }
        await nextVarianty();
        indexBox++;
        await nextBox();
      }
      await nextBox();
    }
    // Logic thực thi sửa giá sẽ nằm ở đây
  }
  // -------------------------------------------------------------------------

  // Định nghĩa các chức năng
  const func_list = [
        // {
        //     name: "Thử Nghiệm",
        //     func: funcTest,
        //     func_name: "funcTest",
        //     layout_name: "",
        //     platform: ["*"]
        // },
    {
      name: "Sửa Giá Theo Giá Đuôi",
      func: gia_duoi,
      func_name: "gia_duoi",
      layout_name: "",
      platform: ["shopee"]
        },
    ];

  /**
   * @func excuseFunction
   * @description 'Tìm và thực thi hàm dựa trên func_name'
   */
  function excuseFunction(name) {
    var func = func_list.find(el => el.func_name === name);
    if (func && func.func)
      func.func();
    else {
      boxAlert(`Không tìm thấy hàm thực thi cho: ${name}`, "error");
      return;
    }
  }

  function flatPrice(price) {
    return ((price.replace(".", "").replace(",", "").replace("₫", "")).trim());
  }

  /**
   * @function findElement
   * @description Tìm kiếm phần tử DOM hỗ trợ kết hợp CSS chuẩn, tiền tố tùy chỉnh và Computed Style (cs).
   * @param {string} selectorString - Chuỗi tìm kiếm kết hợp (ví dụ: '.product[cs:color:purple][tx:Xem chi tiết]').
   * @param {object} context - Phạm vi tìm kiếm (mặc định là document).
   * @returns {object} jQuery object chứa các phần tử được tìm thấy.
   */
  function findElement(selectorString, context = document) {
    const $context = $(context);
    let finalSelector = selectorString;
    let textToFind = null;
    let styleFilters = []; // Mảng chứa các bộ lọc CSS Style

    // --- BƯỚC 1: Xử lý tiền tố tùy chỉnh (tx, cs) và loại bỏ chúng khỏi chuỗi selector CSS ---

    // 1a. Xử lý tiền tố Text (tx)
    const textMatch = finalSelector.match(/\[tx:([^\]]+)\]/i);
    if (textMatch) {
      textToFind = textMatch[1].trim();
      finalSelector = finalSelector.replace(textMatch[0], '');
      console.log(`[findElement] Trích xuất Text (tx): "${textToFind}".`);
    }

    // 1b. Xử lý tiền tố Computed Style (cs:property:value)
    // Pattern: [cs:prop:value] hoặc [cs:prop:value1:value2] (cho giá trị có dấu :)
    const styleMatches = finalSelector.match(/\[cs:([^\]]+)\]/ig);
    if (styleMatches) {
      styleMatches.forEach(match => {
        // Tách 'prop:value' từ [cs:prop:value]
        const content = match.slice(4, -1);
        const parts = content.split(':');

        if (parts.length >= 2) {
          const property = parts[0].trim();
          // Nối các phần tử còn lại thành giá trị, phòng trường hợp giá trị chứa dấu ':'
          const value = parts.slice(1).join(':').trim();
          styleFilters.push({
            property: property,
            value: value
          });
        }
        finalSelector = finalSelector.replace(match, '');
      });
      console.log(`[findElement] Trích xuất ${styleFilters.length} bộ lọc Style (cs).`);
    }

    // --- BƯỚC 2: Chuyển đổi các tiền tố thuộc tính DOM thành cú pháp CSS Selector ---

    // a) Input Type (tp:submit) -> [type="submit"]
    let tempSelector = finalSelector.replace(/\[tp:([^\]]+)\]/ig, (match, value) => `[type="${value.trim()}"]`);

    // b) Role (rl:button) -> [role="button"] (Accessibility)
    tempSelector = tempSelector.replace(/\[rl:([^\]]+)\]/ig, (match, value) => `[role="${value.trim()}"]`);

    // c) Aria-Label (lb:Giỏ Hàng) -> [aria-label="Giỏ Hàng"] (Accessibility)
    finalSelector = tempSelector.replace(/\[lb:([^\]]+)\]/ig, (match, value) => `[aria-label="${value.trim()}"]`);

    // --- BƯỚC 3: Thực hiện tìm kiếm bằng CSS Selector chuẩn ---
    console.log(`[findElement] CSS Selector cuối cùng được sử dụng: ${finalSelector}`);
    let $results = $context.find(finalSelector);

    // --- BƯỚC 4: Áp dụng bộ lọc Computed Style (cs) ---
    if (styleFilters.length > 0) {
      console.log(`[findElement] Áp dụng bộ lọc Computed Style.`);
      $results = $results.filter(function() {
        const $this = $(this);
        // Kiểm tra từng bộ lọc Style
        return styleFilters.every(filter => {
          // Sử dụng .css() của jQuery để lấy giá trị Computed Style
          const computedValue = $this.css(filter.property);

          // Lưu ý: Màu sắc thường được trả về dưới dạng RGB (ví dụ: rgb(128, 0, 128) thay vì 'purple')
          // Chúng ta cần so sánh giá trị computed với giá trị mong muốn.
          return computedValue && computedValue.toLowerCase() === filter.value.toLowerCase();
        });
      });
    }

    // --- BƯỚC 5: Áp dụng bộ lọc Text (tx) ---
    if (textToFind) {
      console.log(`[findElement] Áp dụng bộ lọc Text (tx).`);
      $results = $results.filter(function() {
        // Đảm bảo phần tử chứa text
        return $(this).text().includes(textToFind);
      });
    }

    console.log(`[findElement] Tìm thấy ${$results.length} phần tử.`);
    return $results;
  }

  /**
   * @func delay
   * @description 'Tăng thời gian chờ'
   */
  function delay(ms = 5000) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * @func boxAlert
   * @description 'Ghi console log với định dạng đẹp'
   */
  function boxAlert(content, type = "log") {
    switch (type) {
      case "log":
        console.log(`%cTanPhan: %c${content}`, "color: crimson; font-size: 2rem", "color: orange; font-size: 1.5rem");
        break;
      case "error":
        console.error(`%cTanPhan: %c${content}`, "color: crimson; font-size: 2rem", "color: orange; font-size: 1.5rem")
        break;
      case "warn":
        console.warn(`%cTanPhan: %c${content}`, "color: crimson; font-size: 2rem", "color: orange; font-size: 1.5rem");
        break;
      case "success":
        console.log(`%cTanPhan: %c${content}`, "color: green; font-size: 2rem", "color: lightgreen; font-size: 1.5rem");
        break;
      case "info":
        console.log(`%cTanPhan: %c${content}`, "color: blue; font-size: 2rem", "color: skyblue; font-size: 1.5rem");
        break;
    }
  }

  // =========================================================================
  // KHU VỰC QUẢN LÝ CẤU HÌNH
  // =========================================================================

  /**
   * @func getConfig
   * @description 'Lấy cấu hình'
   */
  var getConfig = (config_name) => {
    var config_value = localStorage.getItem(`TP_CONFIG_${config_name}`);
    if (config_value === null) return null;

    try {
      return JSON.parse(config_value);
    } catch (e) {
      console.error(`Lỗi parse cấu hình ${config_name}`, e);
      return null;
    }
  }

  /**
   * @func setConfig
   * @description 'Ghi cấu hình'
   */
  var setConfig = (config_name, config_value) => {
    localStorage.setItem(`TP_CONFIG_${config_name}`, JSON.stringify(config_value));
  }

  // Giả lập kéo thả tệp vào một phần tử (element)
  function simulateFileDrop(targetElement, files = [], options = {}) {
    var el = targetElement[0] || targetElement; // Đảm bảo el là DOM element

    if (!el) {
      console.warn("simulateFileDrop: Target element not found.");
      return;
    }

    var dataTransfer = new DataTransfer();
    files.forEach(file => {
      // Thay vì kiểm tra instanceof File, kiểm tra instanceof Blob
      // vì File kế thừa từ Blob và Blob ít bị ảnh hưởng bởi ngữ cảnh hơn trong trường hợp này.
      // Hoặc chỉ cần kiểm tra sự tồn tại của các thuộc tính cần thiết của một File/Blob.
      if (file && (file instanceof Blob || (typeof file.name === 'string' && typeof file.size === 'number' && typeof file.type === 'string'))) {
        dataTransfer.items.add(file);
      } else {
        console.warn("simulateFileDrop: Invalid file object provided. Must be an instance of File.", file);
        // Log chi tiết hơn để debug
        console.log("Details of invalid file:", file);
        if (file) {
          console.log("File constructor name:", file.constructor ? file.constructor.name : "N/A");
          try {
            console.log("Is file instanceof window.File?", file instanceof window.File);
            // Có thể thêm kiểm tra instanceof Blob của cửa sổ chính
            console.log("Is file instanceof window.Blob?", file instanceof window.Blob);
          } catch (e) {
            console.log("Error checking instanceof in window context:", e);
          }
        }
      }
    });

    if (dataTransfer.items.length === 0) {
      console.warn("simulateFileDrop: No valid files were added to DataTransfer.", files);
      return; // Không có file nào hợp lệ để kéo thả
    }

    const dragEvents = ['dragenter', 'dragover', 'drop'];

    dragEvents.forEach(eventType => {
      var event;
      if (eventType === 'dragenter' || eventType === 'dragover') {
        event = new DragEvent(eventType, {
          bubbles: true,
          cancelable: true,
          dataTransfer: dataTransfer,
          ...options
        });
        event.preventDefault();
      } else if (eventType === 'drop') {
        event = new DragEvent(eventType, {
          bubbles: true,
          cancelable: true,
          dataTransfer: dataTransfer,
          ...options
        });
        event.preventDefault();
      } else {
        event = new DragEvent(eventType, {
          bubbles: true,
          cancelable: true,
          ...options
        });
      }
      el.dispatchEvent(event);
      console.log(`Dispatched ${eventType} event on`, el);
    });
  }

  // Hàm giả lập thao tác người dùng (đã sửa đổi)
  function simulateReactEvent(input, type, options = {}) {
    var el = input[0];

    if (!el) {
      console.warn(`simulateReactEvent: Element not found for eventType ${type}.`);
      return;
    }

    // Hàm con để xử lý sự kiện bàn phím
    function pressKey(keyName) {
      var keyMap = {
        enter: {
          key: 'Enter',
          code: 'Enter'
        },
        tab: {
          key: 'Tab',
          code: 'Tab'
        },
        escape: {
          key: 'Escape',
          code: 'Escape'
        },
        arrowup: {
          key: 'ArrowUp',
          code: 'ArrowUp'
        },
        arrowdown: {
          key: 'ArrowDown',
          code: 'ArrowDown'
        },
        arrowleft: {
          key: 'ArrowLeft',
          code: 'ArrowLeft'
        },
        arrowright: {
          key: 'ArrowRight',
          code: 'ArrowRight'
        }
      };

      var keyData = keyMap[keyName.toLowerCase()] || {
        key: keyName,
        code: keyName
      };

				['keydown', 'keypress', 'keyup'].forEach(eventType => {
        var event = new KeyboardEvent(eventType, {
          key: keyData.key,
          code: keyData.code,
          bubbles: true,
          cancelable: true,
          ...options // Thêm các tùy chọn khác nếu có (Ctrl, Shift, v.v.)
        });
        el.dispatchEvent(event);
      });
    }

    // --- Xử lý loại sự kiện ---
    var event;
    var knownKeys = ['enter', 'tab', 'escape', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright'];

    if (knownKeys.includes(type.toLowerCase())) {
      pressKey(type);
    }
    // Nếu là sự kiện bàn phím tự do
    else if (['keydown', 'keypress', 'keyup'].includes(type)) {
      event = new KeyboardEvent(type, {
        key: options.key || '',
        code: options.code || '',
        bubbles: true,
        cancelable: true,
        ...options // Các tùy chọn khác như altKey, ctrlKey, shiftKey, metaKey
      });
      el.dispatchEvent(event);
    }
    // Nếu là sự kiện chuột (MouseEvent)
    else if (['click', 'mousedown', 'mouseup', 'dblclick', 'contextmenu', 'mousemove', 'mouseover', 'mouseout'].includes(type.toLowerCase())) {
      event = new MouseEvent(type, {
        bubbles: true,
        cancelable: true,
        // view: window,
        button: options.button !== undefined ? options.button : 0, // 0 cho chuột trái (mặc định)
        buttons: options.buttons !== undefined ? options.buttons : (type === 'mousedown' ? 1 : 0), // 1 cho nút trái đang nhấn
        clientX: options.clientX || 0,
        clientY: options.clientY || 0,
        screenX: options.screenX || 0,
        screenY: options.screenY || 0,
        altKey: options.altKey || false,
        ctrlKey: options.ctrlKey || false,
        shiftKey: options.shiftKey || false,
        metaKey: options.metaKey || false,
        ...options // Các tùy chọn khác như relatedTarget
      });
      el.dispatchEvent(event);
    }
    // Các loại sự kiện khác (input, change, blur, focus, submit,...)
    else {
      event = new Event(type, {
        bubbles: true,
        cancelable: true,
        ...options
      });
      el.dispatchEvent(event);
    }

    console.log(`Dispatched ${type} event on`, el);
  }

  // Giả lập input file
  function simulateReactInputFile(input) {
    var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'files')?.set;

    try {
      if (nativeInputValueSetter) {
        nativeInputValueSetter.call(input, input.files);
      }

      // Trigger lại các sự kiện input và change để React có thể nhận diện sự thay đổi
      var inputEvent = new Event('input', {
        bubbles: true
      });
      var changeEvent = new Event('change', {
        bubbles: true
      });

      input.dispatchEvent(inputEvent);
      input.dispatchEvent(changeEvent);
    } catch (e) {}
  }

  // Giả lập xóa nội dung
  function simulateClearing(inputElement, delay = 50, callback) {
    let text = inputElement.val();
    let index = text.length;

    function deleteNext() {
      if (index > 0) {
        inputElement.val(text.slice(0, --index)); // Xóa ký tự cuối cùng
        inputElement.trigger($.Event("keydown", {
          key: "Backspace",
          keyCode: 8
        }));
        setTimeout(deleteNext, delay);
      } else if (callback) {
        callback(); // Gọi callback sau khi xóa xong
      }
    }

    deleteNext();
  }

  // Giả lập gõ nội dung
  function simulateTyping(inputElement, text, event = "input", delay = 100, callback = null) {
    let index = 0;

    function typeNext() {
      if (index < text.length) {
        let char = text[index];
        inputElement.val(inputElement.val() + char);
        inputElement.trigger($.Event(event, {
          key: char,
          keyCode: char.charCodeAt(0),
          bubbles: true
        }));
        inputElement.trigger($.Event(event, {
          key: char,
          keyCode: char.charCodeAt(0),
          bubbles: true
        }));
        index++;
        setTimeout(typeNext, delay);
      } else {
        // Giả lập xóa khoảng trắng cuối cùng
        inputElement.trigger($.Event(event, {
          key: "Backspace",
          keyCode: 8,
          bubbles: true
        }));
        inputElement.trigger(event);
        inputElement.select();

        if (window.getSelection) {
          window.getSelection().removeAllRanges();
        } else if (document.selection) {
          document.selection.empty();
        }

        if ("createEvent" in document) {
          var evt = document.createEvent("HTMLEvents");
          evt.initEvent(event, false, true);
          $(inputElement).get(0).dispatchEvent(evt);
        } else {
          $(inputElement).get(0).fireEvent(`on${event}`);
        }

        if (typeof callback === "function") {
          callback();
        }
      }
    }

    typeNext();
  }

  // Giả lập dán nội dung
  function simulatePaste(inputElement, pastedText, event = "input", callback = null) {
    // Đặt giá trị như người dùng dán
    var el = inputElement[0];

    // Gán trực tiếp thông qua setter gốc (để React nhận biết)
    var nativeSetter = Object.getOwnPropertyDescriptor(el.__proto__, 'value')?.set;
    nativeSetter ? nativeSetter.call(el, pastedText) : inputElement.val(pastedText);

    // Tạo clipboardData giả để gửi sự kiện paste
    var pasteEvent = new ClipboardEvent('paste', {
      bubbles: true,
      cancelable: true,
      clipboardData: new DataTransfer()
    });

    pasteEvent.clipboardData.setData('text/plain', pastedText);

    // Gửi sự kiện paste
    el.dispatchEvent(pasteEvent);

    // Gửi sự kiện input để đảm bảo state được cập nhật
    el.dispatchEvent(new InputEvent(event, {
      bubbles: true
    }));

    // Gửi sự kiện change nếu cần (để framework bắt được)
    el.dispatchEvent(new Event('change', {
      bubbles: true
    }));

    // Gọi callback nếu có
    if (typeof callback === "function") {
      callback();
    }
  }

  // Giả lập input file
  function simulateReactInput(input, text, delay) {
    delay = delay || 100;
    var el = input[0];
    input.focus();

    var i = 0;

    function setNativeValue(element, value) {
      var lastValue = element.value;
      element.value = value;

      // Gọi setter gốc nếu bị React override
      var event = new Event('input', {
        bubbles: true
      });
      var tracker = element._valueTracker;
      if (tracker) tracker.setValue(lastValue);
      element.dispatchEvent(event);
    }

    function typeChar() {
      if (i < text.length) {
        var newVal = input.val() + text[i];
        setNativeValue(el, newVal);
        i++;
        typeChar();
      }
    }

    typeChar();
  }

  // Giả lập làm trống input
  function simulateClearReactInput(input) {
    var el = input[0];

    function setNativeValue(element, value) {
      var lastValue = element.value;
      element.value = value;

      var event = new Event('input', {
        bubbles: true
      });
      var tracker = element._valueTracker;
      if (tracker) tracker.setValue(lastValue);
      element.dispatchEvent(event);
    }

    input.focus();
    setNativeValue(el, '');
  }

  /**
   * @func gopGia
   * @description 'Gộp giá đầu và giá đuôi để được giá mới'
   * @param giaDau 'params0'
   * @param giaDuoi 'params1'
   * @return {
   *  giaDau: giaDau.toString(),
   *  giaDuoi: giaDuoi.toString(),
   *  gia: result.toString()
   * };
   */
  function gopGia(giaDau, giaDuoi) {
    // Chuẩn hóa đầu vào
    if (giaDau == null || giaDuoi == null) return null;
    var sD = String(Math.abs(Math.trunc(giaDau)));
    var sA = String(Math.abs(Math.trunc(giaDuoi)));
    var L = sD.length;

    // 1) Lấy prefix ban đầu (floor(len/2)), tối thiểu 2 chữ số
    let prefixLen = Math.floor(L / 2);
    if (prefixLen < 2) prefixLen = Math.min(2, L); // không vượt quá L
    let prefixStr = sD.slice(0, prefixLen);
    var rightOfPrefix = sD.slice(prefixLen); // phần còn lại của giaDau

    // 2) Nếu phần còn lại có chữ số khác 0 thì +1 cho prefix
    var hasNonZeroInRight = /[1-9]/.test(rightOfPrefix);
    let prefixNum = prefixStr ? parseInt(prefixStr, 10) : 0;
    if (hasNonZeroInRight) prefixNum = prefixNum + 1;

    // 3) Lấy suffix = giaDuoi bỏ trailing zeros
    let suffix = sA.replace(/000$/, '');
    if (suffix === '') suffix = '0';

    // 4) Lặp điều chỉnh cho tới khi vừa (có guard để tránh vòng vô hạn)
    let guard = 0;
    while ((prefixNum.toString().length + suffix.length) > L && guard < 200) {
      guard++;
      var totalLen = prefixNum.toString().length + suffix.length;
      var over = totalLen - L;

      // Thử cắt prefix nếu có thể (phải giữ >= 2 chữ số)
      var prefixCurStr = prefixNum.toString();
      if (prefixCurStr.length - over >= 2) {
        // Bỏ over chữ số cuối của prefix, rồi +1 (làm tròn như bạn yêu cầu)
        var newPref = prefixCurStr.slice(0, -over);
        prefixNum = (parseInt(newPref, 10) || 0) + 1;
        continue; // kiểm tra lại
      }

      // Nếu không cắt được prefix (đã còn 2 chữ số) -> cắt suffix từ phải qua trái
      // Cho tới khi vừa hoặc suffix chỉ còn 1 chữ số
      while ((prefixNum.toString().length + suffix.length) > L && suffix.length > 1) {
        suffix = suffix.slice(0, -1);
      }
      // Sau khi cắt xong, làm tròn suffix lên +1
      suffix = String((parseInt(suffix, 10) || 0) + 1);

      // Sau khi tăng suffix có thể làm phát sinh overflow (tăng độ dài suffix)
      // -> vòng while bên ngoài sẽ kiểm tra lại và tiếp tục điều chỉnh nếu cần
    }

    if (guard >= 200) {
      // Không thể điều chỉnh trong giới hạn hợp lý
      throw new Error('Không thể gộp theo quy tắc (vòng lặp vượt guard)');
    }

    // 5) Ghép lại: prefix padEnd tới độ dài ban đầu và cộng suffix
    var prefixPad = prefixNum.toString().padEnd(L, '0'); // ví dụ '173' -> '173000'
    var result = parseInt(prefixPad, 10) + parseInt(suffix, 10);

    return {
      giaDau: giaDau.toString(),
      giaDuoi: giaDuoi.toString(),
      gia: result.toString()
    };
  }

  // Tách giá trị thành giá đầu và giá đuôi theo cơ chế gộp
  /**
   * @func tachGia
   * @description 'Tách giá đầu và giá đuôi để được giá mới'
   * @param price 'params0'
   * @return {
   *  gia: gia.toString(),
   *  giaDau: gia_dau_tam.toString(),
   *  giaDuoi: gia_duoi_tam.toString()
   * }
   */
  function tachGia(price) {
    // 1. Chuẩn hóa input
    var gia = price.toString().replace(/[,.]/g, "").trim();

    // 2. Xác định điểm chia ban đầu
    var flag = Math.ceil(gia.length / 2);

    function kiemTraGia(flag) {
      if (flag < 2) {
        // prefix tối thiểu 2 số
        return {
          gia: gia,
          giaDau: parseInt(gia.slice(0, 2).padEnd(gia.length, "0")),
          giaDuoi: parseInt(gia.slice(2).padEnd(gia.length, "0"))
        };
      }

      var gia_dau_tam = parseInt(gia.slice(0, flag).padEnd(gia.length, "0"));
      var gia_duoi_tam = parseInt(gia.slice(flag).padEnd(gia.length, "0"));

      if (gia_dau_tam < gia_duoi_tam) {
        return kiemTraGia(flag - 1);
      } else {
        return {
          gia: gia.toString(),
          giaDau: gia_dau_tam.toString(),
          giaDuoi: gia_duoi_tam.toString()
        };
      }
    }

    return kiemTraGia(flag);
  }

  // =========================================================================
  // HÀM LẤY THÔNG TIN & KIỂM TRA PHIÊN BẢN
  // =========================================================================

  /**
   * @func getUrlServer
   * @description 'Lấy dữ liệu từ file GITHUB'
   */
  async function getUrlServer(owner = "pntan", repo = "TOOLv3", path = "version", branch = "main") {
    try {
      var res = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}&_=${Date.now()}`, {
        headers: {
          Authorization: `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`,
        }
      });

      if (!res.ok) {
        console.error("Lỗi HTTP khi lấy phiên bản:", res.status);
        return null;
      }

      var json = await res.json();
      var content = atob(json.content); // Giải mã base64
      var url = content.trim();

      return url;
    } catch (e) {
      console.error("Không thể lấy URL từ GitHub:", e.message);
      return null;
    }
  }

  function check_version() {
    boxAlert(`Đang kiểm tra phiên bản...`, "log");
    getUrlServer().then(latest_version => {
      if (!latest_version) {
        boxAlert("Không thể lấy phiên bản từ server!", "error");
        return;
      }
      if (latest_version != VERSION)
        boxAlert(`Phiên bản mới đã có: ${latest_version}. Vui lòng cập nhật!`, "warn");
      else
        boxAlert(`Phiên bản hiện tại: ${VERSION}`, "log");
    });
  }

  /**
   * @func boxToast
   * @description 'Hiển thị thông báo toast'
   */
  function boxToast(message, type = "info", duration = 3000) {
    var toast = $(`<div class="toast ${type}">${message}</div>`);
    $(".tp-container.tp-toast").append(toast);

    setTimeout(() => toast.addClass("show"), 10);

    let hideTimeout;

    var startAutoHide = () => {
      hideTimeout = setTimeout(() => {
        toast.removeClass("show");
        setTimeout(() => toast.remove(), 300);
      }, duration);
    };

    var stopAutoHide = () => {
      clearTimeout(hideTimeout);
    };

    toast.on("mouseenter", stopAutoHide);
    toast.on("mouseleave", () => {
      stopAutoHide();
      startAutoHide();
    });

    startAutoHide();
  }

  async function getInfoPage() {
    boxAlert(`ĐANG LẤY THÔNG TIN`);
    const info = {};

    info.url = {
      href: window.location.href,
      host: window.location.host,
    };

    info.url.params = {};
    const urlParams = new URLSearchParams(window.location.search);
    for (const [key, value] of urlParams.entries()) {
      info.url.params[key] = value;
    }

    console.log("Thông tin trang hiện tại:", info);
    return info;
  }


  // =========================================================================
  // HÀM KHỞI TẠO VÀ LỌC CHỨC NĂNG
  // =========================================================================

  /**
   * @func createFunction
   * @description 'Tạo danh sách chức năng dựa trên nền tảng'
   */
  function createFunction() {
    // Tính toán tên nền tảng hiện tại chỉ MỘT LẦN
    const hostParts = INFO_PAGE.url.host.split(".");
    // Lấy phần tử thứ 2 từ cuối (ví dụ: 'shopee' từ 'www.shopee.vn')
    const currentPlatform = hostParts[hostParts.length - 2];

    boxAlert(`Nền tảng hiện tại: ${currentPlatform}`, "log");

    $(".tp-container.tp-main .content-screen .screen.screen-main .list-function").empty();

    func_list.forEach(el => {
      // Logic lọc tối ưu: (Phải là "*") HOẶC (Phải khớp với nền tảng hiện tại)
      const shouldDisplay = el.platform.includes("*") || el.platform.includes(currentPlatform);

      if (shouldDisplay) {
        console.log("Hiển thị:", el.name);
        // Đảm bảo data-layout được truyền ngay cả khi rỗng
        const layoutAttr = el.layout_name ? `data-layout="${el.layout_name}"` : `data-layout=""`;
        $(".tp-container.tp-main .content-screen .screen.screen-main .list-function").append(`
                    <div class="box-function" data-func="${el.func_name}" ${layoutAttr}>
                        <p>${el.name}</p>
                    </div>
                `);
      }
    });
  }


  /**
   * @func INIT_CONFIG
   * @description 'Khởi tạo cấu hình chương trình'
   */
  function INIT_CONFIG() {
    boxAlert("Đang khởi tạo cấu hình...", "log");

    var theme_mode = () => {
      if (!getConfig("theme_mode"))
        setConfig("theme_mode", "light");

      var current_theme = getConfig("theme_mode");

      $(".tp-container.tp-main .header .theme-switcher .btn-theme").removeClass("active");
      $(`.tp-container.tp-main .header .theme-switcher .${current_theme}-mode`).addClass("active");
      return current_theme;
    }

    var screen_display = () => {
      if (!getConfig("screen_display"))
        setConfig("screen_display", "main")

      var current_screen = getConfig("screen_display");

      $(".tp-container.tp-main .list-screen .box-screen").removeClass("active");
      $(`.tp-container.tp-main .list-screen .box-screen.${current_screen}`).addClass("active");

      $(".tp-container.tp-main .content-screen .screen").removeClass("active");
      $(`.tp-container.tp-main .content-screen .screen.screen-${current_screen}`).addClass("active");
      return current_screen;
    }

    theme_mode();
    screen_display();
    return true
  }

  /**
   * @func INIT_UI
   * @description 'Khởi tạo giao diện chương trình (Chỉ chèn DOM)'
   */
  function INIT_UI() {
    boxAlert("Đang khởi tạo giao diện...", "log");

    var root_div = ["body"].find(id => document.querySelector(id) != null);

    if (!root_div) {
      boxAlert("Không tìm thấy phần tử gốc để chèn giao diện!", "error");
      return null;
    }

    $(root_div).append(`${HTML_UI}`);

    return true;
  }

  /**
   * @func INIT
   * @description 'Khởi tạo chương trình'
   */
  async function INIT() {
    // 1. Lấy thông tin trang
    INFO_PAGE = await getInfoPage();

    // 2. Khởi tạo giao diện
    var init_ui = INIT_UI();

    // 3. Khởi tạo cấu hình
    var init_config = INIT_CONFIG();

    // 4. Tạo chức năng (Sau khi INFO_PAGE đã có giá trị)
    if (init_ui) {
      createFunction(); // GỌI HÀM SAU KHI INFO_PAGE CÓ GIÁ TRỊ
    }

    if (init_config && init_ui) {
      boxAlert("KHỞI TẠO TƯƠNG TÁC");
      INIT_FUNCTION();
    }
  }

  /**
   * @func INIT_FUNCTION
   * @description 'Khởi tạo tương tác'
   */
  var INIT_FUNCTION = async () => {
    // Toggle theme (Giữ nguyên)
    $(".tp-container.tp-main .header .btn-theme").on("click", function() {
      var theme = $(this).data("theme");
      var toggleTheme = theme == "light" ? "dark" : "light";

      $(".tp-container.tp-main .header .btn-theme").removeClass("active");
      $(this).parent().find(`.btn-theme.${toggleTheme}-mode`).addClass("active");

      setConfig("theme_mode", toggleTheme);
    })

    // Chọn màn hình hiển thị (Giữ nguyên)
    $(".tp-container.tp-main .list-screen .box-screen").on("click", function() {
      var screen = $(this).data("screen");

      $(".tp-container.tp-main .list-screen .box-screen").removeClass("active");
      $(this).addClass("active")

      $(".tp-container.tp-main .content-screen .screen").removeClass("active");
      $(`.tp-container.tp-main .content-screen .screen.screen-${screen}`).addClass("active");

      setConfig("screen_display", screen);
    })

    // Chọn chức năng (box-function)
    $(".tp-container.tp-main .content-screen .screen.screen-main .list-function").on("click", ".box-function", function(e) {
      var funcName = $(this).attr("data-func");
      var layoutName = $(this).attr("data-layout");
      var hasLayout = layoutName && layoutName.length > 0;

      if (hasLayout) {
        $(".tp-container.tp-main .content-screen .screen.screen-main .list-function").removeClass("active");
        $(".tp-container.tp-main .content-screen .screen.screen-main .layout-function").addClass("active");

        // Hiển thị layout cụ thể
        $(`.tp-container.tp-main .content-screen .screen.screen-main .layout-function .box#${layoutName}_layout`).addClass("show");
      } else {
        excuseFunction(funcName);
      }
    });

    // --- ĐÃ BỔ SUNG: Xử lý sự kiện click trên nút action-btn trong layout ---
    $(".tp-container.tp-main .content-screen .screen.screen-main .layout-function").on("click", ".action-btn", function(e) {
      var actionName = $(this).attr("data-action");
      boxAlert(`Thực thi: ${actionName}`, "log");

      // Gọi hàm thực thi
      excuseFunction(actionName);
    });
    // -----------------------------------------------------------------------


    // Trở lại màn hình chọn chức năng (Giữ nguyên)
    $(".tp-container.tp-main .content-screen .screen.screen-main .layout-function .back").on("click", function() {
      // Ẩn tất cả layout box
      $(".tp-container.tp-main .content-screen .screen.screen-main .layout-function .box").removeClass("show");
      // Ẩn layout container
      $(".tp-container.tp-main .content-screen .screen.screen-main .layout-function").removeClass("active");

      // Hiển thị lại danh sách chức năng
      $(".tp-container.tp-main .content-screen .screen.screen-main .list-function").addClass("active");
    })

    // Trỏ sang trang hướng dẫn
    $(".tp-container.tp-main .help").on("click", function() {
      window.open("https://github.com/pntan/TOOLv3/blob/main/README.md#h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn-s%E1%BB%AD-d%E1%BB%A5ng", "_blank");
    })

    // Theo dõi chuột (giữ nguyên logic của bạn)
    $("body").on("mousemove", function(e) {
      var x = e.clientX;
      var bodyWidth = $("body").width();

      if (x <= X_LIMIT) {
        $(".tp-container.tp-main").css({
          "left": "0",
          "right": ""
        }).addClass("active");
      } else if (X_LIMIT >= bodyWidth - x) {
        $(".tp-container.tp-main").css({
          "right": "0",
          "left": ""
        }).addClass("active");
      } else {
        $(".tp-container.tp-main").removeClass("active");
      }
    })

    // Chạy đồng hồ (Giữ nguyên)
    async function runTime() {
      var now = new Date();
      var hours = now.getHours().toString().padStart(2, '0');
      var minutes = now.getMinutes().toString().padStart(2, '0');
      var seconds = now.getSeconds().toString().padStart(2, '0');

      $(".tp-container.tp-main .header .time").text(`${hours}:${minutes}:${seconds}`);
      await delay(1000);
      runTime();
    }

    runTime();
  }

  // Bắt đầu
  check_version();
  delay(3000).then(() => {
    INIT();
    boxToast("ĐÃ KHỞI TẠO CHƯƠNG TRÌNH", "success");
    boxAlert("ĐÃ KHỞI TẠO CHƯƠNG TRÌNH");
  });
})();