719327c170b1689e4661feaae0f60fd798df3f3588bb9eb25c889e601e7d9e3153e0172babb83780c9969e5c09848d51054ef1589b4d6758792478614782a2 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. import { merge } from 'lodash-es';
  2. import * as Parchment from 'parchment';
  3. import Delta from 'quill-delta';
  4. import Editor from './editor.js';
  5. import Emitter from './emitter.js';
  6. import instances from './instances.js';
  7. import logger from './logger.js';
  8. import Module from './module.js';
  9. import Selection, { Range } from './selection.js';
  10. import Composition from './composition.js';
  11. import Theme from './theme.js';
  12. import scrollRectIntoView from './utils/scrollRectIntoView.js';
  13. import createRegistryWithFormats from './utils/createRegistryWithFormats.js';
  14. const debug = logger('quill');
  15. const globalRegistry = new Parchment.Registry();
  16. Parchment.ParentBlot.uiClass = 'ql-ui';
  17. /**
  18. * Options for initializing a Quill instance
  19. */
  20. /**
  21. * Similar to QuillOptions, but with all properties expanded to their default values,
  22. * and all selectors resolved to HTMLElements.
  23. */
  24. class Quill {
  25. static DEFAULTS = {
  26. bounds: null,
  27. modules: {
  28. clipboard: true,
  29. keyboard: true,
  30. history: true,
  31. uploader: true
  32. },
  33. placeholder: '',
  34. readOnly: false,
  35. registry: globalRegistry,
  36. theme: 'default'
  37. };
  38. static events = Emitter.events;
  39. static sources = Emitter.sources;
  40. static version = typeof "2.0.2" === 'undefined' ? 'dev' : "2.0.2";
  41. static imports = {
  42. delta: Delta,
  43. parchment: Parchment,
  44. 'core/module': Module,
  45. 'core/theme': Theme
  46. };
  47. static debug(limit) {
  48. if (limit === true) {
  49. limit = 'log';
  50. }
  51. logger.level(limit);
  52. }
  53. static find(node) {
  54. let bubble = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  55. return instances.get(node) || globalRegistry.find(node, bubble);
  56. }
  57. static import(name) {
  58. if (this.imports[name] == null) {
  59. debug.error(`Cannot import ${name}. Are you sure it was registered?`);
  60. }
  61. return this.imports[name];
  62. }
  63. static register() {
  64. if (typeof (arguments.length <= 0 ? undefined : arguments[0]) !== 'string') {
  65. const target = arguments.length <= 0 ? undefined : arguments[0];
  66. const overwrite = !!(arguments.length <= 1 ? undefined : arguments[1]);
  67. const name = 'attrName' in target ? target.attrName : target.blotName;
  68. if (typeof name === 'string') {
  69. // Shortcut for formats:
  70. // register(Blot | Attributor, overwrite)
  71. this.register(`formats/${name}`, target, overwrite);
  72. } else {
  73. Object.keys(target).forEach(key => {
  74. this.register(key, target[key], overwrite);
  75. });
  76. }
  77. } else {
  78. const path = arguments.length <= 0 ? undefined : arguments[0];
  79. const target = arguments.length <= 1 ? undefined : arguments[1];
  80. const overwrite = !!(arguments.length <= 2 ? undefined : arguments[2]);
  81. if (this.imports[path] != null && !overwrite) {
  82. debug.warn(`Overwriting ${path} with`, target);
  83. }
  84. this.imports[path] = target;
  85. if ((path.startsWith('blots/') || path.startsWith('formats/')) && target && typeof target !== 'boolean' && target.blotName !== 'abstract') {
  86. globalRegistry.register(target);
  87. }
  88. if (typeof target.register === 'function') {
  89. target.register(globalRegistry);
  90. }
  91. }
  92. }
  93. constructor(container) {
  94. let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  95. this.options = expandConfig(container, options);
  96. this.container = this.options.container;
  97. if (this.container == null) {
  98. debug.error('Invalid Quill container', container);
  99. return;
  100. }
  101. if (this.options.debug) {
  102. Quill.debug(this.options.debug);
  103. }
  104. const html = this.container.innerHTML.trim();
  105. this.container.classList.add('ql-container');
  106. this.container.innerHTML = '';
  107. instances.set(this.container, this);
  108. this.root = this.addContainer('ql-editor');
  109. this.root.classList.add('ql-blank');
  110. this.emitter = new Emitter();
  111. const scrollBlotName = Parchment.ScrollBlot.blotName;
  112. const ScrollBlot = this.options.registry.query(scrollBlotName);
  113. if (!ScrollBlot || !('blotName' in ScrollBlot)) {
  114. throw new Error(`Cannot initialize Quill without "${scrollBlotName}" blot`);
  115. }
  116. this.scroll = new ScrollBlot(this.options.registry, this.root, {
  117. emitter: this.emitter
  118. });
  119. this.editor = new Editor(this.scroll);
  120. this.selection = new Selection(this.scroll, this.emitter);
  121. this.composition = new Composition(this.scroll, this.emitter);
  122. this.theme = new this.options.theme(this, this.options); // eslint-disable-line new-cap
  123. this.keyboard = this.theme.addModule('keyboard');
  124. this.clipboard = this.theme.addModule('clipboard');
  125. this.history = this.theme.addModule('history');
  126. this.uploader = this.theme.addModule('uploader');
  127. this.theme.addModule('input');
  128. this.theme.addModule('uiNode');
  129. this.theme.init();
  130. this.emitter.on(Emitter.events.EDITOR_CHANGE, type => {
  131. if (type === Emitter.events.TEXT_CHANGE) {
  132. this.root.classList.toggle('ql-blank', this.editor.isBlank());
  133. }
  134. });
  135. this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {
  136. const oldRange = this.selection.lastRange;
  137. const [newRange] = this.selection.getRange();
  138. const selectionInfo = oldRange && newRange ? {
  139. oldRange,
  140. newRange
  141. } : undefined;
  142. modify.call(this, () => this.editor.update(null, mutations, selectionInfo), source);
  143. });
  144. this.emitter.on(Emitter.events.SCROLL_EMBED_UPDATE, (blot, delta) => {
  145. const oldRange = this.selection.lastRange;
  146. const [newRange] = this.selection.getRange();
  147. const selectionInfo = oldRange && newRange ? {
  148. oldRange,
  149. newRange
  150. } : undefined;
  151. modify.call(this, () => {
  152. const change = new Delta().retain(blot.offset(this)).retain({
  153. [blot.statics.blotName]: delta
  154. });
  155. return this.editor.update(change, [], selectionInfo);
  156. }, Quill.sources.USER);
  157. });
  158. if (html) {
  159. const contents = this.clipboard.convert({
  160. html: `${html}<p><br></p>`,
  161. text: '\n'
  162. });
  163. this.setContents(contents);
  164. }
  165. this.history.clear();
  166. if (this.options.placeholder) {
  167. this.root.setAttribute('data-placeholder', this.options.placeholder);
  168. }
  169. if (this.options.readOnly) {
  170. this.disable();
  171. }
  172. this.allowReadOnlyEdits = false;
  173. }
  174. addContainer(container) {
  175. let refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  176. if (typeof container === 'string') {
  177. const className = container;
  178. container = document.createElement('div');
  179. container.classList.add(className);
  180. }
  181. this.container.insertBefore(container, refNode);
  182. return container;
  183. }
  184. blur() {
  185. this.selection.setRange(null);
  186. }
  187. deleteText(index, length, source) {
  188. // @ts-expect-error
  189. [index, length,, source] = overload(index, length, source);
  190. return modify.call(this, () => {
  191. return this.editor.deleteText(index, length);
  192. }, source, index, -1 * length);
  193. }
  194. disable() {
  195. this.enable(false);
  196. }
  197. editReadOnly(modifier) {
  198. this.allowReadOnlyEdits = true;
  199. const value = modifier();
  200. this.allowReadOnlyEdits = false;
  201. return value;
  202. }
  203. enable() {
  204. let enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  205. this.scroll.enable(enabled);
  206. this.container.classList.toggle('ql-disabled', !enabled);
  207. }
  208. focus() {
  209. let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  210. this.selection.focus();
  211. if (!options.preventScroll) {
  212. this.scrollSelectionIntoView();
  213. }
  214. }
  215. format(name, value) {
  216. let source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Emitter.sources.API;
  217. return modify.call(this, () => {
  218. const range = this.getSelection(true);
  219. let change = new Delta();
  220. if (range == null) return change;
  221. if (this.scroll.query(name, Parchment.Scope.BLOCK)) {
  222. change = this.editor.formatLine(range.index, range.length, {
  223. [name]: value
  224. });
  225. } else if (range.length === 0) {
  226. this.selection.format(name, value);
  227. return change;
  228. } else {
  229. change = this.editor.formatText(range.index, range.length, {
  230. [name]: value
  231. });
  232. }
  233. this.setSelection(range, Emitter.sources.SILENT);
  234. return change;
  235. }, source);
  236. }
  237. formatLine(index, length, name, value, source) {
  238. let formats;
  239. // eslint-disable-next-line prefer-const
  240. [index, length, formats, source] = overload(index, length,
  241. // @ts-expect-error
  242. name, value, source);
  243. return modify.call(this, () => {
  244. return this.editor.formatLine(index, length, formats);
  245. }, source, index, 0);
  246. }
  247. formatText(index, length, name, value, source) {
  248. let formats;
  249. // eslint-disable-next-line prefer-const
  250. [index, length, formats, source] = overload(
  251. // @ts-expect-error
  252. index, length, name, value, source);
  253. return modify.call(this, () => {
  254. return this.editor.formatText(index, length, formats);
  255. }, source, index, 0);
  256. }
  257. getBounds(index) {
  258. let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  259. let bounds = null;
  260. if (typeof index === 'number') {
  261. bounds = this.selection.getBounds(index, length);
  262. } else {
  263. bounds = this.selection.getBounds(index.index, index.length);
  264. }
  265. if (!bounds) return null;
  266. const containerBounds = this.container.getBoundingClientRect();
  267. return {
  268. bottom: bounds.bottom - containerBounds.top,
  269. height: bounds.height,
  270. left: bounds.left - containerBounds.left,
  271. right: bounds.right - containerBounds.left,
  272. top: bounds.top - containerBounds.top,
  273. width: bounds.width
  274. };
  275. }
  276. getContents() {
  277. let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  278. let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;
  279. [index, length] = overload(index, length);
  280. return this.editor.getContents(index, length);
  281. }
  282. getFormat() {
  283. let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true);
  284. let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  285. if (typeof index === 'number') {
  286. return this.editor.getFormat(index, length);
  287. }
  288. return this.editor.getFormat(index.index, index.length);
  289. }
  290. getIndex(blot) {
  291. return blot.offset(this.scroll);
  292. }
  293. getLength() {
  294. return this.scroll.length();
  295. }
  296. getLeaf(index) {
  297. return this.scroll.leaf(index);
  298. }
  299. getLine(index) {
  300. return this.scroll.line(index);
  301. }
  302. getLines() {
  303. let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  304. let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;
  305. if (typeof index !== 'number') {
  306. return this.scroll.lines(index.index, index.length);
  307. }
  308. return this.scroll.lines(index, length);
  309. }
  310. getModule(name) {
  311. return this.theme.modules[name];
  312. }
  313. getSelection() {
  314. let focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  315. if (focus) this.focus();
  316. this.update(); // Make sure we access getRange with editor in consistent state
  317. return this.selection.getRange()[0];
  318. }
  319. getSemanticHTML() {
  320. let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  321. let length = arguments.length > 1 ? arguments[1] : undefined;
  322. if (typeof index === 'number') {
  323. length = length ?? this.getLength() - index;
  324. }
  325. // @ts-expect-error
  326. [index, length] = overload(index, length);
  327. return this.editor.getHTML(index, length);
  328. }
  329. getText() {
  330. let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  331. let length = arguments.length > 1 ? arguments[1] : undefined;
  332. if (typeof index === 'number') {
  333. length = length ?? this.getLength() - index;
  334. }
  335. // @ts-expect-error
  336. [index, length] = overload(index, length);
  337. return this.editor.getText(index, length);
  338. }
  339. hasFocus() {
  340. return this.selection.hasFocus();
  341. }
  342. insertEmbed(index, embed, value) {
  343. let source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API;
  344. return modify.call(this, () => {
  345. return this.editor.insertEmbed(index, embed, value);
  346. }, source, index);
  347. }
  348. insertText(index, text, name, value, source) {
  349. let formats;
  350. // eslint-disable-next-line prefer-const
  351. // @ts-expect-error
  352. [index,, formats, source] = overload(index, 0, name, value, source);
  353. return modify.call(this, () => {
  354. return this.editor.insertText(index, text, formats);
  355. }, source, index, text.length);
  356. }
  357. isEnabled() {
  358. return this.scroll.isEnabled();
  359. }
  360. off() {
  361. return this.emitter.off(...arguments);
  362. }
  363. on() {
  364. return this.emitter.on(...arguments);
  365. }
  366. once() {
  367. return this.emitter.once(...arguments);
  368. }
  369. removeFormat(index, length, source) {
  370. [index, length,, source] = overload(index, length, source);
  371. return modify.call(this, () => {
  372. return this.editor.removeFormat(index, length);
  373. }, source, index);
  374. }
  375. scrollRectIntoView(rect) {
  376. scrollRectIntoView(this.root, rect);
  377. }
  378. /**
  379. * @deprecated Use Quill#scrollSelectionIntoView() instead.
  380. */
  381. scrollIntoView() {
  382. console.warn('Quill#scrollIntoView() has been deprecated and will be removed in the near future. Please use Quill#scrollSelectionIntoView() instead.');
  383. this.scrollSelectionIntoView();
  384. }
  385. /**
  386. * Scroll the current selection into the visible area.
  387. * If the selection is already visible, no scrolling will occur.
  388. */
  389. scrollSelectionIntoView() {
  390. const range = this.selection.lastRange;
  391. const bounds = range && this.selection.getBounds(range.index, range.length);
  392. if (bounds) {
  393. this.scrollRectIntoView(bounds);
  394. }
  395. }
  396. setContents(delta) {
  397. let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Emitter.sources.API;
  398. return modify.call(this, () => {
  399. delta = new Delta(delta);
  400. const length = this.getLength();
  401. // Quill will set empty editor to \n
  402. const delete1 = this.editor.deleteText(0, length);
  403. const applied = this.editor.insertContents(0, delta);
  404. // Remove extra \n from empty editor initialization
  405. const delete2 = this.editor.deleteText(this.getLength() - 1, 1);
  406. return delete1.compose(applied).compose(delete2);
  407. }, source);
  408. }
  409. setSelection(index, length, source) {
  410. if (index == null) {
  411. // @ts-expect-error https://github.com/microsoft/TypeScript/issues/22609
  412. this.selection.setRange(null, length || Quill.sources.API);
  413. } else {
  414. // @ts-expect-error
  415. [index, length,, source] = overload(index, length, source);
  416. this.selection.setRange(new Range(Math.max(0, index), length), source);
  417. if (source !== Emitter.sources.SILENT) {
  418. this.scrollSelectionIntoView();
  419. }
  420. }
  421. }
  422. setText(text) {
  423. let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Emitter.sources.API;
  424. const delta = new Delta().insert(text);
  425. return this.setContents(delta, source);
  426. }
  427. update() {
  428. let source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Emitter.sources.USER;
  429. const change = this.scroll.update(source); // Will update selection before selection.update() does if text changes
  430. this.selection.update(source);
  431. // TODO this is usually undefined
  432. return change;
  433. }
  434. updateContents(delta) {
  435. let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Emitter.sources.API;
  436. return modify.call(this, () => {
  437. delta = new Delta(delta);
  438. return this.editor.applyDelta(delta);
  439. }, source, true);
  440. }
  441. }
  442. function resolveSelector(selector) {
  443. return typeof selector === 'string' ? document.querySelector(selector) : selector;
  444. }
  445. function expandModuleConfig(config) {
  446. return Object.entries(config ?? {}).reduce((expanded, _ref) => {
  447. let [key, value] = _ref;
  448. return {
  449. ...expanded,
  450. [key]: value === true ? {} : value
  451. };
  452. }, {});
  453. }
  454. function omitUndefinedValuesFromOptions(obj) {
  455. return Object.fromEntries(Object.entries(obj).filter(entry => entry[1] !== undefined));
  456. }
  457. function expandConfig(containerOrSelector, options) {
  458. const container = resolveSelector(containerOrSelector);
  459. if (!container) {
  460. throw new Error('Invalid Quill container');
  461. }
  462. const shouldUseDefaultTheme = !options.theme || options.theme === Quill.DEFAULTS.theme;
  463. const theme = shouldUseDefaultTheme ? Theme : Quill.import(`themes/${options.theme}`);
  464. if (!theme) {
  465. throw new Error(`Invalid theme ${options.theme}. Did you register it?`);
  466. }
  467. const {
  468. modules: quillModuleDefaults,
  469. ...quillDefaults
  470. } = Quill.DEFAULTS;
  471. const {
  472. modules: themeModuleDefaults,
  473. ...themeDefaults
  474. } = theme.DEFAULTS;
  475. let userModuleOptions = expandModuleConfig(options.modules);
  476. // Special case toolbar shorthand
  477. if (userModuleOptions != null && userModuleOptions.toolbar && userModuleOptions.toolbar.constructor !== Object) {
  478. userModuleOptions = {
  479. ...userModuleOptions,
  480. toolbar: {
  481. container: userModuleOptions.toolbar
  482. }
  483. };
  484. }
  485. const modules = merge({}, expandModuleConfig(quillModuleDefaults), expandModuleConfig(themeModuleDefaults), userModuleOptions);
  486. const config = {
  487. ...quillDefaults,
  488. ...omitUndefinedValuesFromOptions(themeDefaults),
  489. ...omitUndefinedValuesFromOptions(options)
  490. };
  491. let registry = options.registry;
  492. if (registry) {
  493. if (options.formats) {
  494. debug.warn('Ignoring "formats" option because "registry" is specified');
  495. }
  496. } else {
  497. registry = options.formats ? createRegistryWithFormats(options.formats, config.registry, debug) : config.registry;
  498. }
  499. return {
  500. ...config,
  501. registry,
  502. container,
  503. theme,
  504. modules: Object.entries(modules).reduce((modulesWithDefaults, _ref2) => {
  505. let [name, value] = _ref2;
  506. if (!value) return modulesWithDefaults;
  507. const moduleClass = Quill.import(`modules/${name}`);
  508. if (moduleClass == null) {
  509. debug.error(`Cannot load ${name} module. Are you sure you registered it?`);
  510. return modulesWithDefaults;
  511. }
  512. return {
  513. ...modulesWithDefaults,
  514. // @ts-expect-error
  515. [name]: merge({}, moduleClass.DEFAULTS || {}, value)
  516. };
  517. }, {}),
  518. bounds: resolveSelector(config.bounds)
  519. };
  520. }
  521. // Handle selection preservation and TEXT_CHANGE emission
  522. // common to modification APIs
  523. function modify(modifier, source, index, shift) {
  524. if (!this.isEnabled() && source === Emitter.sources.USER && !this.allowReadOnlyEdits) {
  525. return new Delta();
  526. }
  527. let range = index == null ? null : this.getSelection();
  528. const oldDelta = this.editor.delta;
  529. const change = modifier();
  530. if (range != null) {
  531. if (index === true) {
  532. index = range.index; // eslint-disable-line prefer-destructuring
  533. }
  534. if (shift == null) {
  535. range = shiftRange(range, change, source);
  536. } else if (shift !== 0) {
  537. // @ts-expect-error index should always be number
  538. range = shiftRange(range, index, shift, source);
  539. }
  540. this.setSelection(range, Emitter.sources.SILENT);
  541. }
  542. if (change.length() > 0) {
  543. const args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source];
  544. this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);
  545. if (source !== Emitter.sources.SILENT) {
  546. this.emitter.emit(...args);
  547. }
  548. }
  549. return change;
  550. }
  551. function overload(index, length, name, value, source) {
  552. let formats = {};
  553. // @ts-expect-error
  554. if (typeof index.index === 'number' && typeof index.length === 'number') {
  555. // Allow for throwaway end (used by insertText/insertEmbed)
  556. if (typeof length !== 'number') {
  557. // @ts-expect-error
  558. source = value;
  559. value = name;
  560. name = length;
  561. // @ts-expect-error
  562. length = index.length; // eslint-disable-line prefer-destructuring
  563. // @ts-expect-error
  564. index = index.index; // eslint-disable-line prefer-destructuring
  565. } else {
  566. // @ts-expect-error
  567. length = index.length; // eslint-disable-line prefer-destructuring
  568. // @ts-expect-error
  569. index = index.index; // eslint-disable-line prefer-destructuring
  570. }
  571. } else if (typeof length !== 'number') {
  572. // @ts-expect-error
  573. source = value;
  574. value = name;
  575. name = length;
  576. length = 0;
  577. }
  578. // Handle format being object, two format name/value strings or excluded
  579. if (typeof name === 'object') {
  580. // @ts-expect-error Fix me later
  581. formats = name;
  582. // @ts-expect-error
  583. source = value;
  584. } else if (typeof name === 'string') {
  585. if (value != null) {
  586. formats[name] = value;
  587. } else {
  588. // @ts-expect-error
  589. source = name;
  590. }
  591. }
  592. // Handle optional source
  593. source = source || Emitter.sources.API;
  594. // @ts-expect-error
  595. return [index, length, formats, source];
  596. }
  597. function shiftRange(range, index, lengthOrSource, source) {
  598. const length = typeof lengthOrSource === 'number' ? lengthOrSource : 0;
  599. if (range == null) return null;
  600. let start;
  601. let end;
  602. // @ts-expect-error -- TODO: add a better type guard around `index`
  603. if (index && typeof index.transformPosition === 'function') {
  604. [start, end] = [range.index, range.index + range.length].map(pos =>
  605. // @ts-expect-error -- TODO: add a better type guard around `index`
  606. index.transformPosition(pos, source !== Emitter.sources.USER));
  607. } else {
  608. [start, end] = [range.index, range.index + range.length].map(pos => {
  609. // @ts-expect-error -- TODO: add a better type guard around `index`
  610. if (pos < index || pos === index && source === Emitter.sources.USER) return pos;
  611. if (length >= 0) {
  612. return pos + length;
  613. }
  614. // @ts-expect-error -- TODO: add a better type guard around `index`
  615. return Math.max(index, pos + length);
  616. });
  617. }
  618. return new Range(start, end - start);
  619. }
  620. export { Parchment, Range };
  621. export { globalRegistry, expandConfig, overload, Quill as default };
  622. //# sourceMappingURL=quill.js.map