import { PopsCommonCSSClassName } from "../config/CommonCSSClassName";
import { OriginPrototype, PopsCore } from "../PopsCore";
import type {
  ParseHTMLReturnType,
  PopsDOMUtils_Event,
  PopsDOMUtils_EventType,
  PopsDOMUtilsAddEventListenerResult,
  PopsDOMUtilsCreateElementAttributesMap,
  PopsDOMUtilsCSSProperty,
  PopsDOMUtilsCSSPropertyType,
  PopsDOMUtilsElementEventType,
  PopsDOMUtilsEventListenerOption,
  PopsDOMUtilsEventListenerOptionsAttribute,
  PopsDOMUtilsTargetElementType,
} from "../types/PopsDOMUtilsEventType";
import { PopsSafeUtils } from "./PopsSafeUtils";
import { popsUtils } from "./PopsUtils";

/* 数据 */
const GlobalData = {
  /** .on添加在元素存储的事件 */
  domEventSymbol: Symbol("events_" + (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)),
};

const CommonUtils = {
  isWin: popsUtils.isWin.bind(popsUtils),
  delete: popsUtils.delete.bind(popsUtils),
  isNodeList: popsUtils.isNodeList.bind(popsUtils),
};

class PopsDOMUtilsEvent {
  get windowApi() {
    return PopsCore.window;
  }
  /**
   * 绑定事件
   * @param element 需要绑定的元素|元素数组|window
   * @param eventType 需要监听的事件
   * @param handler 绑定事件触发的回调函数
   * @param option
   * + capture 表示事件是否在捕获阶段触发。默认为false，即在冒泡阶段触发
   * + once 表示事件是否只触发一次。默认为false
   * + passive 表示事件监听器是否不会调用preventDefault()。默认为false
   * @example
   * // 监听元素a.xx的click事件
   * DOMUtils.on(document.querySelector("a.xx"),"click",(event)=>{
   *    console.log("事件触发",event)
   * })
   * DOMUtils.on("a.xx","click",(event)=>{
   *    console.log("事件触发",event)
   * })
   */
  on<T extends PopsDOMUtils_EventType = PopsDOMUtils_EventType>(
    element: PopsDOMUtilsElementEventType,
    eventType: T | T[],
    handler: <E extends HTMLElement = HTMLElement>(this: E, event: PopsDOMUtils_Event[T]) => void,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ): PopsDOMUtilsAddEventListenerResult;
  /**
   * 绑定事件
   * @param element 需要绑定的元素|元素数组|window
   * @param eventType 需要监听的事件
   * @param handler 绑定事件触发的回调函数
   * @param option
   * + capture 表示事件是否在捕获阶段触发。默认为false，即在冒泡阶段触发
   * + once 表示事件是否只触发一次。默认为false
   * + passive 表示事件监听器是否不会调用preventDefault()。默认为false
   * @example
   * // 监听元素a.xx的click事件
   * DOMUtils.on(document.querySelector("a.xx"),"click",(event)=>{
   *    console.log("事件触发",event)
   * })
   * DOMUtils.on("a.xx","click",(event)=>{
   *    console.log("事件触发",event)
   * })
   */
  on<T extends Event = Event>(
    element: PopsDOMUtilsElementEventType,
    eventType: string | string[],
    handler: <E extends HTMLElement = HTMLElement>(this: E, event: T) => void,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ): PopsDOMUtilsAddEventListenerResult;
  /**
   * 绑定事件
   * @param element 需要绑定的元素|元素数组|window
   * @param eventType 需要监听的事件
   * @param selector 子元素选择器
   * @param handler 绑定事件触发的回调函数
   * @param option
   * + capture 表示事件是否在捕获阶段触发。默认为false，即在冒泡阶段触发
   * + once 表示事件是否只触发一次。默认为false
   * + passive 表示事件监听器是否不会调用preventDefault()。默认为false
   * @example
   * // 监听元素a.xx的click、tap、hover事件
   * DOMUtils.on(document.querySelector("a.xx"),"click tap hover",(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   * DOMUtils.on("a.xx",["click","tap","hover"],(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   * @example
   * // 监听全局document下的子元素a.xx的click事件
   * DOMUtils.on(document,"click tap hover","a.xx",(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   */
  on<T extends PopsDOMUtils_EventType = PopsDOMUtils_EventType>(
    element: PopsDOMUtilsElementEventType,
    eventType: T | T[],
    selector: string | string[] | undefined | null,
    handler: <E extends HTMLElement = HTMLElement>(this: E, event: PopsDOMUtils_Event[T], $selector: E) => void,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ): PopsDOMUtilsAddEventListenerResult;
  /**
   * 绑定事件
   * @param element 需要绑定的元素|元素数组|window
   * @param eventType 需要监听的事件
   * @param selector 子元素选择器
   * @param handler 绑定事件触发的回调函数
   * @param option
   * + capture 表示事件是否在捕获阶段触发。默认为false，即在冒泡阶段触发
   * + once 表示事件是否只触发一次。默认为false
   * + passive 表示事件监听器是否不会调用preventDefault()。默认为false
   * @example
   * // 监听元素a.xx的click、tap、hover事件
   * DOMUtils.on(document.querySelector("a.xx"),"click tap hover",(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   * DOMUtils.on("a.xx",["click","tap","hover"],(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   * @example
   * // 监听全局document下的子元素a.xx的click事件
   * DOMUtils.on(document,"click tap hover","a.xx",(event, $selector)=>{
   *    console.log("事件触发", event, $selector)
   * })
   */
  on<T extends Event = Event>(
    element: PopsDOMUtilsElementEventType,
    eventType: string | string[],
    selector: string | string[] | undefined | null,
    handler: <E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ): PopsDOMUtilsAddEventListenerResult;
  on<T extends Event = Event>(
    element: HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis,
    eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string | string[],
    selector:
      | string
      | string[]
      | undefined
      | (<E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void)
      | null,
    callback?:
      | (<E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void)
      | PopsDOMUtilsEventListenerOption
      | boolean,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ): PopsDOMUtilsAddEventListenerResult {
    /**
     * 获取option配置
     * @param args
     * @param startIndex
     * @param option
     */
    const getOption = function (args: IArguments, startIndex: number, option: PopsDOMUtilsEventListenerOption) {
      const currentParam: boolean | PopsDOMUtilsEventListenerOption = args[startIndex];
      if (typeof currentParam === "boolean") {
        option.capture = currentParam;
        if (typeof args[startIndex + 1] === "boolean") {
          option.once = args[startIndex + 1];
        }
        if (typeof args[startIndex + 2] === "boolean") {
          option.passive = args[startIndex + 2];
        }
      } else if (currentParam && typeof currentParam === "object") {
        for (const key in option) {
          if (Reflect.has(currentParam, key)) {
            Reflect.set(option, key, currentParam[key as keyof typeof currentParam]);
          }
        }
      }
      return option;
    };

    const that = this;
    // eslint-disable-next-line prefer-rest-params
    const args = arguments;
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (element == null) {
      return {
        off() {},
        emit() {},
      };
    }
    let $elList: (Element | Document | Window)[] = [];
    if (element instanceof NodeList || Array.isArray(element)) {
      $elList = $elList.concat(Array.from(element as Element[]));
    } else {
      $elList.push(element as Element);
    }
    // 事件名
    let eventTypeList: string[] = [];
    if (Array.isArray(eventType)) {
      eventTypeList = eventTypeList.concat(eventType.filter((it) => typeof it === "string" && it.toString() !== ""));
    } else if (typeof eventType === "string") {
      eventTypeList = eventTypeList.concat(eventType.split(" ").filter((it) => it !== ""));
    }
    // 子元素选择器
    let selectorList: string[] = [];
    if (Array.isArray(selector)) {
      selectorList = selectorList.concat(selector.filter((it) => typeof it === "string" && it.toString() !== ""));
    } else if (typeof selector === "string") {
      selectorList.push(selector);
    }
    // 事件回调
    let listenerCallBack: (this: Element, event: Event, $selector?: HTMLElement) => void | boolean = callback as (
      this: Element,
      event: Event,
      $selector?: HTMLElement
    ) => void | boolean;
    // 事件配置
    let listenerOption: PopsDOMUtilsEventListenerOption = {
      capture: false,
      once: false,
      passive: false,
      isComposedPath: false,
      overrideTarget: true,
    };
    if (typeof selector === "function") {
      // 这是为没有selector的情况
      // 那么它就是callback
      listenerCallBack = selector as any;
      listenerOption = getOption(args, 3, listenerOption);
    } else {
      // 这是存在selector的情况
      listenerOption = getOption(args, 4, listenerOption);
    }
    $elList.forEach(($elItem) => {
      // window和document共用一个对象
      // 这样就能处理子元素选择器无法匹配的问题
      const targetIsWindow = CommonUtils.isWin($elItem);
      // 遍历事件名设置元素事件
      eventTypeList.forEach((eventName) => {
        /**
         * 如果是option.once，那么删除该监听和元素上的事件和监听
         */
        const checkOptionOnceToRemoveEventListener = () => {
          if (listenerOption.once) {
            this.off($elItem, eventName, selector as any, callback as any, option);
          }
        };
        /**
         * 事件回调
         * @param event
         */
        const handlerCallBack = function (event: Event) {
          if (listenerOption.isPreventEvent) {
            that.preventEvent(event);
          }
          let call_this: Element | undefined = void 0;
          let call_event: Event | undefined = void 0;
          let call_$selector: HTMLElement | undefined = void 0;
          let execCallback = false;
          if (selectorList.length) {
            // 存在子元素选择器
            // 这时候的this和target都是子元素选择器的元素
            let $target: HTMLElement;
            if (listenerOption.isComposedPath) {
              // 可能为空
              const composedPath = event.composedPath();
              if (!composedPath.length && event.target) {
                composedPath.push(event.target);
              }
              $target = composedPath[0] as HTMLElement;
            } else {
              $target = event.target as HTMLElement;
            }
            const $parent = targetIsWindow ? that.windowApi.document.documentElement : $elItem;
            const findValue = selectorList.find((selectors) => {
              // 判断目标元素是否匹配选择器
              if (that.matches($target, selectors)) {
                // 当前目标可以被selector所匹配到
                return true;
              }
              // 在上层与主元素之间寻找可以被selector所匹配到的
              const $closestMatches = that.closest<HTMLElement>($target, selectors);
              if ($closestMatches && (<HTMLElement>$parent)?.contains?.($closestMatches)) {
                $target = $closestMatches;
                return true;
              }
              return false;
            });
            if (findValue) {
              if (listenerOption.overrideTarget) {
                // 这里尝试使用defineProperty修改event的target值
                try {
                  const originTarget = event.target;
                  OriginPrototype.Object.defineProperties(event, {
                    target: {
                      get() {
                        return $target;
                      },
                    },
                    originTarget: {
                      get() {
                        return originTarget;
                      },
                    },
                  });
                  // oxlint-disable-next-line no-empty
                } catch {}
              }
              execCallback = true;
              call_this = $target;
              call_event = event;
              call_$selector = $target;
            }
          } else {
            execCallback = true;
            call_this = $elItem as Element;
            call_event = event;
          }
          if (execCallback) {
            const result = listenerCallBack.call(call_this!, call_event!, call_$selector!);
            checkOptionOnceToRemoveEventListener();
            if (typeof result === "boolean" && !result) {
              return false;
            }
          }
        };
        // add listener
        $elItem.addEventListener(eventName, handlerCallBack, listenerOption);
        // 获取对象上的事件
        const elementEvents: {
          [k: string]: PopsDOMUtilsEventListenerOptionsAttribute[];
        } = Reflect.get($elItem, GlobalData.domEventSymbol) || {};
        // 初始化对象上的xx事件
        elementEvents[eventName] = elementEvents[eventName] || [];
        elementEvents[eventName].push({
          selector: selectorList,
          option: listenerOption,
          handlerCallBack: handlerCallBack,
          callback: listenerCallBack,
        });
        // 覆盖事件
        Reflect.set($elItem, GlobalData.domEventSymbol, elementEvents);
      });
    });

    return {
      /**
       * 取消绑定的监听事件
       * @param filter (可选)过滤函数，对元素属性上的事件进行过滤出想要删除的事件
       */
      off: (
        filter?: (
          value: PopsDOMUtilsEventListenerOptionsAttribute,
          index: number,
          array: PopsDOMUtilsEventListenerOptionsAttribute[]
        ) => boolean
      ) => {
        that.off($elList, eventTypeList, selectorList, listenerCallBack, listenerOption, filter);
      },
      /**
       * 主动触发事件
       * @param extraDetails 赋予触发的Event的额外属性，如果是Event类型，那么将自动代替默认new的Event对象
       * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用callback，但是这种会让使用了`$selector`的没有值
       */
      emit: (extraDetails?: object, useDispatchToTriggerEvent?: boolean) => {
        that.emit($elList, eventTypeList, extraDetails, useDispatchToTriggerEvent);
      },
    };
  }
  /**
   * 取消绑定事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType 需要取消监听的事件
   * @param callback 通过DOMUtils.on绑定的事件函数
   * @param option
   * + capture 如果在添加事件监听器时指定了useCapture为true，则在移除事件监听器时也必须指定为true
   * @param filter (可选)过滤函数，对元素属性上的事件进行过滤出想要删除的事件
   * @example
   * // 取消监听元素a.xx所有的click事件
   * DOMUtils.off(document.querySelector("a.xx"),"click")
   * DOMUtils.off("a.xx","click")
   */
  off<T extends PopsDOMUtils_EventType = PopsDOMUtils_EventType>(
    element: PopsDOMUtilsElementEventType,
    eventType: T | T[],
    callback?: <E extends HTMLElement = HTMLElement>(this: E, event: PopsDOMUtils_Event[T]) => void,
    option?: EventListenerOptions | boolean,
    filter?: (
      value: PopsDOMUtilsEventListenerOptionsAttribute,
      index: number,
      array: PopsDOMUtilsEventListenerOptionsAttribute[]
    ) => boolean
  ): void;
  /**
   * 取消绑定事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType 需要取消监听的事件
   * @param callback 通过DOMUtils.on绑定的事件函数
   * @param option
   * + capture 如果在添加事件监听器时指定了useCapture为true，则在移除事件监听器时也必须指定为true
   * @param filter (可选)过滤函数，对元素属性上的事件进行过滤出想要删除的事件
   * @example
   * // 取消监听元素a.xx的click事件
   * DOMUtils.off(document.querySelector("a.xx"),"click")
   * DOMUtils.off("a.xx","click")
   */
  off<T extends Event = Event>(
    element: PopsDOMUtilsElementEventType,
    eventType: string | string[],
    callback?: <E extends HTMLElement = HTMLElement>(this: E, event: T) => void,
    option?: EventListenerOptions | boolean,
    filter?: (
      value: PopsDOMUtilsEventListenerOptionsAttribute,
      index: number,
      array: PopsDOMUtilsEventListenerOptionsAttribute[]
    ) => boolean
  ): void;
  /**
   * 取消绑定事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType 需要取消监听的事件
   * @param selector 子元素选择器
   * @param callback 通过DOMUtils.on绑定的事件函数
   * @param option
   * + capture 如果在添加事件监听器时指定了useCapture为true，则在移除事件监听器时也必须指定为true
   * @param filter (可选)过滤函数，对元素属性上的事件进行过滤出想要删除的事件
   * @example
   * // 取消监听元素a.xx的click、tap、hover事件
   * DOMUtils.off(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.off("a.xx",["click","tap","hover"])
   */
  off<T extends PopsDOMUtils_EventType = PopsDOMUtils_EventType>(
    element: PopsDOMUtilsElementEventType,
    eventType: T | T[],
    selector?: string | string[] | undefined | null,
    callback?: <E extends HTMLElement = HTMLElement>(this: E, event: PopsDOMUtils_Event[T], $selector: E) => void,
    option?: EventListenerOptions | boolean,
    filter?: (
      value: PopsDOMUtilsEventListenerOptionsAttribute,
      index: number,
      array: PopsDOMUtilsEventListenerOptionsAttribute[]
    ) => boolean
  ): void;
  /**
   * 取消绑定事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType 需要取消监听的事件
   * @param selector 子元素选择器
   * @param callback 通过DOMUtils.on绑定的事件函数
   * @param option
   * + capture 如果在添加事件监听器时指定了useCapture为true，则在移除事件监听器时也必须指定为true
   * @param filter (可选)过滤函数，对元素属性上的事件进行过滤出想要删除的事件
   * @example
   * // 取消监听元素a.xx的click、tap、hover事件
   * DOMUtils.off(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.off("a.xx",["click","tap","hover"])
   */
  off<T extends Event = Event>(
    element: PopsDOMUtilsElementEventType,
    eventType: string | string[],
    selector?: string | string[] | undefined | null,
    callback?: <E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void,
    option?: EventListenerOptions | boolean,
    filter?: (
      value: PopsDOMUtilsEventListenerOptionsAttribute,
      index: number,
      array: PopsDOMUtilsEventListenerOptionsAttribute[]
    ) => boolean
  ): void;
  off<T extends Event = Event>(
    element: HTMLElement | string | NodeList | HTMLElement[] | Window | Document | Element | null | typeof globalThis,
    eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string | string[],
    selector:
      | string
      | string[]
      | undefined
      | (<E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void)
      | null,
    callback?:
      | (<E extends HTMLElement = HTMLElement>(this: E, event: T, $selector: E) => void)
      | EventListenerOptions
      | boolean,
    option?:
      | EventListenerOptions
      | boolean
      | ((
          value: PopsDOMUtilsEventListenerOptionsAttribute,
          index: number,
          array: PopsDOMUtilsEventListenerOptionsAttribute[]
        ) => boolean),
    filter?: (
      value: PopsDOMUtilsEventListenerOptionsAttribute,
      index: number,
      array: PopsDOMUtilsEventListenerOptionsAttribute[]
    ) => boolean
  ) {
    /**
     * 获取option配置
     * @param args1
     * @param startIndex
     * @param option
     */
    const getOption = function (args1: IArguments, startIndex: number, option: EventListenerOptions) {
      const currentParam: boolean | PopsDOMUtilsEventListenerOption = args1[startIndex];
      if (typeof currentParam === "boolean") {
        option.capture = currentParam;
      } else if (currentParam && typeof currentParam === "object" && "capture" in currentParam) {
        option.capture = currentParam.capture;
      }
      return option;
    };
    const that = this;
    // eslint-disable-next-line prefer-rest-params
    const args = arguments;
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (element == null) {
      return;
    }
    let $elList: HTMLElement[] = [];
    if (element instanceof NodeList || Array.isArray(element)) {
      element = element as HTMLElement[];
      $elList = $elList.concat(Array.from(element));
    } else {
      $elList.push(element as HTMLElement);
    }
    let eventTypeList: string[] = [];
    if (Array.isArray(eventType)) {
      eventTypeList = eventTypeList.concat(eventType.filter((it) => typeof it === "string" && it.toString() !== ""));
    } else if (typeof eventType === "string") {
      eventTypeList = eventTypeList.concat(eventType.split(" ").filter((it) => it !== ""));
    }
    // 子元素选择器
    let selectorList: string[] = [];
    if (Array.isArray(selector)) {
      selectorList = selectorList.concat(selector.filter((it) => typeof it === "string" && it.toString() !== ""));
    } else if (typeof selector === "string") {
      selectorList.push(selector);
    }
    /**
     * 事件的回调函数
     */
    let listenerCallBack: (this: HTMLElement, event: T, $selector: HTMLElement) => void = callback as (
      this: HTMLElement,
      event: Event,
      $selector?: HTMLElement
    ) => void | boolean;

    /**
     * 事件的配置
     */
    let listenerOption: EventListenerOptions = {
      capture: false,
    };
    if (typeof selector === "function") {
      // 这是为没有selector的情况
      // 那么它就是callback
      listenerCallBack = selector;
      listenerOption = getOption(args, 3, listenerOption);
    } else {
      // 这是存在selector的情况
      listenerOption = getOption(args, 4, listenerOption);
    }
    if (args.length === 5 && typeof args[4] === "function" && typeof filter !== "function") {
      // 目标函数、事件名、回调函数、事件配置、过滤函数
      filter = option as (
        value: PopsDOMUtilsEventListenerOptionsAttribute,
        index: number,
        array: PopsDOMUtilsEventListenerOptionsAttribute[]
      ) => boolean;
    }
    $elList.forEach(($elItem) => {
      // 获取对象上的事件
      const elementEvents: {
        [key: string]: PopsDOMUtilsEventListenerOptionsAttribute[];
      } = Reflect.get($elItem, GlobalData.domEventSymbol) || {};
      eventTypeList.forEach((eventName) => {
        const handlers = elementEvents[eventName] || [];
        // 过滤出需要删除的事件
        const handlersFiltered = typeof filter === "function" ? handlers.filter(filter) : handlers;
        for (let index = 0; index < handlersFiltered.length; index++) {
          const handler = handlersFiltered[index];
          // 过滤出的事件再根据下面的条件进行判断处理移除
          // 1. callback内存地址必须相同
          // 2. selector必须相同
          // 3. option.capture必须相同
          let flag = true;
          if (flag && listenerCallBack && handler.callback !== listenerCallBack) {
            flag = false;
          }
          if (flag && selectorList.length && Array.isArray(handler.selector)) {
            if (JSON.stringify(handler.selector) !== JSON.stringify(selectorList)) {
              flag = false;
            }
          }
          if (
            flag &&
            typeof handler.option.capture === "boolean" &&
            listenerOption.capture !== handler.option.capture
          ) {
            flag = false;
          }
          if (flag) {
            $elItem.removeEventListener(eventName, handler.handlerCallBack, handler.option);
            for (let i = handlers.length - 1; i >= 0; i--) {
              if (handlers[i] === handler) {
                handlers.splice(i, 1);
              }
            }
          }
        }
        if (handlers.length === 0) {
          // 如果没有任意的handler，那么删除该属性
          CommonUtils.delete(elementEvents, eventType);
          if (Object.keys(elementEvents).length === 0) {
            CommonUtils.delete($elItem, GlobalData.domEventSymbol);
          }
        }
      });
      Reflect.set($elItem, GlobalData.domEventSymbol, elementEvents);
    });
  }
  /**
   * 取消绑定所有的事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType （可选）需要取消监听的事件，不传入该参数则遍历所有监听的事件
   */
  offAll(element: PopsDOMUtilsElementEventType, eventType?: string): void;
  /**
   * 取消绑定所有的事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType （可选）需要取消监听的事件，不传入该参数则遍历所有监听的事件
   */
  offAll(element: PopsDOMUtilsElementEventType, eventType?: PopsDOMUtils_EventType | PopsDOMUtils_EventType[]): void;
  /**
   * 取消绑定所有的事件
   * @param element 需要取消绑定的元素|元素数组
   * @param eventType （可选）需要取消监听的事件，不传入该参数则遍历所有监听的事件
   */
  offAll(
    element: PopsDOMUtilsElementEventType,
    eventType?: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string
  ) {
    const that = this;
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (element == null) {
      return;
    }
    let $elList: (Element | Document | Window | typeof globalThis | Node | ChildNode | EventTarget)[] = [];
    if (element instanceof NodeList || Array.isArray(element)) {
      $elList = $elList.concat(Array.from(element as HTMLElement[]));
    } else {
      $elList.push(element);
    }

    let eventTypeList: string[] = [];
    if (Array.isArray(eventType)) {
      eventTypeList = eventTypeList.concat(eventType as string[]);
    } else if (typeof eventType === "string") {
      eventTypeList = eventTypeList.concat(eventType.split(" "));
    }
    $elList.forEach(($elItem) => {
      const symbolList = [...new Set([...Object.getOwnPropertySymbols($elItem), GlobalData.domEventSymbol])];
      symbolList.forEach((__symbol__) => {
        if (!__symbol__.toString().startsWith("Symbol(events_")) {
          return;
        }
        const elementEvents: {
          [key: string]: PopsDOMUtilsEventListenerOptionsAttribute[];
        } = Reflect.get($elItem, __symbol__) || {};
        const iterEventNameList = eventTypeList.length ? eventTypeList : Object.keys(elementEvents);
        iterEventNameList.forEach((eventName) => {
          const handlers: PopsDOMUtilsEventListenerOptionsAttribute[] = elementEvents[eventName];
          if (!handlers) {
            return;
          }
          for (const handler of handlers) {
            $elItem.removeEventListener(eventName, handler.handlerCallBack, {
              capture: handler.option.capture,
            });
          }
          const events = Reflect.get($elItem, __symbol__);
          CommonUtils.delete(events, eventName);
          if (Object.keys(events).length === 0) {
            CommonUtils.delete($elItem, __symbol__);
          }
        });
      });
    });
  }

