UNPKG

jodit

Version:

Jodit is awesome and usefully wysiwyg editor with filebrowser

215 lines (179 loc) 4.47 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Released under MIT see LICENSE.txt in the project root for license information. * Copyright (c) 2013-2020 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import { IS_IE } from '../constants'; import { HTMLTagNames, IDictionary, Nullable } from '../../types/'; import { isString } from './checker'; import { attr } from './utils'; import { error } from './type'; import { Dom } from '../dom'; import { camelCase } from './string'; let temp = 1; const $$temp = () => { temp++; return temp; }; /** * Find all elements by selector and return Array. If it did not find any element it return empty array * * @example * ```javascript * Jodit.modules.Helpers.$$('.someselector').forEach(function (elm) { * elm.addEventListener('click', function () { * alert(''Clicked'); * }); * }) * ``` * @param selector CSS like selector * @param root * * @return {HTMLElement[]} */ export function $$<K extends HTMLTagNames>( selector: K, root: HTMLElement | HTMLDocument ): Array<HTMLElementTagNameMap[K]>; export function $$<T extends HTMLElement>( selector: string, root: HTMLElement | HTMLDocument ): T[]; export function $$<T extends HTMLElement>( selector: string | HTMLTagNames, root: HTMLElement | HTMLDocument ): T[] { let result: NodeList; if ( /:scope/.test(selector) && IS_IE && !(root && root.nodeType === Node.DOCUMENT_NODE) ) { const id: string = (root as HTMLElement).id, temp_id: string = id || '_selector_id_' + ('' + Math.random()).slice(2) + $$temp(); selector = selector.replace(/:scope/g, '#' + temp_id); !id && (root as HTMLElement).setAttribute('id', temp_id); result = (root.parentNode as HTMLElement).querySelectorAll(selector); if (!id) { (root as HTMLElement).removeAttribute('id'); } } else { result = root.querySelectorAll(selector); } return [].slice.call(result); } /** * Calculate XPath selector * * @param element * @param root */ export const getXPathByElement = ( element: HTMLElement, root: HTMLElement ): string => { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return ''; } if (!element.parentNode || root === element) { return ''; } if (element.id) { return "//*[@id='" + element.id + "']"; } const sames: Node[] = [].filter.call( element.parentNode.childNodes, (x: Node) => x.nodeName === element.nodeName ); return ( getXPathByElement(element.parentNode as HTMLElement, root) + '/' + element.nodeName.toLowerCase() + (sames.length > 1 ? '[' + (Array.from(sames).indexOf(element) + 1) + ']' : '') ); }; /** * Find all `ref` or `data-ref` elements inside HTMLElement * @param root */ export const refs = <T extends HTMLElement>( root: HTMLElement ): IDictionary<T> => { return $$('[ref],[data-ref]', root).reduce((def, child) => { const key = attr(child, '-ref'); if (key && isString(key)) { def[camelCase(key)] = child as T; def[key] = child as T; } return def; }, {} as IDictionary<T>); }; /** * Calculate full CSS selector * @param el */ export const cssPath = (el: Element): Nullable<string> => { if (!Dom.isElement(el)) { return null; } const path = []; let start: Nullable<Element> = el; while (start && start.nodeType === Node.ELEMENT_NODE) { let selector = start.nodeName.toLowerCase(); if (start.id) { selector += '#' + start.id; path.unshift(selector); break; } else { let sib: Nullable<Element> = start, nth = 1; do { sib = sib.previousElementSibling; if (sib && sib.nodeName.toLowerCase() === selector) { nth++; } } while (sib); selector += ':nth-of-type(' + nth + ')'; } path.unshift(selector); start = start.parentNode as Element; } return path.join(' > '); }; /** * Try to find element by selector * * @param element * @param od */ export function resolveElement( element: string | HTMLElement, od: ShadowRoot | Document ): HTMLElement { let resolved = element; if (isString(element)) { try { resolved = od.querySelector(element) as HTMLInputElement; } catch { throw error( 'String "' + element + '" should be valid HTML selector' ); } } // Duck checking if ( !resolved || typeof resolved !== 'object' || !Dom.isElement(resolved) || !resolved.cloneNode ) { throw error( 'Element "' + element + '" should be string or HTMLElement instance' ); } return resolved; }