123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import Attributor from './attributor/attributor.js';
- import {
- type Blot,
- type BlotConstructor,
- type Root,
- } from './blot/abstract/blot.js';
- import ParchmentError from './error.js';
- import Scope from './scope.js';
- export type RegistryDefinition = Attributor | BlotConstructor;
- export interface RegistryInterface {
- create(scroll: Root, input: Node | string | Scope, value?: any): Blot;
- query(query: string | Node | Scope, scope: Scope): RegistryDefinition | null;
- register(...definitions: any[]): any;
- }
- export default class Registry implements RegistryInterface {
- public static blots = new WeakMap<Node, Blot>();
- public static find(node?: Node | null, bubble = false): Blot | null {
- if (node == null) {
- return null;
- }
- if (this.blots.has(node)) {
- return this.blots.get(node) || null;
- }
- if (bubble) {
- let parentNode: Node | null = null;
- try {
- parentNode = node.parentNode;
- } catch (err) {
- // Probably hit a permission denied error.
- // A known case is in Firefox, event targets can be anonymous DIVs
- // inside an input element.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
- return null;
- }
- return this.find(parentNode, bubble);
- }
- return null;
- }
- private attributes: { [key: string]: Attributor } = {};
- private classes: { [key: string]: BlotConstructor } = {};
- private tags: { [key: string]: BlotConstructor } = {};
- private types: { [key: string]: RegistryDefinition } = {};
- public create(scroll: Root, input: Node | string | Scope, value?: any): Blot {
- const match = this.query(input);
- if (match == null) {
- throw new ParchmentError(`Unable to create ${input} blot`);
- }
- const blotClass = match as BlotConstructor;
- const node =
- // @ts-expect-error Fix me later
- input instanceof Node || input.nodeType === Node.TEXT_NODE
- ? input
- : blotClass.create(value);
- const blot = new blotClass(scroll, node as Node, value);
- Registry.blots.set(blot.domNode, blot);
- return blot;
- }
- public find(node: Node | null, bubble = false): Blot | null {
- return Registry.find(node, bubble);
- }
- public query(
- query: string | Node | Scope,
- scope: Scope = Scope.ANY,
- ): RegistryDefinition | null {
- let match;
- if (typeof query === 'string') {
- match = this.types[query] || this.attributes[query];
- // @ts-expect-error Fix me later
- } else if (query instanceof Text || query.nodeType === Node.TEXT_NODE) {
- match = this.types.text;
- } else if (typeof query === 'number') {
- if (query & Scope.LEVEL & Scope.BLOCK) {
- match = this.types.block;
- } else if (query & Scope.LEVEL & Scope.INLINE) {
- match = this.types.inline;
- }
- } else if (query instanceof Element) {
- const names = (query.getAttribute('class') || '').split(/\s+/);
- names.some((name) => {
- match = this.classes[name];
- if (match) {
- return true;
- }
- return false;
- });
- match = match || this.tags[query.tagName];
- }
- if (match == null) {
- return null;
- }
- if (
- 'scope' in match &&
- scope & Scope.LEVEL & match.scope &&
- scope & Scope.TYPE & match.scope
- ) {
- return match;
- }
- return null;
- }
- public register(...definitions: RegistryDefinition[]): RegistryDefinition[] {
- return definitions.map((definition) => {
- const isBlot = 'blotName' in definition;
- const isAttr = 'attrName' in definition;
- if (!isBlot && !isAttr) {
- throw new ParchmentError('Invalid definition');
- } else if (isBlot && definition.blotName === 'abstract') {
- throw new ParchmentError('Cannot register abstract class');
- }
- const key = isBlot
- ? definition.blotName
- : isAttr
- ? definition.attrName
- : (undefined as never); // already handled by above checks
- this.types[key] = definition;
- if (isAttr) {
- if (typeof definition.keyName === 'string') {
- this.attributes[definition.keyName] = definition;
- }
- } else if (isBlot) {
- if (definition.className) {
- this.classes[definition.className] = definition;
- }
- if (definition.tagName) {
- if (Array.isArray(definition.tagName)) {
- definition.tagName = definition.tagName.map((tagName: string) => {
- return tagName.toUpperCase();
- });
- } else {
- definition.tagName = definition.tagName.toUpperCase();
- }
- const tagNames = Array.isArray(definition.tagName)
- ? definition.tagName
- : [definition.tagName];
- tagNames.forEach((tag: string) => {
- if (this.tags[tag] == null || definition.className == null) {
- this.tags[tag] = definition;
- }
- });
- }
- }
- return definition;
- });
- }
- }
|