diff --git a/.babelrc b/.babelrc
index 93be46583..b2f5cd96d 100644
--- a/.babelrc
+++ b/.babelrc
@@ -2,7 +2,8 @@
"presets": [
["@babel/preset-env", {
"modules": "umd",
- "useBuiltIns": "entry"
+ "useBuiltIns": "entry",
+ "corejs": 3
}]
],
"plugins": [
diff --git a/.editorconfig b/.editorconfig
index 0915b6398..0665d09b8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,4 +6,4 @@ indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
-trim_trailing_whitespace = true
\ No newline at end of file
+trim_trailing_whitespace = true
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 000000000..54958dac1
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,4 @@
+node_modules
+*.d.ts
+src/components/tools/paragraph
+src/polyfills.ts
diff --git a/.eslintrc b/.eslintrc
index 2f369b0cf..3e2802ace 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,92 +1,47 @@
{
- /** Enable ES6 features */
- "parserOptions": {
- "ecmaVersion": 2017,
- "sourceType": "module"
- },
- "rules": {
-
- "arrow-spacing": [2, {
- "before": true,
- "after": true
- }],
-
- /** Variables */
- "no-catch-shadow": 2,
- "no-delete-var": 2,
- "no-label-var": 2,
- "no-shadow-restricted-names": 2,
- "no-shadow": 2,
- "no-undef-init": 2,
- "no-undef": 2,
- "no-unused-vars": 0,
-
- /** Style */
- "array-bracket-spacing": [2, "never", {
- "singleValue": true,
- "objectsInArrays": true,
- "arraysInArrays": true
- }],
- "quotes": [2, "single", {
- "avoidEscape": true,
- "allowTemplateLiterals": true
- }],
- "eqeqeq": 0,
- "brace-style": [2, "1tbs"],
- "comma-spacing": [2, {
- "before": false,
- "after": true
- }],
- "comma-style": [2, "last"],
- "eol-last": 0,
- "no-nested-ternary": 1,
- "no-trailing-spaces": 2,
- "no-mixed-spaces-and-tabs": 2,
- "padded-blocks": [2, "never"],
- "space-before-blocks": 1,
- "space-before-function-paren": [1, {
- "anonymous": "always",
- "named": "never"
- }],
- "spaced-comment": [2, "always", {
- "exceptions": ["-", "+"],
- "markers": ["=", "!"]
- }],
- "semi": [2, "always"],
- "indent": [2, 2, {
- "SwitchCase": 1
- }],
- "camelcase": [2, {
- "properties": "always"
- }],
- "newline-after-var": [1, "always"]
-
- },
- "globals":{
- "document": true,
- "module": true,
- "require": true,
- "window": true,
- "console" : true,
- "codex": true,
- "VERSION" : true,
- "Promise" : true,
- "MutationObserver": true,
- "FormData": true,
- "XMLHttpRequest": true,
- "ActiveXObject": true,
- "RegExp": true,
- "Module": true,
- "Node": true,
- "Element": true,
- "DocumentFragment": true,
- "Proxy": true,
- "Symbol": true,
- "$": true,
- "_": true,
- "setTimeout": true,
- "process": true,
- "__dirname": true,
- "Map": true
+ "extends": [
+ "codex"
+ ],
+ "rules": {
+ /**
+ * Temporary suppress some errors. We need to fix them partially in next patches
+ */
+ "import/no-duplicates": ["warn"],
+ "@typescript-eslint/triple-slash-reference": ["off"]
+ },
+ "settings": {
+ "jsdoc": {
+ "mode": "typescript"
}
+ },
+ "globals": {
+ "Node": true,
+ "Range": true,
+ "HTMLElement": true,
+ "HTMLDivElement": true,
+ "Element": true,
+ "Selection": true,
+ "SVGElement": true,
+ "Text": true,
+ "InsertPosition": true,
+ "PropertyKey": true,
+ "MouseEvent": true,
+ "TouchEvent": true,
+ "KeyboardEvent": true,
+ "ClipboardEvent": true,
+ "DragEvent": true,
+ "Event": true,
+ "EventTarget": true,
+ "Document": true,
+ "NodeList": true,
+ "File": true,
+ "FileList": true,
+ "MutationRecord": true,
+ "AddEventListenerOptions": true,
+ "DataTransfer": true,
+ "DOMRect": true,
+ "ClientRect": true,
+ "ArrayLike": true,
+ "unknown": true
+ }
}
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..971aed277
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at team@codex.so. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..78639a6ca
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,5 @@
+# These are supported funding model platforms
+
+github: neSpecc
+patreon: editorjs
+open_collective: editorjs
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..f8df3f874
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,30 @@
+---
+name: Bug report
+about: Create a report to help us improve Editor.js
+title: "[Bug]"
+labels: bug
+assignees: ''
+
+---
+
+Describe a bug.
+
+Steps to reproduce:
+1. Go to …
+2. Click on …
+3. …
+
+Expected behavior:
+
+Screenshots:
+
+Device, Browser, OS:
+
+Editor.js version:
+
+Plugins you use with their versions:
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..40a9197f0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,11 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Team
+ url: mailto:team@codex.so
+ about: Direct team contact.
+ - name: Editor.js Telegram chat
+ url: https://t.me/codex_editor
+ about: Telegram chat for Editor.js users communication.
+ - name: Editor.js contributors Telegram chat
+ url: https://t.me/editorjsdev
+ about: Telegram chat for Editor.js contributors communication.
diff --git a/.github/ISSUE_TEMPLATE/discussion.md b/.github/ISSUE_TEMPLATE/discussion.md
new file mode 100644
index 000000000..f6f6a3ed3
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/discussion.md
@@ -0,0 +1,17 @@
+---
+name: Discussion
+about: Any question about the Editor.js to discuss
+title: ''
+labels: discussion
+assignees: ''
+
+---
+
+The question.
+
+Why and how the question has come up.
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..6069bf434
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,19 @@
+---
+name: Feature request
+about: Suggest an idea to improve Editor.js
+title: "\U0001F4A1"
+labels: feature
+assignees: ''
+
+---
+
+1. Describe a problem.
+
+2. Describe the solution you'd like. Mockups are welcome.
+
+3. Are there any alternatives?
+
+
diff --git a/.github/ISSUE_TEMPLATE/issue--discussion.md b/.github/ISSUE_TEMPLATE/issue--discussion.md
new file mode 100644
index 000000000..00261395d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/issue--discussion.md
@@ -0,0 +1,24 @@
+---
+name: 'Issue: Discussion'
+about: Any question about the project to discuss
+title: "❓"
+labels: discussion
+assignees: ''
+
+---
+
+**Question**
+
+A clear and consistent question about the project. Ex. How can I do smth? Why smth works this way? etc.
+
+**Context**
+
+Why and how the question has come up
+
+**Related issues**
+
+If there are related issues which describe a bugs or features, put them here
+
+**Comments**
+
+Any thoughts about the question
diff --git a/.github/workflows/bump-version-on-merge-next.yml b/.github/workflows/bump-version-on-merge-next.yml
new file mode 100644
index 000000000..d1e94ea01
--- /dev/null
+++ b/.github/workflows/bump-version-on-merge-next.yml
@@ -0,0 +1,77 @@
+name: Bump version on merge
+
+on:
+ pull_request:
+ branches:
+ - next
+ types: [closed]
+
+jobs:
+ # If pull request was merged then we should check for a package version update
+ check-for-no-version-changing:
+ if: github.event.pull_request.merged == true
+ runs-on: ubuntu-latest
+ steps:
+ # Checkout to target branch
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ # Get package new version name
+ - name: Get package info
+ id: packageNew
+ uses: codex-team/action-nodejs-package-info@v1
+
+ # Checkout to the base commit before merge
+ - name: Checkout to the base commit before merge
+ run: git checkout ${{ github.event.pull_request.base.sha }}
+
+ # Get package old version name
+ - name: Get package info
+ id: packageOld
+ uses: codex-team/action-nodejs-package-info@v1
+
+ # Stop workflow and do not bump version if it was changed already
+ - name: Stop workflow and do not bump version if it was changed already
+ uses: actions/github-script@v3
+ if: steps.packageOld.outputs.version != steps.packageNew.outputs.version
+ with:
+ script: |
+ core.setFailed('Version was changed! ${{ steps.packageOld.outputs.version }} -> ${{ steps.packageNew.outputs.version }}')
+
+ bump-version:
+ needs: check-for-no-version-changing
+ runs-on: ubuntu-latest
+ steps:
+ # Checkout to target branch
+ - uses: actions/checkout@v2
+
+ # Setup node environment
+ - uses: actions/setup-node@v1
+ with:
+ node-version: 15
+ registry-url: https://registry.npmjs.org/
+
+ # Bump version to the next prerelease (patch) with rc suffix
+ - name: Suggest the new version
+ run: yarn version --prerelease --preid rc --no-git-tag-version
+
+ # Get package new version name
+ - name: Get package info
+ id: package
+ uses: codex-team/action-nodejs-package-info@v1
+
+ # Create pull request with changes
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v3
+ with:
+ commit-message: Bump version
+ committer: github-actions 

](http://godban.github.io/browsers-support-badges/)IE / Edge | [
](http://godban.github.io/browsers-support-badges/)Firefox | [
](http://godban.github.io/browsers-support-badges/)Chrome | [
](http://godban.github.io/browsers-support-badges/)Safari | [
](http://godban.github.io/browsers-support-badges/)iOS Safari | [
](http://godban.github.io/browsers-support-badges/)Opera |
+| --------- | --------- | --------- | --------- | --------- | --------- |
+| Edge 12+ | Firefox 18+ | Chrome 49+ | Safari 10+ | Safari 10+ | Opera 36+
-We are glad to introduce the next version of Editor.js. Totally new core, structure and plugins — that was an impressive adventure 🤓.
+## If you like a project 💗💗💗
-Join [public Telegram-chat](//t.me/codex_editor) or [Gitter-channel](https://gitter.im/codex-team/editor.js) where you can always find a support.
+If you like Editor.js you can support project improvements and development of new features with a donation to our collective.
-### Browsers support
+ 👉 [https://opencollective.com/editorjs](https://opencollective.com/editorjs)
-| [
](http://godban.github.io/browsers-support-badges/)IE / Edge | [
](http://godban.github.io/browsers-support-badges/)Firefox | [
](http://godban.github.io/browsers-support-badges/)Chrome | [
](http://godban.github.io/browsers-support-badges/)Safari | [
](http://godban.github.io/browsers-support-badges/)iOS Safari | [
](http://godban.github.io/browsers-support-badges/)Opera |
-| --------- | --------- | --------- | --------- | --------- | --------- |
-| Edge 12+ | Firefox 18+ | Chrome 49+ | Safari 10+ | Safari 10+ | Opera 36+
+### Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/editorjs#sponsor)]
+
+
+
+ ### Backers
+
+ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/editorjs#backer)]
+
+
+
+### Contributors
-### 2.7-2.9 changelog
-
-- `Fix` - Clear focus when click is outside the Editor instance
-- `Fix` — Fix CMD+A Selection on multiple Editor instances
-- `New` — Toolbox now have beautiful helpers with Tool names and shortcuts
-- `Improvements` — Prevent navigating back on Firefox when Block is removing by backspace
-- `New` — Blocks selected with Rectangle Selection can be also removed, copied or cut
-- `New` — Migrate from `postcss-cssnext` to `postcss-preset-env` and disable `postcss-custom-properties` which conflicts with `postcss-preset-env`
-- `New` *RectangeSelection* — Ability to select Block or several Blocks with mouse
-
-### 2.2—2.7 changelog
-
-- `New` *Sanitize API* — [Sanitize Config](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#automatic-sanitize) of `Block Tools` now automatically extends by tags of `Inline Tools` that is enabled by current Tool by `inlineToolbar` option. You don't need more to specify `a, b, mark, code` manually. This feature will be added to fields that supports inline markup.
-- `New` *Block Selection* — Ability to select Block by `CMD+A`, and the whole Editor by double `CMD+A`. After that, you can copy (`CMD+C`), remove (`Backspace`) or clear (`Enter`) selected Blocks.
-- `New` *[Styles API](https://github.com/codex-team/editor.js/blob/master/types/api/styles.d.ts)* — Added `button` class for stylization of any buttons provided by Tools with one unified style.
-- `New` *[Notifier API](https://github.com/codex-team/editor.js/blob/master/docs/api.md#notifierapi)* — methods for showing user notifications: on success, errors, warnings, etc.
-- `New` *Block Tool* — [Table](http://github.com/editor-js/table) constructor 💪
-- `New` If one of the Tools is unavailable on Editor initialization, its Blocks will be rendered with *Dummy Block*, describing that user can not edit content of this Block. Dummy Blocks can be moved, removed and saved as normal Blocks. So saved data won't be lost if one of the Tools is failed
-- `New` [Public TS-types](https://github.com/codex-team/editor.js/tree/master/types) are presented.
-- `Changes` *Tools API* — options `irreplaceable` and `contentless` was removed.
-- `Changes` *Tools API* — [Paste API](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling): tags, patterns and mime-types now should be specified by Tool's `pasteConfig` static property. Custom Paste Event should be handled by `onPaste(event)` that should not be static from now.
-- `Changes` *Tools API* — options `displayInToolbox ` and `toolboxIcon` was removed. Use [`toolbox`](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#internal-tool-settings) instead, that should return object with `icon` and `title` field, or `false` if Tool should not be placed at the Toolbox. Also, there are a way to override `toolbox {icon, title}` settings provided by Tool with you own settings at the Initial Config.
-- `Improvements` — All Projects code now on TypeScript
-- `Improvements` — NPM package size decreased from 1300kb to 422kb
-- `Improvements` — Bundle size decreased from 438kb to 252kb
-- `Improvements` — `Inline Toolbar`: when you add a Link to the selected fragment, Editor will highlight this fragment even when Caret is placed into the URL-input.
-- `Improvements` — Block Settings won't be shown near empty Blocks of `initialType` by default. You should click on them instead.
-- `Improvements` — `onChange`-callback now will be fired even with children attributes changing.
-- `Improvements` — HTMLJanitor package was updated due to found vulnerability
-- `Improvements` — Logging improved: now all Editor's logs will be preceded by beautiful label with current Editor version.
-- `Improvements` — Internal `isEmpty` checking was improved for Blocks with many children nodes (200 and more)
-- `Improvements` — Paste improvements: tags that can be substituted by Tool now will matched even on deep-level of pasted DOM three.
-- `Improvements` — There is no more «unavailable» sound on copying Block by `CMD+C` on macOS
-- `Improvements` — Dozens of bugfixes and small improvements
-
-See a whole [Changelog](/docs/CHANGELOG.md)
+This project exists thanks to all the people who contribute.
+
+We really welcome new contributors. If you want to make some code with us, please take a look at the [Good First Tasks](https://github.com/codex-team/editor.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+task%22). You can write to us on `team@codex.so` or via special [Telegram chat](https://t.me/editorjsdev), or any other way.
## Documentation
-While we develop the new Documentation Site with all stuff, you can check some available docs at the [docs/](docs/) dir.
+Please visit [https://editorjs.io/](https://editorjs.io) to view all documentation articles.
+
+- [Base concepts](https://editorjs.io/base-concepts)
+- [Getting started](https://editorjs.io/getting-started)
+- [Configuration](https://editorjs.io/configuration)
+- [How to create a Block Tool Plugin](https://editorjs.io/creating-a-block-tool)
+- [How to create an Inline Tool Plugin](https://editorjs.io/creating-an-inline-tool)
+- [API for Tools](https://editorjs.io/tools-api)
-- [Installation](docs/installation.md)
-- [How to use](docs/usage.md)
-- [How to create a Block Tool Plugin](docs/tools.md)
-- [How to create an Inline Tool Plugin](docs/tools-inline.md)
-- [API for Tools](docs/api.md)
+You can join a [Gitter-channel](https://gitter.im/codex-team/editor.js) or [Telegram-chat](//t.me/codex_editor) and ask a question.
-Sorry if we missed something. You can join a [Telegram-chat](//t.me/codex_editor) and ask a question.
+## Changelog
----
+See the whole [Changelog](/docs/CHANGELOG.md)
-# So how to use Editor.js
+## How to use Editor.js
-## Basics
+### Basics
Editor.js is a Block-Styled editor. Blocks are structural units, of which the Entry is composed.
For example, `Paragraph`, `Heading`, `Image`, `Video`, `List` are Blocks. Each Block is represented by Plugin.
-We have [many](http://github.com/editor-js/) ready-to-use Plugins and the [simple API](docs/tools.md) for creation new ones.
+We have [many](http://github.com/editor-js/) ready-to-use Plugins and a [simple API](https://editorjs.io/tools-api) for creating new ones.
-So how to use the Editor after [Installation](docs/installation.md).
+How to use the Editor after [Installation](https://editorjs.io/getting-started).
-- Create new Blocks by Enter or with the Plus Button
+- Create new Blocks by pressing Enter or clicking the Plus Button
- Press `TAB` or click on the Plus Button to view the Toolbox
- Press `TAB` again to leaf Toolbox and select a Block you need. Then press Enter.

-- Select text fragment and apply a style or insert a link from the Inline Toolbar
+- Select a text fragment and apply a style or insert a link from the Inline Toolbar

-- Use «three-dots» button on the right to open Block Settings. From here, you can move and delete a Block
-or apply Tool's settings, if it provided. For example, set a Heading level or List style.
+- Use the «three-dots» button on the right to open Block Settings. From here, you can move and delete a Block
+or apply a Tool's settings, if it provided. For example, you can set a Heading level or List style.

-## Shortcuts
+### Shortcuts
-We really appreciate shortcuts. So there are few presets.
+A few shortcuts are preset as available.
Shortcut | Action | Restrictions
-- | -- | --
@@ -108,10 +92,10 @@ Shortcut | Action | Restrictions
`CMD+I` | Italic style | On selection
`CMD+K` | Insert a link | On selection
-Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example:
+Each Tool can also have its own shortcuts. These are specified in the configuration of the Tool, for example:
```js
-var editor = EditorJS({
+var editor = new EditorJS({
//...
tools: {
header: {
@@ -129,7 +113,7 @@ var editor = EditorJS({
```
-# Installation Guide
+## Installation Guide
There are few steps to run Editor.js on your site.
@@ -137,59 +121,50 @@ There are few steps to run Editor.js on your site.
2. [Load Tools](#load-tools)
3. [Initialize Editor's instance](#create-editor-instance)
-## Load Editor's core
+### Step 1. Load Editor's core
-Firstly you need to get Editor.js itself. It is a [minified script](build/editor.js) with Editor's core and some default must-have tools.
+Get Editor.js itself. It is a [minified script](dist/editor.js) with Editor's core and some default must-have tools.
Choose the most usable method of getting Editor for you.
- Node package
- Source from CDN
-- Local file from project
-### Node.js
+##### Option A. NPM install
Install the package via NPM or Yarn
```shell
-npm i @editorjs/editorjs --save-dev
+npm i @editorjs/editorjs
```
Include module in your application
```javascript
-const EditorJS = require('@editorjs/editorjs');
+import EditorJS from '@editorjs/editorjs';
```
-### Use from CDN
+##### Option B. Use a CDN
-You can load specific version of package from [jsDelivr CDN](https://www.jsdelivr.com/package/npm/@editorjs/editorjs).
+You can load EditorJS directly from from [jsDelivr CDN](https://www.jsdelivr.com/package/npm/@editorjs/editorjs).
`https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest`
-Then require this script.
+For example, place this in your HTML:
```html
-
+
```
-### Save sources to project
-
-Copy [editor.js](build/editor.js) file to your project and load it.
+Or download the bundle file and use it from your server.
```html
```
-## Load Tools
+### Step 2. Load the Tools that you want to make available
-Each Block at the Editor.js represented by [Tools](docs/tools.md). There are simple external scripts with own logic. Probably you want to use several Block Tools that should be connected.
-
-For example check out our [Header](https://github.com/editor-js/header) Tool that represents heading blocks.
-
-You can install Header Tool by the same way as the Editor (Node.js, CDN, local file).
-
-Check [Editor.js's community](https://github.com/editor-js/) to see more ready-to-use Tools.
+Each Block is represented by a [Tool](docs/tools.md). Tools are simple external scripts with their own logic. For example, there is a [Header](https://github.com/editor-js/header) Tool into which you type your heading text. If you want to be able to use this, install the Header Tool the same way as the Editor (Node.js, CDN, local file).
**Example:** use Header from CDN
@@ -197,7 +172,9 @@ Check [Editor.js's community](https://github.com/editor-js/) to see more ready-t
```
-## Create Editor instance
+Check [Editor.js's community](https://github.com/editor-js/) to see more ready-to-use Tools.
+
+### Step 3. Create Editor instance
Create an instance of Editor.js and pass [Configuration Object](types/configs/editor-config.d.ts) with `holderId` and tools list.
@@ -222,7 +199,7 @@ var editor = new EditorJS({
/**
* Create a holder for the Editor and pass its ID
*/
- holderId : 'editorjs',
+ holder : 'editorjs',
/**
* Available Tools list.
@@ -243,36 +220,31 @@ var editor = new EditorJS({
});
```
-## Saving Data
+### Saving Data
-Call `editor.saver.save()` and handle returned Promise with saved data.
+Call `editor.save()` and handle returned Promise with saved data.
```javascript
-editor.saver.save()
+editor.save()
.then((savedData) => {
console.log(savedData);
});
```
-## Example
+### Example
Take a look at the [example.html](example/example.html) to view more detailed examples.
## Credits and references
-- We use [HTMLJanitor](https://github.com/guardian/html-janitor) module in our Sanitizer module.
+- We use [HTMLJanitor](https://github.com/guardian/html-janitor) module in our Sanitizer module.
-# About team
+## About team
-We are CodeX and we build products for developers and makers.
+We are CodeX and we build products for developers and makers.
Follow us on Twitter: [twitter.com/codex_team](https://twitter.com/codex_team)
Feel free to contact: team@codex.so
[codex.so](https://codex.so)
-
-# Support project improvements
-
-Love Editor.js? Please consider supporting our collective:
- 👉 [https://opencollective.com/editorjs](https://opencollective.com/editorjs)
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 000000000..732aea680
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1,9 @@
+{
+ "env": {
+ },
+ "fixturesFolder": "test/cypress/fixtures",
+ "integrationFolder": "test/cypress/tests",
+ "screenshotsFolder": "test/cypress/screenshots",
+ "videosFolder": "test/cypress/videos",
+ "supportFile": "test/cypress/support/index.ts"
+}
diff --git a/dist/editor.js b/dist/editor.js
deleted file mode 100644
index 5eafc9bb8..000000000
--- a/dist/editor.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*!
- * Editor.js
- *
- * @version 2.12.3
- *
- * @licence Apache-2.0
- * @author CodeX
2&&void 0!==arguments[2]?arguments[2]:10,o=window.getSelection(),r=null;if(!o||!o.anchorNode||!o.focusNode)return null;var i=[o.anchorNode,o.focusNode];return i.forEach(function(o){for(var i=n;i>0&&o.parentNode&&(o.tagName!==t||(r=o,e&&o.classList&&!o.classList.contains(e)&&(r=null),!r));)o=o.parentNode,i--}),r}},{key:"expandToTag",value:function(t){var e=window.getSelection();e.removeAllRanges();var n=document.createRange();n.selectNodeContents(t),e.addRange(n)}}],[{key:"get",value:function(){return window.getSelection()}},{key:"CSS",get:function(){return{editorWrapper:"codex-editor",editorZone:"codex-editor__redactor"}}},{key:"anchorNode",get:function(){var t=window.getSelection();return t?t.anchorNode:null}},{key:"anchorOffset",get:function(){var t=window.getSelection();return t?t.anchorOffset:null}},{key:"isCollapsed",get:function(){var t=window.getSelection();return t?t.isCollapsed:null}},{key:"isAtEditor",get:function(){var e=t.get(),n=e.anchorNode||e.focusNode;n&&n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);var o=null;return n&&(o=n.closest(".".concat(t.CSS.editorZone))),o&&o.nodeType===Node.ELEMENT_NODE}},{key:"range",get:function(){var t=window.getSelection();return t&&t.rangeCount?t.getRangeAt(0):null}},{key:"rect",get:function(){var t,e=document.selection,n={x:0,y:0,width:0,height:0};if(e&&"Control"!==e.type)return t=(e=e).createRange(),n.x=t.boundingLeft,n.y=t.boundingTop,n.width=t.boundingWidth,n.height=t.boundingHeight,n;if(!window.getSelection)return a.default.log("Method window.getSelection is not supported","warn"),n;if(null===(e=window.getSelection()).rangeCount||isNaN(e.rangeCount))return a.default.log("Method SelectionUtils.rangeCount is not supported","warn"),n;if((t=e.getRangeAt(0).cloneRange()).getBoundingClientRect&&(n=t.getBoundingClientRect()),0===n.x&&0===n.y){var o=document.createElement("span");if(o.getBoundingClientRect){o.appendChild(document.createTextNode("")),t.insertNode(o),n=o.getBoundingClientRect();var r=o.parentNode;r.removeChild(o),r.normalize()}}return n}},{key:"text",get:function(){return window.getSelection?window.getSelection().toString():""}}]),t}();o.default=c,c.displayName="SelectionUtils",t.exports=e.default})?o.apply(e,r):o)||(t.exports=i)},function(t,e,n){var o=n(10);t.exports=function(t,e){if(!o(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!o(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!o(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!o(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var o=n(41)("meta"),r=n(10),i=n(21),a=n(14).f,s=0,c=Object.isExtensible||function(){return!0},u=!n(8)(function(){return c(Object.preventExtensions({}))}),l=function(t){a(t,o,{value:{i:"O"+ ++s,w:{}}})},f=t.exports={KEY:o,NEED:!1,fastKey:function(t,e){if(!r(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,o)){if(!c(t))return"F";if(!e)return"E";l(t)}return t[o].i},getWeak:function(t,e){if(!i(t,o)){if(!c(t))return!0;if(!e)return!1;l(t)}return t[o].w},onFreeze:function(t){return u&&f.NEED&&c(t)&&!i(t,o)&&l(t),t}}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n=0,o=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+o).toString(36))}},function(t,e){t.exports=!1},function(t,e,n){var o=n(101),r=n(76);t.exports=Object.keys||function(t){return o(t,r)}},function(t,e,n){var o=n(35),r=Math.max,i=Math.min;t.exports=function(t,e){return(t=o(t))<0?r(t+e,0):i(t,e)}},function(t,e,n){var o=n(11),r=n(102),i=n(76),a=n(75)("IE_PROTO"),s=function(){},c=function(){var t,e=n(72)("iframe"),o=i.length;for(e.style.display="none",n(78).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write("
@@ -52,11 +52,11 @@ Copy [editorjs.js](../dist/editor.js) file to your project and load it.
## Load Tools
-Each Block at the Editor.js represented by [Tools](tools.md). There are simple external scripts with own logic. Probably you want to use several Block Tools that should be connected.
+Each Block at the Editor.js represented by [Tools](tools.md). There are simple external scripts with their own logic. You'll probably want to use several Block Tools that should be connected.
-For example check out our [Header](https://github.com/editor-js/header) Tool that represents heading blocks.
+For example, check out our [Header](https://github.com/editor-js/header) Tool that represents heading blocks.
-You can install Header Tool via the same ways as an Editor (Node.js, CDN, local file).
+You can install the Header Tool via the same ways as an Editor (Node.js, CDN, local file).
Check [Editor.js's community](https://github.com/editor-js/) to see Tools examples.
@@ -68,8 +68,8 @@ Check [Editor.js's community](https://github.com/editor-js/) to see Tools exampl
## Create Editor instance
-Create an instance of Editor.js and pass [Configuration Object](../src/types-internal/editor-config.ts).
-Minimal params is a `holderId`, `tools` list and `initialBlock` marker.
+Create an instance of Editor.js and pass [Configuration Object](../src/types-internal/editor-config.ts).
+At least the `holderId` option is required.
```html
@@ -115,11 +115,11 @@ var editor = new EditorJS({
## Ready callback
-Editor.js needs a bit time to initialize. It is an asynchronous action so it won't block execution of your main script.
+Editor.js needs a bit of time to initialize. It is an asynchronous action so it won't block execution of your main script.
-If you need to know when editor instance is ready you can use one of following ways:
+If you need to know when the editor instance is ready you can use one of the following ways:
-##### Pass `onReady` property to the configuration object.
+##### Pass `onReady` property to the configuration object.
It must be a function:
@@ -136,9 +136,9 @@ var editor = new EditorJS({
#### Use `isReady` promise.
-After you create new `EditorJS` object it contains `isReady` property.
-It is a Promise object resolved when editor is ready to work and rejected otherwise.
-If there is an error during initialization `isReady` promise will be rejected with error message.
+After you create a new `EditorJS` object it will contain `isReady` property.
+It is a Promise object that resolves when the editor will be ready to work and rejected otherwise.
+If there is an error during initialization `isReady` promise will be rejected with an error message.
```javascript
var editor = new EditorJS();
@@ -179,7 +179,7 @@ editor.saver.save()
## Features
-Also Editor.js provides useful methods to work with Editor's state.
+Also, Editor.js provides useful methods to work with Editor's state.
```javascript
var editor = new EditorJS({
@@ -189,7 +189,7 @@ var editor = new EditorJS({
* onReady callback
*/
onReady: () => {console.log('Editor.js is ready to work!')},
-
+
/**
* onChange callback
*/
diff --git a/docs/releases.md b/docs/releases.md
new file mode 100644
index 000000000..675aaac60
--- /dev/null
+++ b/docs/releases.md
@@ -0,0 +1,100 @@
+# Branches, versions and releases — complete guideline
+
+## Branches
+
+The project has two main branches: `master` and `next`.
+
+Branch `master` contains the latest stable version of the editor.
+The latest version published to NPM available by default or by the tag `latest`.
+
+Branch `next` used for development the next (release candidate) version of the editor.
+It may contain bug fixes, improvements or features. This version is available in NPM by `next` tag.
+
+## Versions
+
+We use [semantic versioning](https://semver.org) as a main guide for naming updates.
+
+`
`icon` - HTML string with icon for Toolbox
`title` - optional title to display in Toolbox |
| `enableLineBreaks` | _Boolean_ | `false` | With this option, Editor.js won't handle Enter keydowns. Can be helpful for Tools like `` where line breaks should be handled by default behaviour. |
| `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) |
+| `sanitize` | _Object_ | `undefined` | Config for automatic sanitizing of saved data. See [Sanitize](#sanitize) section. |
+| `conversionConfig` | _Object_ | `undefined` | Config allows Tool to specify how it can be converted into/from another Tool. See [Conversion config](#conversion-config) section. |
## User configuration
@@ -75,7 +78,7 @@ var editor = new EditorJS({
},
header: Header
},
- initialBlock : 'text',
+ defaultBlock : 'text',
});
```
@@ -86,6 +89,36 @@ There are few options available by Editor.js.
| `inlineToolbar` | _Boolean/Array_ | `false` | Pass `true` to enable the Inline Toolbar with all Tools, or pass an array with specified Tools list |
| `config` | _Object_ | `null` | User's configuration for Plugin.
+## Tool prepare and reset
+
+If you need to prepare some data for Tool (eg. load external script, create HTML nodes in the document, etc) you can use static prepare method.
+
+It accepts tools config passed on Editor's initialization as an argument:
+
+```javascript
+class Tool {
+ static prepare(config) {
+ loadScript();
+ insertNodes();
+ ...
+ }
+}
+```
+
+On Editor destroy you can use an opposite method `reset` to clean up all prepared data:
+
+```javascript
+class Tool {
+ static reset() {
+ cleanUpScripts();
+ deleteNodes();
+ ...
+ }
+}
+```
+
+Both methods might be async.
+
## Paste handling
Editor.js handles paste on Blocks and provides API for Tools to process the pasted data.
@@ -115,7 +148,7 @@ To handle pasted HTML elements object returned from `pasteConfig` getter should
| -- | -- | -- |
| `tags` | `String[]` | _Optional_. Should contain all tag names you want to be extracted from pasted data and processed by your `onPaste` method |
-For correct work you MUST provide `onPaste` handler at least for `initialBlock` Tool.
+For correct work you MUST provide `onPaste` handler at least for `defaultBlock` Tool.
> Example
@@ -141,7 +174,7 @@ Your Tool can analyze text by RegExp patterns to substitute pasted string with d
**Note** Editor will check pattern's full match, so don't forget to handle all available chars in there.
-Pattern will be processed only if paste was on `initialBlock` Tool and pasted string length is less than 450 characters.
+Pattern will be processed only if paste was on `defaultBlock` Tool and pasted string length is less than 450 characters.
> Example
@@ -224,7 +257,18 @@ onPaste (event) {
}
```
-## Sanitize
+### Disable paste handling
+
+If you need to disable paste handling on your Tool for some reason, you can provide `false` as `pasteConfig` value.
+That way paste event won't be processed if fired on your Tool:
+
+```javascript
+static get pasteConfig {
+ return false;
+}
+```
+
+## Sanitize
Editor.js provides [API](sanitizer.md) to clean taint strings.
Use it manually at the `save()` method or or pass `sanitizer` config to do it automatically.
@@ -341,4 +385,130 @@ static get sanitize() {
}
```
+## Conversion config
+
+Editor.js has a Conversion Toolbar that allows user to convert one Block to another.
+
+
+
+1. You can add ability to your Tool to be converted. Specify «export» property of `conversionConfig`.
+2. You can add ability to convert other Tools to your Tool. Specify «import» property of `conversionConfig`.
+
+Conversion Toolbar will be shown only near Blocks that specified an «export» rule, when user selected almost all block's content.
+This Toolbar will contain only Tools that specified an «import» rule.
+
+Example:
+
+```js
+class Header {
+ constructor(){
+ this.data = {
+ text: '',
+ level: 2
+ }
+ }
+
+ /**
+ * Rules specified how our Tool can be converted to/from other Tool.
+ */
+ static get conversionConfig() {
+ return {
+ export: 'text', // this property of tool data will be used as string to pass to other tool
+ import: 'text' // to this property imported string will be passed
+ };
+ }
+}
+```
+
+### Your Tool -> other Tool
+
+The «export» field specifies how to represent your Tool's data as a string to pass it to other tool.
+
+It can be a `String` or a `Function`.
+
+`String` means a key of your Tool data object that should be used as string to export.
+
+`Function` is a method that accepts your Tool data and compose a string to export from it. See example below:
+
+```js
+class ListTool {
+ constructor(){
+ this.data = {
+ items: [
+ 'Fisrt item',
+ 'Second item',
+ 'Third item'
+ ],
+ type: 'ordered'
+ }
+ }
+
+ static get conversionConfig() {
+ return {
+ export: (data) => {
+ return data.items.join('.'); // in this example, all list items will be concatenated to an export string
+ },
+ // ... import rule
+ };
+ }
+}
+```
+
+### Other Tool -> your Tool
+
+The «import» rule specifies how to create your Tool's data object from the string created by original block.
+
+It can be a `String` or a `Function`.
+
+`String` means the key in tool data that will be filled by an exported string.
+For example, `import: 'text'` means that `constructor` of your block will accept a `data` object with `text` property filled with string composed by original block.
+
+`Function` allows you to specify own logic, how a string should be converted to your tool data. For example:
+
+```js
+class ListTool {
+ constructor(data){
+ this.data = data || {
+ items: [],
+ type: 'unordered'
+ }
+ }
+
+ static get conversionConfig() {
+ return {
+ // ... export rule
+
+ /**
+ * In this example, List Tool creates items by splitting original text by a dot symbol.
+ */
+ import: (string) => {
+ const items = string.split('.');
+
+ return {
+ items: items.filter( (text) => text.trim() !== ''),
+ type: 'unordered'
+ };
+ }
+ };
+ }
+}
+```
+
+## Block Lifecycle hooks
+
+### `rendered()`
+
+Called after Block contents is added to the page
+
+### `updated()`
+
+Called each time Block contents is updated
+
+### `removed()`
+
+Called after Block contents is removed from the page but before Block instance deleted
+
+### `moved(MoveEvent)`
+Called after Block was moved. `MoveEvent` contains `fromIndex` and `toIndex`
+respectively.
diff --git a/docs/usage.md b/docs/usage.md
index edd2a40f5..5bb43c08f 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -40,7 +40,7 @@ Action | Shortcut | Restrictions
Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example:
```js
-var editor = EditorJS({
+var editor = new EditorJS({
//...
tools: {
header: {
@@ -63,10 +63,65 @@ If you want to focus Editor after page has been loaded, you can enable autofocus
```js
-var editor = EditorJS({
+var editor = new EditorJS({
//...
autofocus: true
//...
});
```
+
+## Holder
+The `holder` property supports an id or a reference to dom element.
+
+```js
+var editor = new EditorJS({
+ holder: document.querySelector('.editor'),
+})
+
+var editor2 = new EditorJS({
+ holder: 'codex-editor' // like document.getElementById('codex-editor')
+})
+```
+
+
+
+## Placeholder
+
+By default Editor\`s placeholder is empty.
+
+You can pass your own placeholder via `placeholder` field:
+
+
+```js
+var editor = new EditorJS({
+ //...
+ placeholder: 'My awesome placeholder'
+ //...
+ });
+
+```
+
+If you are using your custom `Initial Block`, `placeholder` property is passed in `config` to your Tool constructor.
+
+## Log level
+
+You can specify log level for Editor.js console messages via `logLevel` property of configuration:
+
+```js
+var editor = new EditorJS({
+ //...
+ logLevel: 'WARN'
+ //..
+})
+```
+
+Possible values:
+
+| Value | Description |
+| ----- | ---------------------------- |
+| `VERBOSE` | Show all messages |
+| `INFO` | Show info and debug messages |
+| `WARN` | Show errors and warns only |
+| `ERROR` | Show errors only |
+
diff --git a/example/assets/codex2x.png b/example/assets/codex2x.png
new file mode 100644
index 000000000..09388d080
Binary files /dev/null and b/example/assets/codex2x.png differ
diff --git a/example/assets/demo.css b/example/assets/demo.css
index e8a30aaa7..60ed8b7f4 100644
--- a/example/assets/demo.css
+++ b/example/assets/demo.css
@@ -69,6 +69,12 @@ body {
padding: 0 15px;
}
+.ce-example__content--with-bg {
+ background: #f4f4f4;
+ max-width: none;
+ margin-top: -30px;
+}
+
.ce-example__output {
background: #1B202B;
overflow-x: auto;
@@ -93,7 +99,8 @@ body {
max-width: 180px;
background: #4A9DF8;
padding: 17px 30px;
- box-shadow: 0 6px 4px -4px rgba(137, 207, 255, 0.77);
+ box-shadow: 0 22px 18px -4px rgba(137, 207, 255, 0.77);
+ transition: all 150ms ease;
cursor: pointer;
border-radius: 31px;
color: #fff;
@@ -103,6 +110,8 @@ body {
.ce-example__button:hover {
background: #3D8DE5;
+ transform: translateY(2px);
+ box-shadow: 0 20px 15px -4px rgba(137, 207, 255, 0.77);
}
.ce-example__output-footer {
@@ -117,6 +126,32 @@ body {
text-decoration: none;
}
+.ce-example__statusbar {
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
+ font-size: 12px;
+ padding: 8px 15px;
+ z-index: 1;
+}
+
+.ce-example__statusbar-button {
+ display: inline-flex;
+ margin-left: 10px;
+ background: #4A9DF8;
+ padding: 6px 12px;
+ box-shadow: 0 7px 8px -4px rgba(137, 207, 255, 0.77);
+ transition: all 150ms ease;
+ cursor: pointer;
+ border-radius: 31px;
+ color: #fff;
+ font-family: 'PT Mono', Menlo, Monaco, Consolas, Courier New, monospace;
+ text-align: center;
+}
+
@media all and (max-width: 730px){
.ce-example__header,
.ce-example__content{
@@ -158,3 +193,16 @@ body {
.ce-example h4.ce-header {
font-size: 18px;
}
+
+.ce-example-multiple {
+ display: grid;
+ grid-template-columns: calc(50% - 15px) calc(50% - 15px);
+ gap: 30px;
+ padding: 30px;
+}
+
+.ce-example-multiple > div {
+ background: #fff;
+ border-radius: 7px;
+ padding: 30px;
+}
diff --git a/example/example-dev.html b/example/example-dev.html
index 14b3a8b45..84553b66d 100644
--- a/example/example-dev.html
+++ b/example/example-dev.html
@@ -28,12 +28,24 @@
yarn build
+ yarn pull_tools
diff --git a/example/example-i18n.html b/example/example-i18n.html new file mode 100644 index 000000000..b07a67227 --- /dev/null +++ b/example/example-i18n.html @@ -0,0 +1,418 @@ + + + +
+ +
+ + + + + +
+