  /**
   * 等待文档加载完成后执行指定的函数
   * @param callback 需要执行的函数
   * @example
   * DOMUtils.onReady(function(){
   *   console.log("文档加载完毕")
   * })
   */
  onReady<T extends (...args: any[]) => any>(callback: T) {
    const that = this;
    if (typeof callback !== "function") {
      return;
    }
    /**
     * 检测文档是否加载完毕
     */
    function checkDOMReadyState() {
      try {
        if (
          document.readyState === "complete" ||
          (document.readyState !== "loading" && !(document.documentElement as any).doScroll)
        ) {
          return true;
        } else {
          return false;
        }
      } catch {
        return false;
      }
    }
    /**
     * 成功加载完毕后触发的回调函数
     */
    function completed() {
      removeDomReadyListener();
      callback();
    }

    const targetList = [
      {
        target: PopsCore.document,
        eventType: "DOMContentLoaded",
        callback: completed,
      },
      {
        target: PopsCore.window,
        eventType: "load",
        callback: completed,
      },
    ];
    /**
     * 添加监听
     */
    function addDomReadyListener() {
      for (let index = 0; index < targetList.length; index++) {
        const item = targetList[index];
        that.on(item.target, item.eventType, item.callback);
      }
    }
    /**
     * 移除监听
     */
    function removeDomReadyListener() {
      for (let index = 0; index < targetList.length; index++) {
        const item = targetList[index];
        that.off(item.target, item.eventType, item.callback);
      }
    }
    if (checkDOMReadyState()) {
      // 检查document状态
      popsUtils.setTimeout(callback, 0);
    } else {
      // 添加监听
      addDomReadyListener();
    }
  }
  /**
   * 主动触发事件
   * @param element 需要触发的元素|元素数组|window
   * @param eventType 需要触发的事件
   * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用通过.on监听的callback，但是这种会让使用了$selector的没有值
   * @example
   * // 触发元素a.xx的click事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click")
   * DOMUtils.emit("a.xx","click")
   * // 触发元素a.xx的click、tap、hover事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.emit("a.xx",["click","tap","hover"])
   */
  emit(
    element: PopsDOMUtilsTargetElementType | Element | DocumentFragment | any[] | typeof globalThis | Window | Document,
    eventType: string | string[],
    useDispatchToTriggerEvent?: boolean
  ): void;
  /**
   * 主动触发事件
   * @param element 需要触发的元素|元素数组|window
   * @param eventType 需要触发的事件
   * @param extraDetails 赋予触发的Event的额外属性，如果是Event类型，那么将自动代替默认new的Event对象
   * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用通过.on监听的callback()，但是这种只有一个入参，如果使用$selector则没有值
   * @example
   * // 触发元素a.xx的click事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click")
   * DOMUtils.emit("a.xx","click")
   * // 触发元素a.xx的click、tap、hover事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.emit("a.xx",["click","tap","hover"])
   */
  emit(
    element: PopsDOMUtilsTargetElementType | Element | DocumentFragment | any[] | typeof globalThis | Window | Document,
    eventType: string | string[],
    extraDetails?: object,
    useDispatchToTriggerEvent?: boolean
  ): void;
  /**
   * 主动触发事件
   * @param element 需要触发的元素|元素数组|window
   * @param eventType 需要触发的事件
   * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用通过.on监听的callback()，但是这种只有一个入参，如果使用$selector则没有值
   * @example
   * // 触发元素a.xx的click事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click")
   * DOMUtils.emit("a.xx","click")
   * // 触发元素a.xx的click、tap、hover事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.emit("a.xx",["click","tap","hover"])
   */
  emit(
    element: Element | string | NodeList | any[] | Window | Document,
    eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[],
    useDispatchToTriggerEvent?: boolean
  ): void;
  /**
   * 主动触发事件
   * @param element 需要触发的元素|元素数组|window
   * @param event 触发的事件
   * @param extraDetails （可选）赋予触发的Event的额外属性
   * @param useDispatchToTriggerEvent （可选）是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用通过.on监听的callback()，但是这种只有一个入参，如果使用$selector则没有值
   * @example
   * DOMUtils.emit("a.xx", new Event("click"))
   * @example
   * DOMUtils.emit("a.xx", new Event("click"), {
   *   disableHook: true
   * })
   * @example
   * DOMUtils.emit("a.xx", new Event("click"), {
   *   disableHook: true
   * },false)
   */
  emit(
    element: Element | string | NodeList | any[] | Window | Document,
    event: Event,
    extraDetails?: object,
    useDispatchToTriggerEvent?: boolean
  ): void;
  /**
   * 主动触发事件
   * @param element 需要触发的元素|元素数组|window
   * @param eventType 需要触发的事件
   * @param extraDetails 赋予触发的Event的额外属性，如果是Event类型，那么将自动代替默认new的Event对象
   * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件，默认true，如果为false，则直接调用通过.on监听的callback()，但是这种只有一个入参，如果使用$selector则没有值
   * @example
   * // 触发元素a.xx的click事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click")
   * DOMUtils.emit("a.xx","click")
   * // 触发元素a.xx的click、tap、hover事件
   * DOMUtils.emit(document.querySelector("a.xx"),"click tap hover")
   * DOMUtils.emit("a.xx",["click","tap","hover"])
   */
  emit(
    element: Element | string | NodeList | any[] | Window | Document,
    eventType: PopsDOMUtils_EventType | PopsDOMUtils_EventType[] | string | string[] | Event,
    extraDetails?: object | boolean,
    useDispatchToTriggerEvent: boolean = true
  ) {
    const that = this;
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (element == null) {
      return;
    }
    let $elList: (Document | Window | Element)[] = [];
    if (element instanceof NodeList || Array.isArray(element)) {
      $elList = $elList.concat(Array.from(element));
    } else {
      $elList.push(element);
    }

    /**
     * 主动添加属性
     */
    const addExtraProp = (event: Event, obj: any) => {
      if (event instanceof Event && typeof obj === "object" && obj != null && !Array.isArray(obj)) {
        const detailKeys = Object.keys(obj);
        detailKeys.forEach((keyName) => {
          const value = Reflect.get(obj, keyName);
          // 在event上添加属性
          Reflect.set(event, keyName, value);
        });
      }
    };

    let eventTypeList: string[] = [];
    /**
     * 主动传递的事件
     */
    let __event__: Event | null = null;
    if (Array.isArray(eventType)) {
      eventTypeList = eventType.filter((it) => typeof it === "string" && it.trim() !== "");
    } else if (typeof eventType === "string") {
      eventTypeList = eventType.split(" ");
    } else if (eventType instanceof Event) {
      __event__ = eventType;
      addExtraProp(__event__, extraDetails);
    }

    $elList.forEach(($elItem) => {
      /* 获取对象上的事件 */
      const elementEvents: {
        [key: string]: PopsDOMUtilsEventListenerOptionsAttribute[];
      } = Reflect.get($elItem, GlobalData.domEventSymbol) || {};

      /**
       * 触发事件
       */
      const dispatchEvent = (event: Event, eventTypeItem: string) => {
        if (useDispatchToTriggerEvent == false && eventTypeItem in elementEvents) {
          // 直接调用.on监听的事件
          elementEvents[eventTypeItem].forEach((eventsItem) => {
            eventsItem.handlerCallBack(event);
          });
        } else {
          $elItem.dispatchEvent(event);
        }
      };

      if (__event__) {
        // 使用主动传递的事件直接触发
        const event = __event__;
        const eventTypeItem = event.type;
        dispatchEvent(event, eventTypeItem);
      } else {
        eventTypeList.forEach((eventTypeItem) => {
          // 构造事件
          const event = new Event(eventTypeItem);
          addExtraProp(event, extraDetails);
          dispatchEvent(event, eventTypeItem);
        });
      }
    });
  }
  /**
   * 当按键松开时触发事件
   * keydown - > keypress - > keyup
   * @param element 当前元素
   * @param handler 事件处理函数
   * @param option 配置
   * @example
   * // 监听a.xx元素的按键松开
   * DOMUtils.keyup(document.querySelector("a.xx"),()=>{
   *   console.log("按键松开");
   * })
   * DOMUtils.keyup("a.xx",()=>{
   *   console.log("按键松开");
   * })
   */
  onKeyup(
    element: PopsDOMUtilsTargetElementType | Element | DocumentFragment | Window | Node | typeof globalThis,
    handler: (this: HTMLElement, event: PopsDOMUtils_Event["keyup"]) => void,
    option?: boolean | PopsDOMUtilsEventListenerOption
  ) {
    const that = this;
    if (element == null) {
      return;
    }
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (CommonUtils.isNodeList(element)) {
      // 设置
      const listenerList: (PopsDOMUtilsAddEventListenerResult | undefined)[] = [];
      element.forEach(($ele) => {
        const listener = that.onKeyup($ele as HTMLElement, handler, option);
        listenerList.push(listener);
      });
      return {
        off() {
          listenerList.forEach((listener) => {
            if (!listener) {
              return;
            }
            listener.off();
          });
        },
      } as PopsDOMUtilsAddEventListenerResult;
    }
    return that.on(element, "keyup", null, handler, option);
  }
  /**
   * 当按键按下时触发事件
   * keydown - > keypress(已弃用) - > keyup
   * @param element 目标
   * @param handler 事件处理函数
   * @param option 配置
   * @example
   * // 监听a.xx元素的按键按下
   * DOMUtils.keydown(document.querySelector("a.xx"),()=>{
   *   console.log("按键按下");
   * })
   * DOMUtils.keydown("a.xx",()=>{
   *   console.log("按键按下");
   * })
   */
  onKeydown(
    element: PopsDOMUtilsTargetElementType | Element | DocumentFragment | Window | Node | typeof globalThis,
    handler: (this: HTMLElement, event: PopsDOMUtils_Event["keydown"]) => void,
    option?: boolean | PopsDOMUtilsEventListenerOption
  ) {
    const that = this;
    if (element == null) {
      return;
    }
    if (typeof element === "string") {
      element = that.selectorAll(element);
    }
    if (CommonUtils.isNodeList(element)) {
      // 设置
      const listenerList: (PopsDOMUtilsAddEventListenerResult | undefined)[] = [];
      element.forEach(($ele) => {
        const listener = that.onKeydown($ele as HTMLElement, handler, option);
        listenerList.push(listener);
      });
      return {
        off() {
          listenerList.forEach((listener) => {
            if (!listener) {
              return;
            }
            listener.off();
          });
        },
      } as PopsDOMUtilsAddEventListenerResult;
    }
    return that.on(element, "keydown", null, handler, option);
  }
  /**
   * 阻止事件传递
   *
   * + `.preventDefault()`: 阻止事件的默认行为发生。例如，当点击一个链接时，浏览器会默认打开链接的URL，或者在输入框内输入文字
   * + `.stopPropagation()`: 停止事件的传播，阻止它继续向更上层的元素冒泡，事件将不会再传播给其他的元素
   * + `.stopImmediatePropagation()`: 阻止事件传播，并且还能阻止元素上的其他事件处理程序被触发
   * @param event 要阻止传递的事件
   * @example
   * DOMUtils.preventEvent(event);
   */
  preventEvent(event: Event): false;
  /**
   * 阻止事件传递
   * @param event 要阻止传递的事件
   * @param onlyStopPropagation （可选）是否仅阻止事件的传播，默认false，不调用`.preventDefault()`
   * @example
   * DOMUtils.preventEvent(event, true);
   */
  preventEvent<T extends boolean>(event: Event, onlyStopPropagation: T): T extends true ? void : false;
  /**
   * 通过监听事件来主动阻止事件的传递
   * @param $el 要进行处理的元素
   * @param eventNameList 要阻止的事件名|列表
   * @param option （可选）配置项
   * @example
   * DOMUtils.preventEvent(document.querySelector("a"), "click")
   * @example
   * DOMUtils.preventEvent(document.querySelector("a"), "click", undefined, {
   *   capture: true,
   * })
   * @example
   * DOMUtils.preventEvent(document, "click", "a.xxx", {
   *   capture: true,
   *   onlyStopPropagation: true,
   * })
   */
  preventEvent(
    $el: Element | Document | ShadowRoot,
    eventNameList: string | string[],
    option?: {
      /** （可选）是否捕获，默认false */
      capture?: boolean;
      /** （可选）是否仅阻止事件的传播，默认false，不调用`.preventDefault()` */
      onlyStopPropagation?: boolean;
    }
  ): {
    /** 移除监听事件 */
    off(): void;
  };
  /**
   * 通过监听事件来主动阻止事件的传递
   * @param $el 要进行处理的元素
   * @param eventNameList 要阻止的事件名|列表
   * @param selector 子元素选择器
   * @param option （可选）配置项
   * @example
   * DOMUtils.preventEvent(document.querySelector("a"), "click")
   * @example
   * DOMUtils.preventEvent(document.querySelector("a"), "click", undefined, {
   *   capture: true,
   * })
   * @example
   * DOMUtils.preventEvent(document, "click", "a.xxx", {
   *   capture: true,
   *   onlyStopPropagation: true,
   * })
   */
  preventEvent(
    $el: Element | Document | ShadowRoot,
    eventNameList: string | string[],
    selector: string | string[] | null | undefined,
    option?: {
      /** （可选）是否捕获，默认false */
      capture?: boolean;
      /** （可选）是否仅阻止事件的传播，默认false，不调用`.preventDefault()` */
      onlyStopPropagation?: boolean;
    }
  ): {
    /** 移除监听事件 */
    off(): void;
  };
  preventEvent(...args: any[]): boolean | void | { off(): void } {
    /**
     * 阻止事件的默认行为发生，并阻止事件传播
     */
    const stopEvent = (event: Event, onlyStopPropagation?: boolean) => {
      // 停止事件的传播，阻止它继续向更上层的元素冒泡，事件将不会再传播给其他的元素
      event?.stopPropagation();
      // 阻止事件传播，并且还能阻止元素上的其他事件处理程序被触发
      event?.stopImmediatePropagation();
      if (typeof onlyStopPropagation === "boolean" && onlyStopPropagation) {
        return;
      }
      // 阻止事件的默认行为发生。例如，当点击一个链接时，浏览器会默认打开链接的URL，或者在输入框内输入文字
      event?.preventDefault();
      return false;
    };
    if (args[0] instanceof Event) {
      // 直接阻止事件
      const onlyStopPropagation: boolean = args[1];
      return stopEvent(args[0], onlyStopPropagation);
    } else {
      const $el: Element | Document | ShadowRoot = args[0];
      let eventNameList: string | string[] = args[1];
      let selector: string | string[] | null | undefined = void 0;
      let capture = false;
      let onlyStopPropagation = false;
      // 添加对应的事件来阻止触发
      if (typeof eventNameList === "string") {
        eventNameList = [eventNameList];
      }

      let option:
        | {
            capture?: boolean;
            onlyStopPropagation?: boolean;
          }
        | undefined = void 0;
      if (args.length === 2) {
        // ignore
      } else if (typeof args[2] === "string" || Array.isArray(args[2])) {
        // selector
        selector = args[2];
        if (typeof args[3] === "object" && args[3] != null) {
          option = args[3];
        }
      } else if (typeof args[2] === "object" && args[2] != null && !Array.isArray(args[2])) {
        // option
        option = args[2];
      } else {
        throw new TypeError("Invalid argument");
      }
      if (option) {
        capture = Boolean(option.capture);
        onlyStopPropagation = Boolean(option.onlyStopPropagation);
      }

      const listener = this.on(
        $el,
        eventNameList,
        selector,
        (evt) => {
          return stopEvent(evt, onlyStopPropagation);
        },
        { capture: capture }
      );
      return listener;
    }
  }
  /**
   * 选择器，可使用以下的额外语法
   *
   * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
   * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
   * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
   * @param selector 选择器
   * @example
   * DOMUtils.selector("div:contains('测试')")
   * > div.xxx
   * @example
   * DOMUtils.selector("div:empty")
   * > div.xxx
   * @example
   * DOMUtils.selector("div:regexp('^xxxx$')")
   * > div.xxx
   */
  selector<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K] | undefined;
  selector<E extends Element = Element>(selector: string): E | undefined;
  selector<E extends Element = Element>(selector: string) {
    return this.selectorAll<E>(selector)[0];
  }
  /**
   * 选择器，可使用以下的额外语法
   *
   * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
   * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
   * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
   * @param selector 选择器
   * @example
   * DOMUtils.selectorAll("div:contains('测试')")
   * > [div.xxx]
   * @example
   * DOMUtils.selectorAll("div:empty")
   * > [div.xxx]
   * @example
   * DOMUtils.selectorAll("div:regexp('^xxxx$')")
   * > [div.xxx]
   * @example
   * DOMUtils.selectorAll("div:regexp(/^xxx/ig)")
   * > [div.xxx]
   */
  selectorAll<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K][];
  selectorAll<E extends Element = Element>(selector: string): E[];
  selectorAll<E extends Element = Element>(selector: string) {
    selector = selector.trim();
    if (selector.match(/[^\s]{1}:empty$/gi)) {
      // empty 语法
      selector = selector.replace(/:empty$/gi, "");
      return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter(($ele) => {
        return $ele?.innerHTML?.trim() === "";
      });
    } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
      // contains 语法
      const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
      const text = textMatch![2];
      selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
      return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter(($ele) => {
        return ($ele?.textContent || (<any>$ele)?.innerText)?.includes(text);
      });
    } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
      // regexp 语法
      const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
      let pattern = textMatch![2];
      const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
      let flags = "";
      if (flagMatch) {
        pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
        flags = flagMatch[3];
      }
      const regexp = new RegExp(pattern, flags);
      selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
      return Array.from(PopsCore.document.querySelectorAll<E>(selector)).filter(($ele) => {
        return Boolean(($ele?.textContent || (<any>$ele)?.innerText)?.match(regexp));
      });
    } else {
      // 普通语法
      return Array.from(PopsCore.document.querySelectorAll<E>(selector));
    }
  }
  /**
   * 匹配元素，可使用以下的额外语法
   *
   * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
   * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
   * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
   * @param $el 元素
   * @param selector 选择器
   * @example
   * DOMUtils.matches("div:contains('测试')")
   * > true
   * @example
   * DOMUtils.matches("div:empty")
   * > true
   * @example
   * DOMUtils.matches("div:regexp('^xxxx$')")
   * > true
   * @example
   * DOMUtils.matches("div:regexp(/^xxx/ig)")
   * > false
   */
  matches($el: HTMLElement | Element | null | undefined, selector: string): boolean {
    selector = selector.trim();
    if ($el == null) {
      return false;
    }

    if (selector.match(/[^\s]{1}:empty$/gi)) {
      // empty 语法
      selector = selector.replace(/:empty$/gi, "");
      return $el.matches(selector) && $el?.innerHTML?.trim() === "";
    } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
      // contains 语法
      const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
      const text = textMatch![2];
      selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
      let content = $el?.textContent || (<any>$el)?.innerText;
      if (typeof content !== "string") {
        content = "";
      }
      return $el.matches(selector) && content?.includes(text);
    } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
      // regexp 语法
      const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
      let pattern = textMatch![2];
      const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
      let flags = "";
      if (flagMatch) {
        pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
        flags = flagMatch[3];
      }
      const regexp = new RegExp(pattern, flags);
      selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
      let content = $el?.textContent || (<any>$el)?.innerText;
      if (typeof content !== "string") {
        content = "";
      }
      return $el.matches(selector) && Boolean(content?.match(regexp));
    } else {
      // 普通语法
      return $el.matches(selector);
    }
  }
  /**
   * 根据选择器获取上层元素，可使用以下的额外语法
   *
   * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
   * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
   * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
   * @param $el 元素
   * @param selector 选择器
   * @example
   * DOMUtils.closest("div:contains('测试')")
   * > div.xxx
   * @example
   * DOMUtils.closest("div:empty")
   * > div.xxx
   * @example
   * DOMUtils.closest("div:regexp('^xxxx$')")
   * > div.xxxx
   * @example
   * DOMUtils.closest("div:regexp(/^xxx/ig)")
   * > null
   */
  closest<K extends keyof HTMLElementTagNameMap>(
    $el: HTMLElement | Element,
    selector: string
  ): HTMLElementTagNameMap[K] | null;
  closest<E extends Element = Element>($el: HTMLElement | Element, selector: string): E | null;
  closest<E extends Element = Element>($el: HTMLElement | Element, selector: string): E | null {
    selector = selector.trim();

    if (selector.match(/[^\s]{1}:empty$/gi)) {
      // empty 语法
      selector = selector.replace(/:empty$/gi, "");
      const $closest = $el?.closest<E>(selector);
      if ($closest && $closest?.innerHTML?.trim() === "") {
        return $closest;
      }
      return null;
    } else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) || selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
      // contains 语法
      const textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
      const text = textMatch![2];
      selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
      const $closest = $el?.closest<E>(selector);
      if ($closest) {
        const content = $el?.textContent || (<any>$el)?.innerText;
        if (typeof content === "string" && content.includes(text)) {
          return $closest;
        }
      }
      return null;
    } else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) || selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
      // regexp 语法
      const textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
      let pattern = textMatch![2];
      const flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
      let flags = "";
      if (flagMatch) {
        pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
        flags = flagMatch[3];
      }
      const regexp = new RegExp(pattern, flags);
      selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
      const $closest = $el?.closest<E>(selector);
      if ($closest) {
        const content = $el?.textContent || (<any>$el)?.innerText;
        if (typeof content === "string" && content.match(regexp)) {
          return $closest;
        }
      }
      return null;
    } else {
      // 普通语法
      const $closest = $el?.closest<E>(selector);
      return $closest;
    }
  }
  /**
   * 监input、textarea的输入框值改变的事件
   */
  onInput(
    $el: HTMLInputElement | HTMLTextAreaElement,
    callback: (evt: InputEvent) => void | Promise<void>,
    option?: PopsDOMUtilsEventListenerOption | boolean
  ) {
    /**
     * 是否正在输入中
     */
    let isComposite = false;
    const __callback = async (event: InputEvent) => {
      if (isComposite) return;
      await callback(event);
    };
    const __composition_start_callback = () => {
      isComposite = true;
    };
    const __composition_end_callback = () => {
      isComposite = false;
      this.emit($el, "input", {
        isComposite,
      });
    };
    const inputListener = this.on($el, "input", __callback, option);
    const compositionStartListener = this.on($el, "compositionstart", __composition_start_callback, option);
    const compositionEndListener = this.on($el, "compositionend", __composition_end_callback, option);

    return {
      off: () => {
        inputListener.off();
        compositionStartListener.off();
        compositionEndListener.off();
      },
    };
  }
}

