Skip to content

Commit

Permalink
Paste handling improvements (codex-team#534)
Browse files Browse the repository at this point in the history
* Make on paste callback non-static method

* Add docs

* change tools.md header levels

* some docs improvements

* upd docs

* Types improvements

* add image tool for testing

* Fix file drag'n'drop

* improve log on paste

* Update submodules

* Update bundle

* Update paragraph submodule

* Fix some bugs with blocks replacement
Remove tag from HTMLPasteEvent

* Use production webpack mode

* minimize: true

* Update docs

* Update submodules

* Update bundle
  • Loading branch information
gohabereg authored Nov 29, 2018
1 parent 4c9aa0f commit 669c11e
Show file tree
Hide file tree
Showing 28 changed files with 385 additions and 10,691 deletions.
166 changes: 88 additions & 78 deletions build/codex-editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/codex-editor.js.map

Large diffs are not rendered by default.

133 changes: 77 additions & 56 deletions docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Method that specifies how to merge two `Blocks` of the same type, for example on
Method does accept data object in same format as the `Render` and it should provide logic how to combine new
data with the currently stored value.

### Internal Tool Settings
## Internal Tool Settings

Options that Tool can specify. All settings should be passed as static properties of Tool's class.

Expand All @@ -58,7 +58,7 @@ Options that Tool can specify. All settings should be passed as static propertie
| `enableLineBreaks` | _Boolean_ | `false` | With this option, CodeX Editor won't handle Enter keydowns. Can be helpful for Tools like `<code>` where line breaks should be handled by default behaviour. |
| `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) |

### User configuration
## User configuration

All Tools can be configured by users. You can set up some of available settings along with Tool's class
to the `tools` property of Editor Config.
Expand All @@ -85,129 +85,150 @@ There are few options available by CodeX Editor.
| `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.

### Paste handling
## Paste handling

CodeX Editor handles paste on Blocks and provides API for Tools to process the pasted data.

When user pastes content into Editor, pasted content is splitted into blocks.
When user pastes content into Editor, pasted content will be splitted into blocks.

1. If plain text has been pasted, it is split by new line characters
2. If HTML string has been pasted, it is split by block tags
1. If plain text will be pasted, it will be splitted by new line characters
2. If HTML string will be pasted, it will be splitted by block tags

Also Editor API allows you to define RegExp patterns to substitute them by your data.
Also Editor API allows you to define your own pasting scenario. You can either:

To provide paste handling for your Tool you need to define static getter `onPaste` in Tool class.
`onPaste` getter should return object with fields described below.
1. Specify **HTML tags**, that can be represented by your Tool. For example, Image Tool can handle `<img>` tags.
If tags you specified will be found on content pasting, your Tool will be rendered.
2. Specify **RegExp** for pasted strings. If pattern has been matched, your Tool will be rendered.
3. Specify **MIME type** or **extensions** of files that can be handled by your Tool on pasting by drag-n-drop or from clipboard.

For each scenario, you should do 2 next things:

1. Define static getter `pasteConfig` in Tool class. Specify handled patterns there.
2. Define public method `onPaste` that will handle PasteEvent to process pasted data.

##### HTML tags handling
### HTML tags handling

To handle pasted HTML elements object returned from `onPaste` getter should contain following fields:
To handle pasted HTML elements object returned from `pasteConfig` getter should contain following field:

| Name | Type | Description |
| -- | -- | -- |
| `handler(content: HTMLElement)` | `Function` | _Optional_. Pasted HTML elements handler. Gets one argument `content`. `content` is HTML element extracted from pasted data. Handler should return the same object as Tool's `save` method |
| `tags` | `String[]` | _Optional_. Should contain all tag names you want to be extracted from pasted data and be passed to your `handler` method |

| `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 `initialBlock` Tool.

> Example
Header tool can handle `H1`-`H6` tags using paste handling API
Header Tool can handle `H1`-`H6` tags using paste handling API

```javascript
static get onPaste() {
static get pasteConfig() {
return {
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'],
handler: (element) => ({
type: element.tagName,
text: element.innerHTML
})
}
}
```

> One tag can be handled by one Tool only.
> Same tag can be handled by one (first specified) Tool only.
##### Patterns handling
### RegExp patterns handling

Your Tool can analyze text by RegExp patterns to substitute pasted string with data you want. Object returned from `onPaste` getter should contain following fields to use patterns:
Your Tool can analyze text by RegExp patterns to substitute pasted string with data you want. Object returned from `pasteConfig` getter should contain following field to use patterns:

| Name | Type | Description |
| -- | -- | -- |
| `patterns` | `Object` | _Optional_. `patterns` object contains RegExp patterns with their names as object's keys |
| `patternHandler(text: string, key: string)` | `Function` | _Optional_. Gets pasted string and pattern name. Should return the same object as Tool `save` method |

**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.

> Example
You can handle youtube links and insert embeded video instead:
You can handle YouTube links and insert embeded video instead:

```javascript
static get onPaste() {
static get pasteConfig() {
return {
patterns: {
youtube: /http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?‌​=]*)?/
},
patternHandler: (text, key) => {
const urlData = Youtube.onPaste.patterns[key].exec(text);

return {
iframe: Youtube.makeEmbededFromURL(urlData)
};
}
}
}
```

> Both `onPaste.handler` and `onPaste.patternHandler` can be `async` or return a `Promise`.
##### Files
### Files pasting

Your Tool can handle files pasted or dropped into the Editor.

To handle file you should provide `files` and `fileHandler` properties in your `onPaste` configuration object.
To handle file you should provide `files` property in your `pasteConfig` configuration object.

`fileHandler` property should be a function which takes File object as an argument and returns the same object as Tool\`s `save` method.

`file` property is an object with the following fields:
`files` property is an object with the following fields:

| Name | Type | Description |
| ---- | ---- | ----------- |
| `extensions` | `string[]` | _Optional_ Array of extensions your Tool can handle |
| `mimeTypes` | `sring[]` | _Optional_ Array of MIME types your Tool can handle |



Example

```javascript
static get onPaste() {
static get pasteConfig() {
return {
files: {
mimeTypes: ['image/png'],
extensions: ['json']
},
fileHandler: (file) => {
/* do smth with the file */

return {
data // Some extracted content
}
}
}
}
```

### Sanitize
### Pasted data handling

If you registered some paste substitutions in `pasteConfig` property, you **should** provide `onPaste` callback in your Tool class.
`onPaste` should be public non-static method. It accepts custom _PasteEvent_ object as argument.

PasteEvent is an alias for three types of events - `tag`, `pattern` and `file`. You can get the type from _PasteEvent_ object's `type` property.
Each of these events provide `detail` property with info about pasted content.

| Type | Detail |
| ----- | ------ |
| `tag` | `data` - pasted HTML element |
| `pattern` | `key` - matched pattern key you specified in `pasteConfig` object <br /> `data` - pasted string |
| `file` | `file` - pasted file |

Example

```javascript
onPaste (event) {
switch (event.type) {
case 'tag':
const element = event.detail.data;

this.handleHTMLPaste(element);
break;

case 'pattern':
const text = event.detail.data;
const key = event.detail.key;

this.handlePatternPaste(key, text);
break;

case 'file':
const file = event.detail.file;

this.handleFilePaste(file);
break;
}
}
```

## Sanitize

CodeX Editor provides [API](sanitizer.md) to clean taint strings.
Use it manually at the `save()` method or or pass `sanitizer` config to do it automatically.

#### Sanitizer Configuration
### Sanitizer Configuration

The example of sanitizer configuration

Expand All @@ -220,7 +241,7 @@ let sanitizerConfig = {

Keys of config object is tags and the values is a rules.

##### Rule
#### Rule

Rule can be boolean, object or function. Object is a dictionary of rules for tag's attributes.

Expand Down Expand Up @@ -262,7 +283,7 @@ a: function(el) {
}
```

#### Manual sanitize
### Manual sanitize

Call API method `sanitizer.clean()` at the save method for each field in returned data.

Expand All @@ -274,7 +295,7 @@ save() {
}
```

#### Automatic sanitize
### Automatic sanitize

If you pass the sanitizer config as static getter, CodeX Editor will automatically sanitize your saved data.

Expand Down
10 changes: 7 additions & 3 deletions example/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<pre class="ce-example__output-content" id="output"></pre>

<div class="ce-example__output-footer">
<a href="https://ifmo.su" style="font-weight: bold">Made by CodeX</a>
<a href="https://ifmo.su" style="font-weight: bold;">Made by CodeX</a>
</div>
</div>
</div>
Expand All @@ -42,6 +42,7 @@
https://github.com/codex-editor/header#installation
-->
<script src="./tools/header/dist/bundle.js"></script><!-- Header -->
<script src="./tools/simple-image/dist/bundle.js"></script><!-- Image -->
<script src="./tools/image/dist/bundle.js"></script><!-- Image -->
<script src="./tools/delimiter/dist/bundle.js"></script><!-- Delimiter -->
<script src="./tools/list/dist/bundle.js"></script><!-- List -->
Expand Down Expand Up @@ -94,7 +95,10 @@
image: {
class: ImageTool,
config: {
url: 'http://localhost:8008',
endpoints: {
byFile: 'http://localhost:8008/uploadFile',
byUrl: 'http://localhost:8008/fetchUrl',
},
},
inlineToolbar: ['link'],
},
Expand Down Expand Up @@ -232,7 +236,7 @@
type: 'image',
data: {
file: {
url : 'https://ifmo.su/upload/redactor_images/o_e48549d1855c7fc1807308dd14990126.jpg',
url: 'https://ifmo.su/upload/redactor_images/o_e48549d1855c7fc1807308dd14990126.jpg',
},
caption: '',
stretched: false,
Expand Down
2 changes: 1 addition & 1 deletion example/tools/code
Submodule code updated from d0874d to 860d30
2 changes: 1 addition & 1 deletion example/tools/embed
Submodule embed updated from f64378 to af3d65
2 changes: 1 addition & 1 deletion example/tools/header
2 changes: 1 addition & 1 deletion example/tools/image
Submodule image updated from 24a5fe to e45da0
2 changes: 1 addition & 1 deletion example/tools/list
Submodule list updated from f14f25 to c6b832
2 changes: 1 addition & 1 deletion example/tools/marker
2 changes: 1 addition & 1 deletion example/tools/simple-image
2 changes: 1 addition & 1 deletion example/tools/table
Submodule table updated from cfde1b to 169bff
Loading

0 comments on commit 669c11e

Please sign in to comment.