editor.js/src/components/modules/paste.ts
Peter Savchenko 29db87525b
Release: 2.19 (#1364)
* typo fixed (#1235)

* Improvements: more translations added to the i18n example (#1250)

* Return the result of block.call (#1205)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* Return the result of block.call

This change allows blocks to return the result of `call` methods, thus allowing them to expose arbitrary data as needed.

My particular use case is I am using Vue to mount components inside of the larger editorjs framework. One of the components that we are developing can be thought of as a nested agenda, where labels need to be in an order like:

```
I. Top level
  a. second level
    i. third level
```

My plan is to have an orchestrator query all blocks, filter those that need labels prepended, and then programmatically tell each block (with another `call` method) to set its depth to the desired level. At that point, Vue can reactively update any labels, etc. that are needed.

I believe this change will allow for other such uses, and I imagine it should not break any existing code since it was returning `null` before.

* Disable ESLint for call method return value

Because we are returning the value of an arbitrary function, the return value can be anything (hence, the return type must be `any`). However, to reduce noise in ESLint output, we disable ESLint checking the line with the `any` type return.

* Change any type to unknown and add to CHANGELOG.md

Change any type of the call method to unknown but eslint shows error
saying the unknown type is undefined, Also, add the chnage to
CHANGELOG.md as an improvement with the link to the PR itself as no
issue was assigned with it.

* Add unknown to eslint globals

* upd

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: ranemihir <mihirrane171@gmail.com>

* <fix> toolbar--opened overlap with certain text [issue 1196] (#1201)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* <fix> toolbar overlap with text

* Add Fix in CHANGELOG.md

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: ranemihir <mihirrane171@gmail.com>

* Rename initialBlock to defaultBlock (#1209)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* Rename initialBlock to defaultBlock
Closes #993

The initialBlock property is renamed to defaultBlock.

* Change keyword 'InitialBlock' to 'DefaultBlock' in all methods
Fixes #993

All the methods using the keyword 'Initial' or 'initial' for initial block
are replace with 'Default' or 'default'.
For example, the Tools.isIntitial() method is changed to Tools.isDefault().

* Keep initialBlock and defaultBlock both.

initialBlock property is still kept but it will deprecated in the
next major release.

* Change defaultBlock in example.html and rebuild.

* Remove package-lock.json file.

* Update docs/tools.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example-dev.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example-dev.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update types/configs/editor-config.d.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update types/configs/editor-config.d.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Fix needAddDefaultBlock to needToAddDefaultBlock

* Add as an Improvement to CHANGELOG.md

* Delete editor.js.map

* fix log, rename some more places

* Update example.html

* Update blockManager.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>

* Fix blocks.delete with undefined index (#1182) (#1218)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* fix: blocks.delete with undefined index (#1182)

* Add as a Fix in CHANGELOG.md.

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: ranemihir <mihirrane171@gmail.com>

* Fix spam clicking the tune button in Firefox  (#1285)

* Fix spam cliclikng tune in Firefox #1273

* build

* Disabled unwanted I18n messages (#1282)

* The unwanted I18n messages from console is disabled

* Update docs/CHANGELOG.md

Improved Change log

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Remove import statement

import * as _ from '../utils';
removed

* Apply suggestions from code review

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Move SavedData and ValidatedData interfaces from internal types (#1251)

* Move SavedData and ValidatedData interfaces from internal types

* Add changelog

* Upd submodules (#1287)

* upd modules

* Revert "upd modules"

This reverts commit e2ff850d9d.

* upd modules

* Tools destroy called when the editor is destroyed (#1264)

* Tools destroy called when the editor is destroyed

When the editor instance is destroyed, it calls the destroy of the blockManager. blockManager inturn calls destroy of all the blocks that it manages.

* Fixed lint errors

* Use Prmoise.all and add as a Fix in CHANGELOG.md

* Fix commit

* Fix CHANGELOG.md

* Add call of Tools reset methods

* Update tools

* Update changelog

* Update docs/CHANGELOG.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* upd all

* bundle

* upd tools

Co-authored-by: ranemihir <mihirrane171@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Bump elliptic from 6.5.2 to 6.5.3 (#1257)

Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix for input and textarea bug (#1214)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* added handling of inputs and textareas in custom plugins

* Upd tools

* Add changelog

* Upd submodules

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>

* Typos changes required to be fixed on website when using the import concept (#1260)

* Typos changes.

Required to fix them too on the official documentation website

* Update README.md

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Use only import

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Add hidden option to toolbox (#1220)

* Add hidden option to toolbox

* Use false in order to hide toolbox

* Add comment what false means

* Add issue #1221 to changelog

Co-authored-by: t.hata <hata@impact-blue.co.jp>

* Add RTL support (#1248)

* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* Added RTL support

* Fixed code style

* Fixed icons positioning in the toolbar in the RTL mode

* Renamed example-dev-rtl.html to example-rtl.html

* Moved 'direction' option to 'i18n' section

* Fixed an issue with arrow navigation between blocks

* Renamed rtl-fix to codex-editor--rtl

* Fixed icons positioning in the narrow mode for RTL

* Replaced 'isRtl' method with getter

* Fixed bug with the editor initialization when 'i18n' option is not set

* narrow mode improved

* Changelog added

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: ImangazalievM <mahach.miangazaliev@gmail.com>

* Fix i18n default configuration (#1290)

* Fix i18n default configuration

* update bundle

* Fixing Bug #1270 and resolve all yarn lint warning. (#1292)

* Fixing Bug #1270 and resolve all yarn lint warning.

* Update CHANGELOG.md

* Change the Log type from Error to Warn

* upd types

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Stop click propagation only if click cause action (#1252)

* Fixing: #843 problem with onchange callback (#1310)

* Fixing: #843 problem with onchange callback

* Update docs/CHANGELOG.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* The read-only mode (#1035)

(ノ◕ヮ◕)ノ*:・゚✧

* Update submodules (#1335)

* Add inlineToolbar property (#1293)

* Add inlineToolbar property

* Fix lint errors

* Fix comments

Co-authored-by: Murod Khaydarov <murod.haydarov@inbox.ru>

* Sort Tools Working, Can be optimized further

* Fix dataset error and use children

* Fix lint errors

* Add as improvement to CHNAGELOG.md

* Fix comments

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add comments and small fixes

* Fix lint errors

* Fix sortTools() and check inlineToolbar property

* Fix lint errors

* Fix conditions and property names

* Separate block toggler from buttons list in ui

* Fix lint errors

* Fix condition names in allowedToShow()

* Minor bug fixes

* Fix linter warnings

* Update docs/CHANGELOG.md

Co-authored-by: Murod Khaydarov <murod.haydarov@inbox.ru>

* create inlineToolbarSettings() method

* Minor fixes

* Clearify boolean casting

* upd bundle

* fix getInlineToolbarSettings

* refactor & create new instance every showing

* remove unused codee

Co-authored-by: Murod Khaydarov <murod.haydarov@inbox.ru>
Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Throw error only if read-only is enabled from the start (#1337)

* Throw error only if read-only is enabled from the start

* update modules

* Fixed the 1302 bug and improve the tab key behaviour (#1342)

* Fixed the 1302 bug and improve the tab key behaviour

* yarn lint:fixed based improvements

* Update docs/CHANGELOG.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/modules/ui.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Fix caret behaviour (#1343)

* Fix caret behaviour

* Fix current input update

* Toggle readonly on start (#1344)

* Toggle readonly on start

* Do not render block twice on start

* Bugfix/fix modification observer disable (#1340)

* Enable modification observer when onChange callback throws an error

* Build

* Update src/components/modules/modificationsObserver.ts

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Update CHANGELOG

Co-authored-by: t.hata <hata@impact-blue.co.jp>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Improve the changelog and read-only toggler (#1347)

* Use activeElement if anchorNode is undefined (#1350)

* FIx errors on enter press when several blocks selected (#1349)

* FIx errors on enter press when several blocks selected

* Fix for safari

* Fix blocks copy in read-only (#1351)

* Fixes for 2.19 (#1356)

* Fixes

* Update docs/CHANGELOG.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Fix RTL

* Optimize tune down

* Add explanation on focus events listeners

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Fixes due code review (#1365)

* Fixes for release: (#1366)

* Fixes for release

* Apply suggestions from code review

Co-authored-by: Murod Khaydarov <murod.haydarov@gmail.com>

* Update toolbox.ts

Co-authored-by: Murod Khaydarov <murod.haydarov@gmail.com>

Co-authored-by: Qays <whosqays@gmail.com>
Co-authored-by: Jacob Smith <jacob.wesley.smith@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: ranemihir <mihirrane171@gmail.com>
Co-authored-by: Mihir Rane <66768874+ranemihir@users.noreply.github.com>
Co-authored-by: Stephen Richard <stephen.richard44@gmail.com>
Co-authored-by: Umang G. Patel <23169768+robonetphy@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nikola Pavlovic <47178050+PavlovicWorkCo@users.noreply.github.com>
Co-authored-by: Cyber_Ninja <49983428+Gicehajunior@users.noreply.github.com>
Co-authored-by: Tomoyuki Hata <hato6502@gmail.com>
Co-authored-by: t.hata <hata@impact-blue.co.jp>
Co-authored-by: Mahach Imangazaliev <mahach.imangazaliev@mail.ru>
Co-authored-by: ImangazalievM <mahach.miangazaliev@gmail.com>
Co-authored-by: Murod Khaydarov <murod.haydarov@gmail.com>
Co-authored-by: Hugh Boylan <bluehugh2@gmail.com>
Co-authored-by: Murod Khaydarov <murod.haydarov@inbox.ru>
2020-10-13 20:50:46 +03:00

896 lines
23 KiB
TypeScript

import Module from '../__module';
import $ from '../dom';
import * as _ from '../utils';
import {
BlockTool,
BlockToolConstructable,
PasteConfig,
PasteEvent,
PasteEventDetail
} from '../../../types';
import Block from '../block';
import { SavedData } from '../../../types/data-formats';
/**
* Tag substitute object.
*/
interface TagSubstitute {
/**
* Name of related Tool
*
* @type {string}
*/
tool: string;
}
/**
* Pattern substitute object.
*/
interface PatternSubstitute {
/**
* Pattern`s key
*
* @type {string}
*/
key: string;
/**
* Pattern regexp
*
* @type {RegExp}
*/
pattern: RegExp;
/**
* Name of related Tool
*
* @type {string}
*/
tool: string;
}
/**
* Files` types substitutions object.
*/
interface FilesSubstitution {
/**
* Array of file extensions Tool can handle
*
* @type {string[]}
*/
extensions: string[];
/**
* Array of MIME types Tool can handle
*
* @type {string[]}
*/
mimeTypes: string[];
}
/**
* Processed paste data object.
*
* @interface PasteData
*/
interface PasteData {
/**
* Name of related Tool
*
* @type {string}
*/
tool: string;
/**
* Pasted data. Processed and wrapped to HTML element
*
* @type {HTMLElement}
*/
content: HTMLElement;
/**
* Pasted data
*/
event: PasteEvent;
/**
* True if content should be inserted as new Block
*
* @type {boolean}
*/
isBlock: boolean;
}
/**
* @class Paste
* @classdesc Contains methods to handle paste on editor
*
* @module Paste
*
* @version 2.0.0
*/
export default class Paste extends Module {
/** If string`s length is greater than this number we don't check paste patterns */
public static readonly PATTERN_PROCESSING_MAX_LENGTH = 450;
/** Custom EditorJS mime-type to handle in-editor copy/paste actions */
public readonly MIME_TYPE = 'application/x-editor-js';
/**
* Tags` substitutions parameters
*/
private toolsTags: {[tag: string]: TagSubstitute} = {};
/**
* Store tags to substitute by tool name
*/
private tagsByTool: {[tools: string]: string[]} = {};
/** Patterns` substitutions parameters */
private toolsPatterns: PatternSubstitute[] = [];
/** Files` substitutions parameters */
private toolsFiles: {
[tool: string]: FilesSubstitution;
} = {};
/**
* List of tools which do not need a paste handling
*/
private exceptionList: string[] = [];
/**
* Set onPaste callback and collect tools` paste configurations
*/
public async prepare(): Promise<void> {
this.processTools();
}
/**
* Set read-only state
*
* @param {boolean} readOnlyEnabled - read only flag value
*/
public toggleReadOnly(readOnlyEnabled: boolean): void {
if (!readOnlyEnabled) {
this.setCallback();
} else {
this.unsetCallback();
}
}
/**
* Handle pasted or dropped data transfer object
*
* @param {DataTransfer} dataTransfer - pasted or dropped data transfer object
* @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events
*/
public async processDataTransfer(dataTransfer: DataTransfer, isDragNDrop = false): Promise<void> {
const { Sanitizer } = this.Editor;
const types = dataTransfer.types;
/**
* In Microsoft Edge types is DOMStringList. So 'contains' is used to check if 'Files' type included
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const includesFiles = types.includes ? types.includes('Files') : (types as any).contains('Files');
if (includesFiles) {
await this.processFiles(dataTransfer.files);
return;
}
const editorJSData = dataTransfer.getData(this.MIME_TYPE);
const plainData = dataTransfer.getData('text/plain');
let htmlData = dataTransfer.getData('text/html');
/**
* If EditorJS json is passed, insert it
*/
if (editorJSData) {
try {
this.insertEditorJSData(JSON.parse(editorJSData));
return;
} catch (e) {} // Do nothing and continue execution as usual if error appears
}
/**
* If text was drag'n'dropped, wrap content with P tag to insert it as the new Block
*/
if (isDragNDrop && plainData.trim() && htmlData.trim()) {
htmlData = '<p>' + (htmlData.trim() ? htmlData : plainData) + '</p>';
}
/** Add all tags that can be substituted to sanitizer configuration */
const toolsTags = Object.keys(this.toolsTags).reduce((result, tag) => {
result[tag.toLowerCase()] = true;
return result;
}, {});
const customConfig = Object.assign({}, toolsTags, Sanitizer.getAllInlineToolsConfig(), { br: {} });
const cleanData = Sanitizer.clean(htmlData, customConfig);
/** If there is no HTML or HTML string is equal to plain one, process it as plain text */
if (!cleanData.trim() || cleanData.trim() === plainData || !$.isHTMLString(cleanData)) {
await this.processText(plainData);
} else {
await this.processText(cleanData, true);
}
}
/**
* Process pasted text and divide them into Blocks
*
* @param {string} data - text to process. Can be HTML or plain.
* @param {boolean} isHTML - if passed string is HTML, this parameter should be true
*/
public async processText(data: string, isHTML = false): Promise<void> {
const { Caret, BlockManager, Tools } = this.Editor;
const dataToInsert = isHTML ? this.processHTML(data) : this.processPlain(data);
if (!dataToInsert.length) {
return;
}
if (dataToInsert.length === 1) {
if (!dataToInsert[0].isBlock) {
this.processInlinePaste(dataToInsert.pop());
} else {
this.processSingleBlock(dataToInsert.pop());
}
return;
}
const isCurrentBlockDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
const needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
dataToInsert.map(
async (content, i) => this.insertBlock(content, i === 0 && needToReplaceCurrentBlock)
);
if (BlockManager.currentBlock) {
Caret.setToBlock(BlockManager.currentBlock, Caret.positions.END);
}
}
/**
* Set onPaste callback handler
*/
private setCallback(): void {
const { Listeners } = this.Editor;
Listeners.on(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
}
/**
* Unset onPaste callback handler
*/
private unsetCallback(): void {
const { Listeners } = this.Editor;
Listeners.off(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
}
/**
* Get and process tool`s paste configs
*/
private processTools(): void {
const tools = this.Editor.Tools.blockTools;
Object.entries(tools).forEach(this.processTool);
}
/**
* Process paste config for each tool
*/
private processTool = ([name, tool]: [string, BlockToolConstructable]): void => {
try {
const toolInstance = new this.Editor.Tools.blockTools[name]({
api: this.Editor.API.getMethodsForTool(name),
config: {},
data: {},
readOnly: false,
}) as BlockTool;
if (tool.pasteConfig === false) {
this.exceptionList.push(name);
return;
}
if (typeof toolInstance.onPaste !== 'function') {
return;
}
const toolPasteConfig = tool.pasteConfig || {};
this.getTagsConfig(name, toolPasteConfig);
this.getFilesConfig(name, toolPasteConfig);
this.getPatternsConfig(name, toolPasteConfig);
} catch (e) {
_.log(
`Paste handling for «${name}» Tool hasn't been set up because of the error`,
'warn',
e
);
}
}
/**
* Get tags to substitute by Tool
*
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getTagsConfig(name: string, toolPasteConfig: PasteConfig): void {
const tags = toolPasteConfig.tags || [];
tags.forEach((tag) => {
if (Object.prototype.hasOwnProperty.call(this.toolsTags, tag)) {
_.log(
`Paste handler for «${name}» Tool on «${tag}» tag is skipped ` +
`because it is already used by «${this.toolsTags[tag].tool}» Tool.`,
'warn'
);
return;
}
this.toolsTags[tag.toUpperCase()] = {
tool: name,
};
});
this.tagsByTool[name] = tags.map((t) => t.toUpperCase());
}
/**
* Get files` types and extensions to substitute by Tool
*
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getFilesConfig(name: string, toolPasteConfig: PasteConfig): void {
const { files = {} } = toolPasteConfig;
let { extensions, mimeTypes } = files;
if (!extensions && !mimeTypes) {
return;
}
if (extensions && !Array.isArray(extensions)) {
_.log(`«extensions» property of the onDrop config for «${name}» Tool should be an array`);
extensions = [];
}
if (mimeTypes && !Array.isArray(mimeTypes)) {
_.log(`«mimeTypes» property of the onDrop config for «${name}» Tool should be an array`);
mimeTypes = [];
}
if (mimeTypes) {
mimeTypes = mimeTypes.filter((type) => {
if (!_.isValidMimeType(type)) {
_.log(`MIME type value «${type}» for the «${name}» Tool is not a valid MIME type`, 'warn');
return false;
}
return true;
});
}
this.toolsFiles[name] = {
extensions: extensions || [],
mimeTypes: mimeTypes || [],
};
}
/**
* Get RegExp patterns to substitute by Tool
*
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getPatternsConfig(name: string, toolPasteConfig: PasteConfig): void {
if (!toolPasteConfig.patterns || _.isEmpty(toolPasteConfig.patterns)) {
return;
}
Object.entries(toolPasteConfig.patterns).forEach(([key, pattern]: [string, RegExp]) => {
/** Still need to validate pattern as it provided by user */
if (!(pattern instanceof RegExp)) {
_.log(
`Pattern ${pattern} for «${name}» Tool is skipped because it should be a Regexp instance.`,
'warn'
);
}
this.toolsPatterns.push({
key,
pattern,
tool: name,
});
});
}
/**
* Check if browser behavior suits better
*
* @param {EventTarget} element - element where content has been pasted
*
* @returns {boolean}
*/
private isNativeBehaviour(element: EventTarget): boolean {
return $.isNativeInput(element);
}
/**
* Check if Editor should process pasted data and pass data transfer object to handler
*
* @param {ClipboardEvent} event - clipboard event
*/
private handlePasteEvent = async (event: ClipboardEvent): Promise<void> => {
const { BlockManager, Toolbar } = this.Editor;
/** If target is native input or is not Block, use browser behaviour */
if (
!BlockManager.currentBlock || (this.isNativeBehaviour(event.target) && !event.clipboardData.types.includes('Files'))
) {
return;
}
/**
* If Tools is in list of errors, skip processing of paste event
*/
if (BlockManager.currentBlock && this.exceptionList.includes(BlockManager.currentBlock.name)) {
return;
}
event.preventDefault();
this.processDataTransfer(event.clipboardData);
BlockManager.clearFocused();
Toolbar.close();
}
/**
* Get files from data transfer object and insert related Tools
*
* @param {FileList} items - pasted or dropped items
*/
private async processFiles(items: FileList): Promise<void> {
const { BlockManager, Tools } = this.Editor;
let dataToInsert: Array<{type: string; event: PasteEvent}>;
dataToInsert = await Promise.all(
Array
.from(items)
.map((item) => this.processFile(item))
);
dataToInsert = dataToInsert.filter((data) => !!data);
const isCurrentBlockDefault = Tools.isDefault(BlockManager.currentBlock.tool);
const needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
dataToInsert.forEach(
(data, i) => {
BlockManager.paste(data.type, data.event, i === 0 && needToReplaceCurrentBlock);
}
);
}
/**
* Get information about file and find Tool to handle it
*
* @param {File} file - file to process
*/
private async processFile(file: File): Promise<{event: PasteEvent; type: string}> {
const extension = _.getFileExtension(file);
const foundConfig = Object
.entries(this.toolsFiles)
.find(([toolName, { mimeTypes, extensions } ]) => {
const [fileType, fileSubtype] = file.type.split('/');
const foundExt = extensions.find((ext) => ext.toLowerCase() === extension.toLowerCase());
const foundMimeType = mimeTypes.find((mime) => {
const [type, subtype] = mime.split('/');
return type === fileType && (subtype === fileSubtype || subtype === '*');
});
return !!foundExt || !!foundMimeType;
});
if (!foundConfig) {
return;
}
const [ tool ] = foundConfig;
const pasteEvent = this.composePasteEvent('file', {
file,
});
return {
event: pasteEvent,
type: tool,
};
}
/**
* Split HTML string to blocks and return it as array of Block data
*
* @param {string} innerHTML - html string to process
*
* @returns {PasteData[]}
*/
private processHTML(innerHTML: string): PasteData[] {
const { Tools, Sanitizer } = this.Editor;
const initialTool = this.config.defaultBlock;
const wrapper = $.make('DIV');
wrapper.innerHTML = innerHTML;
const nodes = this.getNodes(wrapper);
return nodes
.map((node) => {
let content, tool = initialTool, isBlock = false;
switch (node.nodeType) {
/** If node is a document fragment, use temp wrapper to get innerHTML */
case Node.DOCUMENT_FRAGMENT_NODE:
content = $.make('div');
content.appendChild(node);
break;
/** If node is an element, then there might be a substitution */
case Node.ELEMENT_NODE:
content = node as HTMLElement;
isBlock = true;
if (this.toolsTags[content.tagName]) {
tool = this.toolsTags[content.tagName].tool;
}
break;
}
const { tags } = Tools.blockTools[tool].pasteConfig as PasteConfig;
const toolTags = tags.reduce((result, tag) => {
result[tag.toLowerCase()] = {};
return result;
}, {});
const customConfig = Object.assign({}, toolTags, Sanitizer.getInlineToolsConfig(tool));
content.innerHTML = Sanitizer.clean(content.innerHTML, customConfig);
const event = this.composePasteEvent('tag', {
data: content,
});
return {
content,
isBlock,
tool,
event,
};
})
.filter((data) => !$.isNodeEmpty(data.content) || $.isSingleTag(data.content));
}
/**
* Split plain text by new line symbols and return it as array of Block data
*
* @param {string} plain - string to process
*
* @returns {PasteData[]}
*/
private processPlain(plain: string): PasteData[] {
const { defaultBlock } = this.config as {defaultBlock: string};
if (!plain) {
return [];
}
const tool = defaultBlock;
return plain
.split(/\r?\n/)
.filter((text) => text.trim())
.map((text) => {
const content = $.make('div');
content.textContent = text;
const event = this.composePasteEvent('tag', {
data: content,
});
return {
content,
tool,
isBlock: false,
event,
};
});
}
/**
* Process paste of single Block tool content
*
* @param {PasteData} dataToInsert - data of Block to inseret
*/
private async processSingleBlock(dataToInsert: PasteData): Promise<void> {
const { Caret, BlockManager, Tools } = this.Editor;
const { currentBlock } = BlockManager;
/**
* If pasted tool isn`t equal current Block or if pasted content contains block elements, insert it as new Block
*/
if (
!currentBlock ||
dataToInsert.tool !== currentBlock.name ||
!$.containsOnlyInlineElements(dataToInsert.content.innerHTML)
) {
this.insertBlock(dataToInsert, currentBlock && Tools.isDefault(currentBlock.tool) && currentBlock.isEmpty);
return;
}
Caret.insertContentAtCaretPosition(dataToInsert.content.innerHTML);
}
/**
* Process paste to single Block:
* 1. Find patterns` matches
* 2. Insert new block if it is not the same type as current one
* 3. Just insert text if there is no substitutions
*
* @param {PasteData} dataToInsert - data of Block to insert
*/
private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
const { BlockManager, Caret, Sanitizer, Tools } = this.Editor;
const { content } = dataToInsert;
const currentBlockIsDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
if (currentBlockIsDefault && content.textContent.length < Paste.PATTERN_PROCESSING_MAX_LENGTH) {
const blockData = await this.processPattern(content.textContent);
if (blockData) {
const needToReplaceCurrentBlock = BlockManager.currentBlock &&
Tools.isDefault(BlockManager.currentBlock.tool) &&
BlockManager.currentBlock.isEmpty;
const insertedBlock = BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock);
Caret.setToBlock(insertedBlock, Caret.positions.END);
return;
}
}
/** If there is no pattern substitute - insert string as it is */
if (BlockManager.currentBlock && BlockManager.currentBlock.currentInput) {
const currentToolSanitizeConfig = Sanitizer.getInlineToolsConfig(BlockManager.currentBlock.name);
document.execCommand(
'insertHTML',
false,
Sanitizer.clean(content.innerHTML, currentToolSanitizeConfig)
);
} else {
this.insertBlock(dataToInsert);
}
}
/**
* Get patterns` matches
*
* @param {string} text - text to process
*
* @returns {Promise<{event: PasteEvent, tool: string}>}
*/
private async processPattern(text: string): Promise<{event: PasteEvent; tool: string}> {
const pattern = this.toolsPatterns.find((substitute) => {
const execResult = substitute.pattern.exec(text);
if (!execResult) {
return false;
}
return text === execResult.shift();
});
if (!pattern) {
return;
}
const event = this.composePasteEvent('pattern', {
key: pattern.key,
data: text,
});
return {
event,
tool: pattern.tool,
};
}
/**
* Insert pasted Block content to Editor
*
* @param {PasteData} data - data to insert
* @param {boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block
*
* @returns {void}
*/
private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void {
const { BlockManager, Caret } = this.Editor;
const { currentBlock } = BlockManager;
let block: Block;
if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) {
block = BlockManager.paste(data.tool, data.event, true);
Caret.setToBlock(block, Caret.positions.END);
return;
}
block = BlockManager.paste(data.tool, data.event);
Caret.setToBlock(block, Caret.positions.END);
}
/**
* Insert data passed as application/x-editor-js JSON
*
* @param {Array} blocks — Blocks' data to insert
*
* @returns {void}
*/
private insertEditorJSData(blocks: Array<Pick<SavedData, 'data' | 'tool'>>): void {
const { BlockManager, Tools } = this.Editor;
blocks.forEach(({ tool, data }, i) => {
let needToReplaceCurrentBlock = false;
if (i === 0) {
const isCurrentBlockDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
}
BlockManager.insert({
tool,
data,
replace: needToReplaceCurrentBlock,
});
});
}
/**
* Fetch nodes from Element node
*
* @param {Node} node - current node
* @param {Node[]} nodes - processed nodes
* @param {Node} destNode - destination node
*
* @returns {Node[]}
*/
private processElementNode(node: Node, nodes: Node[], destNode: Node): Node[] | void {
const tags = Object.keys(this.toolsTags);
const element = node as HTMLElement;
const { tool = '' } = this.toolsTags[element.tagName] || {};
const toolTags = this.tagsByTool[tool] || [];
const isSubstitutable = tags.includes(element.tagName);
const isBlockElement = $.blockElements.includes(element.tagName.toLowerCase());
const containsAnotherToolTags = Array
.from(element.children)
.some(
({ tagName }) => tags.includes(tagName) && !toolTags.includes(tagName)
);
const containsBlockElements = Array.from(element.children).some(
({ tagName }) => $.blockElements.includes(tagName.toLowerCase())
);
/** Append inline elements to previous fragment */
if (!isBlockElement && !isSubstitutable && !containsAnotherToolTags) {
destNode.appendChild(element);
return [...nodes, destNode];
}
if (
(isSubstitutable && !containsAnotherToolTags) ||
(isBlockElement && !containsBlockElements && !containsAnotherToolTags)
) {
return [...nodes, destNode, element];
}
}
/**
* Recursively divide HTML string to two types of nodes:
* 1. Block element
* 2. Document Fragments contained text and markup tags like a, b, i etc.
*
* @param {Node} wrapper - wrapper of paster HTML content
*
* @returns {Node[]}
*/
private getNodes(wrapper: Node): Node[] {
const children = Array.from(wrapper.childNodes);
let elementNodeProcessingResult: Node[] | void;
const reducer = (nodes: Node[], node: Node): Node[] => {
if ($.isEmpty(node) && !$.isSingleTag(node as HTMLElement)) {
return nodes;
}
const lastNode = nodes[nodes.length - 1];
let destNode: Node = new DocumentFragment();
if (lastNode && $.isFragment(lastNode)) {
destNode = nodes.pop();
}
switch (node.nodeType) {
/**
* If node is HTML element:
* 1. Check if it is inline element
* 2. Check if it contains another block or substitutable elements
*/
case Node.ELEMENT_NODE:
elementNodeProcessingResult = this.processElementNode(node, nodes, destNode);
if (elementNodeProcessingResult) {
return elementNodeProcessingResult;
}
break;
/**
* If node is text node, wrap it with DocumentFragment
*/
case Node.TEXT_NODE:
destNode.appendChild(node);
return [...nodes, destNode];
default:
return [...nodes, destNode];
}
return [...nodes, ...Array.from(node.childNodes).reduce(reducer, [])];
};
return children.reduce(reducer, []);
}
/**
* Compose paste event with passed type and detail
*
* @param {string} type - event type
* @param {PasteEventDetail} detail - event detail
*/
private composePasteEvent(type: string, detail: PasteEventDetail): PasteEvent {
return new CustomEvent(type, {
detail,
}) as PasteEvent;
}
}