class PopsDOMUtils extends PopsDOMUtilsEvent {
  /** 获取 animationend 在各个浏览器的兼容名 */
  getAnimationEndNameList() {
    return ["webkitAnimationEnd", "mozAnimationEnd", "MSAnimationEnd", "oanimationend", "animationend"];
  }
  /** 获取 transitionend 在各个浏览器的兼容名 */
  getTransitionEndNameList() {
    return ["webkitTransitionEnd", "mozTransitionEnd", "MSTransitionEnd", "otransitionend", "transitionend"];
  }
  /**
   * 实现jQuery中的$().offset();
   * @param element
   * @param calcScroll 计算滚动距离
   */
  offset(element: HTMLElement, calcScroll: boolean = true) {
    const rect = element.getBoundingClientRect();
    const win = element.ownerDocument.defaultView;
    const resultRect = new DOMRect(
      calcScroll ? parseFloat((rect.left + (win?.pageXOffset || 0)).toString()) : rect.left,
      calcScroll ? parseFloat((rect.top + (win?.pageYOffset || 0)).toString()) : rect.top,
      rect.width,
      rect.height
    );
    return resultRect;
  }
  /**
   * 获取元素的宽度
   * @param element 要获取宽度的元素
   * @param isShow 是否已进行isShow，避免爆堆栈
   * @param parent 用于判断是否已显示的父元素载体
   * @returns 元素的宽度，单位为像素
   * @example
   * // 获取元素a.xx的宽度
   * DOMUtils.width(document.querySelector("a.xx"))
   * DOMUtils.width("a.xx")
   * > 100
   * // 获取window的宽度
   * DOMUtils.width(window)
   * > 400
   * @example
   * // 设置元素a.xx的宽度为200
   * DOMUtils.width(document.querySelector("a.xx"),200)
   * DOMUtils.width("a.xx",200)
   */
  width(
    element: HTMLElement | string | Window | Document | typeof globalThis,
    isShow?: boolean,
    parent?: HTMLElement | ShadowRoot
  ): number;
  width(
    element: HTMLElement | string | Window | Document | typeof globalThis,
    isShow: boolean = false,
    parent?: HTMLElement | ShadowRoot
  ) {
    const DOMUtilsContext = this;
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    if (popsUtils.isWin(element)) {
      return PopsCore.window.document.documentElement.clientWidth;
    }
    if ((element as HTMLElement).nodeType === 9) {
      // Document文档节点
      element = element as Document;
      return Math.max(
        element.body.scrollWidth,
        element.documentElement.scrollWidth,
        element.body.offsetWidth,
        element.documentElement.offsetWidth,
        element.documentElement.clientWidth
      );
    }
    if (isShow || (!isShow && popsDOMUtils.isShow(element as HTMLElement))) {
      // 已显示
      // 不从style中获取对应的宽度，因为可能使用了class定义了width !important
      element = element as HTMLElement;
      // 如果element.style.width为空  则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width
      if (parseFloat(popsDOMUtils.getStyleValue(element, "width").toString()) > 0) {
        return parseFloat(popsDOMUtils.getStyleValue(element, "width").toString());
      }

      // 如果从css里获取到的值不是大于0  可能是auto 则通过offsetWidth来进行计算
      if (element.offsetWidth > 0) {
        const borderLeftWidth = popsDOMUtils.getStyleValue(element, "borderLeftWidth");
        const borderRightWidth = popsDOMUtils.getStyleValue(element, "borderRightWidth");
        const paddingLeft = popsDOMUtils.getStyleValue(element, "paddingLeft");
        const paddingRight = popsDOMUtils.getStyleValue(element, "paddingRight");
        const backHeight =
          parseFloat(element.offsetWidth.toString()) -
          parseFloat(borderLeftWidth.toString()) -
          parseFloat(borderRightWidth.toString()) -
          parseFloat(paddingLeft.toString()) -
          parseFloat(paddingRight.toString());
        return parseFloat(backHeight.toString());
      }
      return 0;
    } else {
      // 未显示
      element = element as HTMLElement;
      const { cloneNode, recovery } = popsDOMUtils.showElement(element, parent);
      const width = DOMUtilsContext.width(cloneNode, true, parent);
      recovery();
      return width;
    }
  }

