From 78f5e99709b6dfda59acaa429e4873c7cb58cd90 Mon Sep 17 00:00:00 2001 From: Zihua Li Date: Tue, 16 Aug 2022 21:56:53 +0800 Subject: [PATCH] Misc improvements --- .gitignore | 1 + core/editor.ts | 40 ++-- core/quill.ts | 34 +-- modules/clipboard.ts | 3 +- modules/keyboard.ts | 2 +- modules/table.ts | 2 +- modules/tableEmbed.ts | 12 +- package-lock.json | 512 +++++++++++++++++++++++++++++++++++++----- package.json | 10 +- test/random.js | 264 ++++++++++------------ test/random.ts | 165 ++++++++++++++ tsconfig.json | 3 +- ui/color-picker.js | 35 --- ui/icon-picker.js | 22 -- ui/icons.js | 103 --------- ui/picker.js | 167 -------------- ui/tooltip.js | 50 ----- 17 files changed, 800 insertions(+), 625 deletions(-) create mode 100644 test/random.ts delete mode 100644 ui/color-picker.js delete mode 100644 ui/icon-picker.js delete mode 100644 ui/icons.js delete mode 100644 ui/picker.js delete mode 100644 ui/tooltip.js diff --git a/.gitignore b/.gitignore index 44e659726d..e25e257cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ core/*.js formats/*.js modules/*.js themes/*.js +ui/*.js core.js quill.js diff --git a/core/editor.ts b/core/editor.ts index f28d603739..3a01bb55ab 100644 --- a/core/editor.ts +++ b/core/editor.ts @@ -21,7 +21,7 @@ class Editor { this.delta = this.getDelta(); } - applyDelta(delta: Delta) { + applyDelta(delta: Delta): Delta { this.scroll.update(); let scrollLength = this.scroll.length(); this.scroll.batchStart(); @@ -91,12 +91,16 @@ class Editor { return this.update(normalizedDelta); } - deleteText(index, length) { + deleteText(index: number, length: number): Delta { this.scroll.deleteAt(index, length); return this.update(new Delta().retain(index).delete(length)); } - formatLine(index, length, formats = {}) { + formatLine( + index: number, + length: number, + formats: Record = {}, + ): Delta { this.scroll.update(); Object.keys(formats).forEach(format => { this.scroll.lines(index, Math.max(length, 1)).forEach(line => { @@ -108,7 +112,11 @@ class Editor { return this.update(delta); } - formatText(index, length, formats = {}) { + formatText( + index: number, + length: number, + formats: Record = {}, + ): Delta { Object.keys(formats).forEach(format => { this.scroll.formatAt(index, length, format, formats[format]); }); @@ -116,17 +124,17 @@ class Editor { return this.update(delta); } - getContents(index, length) { + getContents(index: number, length: number): Delta { return this.delta.slice(index, index + length); } - getDelta() { + getDelta(): Delta { return this.scroll.lines().reduce((delta, line) => { return delta.concat(line.delta()); }, new Delta()); } - getFormat(index, length = 0) { + getFormat(index: number, length = 0): Record { let lines = []; let leaves = []; if (length === 0) { @@ -156,7 +164,7 @@ class Editor { return { ...lines, ...leaves }; } - getHTML(index, length) { + getHTML(index: number, length: number): string { const [line, lineOffset] = this.scroll.line(index); if (line.length() >= lineOffset + length) { return convertHTML(line, lineOffset, length, true); @@ -164,19 +172,23 @@ class Editor { return convertHTML(this.scroll, index, length, true); } - getText(index, length) { + getText(index: number, length: number): string { return this.getContents(index, length) .filter(op => typeof op.insert === 'string') .map(op => op.insert) .join(''); } - insertEmbed(index, embed, value) { + insertEmbed(index: number, embed: string, value: unknown): Delta { this.scroll.insertAt(index, embed, value); return this.update(new Delta().retain(index).insert({ [embed]: value })); } - insertText(index, text, formats = {}) { + insertText( + index: number, + text: string, + formats: Record = {}, + ): Delta { text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); this.scroll.insertAt(index, text); Object.keys(formats).forEach(format => { @@ -187,7 +199,7 @@ class Editor { ); } - isBlank() { + isBlank(): boolean { if (this.scroll.children.length === 0) return true; if (this.scroll.children.length > 1) return false; const blot = this.scroll.children.head; @@ -197,7 +209,7 @@ class Editor { return block.children.head instanceof Break; } - removeFormat(index, length) { + removeFormat(index: number, length: number): Delta { const text = this.getText(index, length); const [line, offset] = this.scroll.line(index + length); let suffixLength = 0; @@ -215,7 +227,7 @@ class Editor { return this.applyDelta(delta); } - update(change, mutations = [], selectionInfo = undefined) { + update(change: Delta, mutations = [], selectionInfo = undefined): Delta { const oldDelta = this.delta; if ( mutations.length === 1 && diff --git a/core/quill.ts b/core/quill.ts index 26df8739ad..ad8e3285b7 100644 --- a/core/quill.ts +++ b/core/quill.ts @@ -259,6 +259,7 @@ class Quill { return modify.call( this, () => { + // @ts-expect-error return this.editor.deleteText(index, length); }, source, @@ -394,6 +395,7 @@ class Quill { return modify.call( this, () => { + // @ts-expect-error return this.editor.formatText(index, length, formats); }, source, @@ -559,19 +561,19 @@ class Quill { return this.scroll.isEnabled(); } - off(event: string, ...args: unknown[]) { - // @ts-expect-error - return this.emitter.off(event, ...args); + off(...args: Parameters) { + return this.emitter.off(...args); } on( event: typeof Emitter['events']['TEXT_CHANGE'], handler: (delta: Delta, oldContent: Delta, source: EmitterSource) => void, - ): this; + ): Emitter; on( event: typeof Emitter['events']['SELECTION_CHANGE'], handler: (range: Range, oldRange: Range, source: EmitterSource) => void, - ): this; + ): Emitter; + // @ts-expect-error on( event: typeof Emitter['events']['EDITOR_CHANGE'], handler: ( @@ -584,16 +586,14 @@ class Quill { EmitterSource, ] ) => void, - ): this; - on(event: string, ...args: unknown[]): this; - on(event: string, ...args: unknown[]): this { - // @ts-expect-error - return this.emitter.on(event, ...args); + ): Emitter; + on(event: string, ...args: unknown[]): Emitter; + on(...args: Parameters): Emitter { + return this.emitter.on(...args); } - once(event: string, ...args: unknown[]) { - // @ts-expect-error - return this.emitter.once(event, ...args); + once(...args: Parameters) { + return this.emitter.once(...args); } removeFormat(...args: Parameters) { @@ -612,7 +612,10 @@ class Quill { this.selection.scrollIntoView(this.scrollingContainer); } - setContents(delta: Delta | Op[], source = Emitter.sources.API) { + setContents( + delta: Delta | Op[], + source: EmitterSource = Emitter.sources.API, + ) { return modify.call( this, () => { @@ -630,6 +633,7 @@ class Quill { ); } setSelection(range: Range | null, source?: EmitterSource): void; + setSelection(index: number, source?: EmitterSource): void; setSelection(index: number, length?: number, source?: EmitterSource): void; setSelection(index: number, source?: EmitterSource): void; setSelection( @@ -650,7 +654,7 @@ class Quill { } } - setText(text, source = Emitter.sources.API) { + setText(text: string, source: EmitterSource = Emitter.sources.API) { const delta = new Delta().insert(text); return this.setContents(delta, source); } diff --git a/modules/clipboard.ts b/modules/clipboard.ts index bb4c862c46..2fe7466589 100644 --- a/modules/clipboard.ts +++ b/modules/clipboard.ts @@ -139,7 +139,7 @@ class Clipboard extends Module { const delta = this.convert({ html: index, text: '' }); // @ts-expect-error this.quill.setContents(delta, html); - this.quill.setSelection(0, 0, Quill.sources.SILENT); + this.quill.setSelection(0, Quill.sources.SILENT); } else { const paste = this.convert({ html, text: '' }); this.quill.updateContents( @@ -207,7 +207,6 @@ class Clipboard extends Module { // range.length contributes to delta.length() this.quill.setSelection( delta.length() - range.length, - 0, Quill.sources.SILENT, ); this.quill.scrollIntoView(); diff --git a/modules/keyboard.ts b/modules/keyboard.ts index 98aed0a4d1..5414e3f1ff 100644 --- a/modules/keyboard.ts +++ b/modules/keyboard.ts @@ -69,7 +69,7 @@ class Keyboard extends Module { return binding.key === evt.key || binding.key === evt.which; } - bindings: Record = {}; + bindings: Record; constructor(quill: Quill, options: Partial) { super(quill, options); diff --git a/modules/table.ts b/modules/table.ts index d69707b9dc..0cb0521b06 100644 --- a/modules/table.ts +++ b/modules/table.ts @@ -9,7 +9,7 @@ import { tableId, } from '../formats/table'; -class Table extends Module<{}> { +class Table extends Module { static register() { Quill.register(TableCell); Quill.register(TableRow); diff --git a/modules/tableEmbed.ts b/modules/tableEmbed.ts index 5fab8d9d2d..1ee90c7a56 100644 --- a/modules/tableEmbed.ts +++ b/modules/tableEmbed.ts @@ -9,9 +9,7 @@ export type CellData = { export interface TableData { rows?: Delta['ops']; columns?: Delta['ops']; - cells?: { - [identity: string]: CellData; - }; + cells?: Record; } const parseCellIdentity = (identity: string) => { @@ -61,11 +59,7 @@ const compactCellData = ({ content, attributes }) => { }; const compactTableData = ({ rows, columns, cells }) => { - const data: { - rows?: Delta['ops']; - columns?: Delta['ops']; - cells?: Record; - } = {}; + const data: TableData = {}; if (rows.length() > 0) { data.rows = rows.ops; } @@ -231,7 +225,7 @@ export const tableHandler = { }, }; -class TableEmbed extends Module<{}> { +class TableEmbed extends Module { static register() { Delta.registerEmbed('table-embed', tableHandler); } diff --git a/package-lock.json b/package-lock.json index a6b14f3ab0..71f0ed182e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@babel/code-frame": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", @@ -25,57 +35,239 @@ } }, "@babel/core": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", - "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.0", - "@babel/parser": "^7.9.0", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "requires": { - "ms": "^2.1.1" + "@babel/highlight": "^7.18.6" } }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "dev": true + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", "dev": true, "requires": { - "minimist": "^1.2.5" + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + } + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "dev": true + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "caniuse-lite": { + "version": "1.0.30001377", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001377.tgz", + "integrity": "sha512-I5XeHI1x/mRSGl96LFOaSk528LA/yZG3m3iQgImGujjO8gotd/DL8QaI1R1h1dg5ATeI2jqPblMpKq4Tr5iKfQ==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "electron-to-chromium": { + "version": "1.4.221", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.221.tgz", + "integrity": "sha512-aWg2mYhpxZ6Q6Xvyk7B2ziBca4YqrCDlXzmcD7wuRs65pVEVkMT1u2ifdjpAQais2O2o0rW964ZWWWYRlAL/kw==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true }, "ms": { @@ -83,6 +275,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -160,6 +364,12 @@ "lodash": "^4.17.13" } }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, "@babel/helper-explode-assignable-expression": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", @@ -308,12 +518,24 @@ "@babel/types": "^7.8.3" } }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, "@babel/helper-wrap-function": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", @@ -327,14 +549,153 @@ } }, "@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "dev": true + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "@babel/highlight": { @@ -1237,12 +1598,28 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", @@ -1332,6 +1709,12 @@ "@types/node": "*" } }, + "@types/jasmine": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz", + "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -3321,9 +3704,9 @@ "dev": true }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -4192,6 +4575,12 @@ "es6-promise": "^4.0.3" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6731,9 +7120,9 @@ "dev": true }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -9935,6 +10324,12 @@ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -10790,15 +11185,6 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, "resolve-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", @@ -13207,6 +13593,16 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", diff --git a/package.json b/package.json index 9c37beeed0..8dc8b2f4b4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "author": "Jason Chen ", "homepage": "http://quilljs.com", "main": "dist/quill.js", - "types": "dist/typings/quill.d.ts", "config": { "ports": { "proxy": "9000", @@ -23,8 +22,9 @@ "quill-delta": "^5.0.0" }, "devDependencies": { - "@babel/core": "^7.9.0", + "@babel/core": "^7.18.10", "@babel/preset-env": "^7.9.5", + "@types/jasmine": "^4.0.3", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", "babel-loader": "^8.1.0", @@ -79,14 +79,16 @@ "build:webpack": "webpack --config _develop/webpack.config.js; rm dist/quill.core dist/quill.bubble dist/quill.snow", "build:release": "./_develop/scripts/release.sh", "develop": "npm run start", - "lint": "eslint .", + "lint": "npm run lint:eslint && npm run lint:tsc", + "lint:eslint": "eslint .", + "lint:tsc": "tsc --noEmit --skipLibCheck", "prepare": "npm run build", "start": "npm run build:webpack; bundle exec foreman start -f _develop/procfile", "test": "npm run test:unit; npm run test:random", "test:all": "npm run test:unit; npm run test:functional; npm run test:random", "test:functional": "./_develop/scripts/puppeteer.sh", "test:unit": "npm run build; karma start _develop/karma.config.js", - "test:random": "ts-node -O '{\"module\":\"commonjs\"}' ./node_modules/.bin/jasmine test/random.js", + "test:random": "ts-node --preferTsExts -O '{\"module\":\"commonjs\"}' ./node_modules/.bin/jasmine test/random.ts", "test:coverage": "webpack --env.coverage --config _develop/webpack.config.js; karma start _develop/karma.config.js --reporters coverage", "travis": "npm run lint && karma start _develop/karma.config.js --reporters dots,saucelabs" }, diff --git a/test/random.js b/test/random.js index 81ed72ec30..349068f19b 100644 --- a/test/random.js +++ b/test/random.js @@ -1,165 +1,145 @@ import Delta from 'quill-delta'; import TableEmbed from '../modules/tableEmbed'; - // Random testing in order to find unknown issues. - const random = choices => { - if (typeof choices === 'number') { - return Math.floor(Math.random() * choices); - } - return choices[random(choices.length)]; + if (typeof choices === 'number') { + return Math.floor(Math.random() * choices); + } + return choices[random(choices.length)]; }; - const getRandomRowColumnId = () => { - const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; - return new Array(8) - .fill(0) - .map(() => characters.charAt(Math.floor(Math.random() * characters.length))) - .join(''); + const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; + return new Array(8) + .fill(0) + .map(() => characters.charAt(Math.floor(Math.random() * characters.length))) + .join(''); }; - const attachAttributes = obj => { - const getRandomAttributes = () => { - const attributeCount = random([1, 4, 8]); - const allowedAttributes = ['align', 'background', 'color', 'font']; - const allowedValues = ['center', 'red', 'left', 'uppercase']; - const attributes = {}; - new Array(attributeCount).fill(0).forEach(() => { - attributes[random(allowedAttributes)] = random(allowedValues); - }); - return attributes; - }; - if (random([true, false])) { - obj.attributes = getRandomAttributes(); - } - return obj; + const getRandomAttributes = () => { + const attributeCount = random([1, 4, 8]); + const allowedAttributes = ['align', 'background', 'color', 'font']; + const allowedValues = ['center', 'red', 'left', 'uppercase']; + const attributes = {}; + new Array(attributeCount).fill(0).forEach(() => { + attributes[random(allowedAttributes)] = random(allowedValues); + }); + return attributes; + }; + if (random([true, false])) { + obj.attributes = getRandomAttributes(); + } + return obj; }; - const getRandomCellContent = () => { - const opCount = random([1, 2, 3]); - const delta = new Delta(); - new Array(opCount).fill(0).forEach(() => { - delta.push( - attachAttributes({ - insert: new Array(random(10) + 1) - .fill(0) - .map(() => random(['a', 'b', 'c', 'c', 'e', 'f', 'g'])) - .join(''), - }), - ); - }); - return delta.ops; + const opCount = random([1, 2, 3]); + const delta = new Delta(); + new Array(opCount).fill(0).forEach(() => { + delta.push(attachAttributes({ + insert: new Array(random(10) + 1) + .fill(0) + .map(() => random(['a', 'b', 'c', 'c', 'e', 'f', 'g'])) + .join(''), + })); + }); + return delta.ops; }; - const getRandomChange = base => { - const table = {}; - const dimension = { - rows: new Delta(base.ops[0].insert['table-embed'].rows || []).length(), - columns: new Delta( - base.ops[0].insert['table-embed'].columns || [], - ).length(), - }; - ['rows', 'columns'].forEach(field => { - const baseLength = dimension[field]; - const action = random(['insert', 'delete', 'retain']); - const delta = new Delta(); - switch (action) { - case 'insert': - delta.retain(random(baseLength + 1)); - delta.push( - attachAttributes({ insert: { id: getRandomRowColumnId() } }), - ); - break; - case 'delete': - if (baseLength >= 1) { - delta.retain(random(baseLength)); - delta.delete(1); + const table = {}; + const dimension = { + rows: new Delta(base.ops[0].insert['table-embed'].rows || []).length(), + columns: new Delta(base.ops[0].insert['table-embed'].columns || []).length(), + }; + ['rows', 'columns'].forEach(field => { + const baseLength = dimension[field]; + const action = random(['insert', 'delete', 'retain']); + const delta = new Delta(); + switch (action) { + case 'insert': + delta.retain(random(baseLength + 1)); + delta.push(attachAttributes({ insert: { id: getRandomRowColumnId() } })); + break; + case 'delete': + if (baseLength >= 1) { + delta.retain(random(baseLength)); + delta.delete(1); + } + break; + case 'retain': + if (baseLength >= 1) { + delta.retain(random(baseLength)); + delta.push(attachAttributes({ retain: 1 })); + } + break; + default: + break; } - break; - case 'retain': - if (baseLength >= 1) { - delta.retain(random(baseLength)); - delta.push(attachAttributes({ retain: 1 })); + if (delta.length() > 0) { + table[field] = delta.ops; } - break; - default: - break; - } - if (delta.length() > 0) { - table[field] = delta.ops; - } - }); - - const updateCellCount = random([0, 1, 2, 3]); - new Array(updateCellCount).fill(0).forEach(() => { - const row = random(dimension.rows); - const column = random(dimension.columns); - const cellIdentityToModify = `${row + 1}:${column + 1}`; - table.cells = { - [cellIdentityToModify]: attachAttributes({ - content: getRandomCellContent(), - }), - }; - }); - return new Delta([attachAttributes({ retain: { 'table-embed': table } })]); + }); + const updateCellCount = random([0, 1, 2, 3]); + new Array(updateCellCount).fill(0).forEach(() => { + const row = random(dimension.rows); + const column = random(dimension.columns); + const cellIdentityToModify = `${row + 1}:${column + 1}`; + table.cells = { + [cellIdentityToModify]: attachAttributes({ + content: getRandomCellContent(), + }), + }; + }); + return new Delta([attachAttributes({ retain: { 'table-embed': table } })]); }; - const getRandomRowColumnInsert = count => { - return new Delta( - new Array(count) - .fill(0) - .map(() => attachAttributes({ insert: { id: getRandomRowColumnId() } })), - ).ops; + return new Delta(new Array(count) + .fill(0) + .map(() => attachAttributes({ insert: { id: getRandomRowColumnId() } }))).ops; }; - const getRandomBase = () => { - const rowCount = random([0, 1, 2, 3]); - const columnCount = random([0, 1, 2]); - const cellCount = random([0, 1, 2, 3, 4, 5]); - - const table = {}; - if (rowCount) table.rows = getRandomRowColumnInsert(rowCount); - if (columnCount) table.columns = getRandomRowColumnInsert(columnCount); - if (cellCount) { - const cells = {}; - new Array(cellCount).fill(0).forEach(() => { - const row = random(rowCount); - const column = random(columnCount); - const identity = `${row + 1}:${column + 1}`; - const cell = attachAttributes({}); - if (random([true, false])) { - cell.content = getRandomCellContent(); - } - if (Object.keys(cell).length) { - cells[identity] = cell; - } - }); - if (Object.keys(cells).length) table.cells = cells; - } - return new Delta([{ insert: { 'table-embed': table } }]); + const rowCount = random([0, 1, 2, 3]); + const columnCount = random([0, 1, 2]); + const cellCount = random([0, 1, 2, 3, 4, 5]); + const table = {}; + if (rowCount) + table.rows = getRandomRowColumnInsert(rowCount); + if (columnCount) + table.columns = getRandomRowColumnInsert(columnCount); + if (cellCount) { + const cells = {}; + new Array(cellCount).fill(0).forEach(() => { + const row = random(rowCount); + const column = random(columnCount); + const identity = `${row + 1}:${column + 1}`; + const cell = attachAttributes({}); + if (random([true, false])) { + cell.content = getRandomCellContent(); + } + if (Object.keys(cell).length) { + cells[identity] = cell; + } + }); + if (Object.keys(cells).length) + table.cells = cells; + } + return new Delta([{ insert: { 'table-embed': table } }]); }; - const runTestCase = () => { - const base = getRandomBase(); - const change = getRandomChange(base); - expect(base).toEqual(base.compose(change).compose(change.invert(base))); - - const anotherChange = getRandomChange(base); - expect(change.compose(change.transform(anotherChange, true))).toEqual( - anotherChange.compose(anotherChange.transform(change)), - ); + const base = getRandomBase(); + const change = getRandomChange(base); + expect(base).toEqual(base.compose(change).compose(change.invert(base))); + const anotherChange = getRandomChange(base); + expect(change.compose(change.transform(anotherChange, true))).toEqual(anotherChange.compose(anotherChange.transform(change))); }; - describe('random tests', () => { - beforeAll(() => { - TableEmbed.register(); - }); - - it('delta', () => { - for (let i = 0; i < 20; i += 1) { - for (let j = 0; j < 1000; j += 1) { - runTestCase(); - } - } - }); + beforeAll(() => { + TableEmbed.register(); + }); + it('delta', () => { + for (let i = 0; i < 20; i += 1) { + for (let j = 0; j < 1000; j += 1) { + runTestCase(); + } + } + }); }); +//# sourceMappingURL=random.js.map \ No newline at end of file diff --git a/test/random.ts b/test/random.ts new file mode 100644 index 0000000000..346fbf7135 --- /dev/null +++ b/test/random.ts @@ -0,0 +1,165 @@ +import Delta from 'quill-delta'; +import TableEmbed, { TableData } from '../modules/tableEmbed'; + +// Random testing in order to find unknown issues. + +const random = choices => { + if (typeof choices === 'number') { + return Math.floor(Math.random() * choices); + } + return choices[random(choices.length)]; +}; + +const getRandomRowColumnId = () => { + const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; + return new Array(8) + .fill(0) + .map(() => characters.charAt(Math.floor(Math.random() * characters.length))) + .join(''); +}; + +const attachAttributes = obj => { + const getRandomAttributes = () => { + const attributeCount = random([1, 4, 8]); + const allowedAttributes = ['align', 'background', 'color', 'font']; + const allowedValues = ['center', 'red', 'left', 'uppercase']; + const attributes = {}; + new Array(attributeCount).fill(0).forEach(() => { + attributes[random(allowedAttributes)] = random(allowedValues); + }); + return attributes; + }; + if (random([true, false])) { + obj.attributes = getRandomAttributes(); + } + return obj; +}; + +const getRandomCellContent = () => { + const opCount = random([1, 2, 3]); + const delta = new Delta(); + new Array(opCount).fill(0).forEach(() => { + delta.push( + attachAttributes({ + insert: new Array(random(10) + 1) + .fill(0) + .map(() => random(['a', 'b', 'c', 'c', 'e', 'f', 'g'])) + .join(''), + }), + ); + }); + return delta.ops; +}; + +const getRandomChange = base => { + const table: TableData = {}; + const dimension = { + rows: new Delta(base.ops[0].insert['table-embed'].rows || []).length(), + columns: new Delta( + base.ops[0].insert['table-embed'].columns || [], + ).length(), + }; + ['rows', 'columns'].forEach(field => { + const baseLength = dimension[field]; + const action = random(['insert', 'delete', 'retain']); + const delta = new Delta(); + switch (action) { + case 'insert': + delta.retain(random(baseLength + 1)); + delta.push( + attachAttributes({ insert: { id: getRandomRowColumnId() } }), + ); + break; + case 'delete': + if (baseLength >= 1) { + delta.retain(random(baseLength)); + delta.delete(1); + } + break; + case 'retain': + if (baseLength >= 1) { + delta.retain(random(baseLength)); + delta.push(attachAttributes({ retain: 1 })); + } + break; + default: + break; + } + if (delta.length() > 0) { + table[field] = delta.ops; + } + }); + + const updateCellCount = random([0, 1, 2, 3]); + new Array(updateCellCount).fill(0).forEach(() => { + const row = random(dimension.rows); + const column = random(dimension.columns); + const cellIdentityToModify = `${row + 1}:${column + 1}`; + table.cells = { + [cellIdentityToModify]: attachAttributes({ + content: getRandomCellContent(), + }), + }; + }); + return new Delta([attachAttributes({ retain: { 'table-embed': table } })]); +}; + +const getRandomRowColumnInsert = count => { + return new Delta( + new Array(count) + .fill(0) + .map(() => attachAttributes({ insert: { id: getRandomRowColumnId() } })), + ).ops; +}; + +const getRandomBase = () => { + const rowCount = random([0, 1, 2, 3]); + const columnCount = random([0, 1, 2]); + const cellCount = random([0, 1, 2, 3, 4, 5]); + + const table: TableData = {}; + if (rowCount) table.rows = getRandomRowColumnInsert(rowCount); + if (columnCount) table.columns = getRandomRowColumnInsert(columnCount); + if (cellCount) { + const cells = {}; + new Array(cellCount).fill(0).forEach(() => { + const row = random(rowCount); + const column = random(columnCount); + const identity = `${row + 1}:${column + 1}`; + const cell = attachAttributes({}); + if (random([true, false])) { + cell.content = getRandomCellContent(); + } + if (Object.keys(cell).length) { + cells[identity] = cell; + } + }); + if (Object.keys(cells).length) table.cells = cells; + } + return new Delta([{ insert: { 'table-embed': table } }]); +}; + +const runTestCase = () => { + const base = getRandomBase(); + const change = getRandomChange(base); + expect(base).toEqual(base.compose(change).compose(change.invert(base))); + + const anotherChange = getRandomChange(base); + expect(change.compose(change.transform(anotherChange, true))).toEqual( + anotherChange.compose(anotherChange.transform(change)), + ); +}; + +describe('random tests', () => { + beforeAll(() => { + TableEmbed.register(); + }); + + it('delta', () => { + for (let i = 0; i < 20; i += 1) { + for (let j = 0; j < 1000; j += 1) { + runTestCase(); + } + } + }); +}); diff --git a/tsconfig.json b/tsconfig.json index f7fe22b159..b4ffe7634f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,5 @@ "module": "es6", "moduleResolution": "node" }, - "include": ["./**/*"], - "exclude": ["./**/*.d.ts"] + "include": ["./**/*"] } diff --git a/ui/color-picker.js b/ui/color-picker.js deleted file mode 100644 index b8c961841a..0000000000 --- a/ui/color-picker.js +++ /dev/null @@ -1,35 +0,0 @@ -import Picker from './picker'; -class ColorPicker extends Picker { - constructor(select, label) { - super(select); - this.label.innerHTML = label; - this.container.classList.add('ql-color-picker'); - Array.from(this.container.querySelectorAll('.ql-picker-item')) - .slice(0, 7) - .forEach(item => { - item.classList.add('ql-primary'); - }); - } - buildItem(option) { - const item = super.buildItem(option); - item.style.backgroundColor = option.getAttribute('value') || ''; - return item; - } - selectItem(item, trigger) { - super.selectItem(item, trigger); - const colorLabel = this.label.querySelector('.ql-color-label'); - const value = item ? item.getAttribute('data-value') || '' : ''; - if (colorLabel) { - if (colorLabel.tagName === 'line') { - // @ts-expect-error - colorLabel.style.stroke = value; - } - else { - // @ts-expect-error - colorLabel.style.fill = value; - } - } - } -} -export default ColorPicker; -//# sourceMappingURL=color-picker.js.map \ No newline at end of file diff --git a/ui/icon-picker.js b/ui/icon-picker.js deleted file mode 100644 index 7d90a94cdd..0000000000 --- a/ui/icon-picker.js +++ /dev/null @@ -1,22 +0,0 @@ -import Picker from './picker'; -class IconPicker extends Picker { - constructor(select, icons) { - super(select); - this.container.classList.add('ql-icon-picker'); - Array.from(this.container.querySelectorAll('.ql-picker-item')).forEach(item => { - item.innerHTML = icons[item.getAttribute('data-value') || '']; - }); - this.defaultItem = this.container.querySelector('.ql-selected'); - // @ts-expect-error - this.selectItem(this.defaultItem); - } - selectItem(target, trigger) { - super.selectItem(target, trigger); - const item = target || this.defaultItem; - if (this.label.innerHTML === item.innerHTML) - return; - this.label.innerHTML = item.innerHTML; - } -} -export default IconPicker; -//# sourceMappingURL=icon-picker.js.map \ No newline at end of file diff --git a/ui/icons.js b/ui/icons.js deleted file mode 100644 index ce790f5046..0000000000 --- a/ui/icons.js +++ /dev/null @@ -1,103 +0,0 @@ -// @ts-expect-error -import alignLeftIcon from '../assets/icons/align-left.svg'; -// @ts-expect-error -import alignCenterIcon from '../assets/icons/align-center.svg'; -// @ts-expect-error -import alignRightIcon from '../assets/icons/align-right.svg'; -// @ts-expect-error -import alignJustifyIcon from '../assets/icons/align-justify.svg'; -// @ts-expect-error -import backgroundIcon from '../assets/icons/background.svg'; -// @ts-expect-error -import blockquoteIcon from '../assets/icons/blockquote.svg'; -// @ts-expect-error -import boldIcon from '../assets/icons/bold.svg'; -// @ts-expect-error -import cleanIcon from '../assets/icons/clean.svg'; -// @ts-expect-error -import codeIcon from '../assets/icons/code.svg'; -// @ts-expect-error -import colorIcon from '../assets/icons/color.svg'; -// @ts-expect-error -import directionLeftToRightIcon from '../assets/icons/direction-ltr.svg'; -// @ts-expect-error -import directionRightToLeftIcon from '../assets/icons/direction-rtl.svg'; -// @ts-expect-error -import formulaIcon from '../assets/icons/formula.svg'; -// @ts-expect-error -import headerIcon from '../assets/icons/header.svg'; -// @ts-expect-error -import header2Icon from '../assets/icons/header-2.svg'; -// @ts-expect-error -import italicIcon from '../assets/icons/italic.svg'; -// @ts-expect-error -import imageIcon from '../assets/icons/image.svg'; -// @ts-expect-error -import indentIcon from '../assets/icons/indent.svg'; -// @ts-expect-error -import outdentIcon from '../assets/icons/outdent.svg'; -// @ts-expect-error -import linkIcon from '../assets/icons/link.svg'; -// @ts-expect-error -import listBulletIcon from '../assets/icons/list-bullet.svg'; -// @ts-expect-error -import listCheckIcon from '../assets/icons/list-check.svg'; -// @ts-expect-error -import listOrderedIcon from '../assets/icons/list-ordered.svg'; -// @ts-expect-error -import subscriptIcon from '../assets/icons/subscript.svg'; -// @ts-expect-error -import superscriptIcon from '../assets/icons/superscript.svg'; -// @ts-expect-error -import strikeIcon from '../assets/icons/strike.svg'; -// @ts-expect-error -import tableIcon from '../assets/icons/table.svg'; -// @ts-expect-error -import underlineIcon from '../assets/icons/underline.svg'; -// @ts-expect-error -import videoIcon from '../assets/icons/video.svg'; -export default { - align: { - '': alignLeftIcon, - center: alignCenterIcon, - right: alignRightIcon, - justify: alignJustifyIcon, - }, - background: backgroundIcon, - blockquote: blockquoteIcon, - bold: boldIcon, - clean: cleanIcon, - code: codeIcon, - 'code-block': codeIcon, - color: colorIcon, - direction: { - '': directionLeftToRightIcon, - rtl: directionRightToLeftIcon, - }, - formula: formulaIcon, - header: { - '1': headerIcon, - '2': header2Icon, - }, - italic: italicIcon, - image: imageIcon, - indent: { - '+1': indentIcon, - '-1': outdentIcon, - }, - link: linkIcon, - list: { - bullet: listBulletIcon, - check: listCheckIcon, - ordered: listOrderedIcon, - }, - script: { - sub: subscriptIcon, - super: superscriptIcon, - }, - strike: strikeIcon, - table: tableIcon, - underline: underlineIcon, - video: videoIcon, -}; -//# sourceMappingURL=icons.js.map \ No newline at end of file diff --git a/ui/picker.js b/ui/picker.js deleted file mode 100644 index b6359cbe0b..0000000000 --- a/ui/picker.js +++ /dev/null @@ -1,167 +0,0 @@ -// @ts-expect-error -import DropdownIcon from '../assets/icons/dropdown.svg'; -let optionsCounter = 0; -function toggleAriaAttribute(element, attribute) { - element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true')); -} -class Picker { - constructor(select) { - this.select = select; - this.container = document.createElement('span'); - this.buildPicker(); - this.select.style.display = 'none'; - this.select.parentNode.insertBefore(this.container, this.select); - this.label.addEventListener('mousedown', () => { - this.togglePicker(); - }); - this.label.addEventListener('keydown', event => { - switch (event.key) { - case 'Enter': - this.togglePicker(); - break; - case 'Escape': - this.escape(); - event.preventDefault(); - break; - default: - } - }); - this.select.addEventListener('change', this.update.bind(this)); - } - togglePicker() { - this.container.classList.toggle('ql-expanded'); - // Toggle aria-expanded and aria-hidden to make the picker accessible - toggleAriaAttribute(this.label, 'aria-expanded'); - // @ts-expect-error - toggleAriaAttribute(this.options, 'aria-hidden'); - } - buildItem(option) { - const item = document.createElement('span'); - // @ts-expect-error - item.tabIndex = '0'; - item.setAttribute('role', 'button'); - item.classList.add('ql-picker-item'); - if (option.hasAttribute('value')) { - item.setAttribute('data-value', option.getAttribute('value')); - } - if (option.textContent) { - item.setAttribute('data-label', option.textContent); - } - item.addEventListener('click', () => { - this.selectItem(item, true); - }); - item.addEventListener('keydown', event => { - switch (event.key) { - case 'Enter': - this.selectItem(item, true); - event.preventDefault(); - break; - case 'Escape': - this.escape(); - event.preventDefault(); - break; - default: - } - }); - return item; - } - buildLabel() { - const label = document.createElement('span'); - label.classList.add('ql-picker-label'); - label.innerHTML = DropdownIcon; - // @ts-expect-error - label.tabIndex = '0'; - label.setAttribute('role', 'button'); - label.setAttribute('aria-expanded', 'false'); - this.container.appendChild(label); - return label; - } - buildOptions() { - const options = document.createElement('span'); - options.classList.add('ql-picker-options'); - // Don't want screen readers to read this until options are visible - options.setAttribute('aria-hidden', 'true'); - // @ts-expect-error - options.tabIndex = '-1'; - // Need a unique id for aria-controls - options.id = `ql-picker-options-${optionsCounter}`; - optionsCounter += 1; - this.label.setAttribute('aria-controls', options.id); - // @ts-expect-error - this.options = options; - Array.from(this.select.options).forEach(option => { - const item = this.buildItem(option); - options.appendChild(item); - if (option.selected === true) { - this.selectItem(item); - } - }); - this.container.appendChild(options); - } - buildPicker() { - Array.from(this.select.attributes).forEach(item => { - this.container.setAttribute(item.name, item.value); - }); - this.container.classList.add('ql-picker'); - this.label = this.buildLabel(); - this.buildOptions(); - } - escape() { - // Close menu and return focus to trigger label - this.close(); - // Need setTimeout for accessibility to ensure that the browser executes - // focus on the next process thread and after any DOM content changes - setTimeout(() => this.label.focus(), 1); - } - close() { - this.container.classList.remove('ql-expanded'); - this.label.setAttribute('aria-expanded', 'false'); - // @ts-expect-error - this.options.setAttribute('aria-hidden', 'true'); - } - selectItem(item, trigger = false) { - const selected = this.container.querySelector('.ql-selected'); - if (item === selected) - return; - if (selected != null) { - selected.classList.remove('ql-selected'); - } - if (item == null) - return; - item.classList.add('ql-selected'); - this.select.selectedIndex = Array.from(item.parentNode.children).indexOf(item); - if (item.hasAttribute('data-value')) { - this.label.setAttribute('data-value', item.getAttribute('data-value')); - } - else { - this.label.removeAttribute('data-value'); - } - if (item.hasAttribute('data-label')) { - this.label.setAttribute('data-label', item.getAttribute('data-label')); - } - else { - this.label.removeAttribute('data-label'); - } - if (trigger) { - this.select.dispatchEvent(new Event('change')); - this.close(); - } - } - update() { - let option; - if (this.select.selectedIndex > -1) { - const item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex]; - option = this.select.options[this.select.selectedIndex]; - // @ts-expect-error - this.selectItem(item); - } - else { - this.selectItem(null); - } - const isActive = option != null && - option !== this.select.querySelector('option[selected]'); - this.label.classList.toggle('ql-active', isActive); - } -} -export default Picker; -//# sourceMappingURL=picker.js.map \ No newline at end of file diff --git a/ui/tooltip.js b/ui/tooltip.js deleted file mode 100644 index 2c8c2d97bd..0000000000 --- a/ui/tooltip.js +++ /dev/null @@ -1,50 +0,0 @@ -class Tooltip { - constructor(quill, boundsContainer) { - this.quill = quill; - this.boundsContainer = boundsContainer || document.body; - this.root = quill.addContainer('ql-tooltip'); - // @ts-expect-error - this.root.innerHTML = this.constructor.TEMPLATE; - if (this.quill.root === this.quill.scrollingContainer) { - this.quill.root.addEventListener('scroll', () => { - this.root.style.marginTop = `${-1 * this.quill.root.scrollTop}px`; - }); - } - this.hide(); - } - hide() { - this.root.classList.add('ql-hidden'); - } - position(reference) { - const left = reference.left + reference.width / 2 - this.root.offsetWidth / 2; - // root.scrollTop should be 0 if scrollContainer !== root - const top = reference.bottom + this.quill.root.scrollTop; - this.root.style.left = `${left}px`; - this.root.style.top = `${top}px`; - this.root.classList.remove('ql-flip'); - const containerBounds = this.boundsContainer.getBoundingClientRect(); - const rootBounds = this.root.getBoundingClientRect(); - let shift = 0; - if (rootBounds.right > containerBounds.right) { - shift = containerBounds.right - rootBounds.right; - this.root.style.left = `${left + shift}px`; - } - if (rootBounds.left < containerBounds.left) { - shift = containerBounds.left - rootBounds.left; - this.root.style.left = `${left + shift}px`; - } - if (rootBounds.bottom > containerBounds.bottom) { - const height = rootBounds.bottom - rootBounds.top; - const verticalShift = reference.bottom - reference.top + height; - this.root.style.top = `${top - verticalShift}px`; - this.root.classList.add('ql-flip'); - } - return shift; - } - show() { - this.root.classList.remove('ql-editing'); - this.root.classList.remove('ql-hidden'); - } -} -export default Tooltip; -//# sourceMappingURL=tooltip.js.map \ No newline at end of file