  /**
   * 获取元素的高度
   * @param element 要获取高度的元素
   * @param isShow 是否已进行isShow，避免爆堆栈
   * @param parent 用于判断是否已显示的父元素载体
   * @returns 元素的高度，单位为像素
   * @example
   * // 获取元素a.xx的高度
   * DOMUtils.height(document.querySelector("a.xx"))
   * DOMUtils.height("a.xx")
   * > 100
   * // 获取window的高度
   * DOMUtils.height(window)
   * > 700
   * @example
   * // 设置元素a.xx的高度为200
   * DOMUtils.height(document.querySelector("a.xx"),200)
   * DOMUtils.height("a.xx",200)
   */
  height(
    element: HTMLElement | string | Window | Document | typeof globalThis,
    isShow?: boolean,
    parent?: HTMLElement | ShadowRoot
  ): number;
  height(
    element: HTMLElement | string | Window | Document | typeof globalThis,
    isShow: boolean = false,
    parent?: HTMLElement | ShadowRoot
  ) {
    const DOMUtilsContext = this;
    if (popsUtils.isWin(element)) {
      return PopsCore.window.document.documentElement.clientHeight;
    }
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    if ((element as Document).nodeType === 9) {
      element = element as Document;
      // Document文档节点
      return Math.max(
        element.body.scrollHeight,
        element.documentElement.scrollHeight,
        element.body.offsetHeight,
        element.documentElement.offsetHeight,
        element.documentElement.clientHeight
      );
    }
    if (isShow || (!isShow && popsDOMUtils.isShow(element as HTMLElement))) {
      element = element as HTMLElement;
      // 已显示
      // 从style中获取对应的高度，因为可能使用了class定义了width !important
      // 如果element.style.height为空  则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height
      if (parseFloat(popsDOMUtils.getStyleValue(element, "height").toString()) > 0) {
        return parseFloat(popsDOMUtils.getStyleValue(element, "height").toString());
      }

      // 如果从css里获取到的值不是大于0  可能是auto 则通过offsetHeight来进行计算
      if (element.offsetHeight > 0) {
        const borderTopWidth = popsDOMUtils.getStyleValue(element, "borderTopWidth");
        const borderBottomWidth = popsDOMUtils.getStyleValue(element, "borderBottomWidth");
        const paddingTop = popsDOMUtils.getStyleValue(element, "paddingTop");
        const paddingBottom = popsDOMUtils.getStyleValue(element, "paddingBottom");
        const backHeight =
          parseFloat(element.offsetHeight.toString()) -
          parseFloat(borderTopWidth.toString()) -
          parseFloat(borderBottomWidth.toString()) -
          parseFloat(paddingTop.toString()) -
          parseFloat(paddingBottom.toString());
        return parseFloat(backHeight.toString());
      }
      return 0;
    } else {
      // 未显示
      element = element as HTMLElement;
      const { cloneNode, recovery } = popsDOMUtils.showElement(element, parent);
      const height = DOMUtilsContext.height(cloneNode, true, parent);
      recovery();
      return height;
    }
  }
  /**
   * 获取元素的外部宽度（包括边框和外边距）
   * @param element 要获取外部宽度的元素
   * @param 是否已进行isShow，避免爆堆栈
   * @param parent 用于判断是否已显示的父元素载体
   * @returns 元素的外部宽度，单位为像素
   * @example
   * // 获取元素a.xx的外部宽度
   * DOMUtils.outerWidth(document.querySelector("a.xx"))
   * DOMUtils.outerWidth("a.xx")
   * > 100
   * // 获取window的外部宽度
   * DOMUtils.outerWidth(window)
   * > 400
   */
  outerWidth(
    element: HTMLElement | string | Window | Document,
    isShow?: boolean,
    parent?: HTMLElement | ShadowRoot
  ): number;
  outerWidth(
    element: HTMLElement | string | Window | Document,
    isShow: boolean = false,
    parent?: HTMLElement | ShadowRoot
  ) {
    const DOMUtilsContext = this;
    if (popsUtils.isWin(element)) {
      return PopsCore.window.innerWidth;
    }
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    element = element as HTMLElement;
    if (isShow || (!isShow && popsDOMUtils.isShow(element))) {
      const style = getComputedStyle(element, null);
      const marginLeft = popsDOMUtils.getStyleValue(style, "marginLeft");
      const marginRight = popsDOMUtils.getStyleValue(style, "marginRight");
      return element.offsetWidth + marginLeft + marginRight;
    } else {
      const { cloneNode, recovery } = popsDOMUtils.showElement(element, parent);
      const outerWidth = DOMUtilsContext.outerWidth(cloneNode, true, parent);
      recovery();
      return outerWidth;
    }
  }
  /**
   * 获取元素的外部高度（包括边框和外边距）
   * @param element 要获取外部高度的元素
   * @param isShow 是否已进行isShow，避免爆堆栈
   * @param parent 用于判断是否已显示的父元素载体
   * @returns 元素的外部高度，单位为像素
   * @example
   * // 获取元素a.xx的外部高度
   * DOMUtils.outerHeight(document.querySelector("a.xx"))
   * DOMUtils.outerHeight("a.xx")
   * > 100
   * // 获取window的外部高度
   * DOMUtils.outerHeight(window)
   * > 700
   */
  outerHeight(element: HTMLElement | string | Window, isShow?: boolean, parent?: HTMLElement | ShadowRoot): number;
  outerHeight(
    element: HTMLElement | string | Window,
    isShow: boolean = false,
    parent?: HTMLElement | ShadowRoot
  ): number {
    const DOMUtilsContext = this;
    if (popsUtils.isWin(element)) {
      return PopsCore.window.innerHeight;
    }
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    element = element as HTMLElement;
    if (isShow || (!isShow && popsDOMUtils.isShow(element))) {
      const style = getComputedStyle(element, null);
      const marginTop = popsDOMUtils.getStyleValue(style, "marginTop");
      const marginBottom = popsDOMUtils.getStyleValue(style, "marginBottom");
      return element.offsetHeight + marginTop + marginBottom;
    } else {
      const { cloneNode, recovery } = popsDOMUtils.showElement(element, parent);
      const outerHeight = DOMUtilsContext.outerHeight(cloneNode, true, parent);
      recovery();
      return outerHeight;
    }
  }
  /**
   * 添加className
   * @param $el 目标元素
   * @param args className属性
   */
  addClassName(
    $el: Element | undefined | null,
    ...args: (string | string[] | (() => string | string[]) | undefined | null)[]
  ) {
    if ($el == null) return;
    if (!($el instanceof Element)) return;
    if (args.length === 0) return;
    args.forEach((argItem) => {
      if (argItem == null) return;
      if (Array.isArray(argItem)) {
        this.addClassName($el, ...argItem);
        return;
      }
      if (typeof argItem === "function") {
        argItem = argItem();
      }
      if (typeof argItem !== "string") {
        // 不是字符串
        return;
      }
      if (argItem.trim() === "") {
        // 空字符串
        return;
      }
      const classNameList = argItem.split(" ").filter((item) => item.trim() !== "");
      $el.classList.add(...classNameList);
    });
  }
  /**
   * 删除className
   * @param $el 目标元素
   * @param args className属性
   */
  removeClassName(
    $el: Element | undefined | null,
    ...args: (string | string[] | (() => string | string[]) | undefined | null)[]
  ) {
    if ($el == null) return;
    if (!($el instanceof Element)) return;
    if (args.length === 0) return;
    args.forEach((argItem) => {
      if (argItem == null) return;
      if (Array.isArray(argItem)) {
        this.removeClassName($el, ...argItem);
        return;
      }
      if (typeof argItem === "function") {
        argItem = argItem();
      }
      if (typeof argItem !== "string") {
        // 不是字符串
        return;
      }
      if (argItem.trim() === "") {
        // 空字符串
        return;
      }
      const classNameList = argItem.split(" ").filter((item) => item.trim() !== "");
      $el.classList.remove(...classNameList);
    });
  }
  /**
   * 判断元素是否包含某个className
   * @param $el 目标元素
   * @param className className属性
   */
  containsClassName($el: Element | undefined | null, className: string): boolean {
    if ($el == null) {
      return false;
    }
    if (typeof className !== "string") {
      return false;
    }
    if (className.trim() === "") {
      return false;
    }
    return $el.classList.contains(className);
  }
  /**
   * 获取元素的样式属性值
   * @param $el 目标元素
   * @param property 样式属性名或包含多个属性名和属性值的对象
   * @example
   * // 获取元素a.xx的CSS属性display
   * DOMUtils.css(document.querySelector("a.xx"),"display");
   * DOMUtils.css("a.xx","display");
   * > "none"
   * */
  css($el: PopsDOMUtilsTargetElementType, property: PopsDOMUtilsCSSPropertyType): string;
  /**
   * 获取元素的样式属性值
   * @param $el 目标元素
   * @param property 样式属性名或包含多个属性名和属性值的对象
   * @example
   * // 获取元素a.xx的CSS属性display
   * DOMUtils.css(document.querySelector("a.xx"),"display");
   * DOMUtils.css("a.xx","display");
   * > "none"
   * */
  css($el: PopsDOMUtilsTargetElementType, property: string): string;
  /**
   * 设置元素的样式属性
   * @param $el 目标元素
   * @param property 样式属性名或包含多个属性名和属性值的对象
   * @param value 样式属性值
   * @example
   * // 设置元素a.xx的CSS属性display为block
   * DOMUtils.css(document.querySelector("a.xx"),"display","block");
   * DOMUtils.css(document.querySelector("a.xx"),"display","block !important");
   * DOMUtils.css("a.xx","display","block");
   * DOMUtils.css("a.xx","display","block !important");
   * @example
   * // 设置元素a.xx的CSS属性top为10px
   * DOMUtils.css(document.querySelector("a.xx"),"top","10px");
   * DOMUtils.css(document.querySelector("a.xx"),"top",10);
   * */
  css(
    $el: PopsDOMUtilsTargetElementType,
    property: PopsDOMUtilsCSSPropertyType & string,
    value: string | number
  ): string;
  /**
   * 设置元素的样式属性
   * @param $el 目标元素
   * @param property 样式属性名或包含多个属性名和属性值的对象
   * @param value 样式属性值
   * @example
   * // 设置元素a.xx的CSS属性display为block
   * DOMUtils.css(document.querySelector("a.xx"),{ display: "block" }});
   * DOMUtils.css(document.querySelector("a.xx"),{ display: "block !important" }});
   * @example
   * // 设置元素a.xx的CSS属性top为10px
   * DOMUtils.css(document.querySelector("a.xx"),{ top: "10px" });
   * DOMUtils.css(document.querySelector("a.xx"),{ top: 10 });
   * */
  css(
    $el: PopsDOMUtilsTargetElementType,
    property:
      | PopsDOMUtilsCSSProperty
      | {
          [key: string]: string | number;
        }
      | string
  ): string;
  css(
    $el: PopsDOMUtilsTargetElementType,
    property: PopsDOMUtilsCSSPropertyType | string | PopsDOMUtilsCSSProperty,
    value?: string | number
  ) {
    const that = this;
    /**
     * 把纯数字没有px的加上
     */
    function handlePixe(propertyName: string, propertyValue: string | number) {
      const allowAddPixe = ["width", "height", "top", "left", "right", "bottom", "font-size"];
      if (typeof propertyValue === "number") {
        propertyValue = propertyValue.toString();
      }
      if (typeof propertyValue === "string" && allowAddPixe.includes(propertyName) && propertyValue.match(/[0-9]$/gi)) {
        propertyValue = propertyValue + "px";
      }
      return propertyValue;
    }
    if (typeof $el === "string") {
      $el = that.selectorAll($el);
    }
    if ($el == null) {
      return;
    }
    if (Array.isArray($el) || $el instanceof NodeList) {
      if (typeof property === "string") {
        if (value == null) {
          // 获取属性
          return that.css($el[0] as HTMLElement, property);
        } else {
          // 设置属性
          $el.forEach(($elItem) => {
            that.css($elItem as HTMLElement, property);
          });
          return;
        }
      } else if (typeof property === "object") {
        // 设置属性
        $el.forEach(($elItem) => {
          that.css($elItem as HTMLElement, property as PopsDOMUtilsCSSProperty);
        });
        return;
      }
      return;
    }
    const setStyleProperty = (propertyName: string, propertyValue: string | number) => {
      if (typeof propertyValue === "string" && propertyValue.trim().endsWith("!important")) {
        propertyValue = propertyValue
          .trim()
          .replace(/!important$/gi, "")
          .trim();
        $el.style.setProperty(propertyName, propertyValue, "important");
      } else {
        propertyValue = handlePixe(propertyName, propertyValue);
        $el.style.setProperty(propertyName, propertyValue);
      }
    };
    if (typeof property === "string") {
      if (value == null) {
        return PopsCore.globalThis.getComputedStyle($el).getPropertyValue(property);
      } else {
        setStyleProperty(property, value);
      }
    } else if (typeof property === "object") {
      for (const prop in property) {
        const value = property[prop as keyof typeof property];
        setStyleProperty(prop, value!);
      }
    } else {
      // 其他情况
      throw new TypeError("property must be string or object");
    }
  }
  /**
   * 创建元素
   * @param tagName 标签名
   * @param property 属性
   * @param attributes 元素上的自定义属性
   * @example
   * // 创建一个DIV元素，且属性class为xxx
   * DOMUtils.createElement("div",undefined,{ class:"xxx" });
   * > <div class="xxx"></div>
   * @example
   * // 创建一个DIV元素
   * DOMUtils.createElement("div");
   * > <div></div>
   * @example
   * // 创建一个DIV元素
   * DOMUtils.createElement("div","测试");
   * > <div>测试</div>
   */
  createElement<K extends keyof HTMLElementTagNameMap>(
    /** 元素名 */
    tagName: K,
    /** 属性 */
    property?:
      | ({
          [P in keyof HTMLElementTagNameMap[K]]?: HTMLElementTagNameMap[K][P] extends string | boolean | number
            ? HTMLElementTagNameMap[K][P]
            : never;
        } & {
          [key: string]: any;
        })
      | string,
    /** 自定义属性 */
    attributes?: PopsDOMUtilsCreateElementAttributesMap
  ): HTMLElementTagNameMap[K] {
    const $temp = PopsCore.document.createElement(tagName);
    if (typeof property === "string") {
      PopsSafeUtils.setSafeHTML($temp, property);
      return $temp;
    }
    if (property == null) {
      property = {};
    }
    if (attributes == null) {
      attributes = {};
    }
    Object.keys(property).forEach((key) => {
      const value = property[key];
      if (key === "innerHTML") {
        PopsSafeUtils.setSafeHTML($temp, value);
        return;
      }
      Reflect.set($temp, key, value);
    });
    Object.keys(attributes).forEach((key) => {
      let value = attributes[key];
      if (typeof value === "object") {
        // object转字符串
        value = JSON.stringify(value);
      } else if (typeof value === "function") {
        // function转字符串
        value = value.toString();
      }
      $temp.setAttribute(key, value);
    });
    return $temp;
  }
  /**
   * 字符串转HTMLElement
   * @param elementString
   * @returns
   */
  parseTextToDOM<R extends HTMLElement>(elementString: string): R {
    // 去除前后的换行和空格
    elementString = elementString.replace(/^[\n|\s]*/g, "").replace(/[\n|\s]*$/g, "");
    const targetElement = this.createElement("div", {
      innerHTML: elementString,
    });
    return targetElement.firstChild as any as R;
  }
  /**
   * 获取文字的位置信息
   * @param input 输入框
   * @param selectionStart 起始位置
   * @param selectionEnd 结束位置
   * @param debug 是否是调试模式
   * + true 不删除临时节点元素
   * + false 删除临时节点元素
   */
  getTextBoundingRect(
    input: HTMLInputElement | HTMLTextAreaElement,
    selectionStart: number | string,
    selectionEnd: number | string,
    debug: boolean
  ): DOMRect {
    // Basic parameter validation
    if (!input || !("value" in input)) return input;
    if (typeof selectionStart == "string") selectionStart = parseFloat(selectionStart);
    if (typeof selectionStart != "number" || isNaN(selectionStart)) {
      selectionStart = 0;
    }
    if (selectionStart < 0) selectionStart = 0;
    else selectionStart = Math.min(input.value.length, selectionStart);
    if (typeof selectionEnd == "string") selectionEnd = parseFloat(selectionEnd);
    if (typeof selectionEnd != "number" || isNaN(selectionEnd) || selectionEnd < selectionStart) {
      selectionEnd = selectionStart;
    }
    if (selectionEnd < 0) selectionEnd = 0;
    else selectionEnd = Math.min(input.value.length, selectionEnd);

    // If available (thus IE), use the createTextRange method
    if (typeof (input as any).createTextRange == "function") {
      const range = (input as any).createTextRange();
      range.collapse(true);
      range.moveStart("character", selectionStart);
      range.moveEnd("character", selectionEnd - selectionStart);
      return range.getBoundingClientRect();
    }
    // createTextRange is not supported, create a fake text range
    const offset = getInputOffset();
    let topPos = offset.top;
    let leftPos = offset.left;
    const width = getInputCSS("width", true);
    const height = getInputCSS("height", true);

    // Styles to simulate a node in an input field
    let cssDefaultStyles = "white-space:pre;padding:0;margin:0;";
    const listOfModifiers = [
      "direction",
      "font-family",
      "font-size",
      "font-size-adjust",
      "font-variant",
      "font-weight",
      "font-style",
      "letter-spacing",
      "line-height",
      "text-align",
      "text-indent",
      "text-transform",
      "word-wrap",
      "word-spacing",
    ];
    topPos += getInputCSS("padding-top", true);
    topPos += getInputCSS("border-top-width", true);
    leftPos += getInputCSS("padding-left", true);
    leftPos += getInputCSS("border-left-width", true);
    leftPos += 1; //Seems to be necessary

    for (let i = 0; i < listOfModifiers.length; i++) {
      const property = listOfModifiers[i];
      cssDefaultStyles += property + ":" + getInputCSS(property, false) + ";";
    }
    // End of CSS variable checks
    // 不能为空，不然获取不到高度
    const text = input.value || "G",
      textLen = text.length,
      fakeClone = document.createElement("div");
    if (selectionStart > 0) appendPart(0, selectionStart);
    const fakeRange = appendPart(selectionStart, selectionEnd);
    if (textLen > selectionEnd) appendPart(selectionEnd, textLen);

    // Styles to inherit the font styles of the element
    fakeClone.style.cssText = cssDefaultStyles;

    // Styles to position the text node at the desired position
    fakeClone.style.position = "absolute";
    fakeClone.style.top = topPos + "px";
    fakeClone.style.left = leftPos + "px";
    fakeClone.style.width = width + "px";
    fakeClone.style.height = height + "px";
    PopsCore.document.body.appendChild(fakeClone);
    const returnValue = fakeRange.getBoundingClientRect(); //Get rect

    if (!debug) fakeClone.parentNode!.removeChild(fakeClone); //Remove temp
    return returnValue;

    // Local functions for readability of the previous code
    /**
     *
     * @param start
     * @param end
     */
    function appendPart(start: number, end: number): HTMLSpanElement {
      const span = document.createElement("span");
      span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
      span.textContent = text.substring(start, end);
      fakeClone.appendChild(span);
      return span;
    }
    // Computing offset position
    function getInputOffset() {
      const body = document.body,
        win = document.defaultView,
        docElem = document.documentElement,
        box = document.createElement("div");
      box.style.paddingLeft = box.style.width = "1px";
      body.appendChild(box);
      const isBoxModel = box.offsetWidth == 2;
      body.removeChild(box);
      const boxRect = input.getBoundingClientRect();
      const clientTop = docElem.clientTop || body.clientTop || 0,
        clientLeft = docElem.clientLeft || body.clientLeft || 0,
        scrollTop = win?.pageYOffset || (isBoxModel && docElem.scrollTop) || body.scrollTop,
        scrollLeft = win?.pageXOffset || (isBoxModel && docElem.scrollLeft) || body.scrollLeft;
      return {
        top: boxRect.top + scrollTop - clientTop,
        left: boxRect.left + scrollLeft - clientLeft,
      };
    }
    /**
     *
     * @param prop
     * @param isnumber
     */
    function getInputCSS<T extends boolean>(prop: string, isnumber: T): T extends true ? number : string {
      const val = PopsCore.document.defaultView!.getComputedStyle(input, null).getPropertyValue(prop);
      if (isnumber) {
        return parseFloat(val) as T extends true ? number : string;
      } else {
        return val as T extends true ? number : string;
      }
    }
  }
  /**
   * 使用className来隐藏元素
   * @param ele
   * @param isImportant 是否使用!important
   */
  cssHide(ele: Element | null, isImportant = false) {
    if (ele == null) {
      return;
    }
    if (isImportant) {
      popsDOMUtils.addClassName(ele, PopsCommonCSSClassName.hideImportant);
    } else {
      popsDOMUtils.addClassName(ele, PopsCommonCSSClassName.hide);
    }
  }
  /**
   * cssHide的反向使用
   * @param ele
   */
  cssShow(ele: Element | null) {
    if (ele == null) {
      return;
    }
    popsDOMUtils.removeClassName(ele, PopsCommonCSSClassName.hide);
    popsDOMUtils.removeClassName(ele, PopsCommonCSSClassName.hideImportant);
  }
  /**
   * 将字符串转为Element元素
   * @param html
   * @param useParser 是否使用DOMParser来生成元素，有些时候通过DOMParser生成的元素有点问题
   * + true 使用DOMPraser来转换字符串
   * + false （默认）创建一个div，里面放入字符串，然后提取firstChild
   * @param isComplete 是否是完整的
   * + true 如果useParser为true，那么返回整个使用DOMParser转换成的Document
   * 如果useParser为false，返回一个DIV元素，DIV元素内包裹着需要转换的字符串
   * + false （默认）如果useParser为true，那么返回整个使用DOMParser转换成的Document的body
   * 如果useParser为false，返回一个DIV元素的firstChild
   * @example
   * // 将字符串转为Element元素
   * DOMUtils.parseHTML("<a href='xxxx'></a>")
   * > <a href="xxxx"></a>
   * @example
   * // 使用DOMParser将字符串转为Element元素
   * DOMUtils.parseHTML("<a href='xxxx'></a>",true)
   * > <a href="xxxx"></a>
   * @example
   * // 由于需要转换的元素是多个元素，将字符串转为完整的Element元素
   * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",false, true)
   * > <div><a href="xxxx"></a><a href='xxxx'></a></div>
   * @example
   * // 由于需要转换的元素是多个元素，使用DOMParser将字符串转为完整的Element元素
   * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",true, true)
   * > #document
   */
  toElement<T1 extends boolean, T2 extends boolean>(
    html: string,
    useParser?: T1,
    isComplete?: T2
  ): ParseHTMLReturnType<T1, T2>;
  toElement(html: string, useParser = false, isComplete = false) {
    function parseHTMLByDOMParser() {
      const parser = new DOMParser();
      if (isComplete) {
        return parser.parseFromString(html, "text/html");
      } else {
        return parser.parseFromString(html, "text/html").body.firstChild;
      }
    }
    function parseHTMLByCreateDom() {
      const $temp = PopsCore.document.createElement("div");
      PopsSafeUtils.setSafeHTML($temp, html);
      if (isComplete) {
        return $temp;
      } else {
        return $temp.firstChild;
      }
    }
    if (useParser) {
      return parseHTMLByDOMParser();
    } else {
      return parseHTMLByCreateDom();
    }
  }

  /**
   * 函数在元素内部末尾添加子元素或HTML字符串
   * @param element 目标元素
   * @param content 子元素或HTML字符串
   * @example
   * // 元素a.xx的内部末尾添加一个元素
   * DOMUtils.append(document.querySelector("a.xx"), document.querySelector("b.xx"))
   * DOMUtils.append("a.xx","'<b class="xx"></b>")
   * */
  append(
    element: Element | Node | ShadowRoot | HTMLElement | string,
    content: HTMLElement | string | (HTMLElement | string | Element)[] | NodeList
  ) {
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    function elementAppendChild(ele: HTMLElement, text: HTMLElement | string) {
      if (typeof content === "string") {
        ele.insertAdjacentHTML("beforeend", PopsSafeUtils.getSafeHTML(text as string));
      } else {
        ele.appendChild(text as HTMLElement);
      }
    }
    if (Array.isArray(content) || content instanceof NodeList) {
      // 数组
      const fragment = PopsCore.document.createDocumentFragment();
      content.forEach((ele) => {
        if (typeof ele === "string") {
          ele = this.toElement(ele, true, false);
        }
        fragment.appendChild(ele);
      });
      element.appendChild(fragment);
    } else {
      elementAppendChild(element as HTMLElement, content);
    }
  }
  /**
   * 把元素标签添加到head内
   */
  appendHead($ele: HTMLElement) {
    if (PopsCore.document.head) {
      PopsCore.document.head.appendChild($ele);
    } else {
      PopsCore.document.documentElement.appendChild($ele);
    }
  }
  /**
   * 把元素添加进body内
   * @param $ele
   */
  appendBody($ele: HTMLElement) {
    if (PopsCore.document.body) {
      PopsCore.document.body.appendChild($ele);
    } else {
      PopsCore.document.documentElement.appendChild($ele);
    }
  }

  /**
   * 判断元素是否已显示或已连接
   * @param element
   */
  isShow(element: HTMLElement) {
    return Boolean(element.getClientRects().length);
  }
  /**
   * 用于显示元素并获取它的高度宽度等其它属性
   * @param $ele
   * @param parent 父元素
   */
  showElement($ele: HTMLElement, ownParent?: Node) {
    /** 克隆元素 */
    const $cloneNode = $ele.cloneNode(true) as HTMLElement;
    $cloneNode.setAttribute("style", "visibility: hidden !important;display:block !important;");
    let $parent: Node = PopsCore.document.documentElement;
    // 这里需要的是先获取元素的父节点，把元素同样添加到父节点中
    const $root = $ele.getRootNode();
    if (ownParent == null) {
      if ($root == $ele) {
        // 未添加到任意节点中，那么直接添加到页面中去
        $parent = PopsCore.document.documentElement;
      } else {
        // 添加到父节点中
        $parent = $root;
      }
    } else {
      // 自定义的父节点
      $parent = ownParent;
    }
    $parent.appendChild($cloneNode);
    return {
      /**
       * 强制显示的克隆的元素
       */
      cloneNode: $cloneNode,
      /**
       * 恢复修改的style
       */
      recovery() {
        popsDOMUtils.remove($cloneNode);
      },
    };
  }
  /**
   * 获取元素上的Float格式的属性px
   * @param element
   * @param styleName style名
   */
  getStyleValue(element: HTMLElement | CSSStyleDeclaration, styleName: string) {
    let view = null;
    let styles = null;
    if (element instanceof CSSStyleDeclaration) {
      // 直接就获取了style属性
      styles = element;
    } else {
      view = element.ownerDocument.defaultView;
      if (!view || !view.opener) {
        view = window;
      }
      styles = view.getComputedStyle(element);
    }
    const value = parseFloat(styles[styleName as any]);
    if (isNaN(value)) {
      return 0;
    } else {
      return value;
    }
  }
  /**
   * 在元素前面添加兄弟元素或HTML字符串
   * @param element 目标元素
   * @param content 兄弟元素或HTML字符串
   * @example
   * // 元素a.xx前面添加一个元素
   * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
   * DOMUtils.before("a.xx","'<b class="xx"></b>")
   * */
  before(element: HTMLElement | Element | string, content: HTMLElement | string) {
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    if (typeof content === "string") {
      element.insertAdjacentHTML("beforebegin", PopsSafeUtils.getSafeHTML(content));
    } else {
      element!.parentElement!.insertBefore(content, element);
    }
  }
  /**
   * 在元素后面添加兄弟元素或HTML字符串
   * @param element 目标元素
   * @param content 兄弟元素或HTML字符串
   * @example
   * // 元素a.xx后面添加一个元素
   * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
   * DOMUtils.after("a.xx","'<b class="xx"></b>")
   * */
  after(element: HTMLElement | Element | string, content: HTMLElement | string) {
    if (typeof element === "string") {
      element = this.selector(element) as HTMLElement;
    }
    if (element == null) {
      return;
    }
    if (typeof content === "string") {
      element.insertAdjacentHTML("afterend", PopsSafeUtils.getSafeHTML(content));
    } else {
      element!.parentElement!.insertBefore(content, element.nextSibling);
    }
  }
  /**
   * 获取CSS Rule
   * @param sheet
   * @returns
   */
  getKeyFrames(sheet: CSSStyleSheet) {
    const result = {};
    Object.keys(sheet.cssRules).forEach((key) => {
      if ((sheet.cssRules as any)[key].type === 7 && (sheet.cssRules as any)[key].name.startsWith("pops-anim-")) {
        (result as any)[(sheet.cssRules as any)[key].name] = (sheet.cssRules as any)[key];
      }
    });
    return result;
  }
  /**
   * 颜色转换函数
   * @method hexToRgb hex 颜色转 rgb 颜色
   * @method rgbToHex rgb 颜色转 Hex 颜色
   * @method getDarkColor 加深颜色值
   * @method getLightColor 变浅颜色值
   */
  calcColor() {
    function useChangeColor() {
      /**
       * hex 颜色转 rgb 颜色
       */
      const hexToRgb = (
        /**
         * hex 颜色值
         */
        str: string
      ): any => {
        let hexs: any = "";
        const reg = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
        if (!reg.test(str)) {
          console.warn("输入错误的hex");
          return "";
        }
        str = str.replace("#", "");
        hexs = str.match(/../g);
        for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
        return hexs;
      };

      /**
       * rgb转hex
       */
      const rgbToHex = (
        /**
         * 红色
         */
        r: any,
        /**
         * 绿色
         */
        g: any,
        /**
         * 蓝色
         */
        b: any
      ): string => {
        const reg = /^\d{1,3}$/;
        if (!reg.test(r) || !reg.test(g) || !reg.test(b)) {
          console.warn("输入错误的rgb颜色值");
          return "";
        }
        const hexs = [r.toString(16), g.toString(16), b.toString(16)];
        for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
        return `#${hexs.join("")}`;
      };

      /**
       * 获取深色
       */
      const getDarkColor = (
        /**
         * 颜色值字符串
         */
        color: string,
        /**
         * 加深的程度，限0-1之间
         */
        level: number
      ): string => {
        const reg = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
        if (!reg.test(color)) {
          console.warn("输入错误的hex颜色值");
          return "";
        }
        const rgb = useChangeColor().hexToRgb(color);
        for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
        return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
      };

      /**
       * 获取颜色变浅后的颜色值
       */
      const getLightColor = (
        /**
         * 颜色值字符串
         */
        color: string,
        /**
         * 加深的程度，限0-1之间
         */
        level: number
      ): string => {
        const reg = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
        if (!reg.test(color)) {
          console.warn("输入错误的hex颜色值");
          return "";
        }
        const rgb = useChangeColor().hexToRgb(color);
        for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
        return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
      };
      return {
        hexToRgb,
        rgbToHex,
        getDarkColor,
        getLightColor,
      };
    }
    return useChangeColor();
  }
  /**
   * 获取移动元素的transform偏移
   * @param element 元素
   */
  getTransform(element: HTMLElement) {
    let transform_left = 0;
    let transform_top = 0;
    const elementTransform = PopsCore.globalThis.getComputedStyle(element).transform;
    if (elementTransform !== "none" && elementTransform != null && elementTransform !== "") {
      const elementTransformMatch = elementTransform.match(/\((.+)\)/);
      // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
      const elementTransformSplit = elementTransformMatch?.[1]?.split?.(",")!;
      transform_left = Math.abs(parseInt(elementTransformSplit[4]));
      transform_top = Math.abs(parseInt(elementTransformSplit[5]));
    }
    return {
      transformLeft: transform_left,
      transformTop: transform_top,
    };
  }
  /**
   * 移除元素（包括它和内部使用.on添加的监听事件）
   * @param $el 目标元素，可以是数组、单个元素、NodeList、元素选择器
   * @example
   * DOMUtils.remove(document.querySelector("a.xx"))
   * DOMUtils.remove(document.querySelectorAll("a.xx"))
   * DOMUtils.remove("a.xx")
   * DOMUtils.remove([a.xxx, div.xxx, span.xxx])
   * */
  remove($el: PopsDOMUtilsTargetElementType | Element | null | undefined) {
    if (typeof $el === "string") {
      $el = this.selectorAll($el);
    }
    if ($el == null) {
      return;
    }
    if (CommonUtils.isNodeList($el)) {
      $el.forEach(($elItem) => {
        this.remove($elItem as HTMLElement);
      });
      return;
    }
    // 移除事件
    $el.querySelectorAll("*").forEach(($elItem) => {
      if (!($elItem instanceof Element)) return;
      this.offAll($elItem);
    });
    this.offAll($el);
    if (typeof $el.remove === "function") {
      $el.remove();
    } else if ($el.parentElement) {
      $el.parentElement.removeChild($el);
    } else if ($el.parentNode) {
      $el.parentNode.removeChild($el);
    }
  }
}

const popsDOMUtils = new PopsDOMUtils();
export { popsDOMUtils };
