mirror of
https://github.com/codex-team/editor.js
synced 2024-05-17 13:56:45 +02:00
parent
78775703c9
commit
b223d63c59
|
@ -40,7 +40,6 @@
|
|||
"DataTransfer": true,
|
||||
"DOMRect": true,
|
||||
"ClientRect": true,
|
||||
"ArrayLike": true,
|
||||
"unknown": true
|
||||
"ArrayLike": true
|
||||
}
|
||||
}
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -46,6 +46,3 @@
|
|||
[submodule "example/tools/warning"]
|
||||
path = example/tools/warning
|
||||
url = https://github.com/editor-js/warning
|
||||
[submodule "example/tools/underline"]
|
||||
path = example/tools/underline
|
||||
url = https://github.com/editor-js/underline
|
||||
|
|
20
README.md
20
README.md
|
@ -4,7 +4,7 @@
|
|||
[![](https://flat.badgen.net/bundlephobia/min/@editorjs/editorjs?color=cyan)](https://www.npmjs.com/package/@editorjs/editorjs)
|
||||
[![](https://flat.badgen.net/bundlephobia/minzip/@editorjs/editorjs?color=green)](https://www.npmjs.com/package/@editorjs/editorjs)
|
||||
[![Backers on Open Collective](https://opencollective.com/editorjs/backers/badge.svg)](#backers)
|
||||
[![Sponsors on Open Collective](https://opencollective.com/editorjs/sponsors/badge.svg)](#sponsors)
|
||||
[![Sponsors on Open Collective](https://opencollective.com/editorjs/sponsors/badge.svg)](#sponsors)
|
||||
[![](https://flat.badgen.net/npm/license/@editorjs/editorjs)](https://www.npmjs.com/package/@editorjs/editorjs)
|
||||
[![Join the chat at https://gitter.im/codex-team/editor.js](https://badges.gitter.im/codex-team/editor.js.svg)](https://gitter.im/codex-team/editor.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
|
@ -17,24 +17,24 @@
|
|||
If you like Editor.js you can support project improvements and development of new features with a donation to our collective.
|
||||
|
||||
👉 [https://opencollective.com/editorjs](https://opencollective.com/editorjs)
|
||||
|
||||
|
||||
### 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)]
|
||||
|
||||
<a href="https://opencollective.com/editorjs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/editorjs/sponsor/0/avatar.svg"></a>
|
||||
|
||||
|
||||
### Backers
|
||||
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/editorjs#backer)]
|
||||
|
||||
|
||||
<a href="https://opencollective.com/editorjs#backers" target="_blank"><img src="https://opencollective.com/editorjs/backers.svg?width=890"></a>
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. <img src="https://opencollective.com/editorjs/contributors.svg?width=890&button=false" />
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
|
@ -142,7 +142,7 @@ npm i @editorjs/editorjs
|
|||
Include module in your application
|
||||
|
||||
```javascript
|
||||
import EditorJS from '@editorjs/editorjs';
|
||||
const EditorJS = require('@editorjs/editorjs');
|
||||
```
|
||||
|
||||
##### Option B. Use a CDN
|
||||
|
@ -240,11 +240,11 @@ Take a look at the [example.html](example/example.html) to view more detailed ex
|
|||
|
||||
## 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
|
||||
|
||||
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)
|
||||
|
||||
|
|
2
dist/editor.js
vendored
2
dist/editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,27 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
### 2.19
|
||||
|
||||
- `New` - Read-only mode 🥳 [#837](https://github.com/codex-team/editor.js/issues/837)
|
||||
- `New` - RTL mode added [#670](https://github.com/codex-team/editor.js/issues/670)
|
||||
- `New` - Allows users to provide common `inlineToolbar` property which will be used for all tools whose `inlineToolbar` property is set to `true`. It can be overridden by the tool's own `inlineToolbar` property. Also, inline tools will be ordered according to the order of the inline tools in array provided in the `inlineToolbar` property. [#1056](https://github.com/codex-team/editor.js/issues/1056)
|
||||
- `New` - Tool's `reset` static method added to the API to clean up any data added by Tool on initialization
|
||||
- `Improvements` - The `initialBlock` property of Editor config is deprecated. Use the `defaultBlock` instead. [#993](https://github.com/codex-team/editor.js/issues/993)
|
||||
- `Improvements` - BlockAPI `call()` method now returns the result of calling method, thus allowing it to expose arbitrary data as needed [#1205](https://github.com/codex-team/editor.js/pull/1205)
|
||||
- `Improvements` - Unuseful log about missed i18n section has been removed [#1269](https://github.com/codex-team/editor.js/issues/1269)
|
||||
- `Improvements` - Allowed to set `false` as `toolbox` config in order to hide Toolbox button [#1221](https://github.com/codex-team/editor.js/issues/1221)
|
||||
- `Fix` — Fix problem with types usage [#1183](https://github.com/codex-team/editor.js/issues/1183)
|
||||
- `Fix` - Fixed issue with Spam clicking the "Click to tune" button duplicates the icons on FireFox. [#1273](https://github.com/codex-team/editor.js/issues/1273)
|
||||
- `Fix` - Fixed issue with `editor.blocks.delete(index)` method which throws an error when Editor.js is not focused, even after providing a valid index. [#1182](https://github.com/codex-team/editor.js/issues/1182)
|
||||
- `Fix` - Fixed the issue of toolbar not disappearing on entering input in Chinese, Hindi and some other languages. [#1196](https://github.com/codex-team/editor.js/issues/1196)
|
||||
- `Fix` - Do not stop events propagation if not needed (essential for React synthetic events) [#1051](https://github.com/codex-team/editor.js/issues/1051) [#946](https://github.com/codex-team/editor.js/issues/946)
|
||||
- `Fix` - Tool's `destroy` method is not invoked when `editor.destroy()` is called. [#1047](https://github.com/codex-team/editor.js/issues/1047)
|
||||
- `Fix` - Fixed issue with enter key in inputs and textareas [#920](https://github.com/codex-team/editor.js/issues/920)
|
||||
- `Fix` - blocks.getBlockByIndex() API method now returns void for indexes out of range [#1270](https://github.com/codex-team/editor.js/issues/1270)
|
||||
- `Fix` - Fixed the `Tab` key behavior when the caret is not set inside contenteditable element, but the block is selected [#1302](https://github.com/codex-team/editor.js/issues/1302).
|
||||
- `Fix` - Fixed the `onChange` callback issue. This method didn't be called for native inputs before some contentedtable element changed [#843](https://github.com/codex-team/editor.js/issues/843)
|
||||
- `Fix` - Fixed the `onChange` callback issue. This method didn't be called after the callback throws an exception [#1339](https://github.com/codex-team/editor.js/issues/1339)
|
||||
|
||||
### 2.18
|
||||
|
||||
- `New` *I18n API* — Ability to provide internalization for Editor.js core and tools. [#751](https://github.com/codex-team/editor.js/issues/751)
|
||||
|
@ -46,6 +24,7 @@
|
|||
|
||||
> *Breaking changes* `blocks.getBlockByIndex` method now returns BlockAPI object. To access old value, use BlockAPI.holder property
|
||||
|
||||
|
||||
### 2.17
|
||||
|
||||
- `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter
|
||||
|
|
|
@ -24,10 +24,10 @@ Install the package via NPM or Yarn
|
|||
npm i @editorjs/editorjs
|
||||
```
|
||||
|
||||
Include module at your application
|
||||
Include module at your application
|
||||
|
||||
```javascript
|
||||
import EditorJS from '@editorjs/editorjs';
|
||||
const EditorJS = require('@editorjs/editorjs');
|
||||
```
|
||||
|
||||
### Use from CDN
|
||||
|
@ -68,7 +68,7 @@ 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).
|
||||
Create an instance of Editor.js and pass [Configuration Object](../src/types-internal/editor-config.ts).
|
||||
At least the `holderId` option is required.
|
||||
|
||||
```html
|
||||
|
@ -119,7 +119,7 @@ Editor.js needs a bit of time to initialize. It is an asynchronous action so it
|
|||
|
||||
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:
|
||||
|
||||
|
@ -189,7 +189,7 @@ var editor = new EditorJS({
|
|||
* onReady callback
|
||||
*/
|
||||
onReady: () => {console.log('Editor.js is ready to work!')},
|
||||
|
||||
|
||||
/**
|
||||
* onChange callback
|
||||
*/
|
||||
|
|
|
@ -78,7 +78,7 @@ var editor = new EditorJS({
|
|||
},
|
||||
header: Header
|
||||
},
|
||||
defaultBlock : 'text',
|
||||
initialBlock : 'text',
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -89,36 +89,6 @@ 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.
|
||||
|
@ -148,7 +118,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 `defaultBlock` Tool.
|
||||
For correct work you MUST provide `onPaste` handler at least for `initialBlock` Tool.
|
||||
|
||||
> Example
|
||||
|
||||
|
@ -174,7 +144,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 `defaultBlock` Tool and pasted string length is less than 450 characters.
|
||||
Pattern will be processed only if paste was on `initialBlock` Tool and pasted string length is less than 450 characters.
|
||||
|
||||
> Example
|
||||
|
||||
|
|
|
@ -120,32 +120,6 @@ 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{
|
||||
|
|
|
@ -34,15 +34,6 @@
|
|||
<div class="ce-example__button" id="saveButton">
|
||||
editor.save()
|
||||
</div>
|
||||
<div class="ce-example__statusbar">
|
||||
Readonly:
|
||||
<b id="readonly-state">
|
||||
Off
|
||||
</b>
|
||||
<div class="ce-example__statusbar-button" id="toggleReadOnlyButton">
|
||||
toggle
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ce-example__output">
|
||||
<pre class="ce-example__output-content" id="output"></pre>
|
||||
|
@ -83,29 +74,21 @@
|
|||
|
||||
<!-- Initialization -->
|
||||
<script>
|
||||
/**
|
||||
* Saving button
|
||||
*/
|
||||
const saveButton = document.getElementById('saveButton');
|
||||
|
||||
/**
|
||||
* To initialize the Editor, create a new instance with configuration object
|
||||
* @see docs/installation.md for mode details
|
||||
*/
|
||||
var editor = new EditorJS({
|
||||
/**
|
||||
* Enable/Disable the read only mode
|
||||
*/
|
||||
readOnly: false,
|
||||
|
||||
/**
|
||||
* Wrapper of Editor
|
||||
*/
|
||||
holder: 'editorjs',
|
||||
|
||||
/**
|
||||
* Common Inline Toolbar settings
|
||||
* - if true (or not specified), the order from 'tool' property will be used
|
||||
* - if an array of tool names, this order will be used
|
||||
*/
|
||||
// inlineToolbar: ['link', 'marker', 'bold', 'italic'],
|
||||
// inlineToolbar: true,
|
||||
|
||||
/**
|
||||
* Tools list
|
||||
*/
|
||||
|
@ -115,7 +98,7 @@
|
|||
*/
|
||||
header: {
|
||||
class: Header,
|
||||
inlineToolbar: ['marker', 'link'],
|
||||
inlineToolbar: ['link'],
|
||||
config: {
|
||||
placeholder: 'Header'
|
||||
},
|
||||
|
@ -184,7 +167,7 @@
|
|||
/**
|
||||
* This Tool will be used as default
|
||||
*/
|
||||
// defaultBlock: 'paragraph',
|
||||
// initialBlock: 'paragraph',
|
||||
|
||||
/**
|
||||
* Initial Editor data
|
||||
|
@ -296,37 +279,14 @@
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Saving button
|
||||
*/
|
||||
const saveButton = document.getElementById('saveButton');
|
||||
|
||||
/**
|
||||
* Toggle read-only button
|
||||
*/
|
||||
const toggleReadOnlyButton = document.getElementById('toggleReadOnlyButton');
|
||||
const readOnlyIndicator = document.getElementById('readonly-state');
|
||||
|
||||
/**
|
||||
* Saving example
|
||||
*/
|
||||
saveButton.addEventListener('click', function () {
|
||||
editor.save()
|
||||
.then((savedData) => {
|
||||
cPreview.show(savedData, document.getElementById("output"));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Saving error', error);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle read-only example
|
||||
*/
|
||||
toggleReadOnlyButton.addEventListener('click', async () => {
|
||||
const readOnlyState = await editor.readOnly.toggle();
|
||||
|
||||
readOnlyIndicator.textContent = readOnlyState ? 'On' : 'Off';
|
||||
editor.save().then((savedData) => {
|
||||
cPreview.show(savedData, document.getElementById("output"));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
<!-- Load Tools -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script><!-- Header -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/image@latest"></script><!-- Image -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/simple-image@latest"></script><!-- Image -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/delimiter@latest"></script><!-- Delimiter -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/list@latest"></script><!-- List -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/checklist@latest"></script><!-- Checklist -->
|
||||
|
@ -81,11 +81,6 @@
|
|||
* Tools list
|
||||
*/
|
||||
tools: {
|
||||
paragraph: {
|
||||
config: {
|
||||
placeholder: "Enter something"
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
|
||||
*/
|
||||
|
@ -101,7 +96,7 @@
|
|||
/**
|
||||
* Or pass class directly without any configuration
|
||||
*/
|
||||
image: ImageTool,
|
||||
image: SimpleImage,
|
||||
|
||||
list: {
|
||||
class: List,
|
||||
|
@ -214,7 +209,6 @@
|
|||
"Bold": "Полужирный",
|
||||
"Italic": "Курсив",
|
||||
"InlineCode": "Моноширинный",
|
||||
"Image": "Картинка"
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -241,32 +235,6 @@
|
|||
*/
|
||||
"stub": {
|
||||
'The block can not be displayed correctly.': 'Блок не может быть отображен'
|
||||
},
|
||||
"image": {
|
||||
"Caption": "Подпись",
|
||||
"Select an Image": "Выберите файл",
|
||||
"With border": "Добавить рамку",
|
||||
"Stretch image": "Растянуть",
|
||||
"With background": "Добавить подложку",
|
||||
},
|
||||
"code": {
|
||||
"Enter a code": "Код",
|
||||
},
|
||||
"linkTool": {
|
||||
"Link": "Ссылка",
|
||||
"Couldn't fetch the link data": "Не удалось получить данные",
|
||||
"Couldn't get this link data, try the other one": "Не удалось получить данные по ссылке, попробуйте другую",
|
||||
"Wrong response format from the server": "Неполадки на сервере",
|
||||
},
|
||||
"header": {
|
||||
"Header": "Заголовок",
|
||||
},
|
||||
"paragraph": {
|
||||
"Enter something": "Введите текст"
|
||||
},
|
||||
"list": {
|
||||
"Ordered": "Нумерованный",
|
||||
"Unordered": "Маркированный",
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -386,9 +354,7 @@
|
|||
{
|
||||
type: 'image',
|
||||
data: {
|
||||
file : {
|
||||
url: 'assets/codex2x.png',
|
||||
},
|
||||
url: 'assets/codex2x.png',
|
||||
caption: '',
|
||||
stretched: false,
|
||||
withBorder: true,
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
<!--
|
||||
Use this page for debugging purposes.
|
||||
Editor Tools are loaded as git-submodules.
|
||||
You can pull modules by running `yarn pull_tools` and start experimenting.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Editor.js 🤩🧦🤨 example</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=PT+Mono" rel="stylesheet">
|
||||
<link href="assets/demo.css" rel="stylesheet">
|
||||
<script src="assets/json-preview.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="ce-example">
|
||||
<div class="ce-example__header">
|
||||
<a class="ce-example__header-logo" href="https://codex.so/editor">Editor.js 🤩🧦🤨</a>
|
||||
|
||||
<div class="ce-example__header-menu">
|
||||
<a href="https://github.com/editor-js" target="_blank">Plugins</a>
|
||||
<a href="https://editorjs.io/usage" target="_blank">Usage</a>
|
||||
<a href="https://editorjs.io/configuration" target="_blank">Configuration</a>
|
||||
<a href="https://editorjs.io/creating-a-block-tool" target="_blank">API</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ce-example__content _ce-example__content--small">
|
||||
<div id="editorjs"></div>
|
||||
<div id="hint" style="text-align: center;">
|
||||
No submodules found. Run <code class="inline-code">yarn pull_tools</code>
|
||||
</div>
|
||||
<div class="ce-example__button" id="saveButton">
|
||||
editor.save()
|
||||
</div>
|
||||
</div>
|
||||
<div class="ce-example__output">
|
||||
<pre class="ce-example__output-content" id="output"></pre>
|
||||
|
||||
<div class="ce-example__output-footer">
|
||||
<a href="https://codex.so" style="font-weight: bold;">Made by CodeX</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load Tools -->
|
||||
<!--
|
||||
You can upload Tools to your project's directory and use as in example below.
|
||||
Also you can load each Tool from CDN or use NPM/Yarn packages.
|
||||
Read more in Tool's README file. For example:
|
||||
https://github.com/editor-js/header#installation
|
||||
-->
|
||||
<script src="./tools/header/dist/bundle.js" onload="document.getElementById('hint').hidden = true"></script><!-- Header -->
|
||||
<script src="./tools/simple-image/dist/bundle.js"></script><!-- Image -->
|
||||
<script src="./tools/delimiter/dist/bundle.js"></script><!-- Delimiter -->
|
||||
<script src="./tools/list/dist/bundle.js"></script><!-- List -->
|
||||
<script src="./tools/checklist/dist/bundle.js"></script><!-- Checklist -->
|
||||
<script src="./tools/quote/dist/bundle.js"></script><!-- Quote -->
|
||||
<script src="./tools/code/dist/bundle.js"></script><!-- Code -->
|
||||
<script src="./tools/embed/dist/bundle.js"></script><!-- Embed -->
|
||||
<script src="./tools/table/dist/bundle.js"></script><!-- Table -->
|
||||
<script src="./tools/link/dist/bundle.js"></script><!-- Link -->
|
||||
<script src="./tools/raw/dist/bundle.js"></script><!-- Raw -->
|
||||
<script src="./tools/warning/dist/bundle.js"></script><!-- Warning -->
|
||||
|
||||
<script src="./tools/marker/dist/bundle.js"></script><!-- Marker -->
|
||||
<script src="./tools/inline-code/dist/bundle.js"></script><!-- Inline Code -->
|
||||
|
||||
<!-- Load Editor.js's Core -->
|
||||
<script src="../dist/editor.js"></script>
|
||||
|
||||
<!-- Initialization -->
|
||||
<script>
|
||||
/**
|
||||
* Saving button
|
||||
*/
|
||||
const saveButton = document.getElementById('saveButton');
|
||||
|
||||
/**
|
||||
* To initialize the Editor, create a new instance with configuration object
|
||||
* @see docs/installation.md for mode details
|
||||
*/
|
||||
var editor = new EditorJS({
|
||||
/**
|
||||
* Wrapper of Editor
|
||||
*/
|
||||
holder: 'editorjs',
|
||||
i18n: {
|
||||
|
||||
/**
|
||||
* Text direction
|
||||
*/
|
||||
direction: 'rtl',
|
||||
},
|
||||
|
||||
/**
|
||||
* Tools list
|
||||
*/
|
||||
tools: {
|
||||
/**
|
||||
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
|
||||
*/
|
||||
header: {
|
||||
class: Header,
|
||||
inlineToolbar: ['link'],
|
||||
config: {
|
||||
placeholder: 'Header'
|
||||
},
|
||||
shortcut: 'CMD+SHIFT+H'
|
||||
},
|
||||
|
||||
/**
|
||||
* Or pass class directly without any configuration
|
||||
*/
|
||||
image: {
|
||||
class: SimpleImage,
|
||||
inlineToolbar: ['link'],
|
||||
},
|
||||
|
||||
list: {
|
||||
class: List,
|
||||
inlineToolbar: true,
|
||||
shortcut: 'CMD+SHIFT+L'
|
||||
},
|
||||
|
||||
checklist: {
|
||||
class: Checklist,
|
||||
inlineToolbar: true,
|
||||
},
|
||||
|
||||
quote: {
|
||||
class: Quote,
|
||||
inlineToolbar: true,
|
||||
config: {
|
||||
quotePlaceholder: 'Enter a quote',
|
||||
captionPlaceholder: 'Quote\'s author',
|
||||
},
|
||||
shortcut: 'CMD+SHIFT+O'
|
||||
},
|
||||
|
||||
warning: Warning,
|
||||
|
||||
marker: {
|
||||
class: Marker,
|
||||
shortcut: 'CMD+SHIFT+M'
|
||||
},
|
||||
|
||||
code: {
|
||||
class: CodeTool,
|
||||
shortcut: 'CMD+SHIFT+C'
|
||||
},
|
||||
|
||||
delimiter: Delimiter,
|
||||
|
||||
inlineCode: {
|
||||
class: InlineCode,
|
||||
shortcut: 'CMD+SHIFT+C'
|
||||
},
|
||||
|
||||
linkTool: LinkTool,
|
||||
|
||||
raw: RawTool,
|
||||
|
||||
embed: Embed,
|
||||
|
||||
table: {
|
||||
class: Table,
|
||||
inlineToolbar: true,
|
||||
shortcut: 'CMD+ALT+T'
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* This Tool will be used as default
|
||||
*/
|
||||
// initialBlock: 'paragraph',
|
||||
|
||||
/**
|
||||
* Initial Editor data
|
||||
*/
|
||||
data: {
|
||||
blocks: [
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "محرر.js",
|
||||
level: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
type : 'paragraph',
|
||||
data : {
|
||||
text : 'مرحبا! تعرف على المحرر الجديد. في هذه الصفحة ، يمكنك رؤيتها في العمل - حاول تحرير هذا النص.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "دلائل الميزات",
|
||||
level: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
type : 'list',
|
||||
data : {
|
||||
items : [
|
||||
'وهو محرر بنمط الكتلة',
|
||||
'تقوم بإرجاع إخراج بيانات نظيفة في JSON',
|
||||
'مصممة لتكون قابلة للتوسيع والتوصيل مع واجهة برمجة تطبيقات بسيطة'
|
||||
],
|
||||
style: 'unordered'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
onReady: function(){
|
||||
saveButton.click();
|
||||
},
|
||||
onChange: function() {
|
||||
console.log('something changed');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Saving example
|
||||
*/
|
||||
saveButton.addEventListener('click', function () {
|
||||
editor.save().then((savedData) => {
|
||||
cPreview.show(savedData, document.getElementById("output"));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -156,7 +156,7 @@
|
|||
/**
|
||||
* This Tool will be used as default
|
||||
*/
|
||||
// defaultBlock: 'paragraph',
|
||||
// initialBlock: 'paragraph',
|
||||
|
||||
/**
|
||||
* Initial Editor data
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 197d5d53e0c7d869d76afc4eabae566d391e6d0e
|
||||
Subproject commit df6dcedb92ef586901b04cf72946aced9e36e58d
|
|
@ -1 +1 @@
|
|||
Subproject commit 83d2d9d3136d48ab67b52bc034370c9a26c82c8c
|
||||
Subproject commit 1297b8c280ff34efaca8f3a2a3d263ec4201077d
|
|
@ -1 +1 @@
|
|||
Subproject commit 1f2ec8c709a94c5f4f499bb1aaba7f4930e10484
|
||||
Subproject commit 6819831b7166c1cdfa31df77ab569274d9910aac
|
|
@ -1 +1 @@
|
|||
Subproject commit 9d3d4b5216dce1a933b1e92b45b7b8c404d889b7
|
||||
Subproject commit 44473de4c60dd836ccb61b4dbcf4cc00088acd19
|
|
@ -1 +1 @@
|
|||
Subproject commit 2b21da39b57d0abfcd4979444fb0c3d2d435af7d
|
||||
Subproject commit 93e0b6d6418034f4e7ee704aba090cc25ca16ac2
|
|
@ -1 +1 @@
|
|||
Subproject commit a983c4e62135c88d6cfd926527e6fc92c304451b
|
||||
Subproject commit 1d6f474c14613c60344d30ebd930a18ca123e4a4
|
|
@ -1 +1 @@
|
|||
Subproject commit 051b8e9e03e2f6bea30875da8253f6c0d858b231
|
||||
Subproject commit 37a5e8d1db305cf75acd6622d9b82e2f308f71c6
|
|
@ -1 +1 @@
|
|||
Subproject commit 6a5563630977f223ebafaa03a7df3bf85797437b
|
||||
Subproject commit 33ffba6f9104d163c69c963ca06fed329a819238
|
|
@ -1 +1 @@
|
|||
Subproject commit 458a5fe364e33ad5b5913155d665f335ab742e0f
|
||||
Subproject commit f537cf6ecb26fece34c56a85b51e79b07451e69e
|
|
@ -1 +1 @@
|
|||
Subproject commit 6708697c1af79abbf6650f0f14e1cedc45eb5213
|
||||
Subproject commit c68375288c40774e8d8ceff79aa559d562078aaa
|
|
@ -1 +1 @@
|
|||
Subproject commit 07881fc1020fde79ab9468f740227a99935abb2a
|
||||
Subproject commit 58bf8bd571ae259e3d150ac0c12d1676e5706470
|
|
@ -1 +1 @@
|
|||
Subproject commit 3f40a9cfdb0086c94ee2c7295a96d69c9b266dec
|
||||
Subproject commit 7c6d41603797ebfd00d59fc7ff623342b8f5a48c
|
|
@ -1 +1 @@
|
|||
Subproject commit 1883b28d8aac863d3907c21d5fda231c8e6f799a
|
||||
Subproject commit 0fd96a70b371af0cc0720b8c2c0d0888b8a44bc5
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c1a73a8022c18ac1c15ee8d0134caae029bfbe9
|
||||
Subproject commit af9dc3885077ab2ea1b0ae8ae0d145ff1a40fc40
|
|
@ -1 +1 @@
|
|||
Subproject commit c0507d91014f9b4fdb514f0499348d7619e3e1a2
|
||||
Subproject commit 293109d03d9ff3cdecc52ec959866662d80dd0ce
|
|
@ -2,12 +2,6 @@ import { EditorModules } from '../types-internal/editor-modules';
|
|||
import { EditorConfig } from '../../types';
|
||||
import { ModuleConfig } from '../types-internal/module-config';
|
||||
|
||||
/**
|
||||
* The type <T> of the Module generic.
|
||||
* It describes the structure of nodes used in modules.
|
||||
*/
|
||||
export type ModuleNodes = object;
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @class Module
|
||||
|
@ -17,13 +11,7 @@ export type ModuleNodes = object;
|
|||
* @property {object} config - Editor user settings
|
||||
* @property {EditorModules} Editor - List of Editor modules
|
||||
*/
|
||||
export default class Module<T extends ModuleNodes = {}> {
|
||||
/**
|
||||
* Each module can provide some UI elements that will be stored in this property
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public nodes: T = {} as any;
|
||||
|
||||
export default class Module {
|
||||
/**
|
||||
* Editor modules list
|
||||
*
|
||||
|
@ -38,50 +26,6 @@ export default class Module<T extends ModuleNodes = {}> {
|
|||
*/
|
||||
protected config: EditorConfig;
|
||||
|
||||
/**
|
||||
* This object provides methods to push into set of listeners that being dropped when read-only mode is enabled
|
||||
*/
|
||||
protected readOnlyMutableListeners = {
|
||||
/**
|
||||
* Assigns event listener on DOM element and pushes into special array that might be removed
|
||||
*
|
||||
* @param {EventTarget} element - DOM Element
|
||||
* @param {string} eventType - Event name
|
||||
* @param {Function} handler - Event handler
|
||||
* @param {boolean|AddEventListenerOptions} options - Listening options
|
||||
*/
|
||||
on: (
|
||||
element: EventTarget,
|
||||
eventType: string,
|
||||
handler: (event: Event) => void,
|
||||
options: boolean | AddEventListenerOptions = false
|
||||
): void => {
|
||||
const { Listeners } = this.Editor;
|
||||
|
||||
this.mutableListenerIds.push(
|
||||
Listeners.on(element, eventType, handler, options)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all mutable listeners
|
||||
*/
|
||||
clearAll: (): void => {
|
||||
const { Listeners } = this.Editor;
|
||||
|
||||
for (const id of this.mutableListenerIds) {
|
||||
Listeners.offById(id);
|
||||
}
|
||||
|
||||
this.mutableListenerIds = [];
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The set of listener identifiers which will be dropped in read-only mode
|
||||
*/
|
||||
private mutableListenerIds: string[] = [];
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @param {EditorConfig} config - Editor's config
|
||||
|
@ -102,24 +46,4 @@ export default class Module<T extends ModuleNodes = {}> {
|
|||
public set state(Editor: EditorModules) {
|
||||
this.Editor = Editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove memorized nodes
|
||||
*/
|
||||
public removeAllNodes(): void {
|
||||
for (const key in this.nodes) {
|
||||
const node = this.nodes[key];
|
||||
|
||||
if (node instanceof HTMLElement) {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current direction is RTL (Right-To-Left)
|
||||
*/
|
||||
protected get isRtl(): boolean {
|
||||
return this.config.i18n.direction === 'rtl';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,11 +84,6 @@ export default class MoveDownTune implements BlockTune {
|
|||
}
|
||||
|
||||
const nextBlock = this.api.blocks.getBlockByIndex(currentBlockIndex + 1);
|
||||
|
||||
if (!nextBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextBlockElement = nextBlock.holder;
|
||||
const nextBlockCoords = nextBlockElement.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -82,13 +82,8 @@ export default class MoveUpTune implements BlockTune {
|
|||
}
|
||||
|
||||
const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
|
||||
const previousBlock = this.api.blocks.getBlockByIndex(currentBlockIndex - 1);
|
||||
|
||||
if (!currentBlock || !previousBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentBlockElement = currentBlock.holder;
|
||||
const previousBlock = this.api.blocks.getBlockByIndex(currentBlockIndex - 1);
|
||||
const previousBlockElement = previousBlock.holder;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Block from './index';
|
||||
import { BlockToolData, ToolConfig } from '../../../types/tools';
|
||||
import { SavedData } from '../../../types/data-formats';
|
||||
import { SavedData } from '../../types-internal/block-data';
|
||||
import { BlockAPI as BlockAPIInterface } from '../../../types/api';
|
||||
|
||||
/**
|
||||
|
@ -10,9 +10,7 @@ import { BlockAPI as BlockAPIInterface } from '../../../types/api';
|
|||
*
|
||||
* @param {Block} block - Block to expose
|
||||
*/
|
||||
function BlockAPI(
|
||||
block: Block
|
||||
): void {
|
||||
function BlockAPI(block: Block): void {
|
||||
const blockAPI: BlockAPIInterface = {
|
||||
/**
|
||||
* Tool name
|
||||
|
@ -83,10 +81,10 @@ function BlockAPI(
|
|||
* @param {string} methodName - method to call
|
||||
* @param {object} param - object with parameters
|
||||
*
|
||||
* @returns {unknown}
|
||||
* @returns {void}
|
||||
*/
|
||||
call(methodName: string, param?: object): unknown {
|
||||
return block.call(methodName, param);
|
||||
call(methodName: string, param?: object): void {
|
||||
block.call(methodName, param);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,10 +10,11 @@ import {
|
|||
ToolSettings
|
||||
} from '../../../types';
|
||||
|
||||
import { SavedData } from '../../../types/data-formats';
|
||||
import { SavedData } from '../../types-internal/block-data';
|
||||
import $ from '../dom';
|
||||
import * as _ from '../utils';
|
||||
import ApiModules from '../modules/api';
|
||||
import ApiModule from '../modules/api';
|
||||
import SelectionUtils from '../selection';
|
||||
import BlockAPI from './api';
|
||||
import { ToolType } from '../modules/tools';
|
||||
|
||||
|
@ -21,7 +22,6 @@ import { ToolType } from '../modules/tools';
|
|||
import MoveUpTune from '../block-tunes/block-tune-move-up';
|
||||
import DeleteTune from '../block-tunes/block-tune-delete';
|
||||
import MoveDownTune from '../block-tunes/block-tune-move-down';
|
||||
import SelectionUtils from '../selection';
|
||||
|
||||
/**
|
||||
* Interface describes Block class constructor argument
|
||||
|
@ -50,12 +50,7 @@ interface BlockConstructorOptions {
|
|||
/**
|
||||
* Editor's API methods
|
||||
*/
|
||||
api: ApiModules;
|
||||
|
||||
/**
|
||||
* This flag indicates that the Block should be constructed in the read-only mode.
|
||||
*/
|
||||
readOnly: boolean;
|
||||
api: ApiModule;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +147,7 @@ export default class Block {
|
|||
/**
|
||||
* Editor`s API module
|
||||
*/
|
||||
private readonly api: ApiModules;
|
||||
private readonly api: ApiModule;
|
||||
|
||||
/**
|
||||
* Focused input index
|
||||
|
@ -203,8 +198,7 @@ export default class Block {
|
|||
* @param {BlockToolData} options.data - Tool's initial data
|
||||
* @param {BlockToolConstructable} options.Tool — Tool's class
|
||||
* @param {ToolSettings} options.settings - default tool's config
|
||||
* @param {Module} options.api - Editor API module for pass it to the Block Tunes
|
||||
* @param {boolean} options.readOnly - Read-Only flag
|
||||
* @param {ApiModule} options.api - Editor API module for pass it to the Block Tunes
|
||||
*/
|
||||
constructor({
|
||||
name,
|
||||
|
@ -212,7 +206,6 @@ export default class Block {
|
|||
Tool,
|
||||
settings,
|
||||
api,
|
||||
readOnly,
|
||||
}: BlockConstructorOptions) {
|
||||
this.name = name;
|
||||
this.class = Tool;
|
||||
|
@ -228,7 +221,6 @@ export default class Block {
|
|||
config: this.config,
|
||||
api: this.api.getMethodsForTool(name, ToolType.Block),
|
||||
block: this.blockAPI,
|
||||
readOnly: readOnly,
|
||||
});
|
||||
|
||||
this.holder = this.compose();
|
||||
|
@ -629,15 +621,7 @@ export default class Block {
|
|||
* Update current input index with selection anchor node
|
||||
*/
|
||||
public updateCurrentInput(): void {
|
||||
/**
|
||||
* If activeElement is native input, anchorNode points to its parent.
|
||||
* So if it is native input use it instead of anchorNode
|
||||
*
|
||||
* If anchorNode is undefined, also use activeElement
|
||||
*/
|
||||
this.currentInput = $.isNativeInput(document.activeElement) || !SelectionUtils.anchorNode
|
||||
? document.activeElement
|
||||
: SelectionUtils.anchorNode;
|
||||
this.currentInput = SelectionUtils.anchorNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -656,12 +640,6 @@ export default class Block {
|
|||
attributes: true,
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Mutation observer doesn't track changes in "<input>" and "<textarea>"
|
||||
* so we need to track focus events
|
||||
*/
|
||||
this.addInputEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -669,7 +647,6 @@ export default class Block {
|
|||
*/
|
||||
public willUnselect(): void {
|
||||
this.mutationObserver.disconnect();
|
||||
this.removeInputEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -687,37 +664,4 @@ export default class Block {
|
|||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is fired when text input or contentEditable is focused
|
||||
*/
|
||||
private handleFocus = (): void => {
|
||||
/**
|
||||
* Drop cache
|
||||
*/
|
||||
this.cachedInputs = [];
|
||||
|
||||
/**
|
||||
* Update current input
|
||||
*/
|
||||
this.updateCurrentInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds focus event listeners to all inputs and contentEditables
|
||||
*/
|
||||
private addInputEvents(): void {
|
||||
this.inputs.forEach(input => {
|
||||
input.addEventListener('focus', this.handleFocus);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* removes focus event listeners from all inputs and contentEditables
|
||||
*/
|
||||
private removeInputEvents(): void {
|
||||
this.inputs.forEach(input => {
|
||||
input.removeEventListener('focus', this.handleFocus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import $ from './dom';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import * as _ from './utils';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import { LogLevels } from './utils';
|
||||
import { EditorConfig, OutputData, SanitizerConfig } from '../../types';
|
||||
import { EditorModules } from '../types-internal/editor-modules';
|
||||
import I18n from './i18n';
|
||||
import { CriticalError } from './errors/critical';
|
||||
|
||||
/**
|
||||
* @typedef {Core} Core - editor core class
|
||||
|
@ -128,10 +130,11 @@ export default class Core {
|
|||
/**
|
||||
* If holderId is preset, assign him to holder property and work next only with holder
|
||||
*/
|
||||
_.deprecationAssert(!!config.holderId, 'config.holderId', 'config.holder');
|
||||
if (config.holderId && !config.holder) {
|
||||
config.holder = config.holderId;
|
||||
config.holderId = null;
|
||||
_.log('holderId property is deprecated and will be removed in the next major release. ' +
|
||||
'Use holder property instead.', 'warn');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,16 +152,15 @@ export default class Core {
|
|||
}
|
||||
|
||||
if (!this.config.logLevel) {
|
||||
this.config.logLevel = _.LogLevels.VERBOSE;
|
||||
this.config.logLevel = LogLevels.VERBOSE;
|
||||
}
|
||||
|
||||
_.setLogLevel(this.config.logLevel);
|
||||
|
||||
/**
|
||||
* If default Block's Tool was not passed, use the Paragraph Tool
|
||||
* If initial Block's Tool was not passed, use the Paragraph Tool
|
||||
*/
|
||||
_.deprecationAssert(Boolean(this.config.initialBlock), 'config.initialBlock', 'config.defaultBlock');
|
||||
this.config.defaultBlock = this.config.defaultBlock || this.config.initialBlock || 'paragraph';
|
||||
this.config.initialBlock = this.config.initialBlock || 'paragraph';
|
||||
|
||||
/**
|
||||
* Height of Editor's bottom area that allows to set focus on the last Block
|
||||
|
@ -168,13 +170,13 @@ export default class Core {
|
|||
this.config.minHeight = this.config.minHeight !== undefined ? this.config.minHeight : 300;
|
||||
|
||||
/**
|
||||
* Default block type
|
||||
* Initial block type
|
||||
* Uses in case when there is no blocks passed
|
||||
*
|
||||
* @type {{type: (*), data: {text: null}}}
|
||||
*/
|
||||
const defaultBlockData = {
|
||||
type: this.config.defaultBlock,
|
||||
const initialBlockData = {
|
||||
type: this.config.initialBlock,
|
||||
data: {},
|
||||
};
|
||||
|
||||
|
@ -187,44 +189,30 @@ export default class Core {
|
|||
|
||||
this.config.hideToolbar = this.config.hideToolbar ? this.config.hideToolbar : false;
|
||||
this.config.tools = this.config.tools || {};
|
||||
this.config.i18n = this.config.i18n || {};
|
||||
this.config.data = this.config.data || {} as OutputData;
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
this.config.onReady = this.config.onReady || ((): void => {});
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
this.config.onChange = this.config.onChange || ((): void => {});
|
||||
this.config.inlineToolbar = this.config.inlineToolbar !== undefined ? this.config.inlineToolbar : true;
|
||||
|
||||
/**
|
||||
* Initialize default Block to pass data to the Renderer
|
||||
* Initialize Blocks to pass data to the Renderer
|
||||
*/
|
||||
if (_.isEmpty(this.config.data)) {
|
||||
this.config.data = {} as OutputData;
|
||||
this.config.data.blocks = [ defaultBlockData ];
|
||||
this.config.data.blocks = [ initialBlockData ];
|
||||
} else {
|
||||
if (!this.config.data.blocks || this.config.data.blocks.length === 0) {
|
||||
this.config.data.blocks = [ defaultBlockData ];
|
||||
this.config.data.blocks = [ initialBlockData ];
|
||||
}
|
||||
}
|
||||
|
||||
this.config.readOnly = this.config.readOnly as boolean || false;
|
||||
this.config.i18n = {};
|
||||
|
||||
/**
|
||||
* Adjust i18n
|
||||
*/
|
||||
if (config.i18n?.messages) {
|
||||
if (config.i18n && config.i18n.messages) {
|
||||
I18n.setDictionary(config.i18n.messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Text direction. If not set, uses ltr
|
||||
*/
|
||||
if (config.i18n?.direction) {
|
||||
this.config.i18n = {
|
||||
direction: config.i18n?.direction || 'ltr',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,16 +276,12 @@ export default class Core {
|
|||
const modulesToPrepare = [
|
||||
'Tools',
|
||||
'UI',
|
||||
'Toolbar',
|
||||
'InlineToolbar',
|
||||
'BlockManager',
|
||||
'Paste',
|
||||
'DragNDrop',
|
||||
'ModificationsObserver',
|
||||
'BlockSelection',
|
||||
'RectangleSelection',
|
||||
'CrossBlockSelection',
|
||||
'ReadOnly',
|
||||
];
|
||||
|
||||
await modulesToPrepare.reduce(
|
||||
|
@ -307,13 +291,6 @@ export default class Core {
|
|||
try {
|
||||
await this.moduleInstances[module].prepare();
|
||||
} catch (e) {
|
||||
/**
|
||||
* CriticalError's will not be caught
|
||||
* It is used when Editor is rendering in read-only mode with unsupported plugin
|
||||
*/
|
||||
if (e instanceof CriticalError) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
_.log(`Module ${module} was skipped because of %o`, 'warn', e);
|
||||
}
|
||||
// _.log(`Preparing ${module} module`, 'timeEnd');
|
||||
|
|
|
@ -296,7 +296,7 @@ export default class Dom {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if object is DocumentFragment node
|
||||
* Check if object is DocumentFragmemt node
|
||||
*
|
||||
* @param {object} node - object to check
|
||||
* @returns {boolean}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
/**
|
||||
* This type of exception will destroy the Editor! Be careful when using it
|
||||
*/
|
||||
export class CriticalError extends Error {
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import defaultDictionary from './locales/en/messages.json';
|
||||
import * as _ from '../utils';
|
||||
import { I18nDictionary, Dictionary } from '../../../types/configs';
|
||||
import { LeavesDictKeys } from '../../types-internal/i18n-internal-namespace';
|
||||
|
||||
|
@ -59,12 +60,9 @@ export default class I18n {
|
|||
private static _t(namespace: string, dictKey: string): string {
|
||||
const section = I18n.getNamespace(namespace);
|
||||
|
||||
/**
|
||||
* For Console Message to Check Section is defined or not
|
||||
* if (section === undefined) {
|
||||
* _.logLabeled('I18n: section %o was not found in current dictionary', 'log', namespace);
|
||||
* }
|
||||
*/
|
||||
if (section === undefined) {
|
||||
_.logLabeled('I18n: section %o was not found in current dictionary', 'log', namespace);
|
||||
}
|
||||
|
||||
if (!section || !section[dictKey]) {
|
||||
return dictKey;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import Module from '../../__module';
|
||||
|
||||
import { BlockAPI as BlockAPIInterface, Blocks } from '../../../../types/api';
|
||||
import { BlockToolData, OutputData, ToolConfig } from '../../../../types';
|
||||
import * as _ from './../../utils';
|
||||
import BlockAPI from '../../block/api';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class BlocksAPI
|
||||
|
@ -22,7 +23,7 @@ export default class BlocksAPI extends Module {
|
|||
delete: (index?: number): void => this.delete(index),
|
||||
swap: (fromIndex: number, toIndex: number): void => this.swap(fromIndex, toIndex),
|
||||
move: (toIndex: number, fromIndex?: number): void => this.move(toIndex, fromIndex),
|
||||
getBlockByIndex: (index: number): BlockAPIInterface | void => this.getBlockByIndex(index),
|
||||
getBlockByIndex: (index: number): BlockAPIInterface => this.getBlockByIndex(index),
|
||||
getCurrentBlockIndex: (): number => this.getCurrentBlockIndex(),
|
||||
getBlocksCount: (): number => this.getBlocksCount(),
|
||||
stretchBlock: (index: number, status = true): void => this.stretchBlock(index, status),
|
||||
|
@ -50,19 +51,15 @@ export default class BlocksAPI extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns BlockAPI object by Block index
|
||||
* Returns Block holder by Block index
|
||||
*
|
||||
* @param {number} index - index to get
|
||||
*
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
public getBlockByIndex(index: number): BlockAPIInterface | void {
|
||||
public getBlockByIndex(index: number): BlockAPIInterface {
|
||||
const block = this.Editor.BlockManager.getBlockByIndex(index);
|
||||
|
||||
if (block === undefined) {
|
||||
_.log('There is no block at index `' + index + '`', 'warn');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return new BlockAPI(block);
|
||||
}
|
||||
|
||||
|
@ -121,7 +118,7 @@ export default class BlocksAPI extends Module {
|
|||
|
||||
/**
|
||||
* in case of last block deletion
|
||||
* Insert the new default empty block
|
||||
* Insert new initial empty block
|
||||
*/
|
||||
if (this.Editor.BlockManager.blocks.length === 0) {
|
||||
this.Editor.BlockManager.insert();
|
||||
|
@ -130,9 +127,7 @@ export default class BlocksAPI extends Module {
|
|||
/**
|
||||
* After Block deletion currentBlock is updated
|
||||
*/
|
||||
if (this.Editor.BlockManager.currentBlock) {
|
||||
this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END);
|
||||
}
|
||||
this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END);
|
||||
|
||||
this.Editor.Toolbar.close();
|
||||
}
|
||||
|
@ -177,10 +172,10 @@ export default class BlocksAPI extends Module {
|
|||
* @deprecated Use BlockAPI interface to stretch Blocks
|
||||
*/
|
||||
public stretchBlock(index: number, status = true): void {
|
||||
_.deprecationAssert(
|
||||
true,
|
||||
'blocks.stretchBlock()',
|
||||
'BlockAPI'
|
||||
_.log(
|
||||
'`blocks.stretchBlock()` method is deprecated and will be removed in the next major release. ' +
|
||||
'Use BlockAPI interface instead',
|
||||
'warn'
|
||||
);
|
||||
|
||||
const block = this.Editor.BlockManager.getBlockByIndex(index);
|
||||
|
@ -202,7 +197,7 @@ export default class BlocksAPI extends Module {
|
|||
* @param {boolean?} needToFocus - flag to focus inserted Block
|
||||
*/
|
||||
public insert = (
|
||||
type: string = this.config.defaultBlock,
|
||||
type: string = this.config.initialBlock,
|
||||
data: BlockToolData = {},
|
||||
config: ToolConfig = {},
|
||||
index?: number,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Caret } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
import { Caret } from '../../../../types/api';
|
||||
|
||||
/**
|
||||
* @class CaretAPI
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Events } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
import { Events } from '../../../../types/api';
|
||||
|
||||
/**
|
||||
* @class EventsAPI
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Module from '../../__module';
|
||||
import { I18n } from '../../../../types/api';
|
||||
import I18nInternal from '../../i18n';
|
||||
import { ToolType } from '../tools';
|
||||
import { logLabeled } from '../../utils';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* Provides methods for working with i18n
|
||||
|
|
|
@ -31,8 +31,7 @@ export default class API extends Module {
|
|||
inlineToolbar: this.Editor.InlineToolbarAPI.methods,
|
||||
tooltip: this.Editor.TooltipAPI.methods,
|
||||
i18n: this.Editor.I18nAPI.methods,
|
||||
readOnly: this.Editor.ReadOnlyAPI.methods,
|
||||
};
|
||||
} as APIInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { InlineToolbar } from '../../../../types/api/inline-toolbar';
|
||||
import Module from '../../__module';
|
||||
import { InlineToolbar } from '../../../../types/api/inline-toolbar';
|
||||
|
||||
/**
|
||||
* @class InlineToolbarAPI
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Listeners } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
import { Listeners } from '../../../../types/api';
|
||||
|
||||
/**
|
||||
* @class ListenersAPI
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Module from '../../__module';
|
||||
import { Notifier } from '../../../../types/api';
|
||||
import { ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions } from 'codex-notifier';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import { ReadOnly } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class ReadOnlyAPI
|
||||
* @classdesc ReadOnly API
|
||||
*/
|
||||
export default class ReadOnlyAPI extends Module {
|
||||
/**
|
||||
* Available methods
|
||||
*/
|
||||
public get methods(): ReadOnly {
|
||||
return {
|
||||
toggle: (state): Promise<boolean> => this.toggle(state),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or toggle read-only state
|
||||
*
|
||||
* @param {boolean|undefined} state - set or toggle state
|
||||
*
|
||||
* @returns {boolean} current value
|
||||
*/
|
||||
public toggle(state?: boolean): Promise<boolean> {
|
||||
return this.Editor.ReadOnly.toggle(state);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import Module from '../../__module';
|
||||
import { Sanitizer } from '../../../../types/api';
|
||||
import { SanitizerConfig } from '../../../../types/configs';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class SanitizerAPI
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Module from '../../__module';
|
||||
import { Saver } from '../../../../types/api';
|
||||
import { OutputData } from '../../../../types';
|
||||
import * as _ from '../../utils';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class SaverAPI
|
||||
|
@ -21,18 +20,8 @@ export default class SaverAPI extends Module {
|
|||
|
||||
/**
|
||||
* Return Editor's data
|
||||
*
|
||||
* @returns {OutputData}
|
||||
*/
|
||||
public save(): Promise<OutputData> {
|
||||
const errorText = 'Editor\'s content can not be saved in read-only mode';
|
||||
|
||||
if (this.Editor.ReadOnly.isEnabled) {
|
||||
_.logLabeled(errorText, 'warn');
|
||||
|
||||
return Promise.reject(new Error(errorText));
|
||||
}
|
||||
|
||||
return this.Editor.Saver.save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Module from '../../__module';
|
||||
import SelectionUtils from '../../selection';
|
||||
import { Selection as SelectionAPIInterface } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class SelectionAPI
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Styles } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
import { Styles } from '../../../../types/api';
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Toolbar } from '../../../../types/api';
|
||||
import Module from '../../__module';
|
||||
import { Toolbar } from '../../../../types/api';
|
||||
|
||||
/**
|
||||
* @class ToolbarAPI
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Module from '../../__module';
|
||||
import { Tooltip } from '../../../../types/api';
|
||||
import { TooltipContent, TooltipOptions } from 'codex-tooltip';
|
||||
import Module from '../../__module';
|
||||
|
||||
/**
|
||||
* @class TooltipAPI
|
||||
|
|
|
@ -93,7 +93,7 @@ export default class BlockEvents extends Module {
|
|||
*
|
||||
* @param {KeyboardEvent} event - keyup event
|
||||
*/
|
||||
public keyup(event: KeyboardEvent): void {
|
||||
public keyup(event): void {
|
||||
/**
|
||||
* If shift key was pressed some special shortcut is used (eg. cross block selection via shift + arrows)
|
||||
*/
|
||||
|
@ -107,6 +107,21 @@ export default class BlockEvents extends Module {
|
|||
this.Editor.UI.checkEmptiness();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up mouse selection handlers
|
||||
*
|
||||
* @param {MouseEvent} event - mouse down event
|
||||
*/
|
||||
public mouseDown(event: MouseEvent): void {
|
||||
/**
|
||||
* Each mouse down on Block must disable selectAll state
|
||||
*/
|
||||
if (!SelectionUtils.isCollapsed) {
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
this.Editor.CrossBlockSelection.watchSelection(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Toolbox to leaf Tools
|
||||
*
|
||||
|
@ -125,12 +140,12 @@ export default class BlockEvents extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
const canOpenToolbox = Tools.isDefault(currentBlock.tool) && currentBlock.isEmpty;
|
||||
const canOpenToolbox = Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty;
|
||||
const conversionToolbarOpened = !currentBlock.isEmpty && ConversionToolbar.opened;
|
||||
const inlineToolbarOpened = !currentBlock.isEmpty && !SelectionUtils.isCollapsed && InlineToolbar.opened;
|
||||
|
||||
/**
|
||||
* For empty Blocks we show Plus button via Toolbox only for default Blocks
|
||||
* For empty Blocks we show Plus button via Toolbox only for initial Blocks
|
||||
*/
|
||||
if (canOpenToolbox) {
|
||||
this.activateToolbox();
|
||||
|
@ -142,10 +157,10 @@ export default class BlockEvents extends Module {
|
|||
/**
|
||||
* Add drop target styles
|
||||
*
|
||||
* @param {DragEvent} event - drag over event
|
||||
* @param {DragEvent} e - drag over event
|
||||
*/
|
||||
public dragOver(event: DragEvent): void {
|
||||
const block = this.Editor.BlockManager.getBlockByChildNode(event.target as Node);
|
||||
public dragOver(e: DragEvent): void {
|
||||
const block = this.Editor.BlockManager.getBlockByChildNode(e.target as Node);
|
||||
|
||||
block.dropTarget = true;
|
||||
}
|
||||
|
@ -153,10 +168,10 @@ export default class BlockEvents extends Module {
|
|||
/**
|
||||
* Remove drop target style
|
||||
*
|
||||
* @param {DragEvent} event - drag leave event
|
||||
* @param {DragEvent} e - drag leave event
|
||||
*/
|
||||
public dragLeave(event: DragEvent): void {
|
||||
const block = this.Editor.BlockManager.getBlockByChildNode(event.target as Node);
|
||||
public dragLeave(e: DragEvent): void {
|
||||
const block = this.Editor.BlockManager.getBlockByChildNode(e.target as Node);
|
||||
|
||||
block.dropTarget = false;
|
||||
}
|
||||
|
@ -194,7 +209,7 @@ export default class BlockEvents extends Module {
|
|||
|
||||
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
|
||||
|
||||
Caret.setToBlock(BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||
|
||||
/** Clear selection */
|
||||
BlockSelection.clearSelection(event);
|
||||
|
@ -239,7 +254,7 @@ export default class BlockEvents extends Module {
|
|||
* If enter has been pressed at the start of the text, just insert paragraph Block above
|
||||
*/
|
||||
if (this.Editor.Caret.isAtStart && !this.Editor.BlockManager.currentBlock.hasMedia) {
|
||||
this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex);
|
||||
this.Editor.BlockManager.insertInitialBlockAtIndex(this.Editor.BlockManager.currentBlockIndex);
|
||||
} else {
|
||||
/**
|
||||
* Split the Current Block into two blocks
|
||||
|
@ -253,7 +268,7 @@ export default class BlockEvents extends Module {
|
|||
/**
|
||||
* If new Block is empty
|
||||
*/
|
||||
if (this.Editor.Tools.isDefault(newCurrent.tool) && newCurrent.isEmpty) {
|
||||
if (this.Editor.Tools.isInitial(newCurrent.tool) && newCurrent.isEmpty) {
|
||||
/**
|
||||
* Show Toolbar
|
||||
*/
|
||||
|
@ -411,10 +426,7 @@ export default class BlockEvents extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
const navigateNext = event.keyCode === _.keyCodes.DOWN || (event.keyCode === _.keyCodes.RIGHT && !this.isRtl);
|
||||
const isNavigated = navigateNext ? this.Editor.Caret.navigateNext() : this.Editor.Caret.navigatePrevious();
|
||||
|
||||
if (isNavigated) {
|
||||
if (this.Editor.Caret.navigateNext()) {
|
||||
/**
|
||||
* Default behaviour moves cursor by 1 character, we need to prevent it
|
||||
*/
|
||||
|
@ -469,10 +481,7 @@ export default class BlockEvents extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
const navigatePrevious = event.keyCode === _.keyCodes.UP || (event.keyCode === _.keyCodes.LEFT && !this.isRtl);
|
||||
const isNavigated = navigatePrevious ? this.Editor.Caret.navigatePrevious() : this.Editor.Caret.navigateNext();
|
||||
|
||||
if (isNavigated) {
|
||||
if (this.Editor.Caret.navigatePrevious()) {
|
||||
/**
|
||||
* Default behaviour moves cursor by 1 character, we need to prevent it
|
||||
*/
|
||||
|
|
|
@ -160,9 +160,12 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Should be called after Editor.UI preparation
|
||||
* Define this._blocks property
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
public prepare(): void {
|
||||
public async prepare(): Promise<void> {
|
||||
const blocks = new Blocks(this.Editor.UI.nodes.redactor);
|
||||
const { BlockEvents, Listeners } = this.Editor;
|
||||
|
||||
/**
|
||||
* We need to use Proxy to overload set/get [] operator.
|
||||
|
@ -184,30 +187,18 @@ export default class BlockManager extends Module {
|
|||
});
|
||||
|
||||
/** Copy event */
|
||||
this.Editor.Listeners.on(
|
||||
Listeners.on(
|
||||
document,
|
||||
'copy',
|
||||
(e: ClipboardEvent) => this.Editor.BlockEvents.handleCommandC(e)
|
||||
(e: ClipboardEvent) => BlockEvents.handleCommandC(e)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle read-only state
|
||||
*
|
||||
* If readOnly is true:
|
||||
* - Unbind event handlers from created Blocks
|
||||
*
|
||||
* if readOnly is false:
|
||||
* - Bind event handlers to all existing Blocks
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - "read only" state
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (!readOnlyEnabled) {
|
||||
this.enableModuleBindings();
|
||||
} else {
|
||||
this.disableModuleBindings();
|
||||
}
|
||||
/** Copy and cut */
|
||||
Listeners.on(
|
||||
document,
|
||||
'cut',
|
||||
(e: ClipboardEvent) => BlockEvents.handleCommandX(e)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,7 +211,6 @@ export default class BlockManager extends Module {
|
|||
* @returns {Block}
|
||||
*/
|
||||
public composeBlock({ tool, data = {} }: {tool: string; data?: BlockToolData}): Block {
|
||||
const readOnly = this.Editor.ReadOnly.isEnabled;
|
||||
const settings = this.Editor.Tools.getToolSettings(tool);
|
||||
const Tool = this.Editor.Tools.available[tool] as BlockToolConstructable;
|
||||
const block = new Block({
|
||||
|
@ -229,12 +219,9 @@ export default class BlockManager extends Module {
|
|||
Tool,
|
||||
settings,
|
||||
api: this.Editor.API,
|
||||
readOnly,
|
||||
});
|
||||
|
||||
if (!readOnly) {
|
||||
this.bindBlockEvents(block);
|
||||
}
|
||||
this.bindEvents(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
@ -243,7 +230,7 @@ export default class BlockManager extends Module {
|
|||
* Insert new block into _blocks
|
||||
*
|
||||
* @param {object} options - insert options
|
||||
* @param {string} options.tool - plugin name, by default method inserts the default block type
|
||||
* @param {string} options.tool - plugin name, by default method inserts initial block type
|
||||
* @param {object} options.data - plugin data
|
||||
* @param {number} options.index - index where to insert new Block
|
||||
* @param {boolean} options.needToFocus - flag shows if needed to update current Block index
|
||||
|
@ -252,7 +239,7 @@ export default class BlockManager extends Module {
|
|||
* @returns {Block}
|
||||
*/
|
||||
public insert({
|
||||
tool = this.config.defaultBlock,
|
||||
tool = this.config.initialBlock,
|
||||
data = {},
|
||||
index,
|
||||
needToFocus = true,
|
||||
|
@ -296,7 +283,7 @@ export default class BlockManager extends Module {
|
|||
* @returns {Block}
|
||||
*/
|
||||
public replace({
|
||||
tool = this.config.defaultBlock,
|
||||
tool = this.config.initialBlock,
|
||||
data = {},
|
||||
}): Block {
|
||||
return this.insert({
|
||||
|
@ -334,7 +321,7 @@ export default class BlockManager extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* Insert new default block at passed index
|
||||
* Insert new initial block at passed index
|
||||
*
|
||||
* @param {number} index - index where Block should be inserted
|
||||
* @param {boolean} needToFocus - if true, updates current Block index
|
||||
|
@ -343,8 +330,8 @@ export default class BlockManager extends Module {
|
|||
*
|
||||
* @returns {Block} inserted Block
|
||||
*/
|
||||
public insertDefaultBlockAtIndex(index: number, needToFocus = false): Block {
|
||||
const block = this.composeBlock({ tool: this.config.defaultBlock });
|
||||
public insertInitialBlockAtIndex(index: number, needToFocus = false): Block {
|
||||
const block = this.composeBlock({ tool: this.config.initialBlock });
|
||||
|
||||
this._blocks[index] = block;
|
||||
|
||||
|
@ -369,7 +356,7 @@ export default class BlockManager extends Module {
|
|||
this.currentBlockIndex = this.blocks.length - 1;
|
||||
|
||||
/**
|
||||
* Insert the default typed block
|
||||
* Insert initial typed block
|
||||
*/
|
||||
return this.insert();
|
||||
}
|
||||
|
@ -456,7 +443,7 @@ export default class BlockManager extends Module {
|
|||
|
||||
/**
|
||||
* Attention!
|
||||
* After removing insert the new default typed Block and focus on it
|
||||
* After removing insert new initial typed Block and focus on it
|
||||
* Removes all blocks
|
||||
*/
|
||||
public removeAllBlocks(): void {
|
||||
|
@ -581,11 +568,6 @@ export default class BlockManager extends Module {
|
|||
*/
|
||||
this.currentBlockIndex = this._blocks.nodes.indexOf(parentFirstLevelBlock as HTMLElement);
|
||||
|
||||
/**
|
||||
* Update current block active input
|
||||
*/
|
||||
this.currentBlock.updateCurrentInput();
|
||||
|
||||
return this.currentBlock;
|
||||
} else {
|
||||
throw new Error('Can not find a Block from this child Node');
|
||||
|
@ -667,15 +649,15 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Clears Editor
|
||||
*
|
||||
* @param {boolean} needToAddDefaultBlock - 1) in internal calls (for example, in api.blocks.render)
|
||||
* we don't need to add an empty default block
|
||||
* @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render)
|
||||
* we don't need to add empty initial block
|
||||
* 2) in api.blocks.clear we should add empty block
|
||||
*/
|
||||
public clear(needToAddDefaultBlock = false): void {
|
||||
public clear(needAddInitialBlock = false): void {
|
||||
this._blocks.removeAll();
|
||||
this.dropPointer();
|
||||
|
||||
if (needToAddDefaultBlock) {
|
||||
if (needAddInitialBlock) {
|
||||
this.insert();
|
||||
}
|
||||
|
||||
|
@ -686,63 +668,18 @@ export default class BlockManager extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* Cleans up all the block tools' resources
|
||||
* This is called when editor is destroyed
|
||||
*/
|
||||
public async destroy(): Promise<void> {
|
||||
await Promise.all(this.blocks.map((block) => {
|
||||
if (_.isFunction(block.tool.destroy)) {
|
||||
return block.tool.destroy();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind Block events
|
||||
* Bind Events
|
||||
*
|
||||
* @param {Block} block - Block to which event should be bound
|
||||
*/
|
||||
private bindBlockEvents(block: Block): void {
|
||||
const { BlockEvents } = this.Editor;
|
||||
private bindEvents(block: Block): void {
|
||||
const { BlockEvents, Listeners } = this.Editor;
|
||||
|
||||
this.readOnlyMutableListeners.on(block.holder, 'keydown', (event: KeyboardEvent) => {
|
||||
BlockEvents.keydown(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(block.holder, 'keyup', (event: KeyboardEvent) => {
|
||||
BlockEvents.keyup(event);
|
||||
});
|
||||
|
||||
this.readOnlyMutableListeners.on(block.holder, 'dragover', (event: DragEvent) => {
|
||||
BlockEvents.dragOver(event);
|
||||
});
|
||||
|
||||
this.readOnlyMutableListeners.on(block.holder, 'dragleave', (event: DragEvent) => {
|
||||
BlockEvents.dragLeave(event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable mutable handlers and bindings
|
||||
*/
|
||||
private disableModuleBindings(): void {
|
||||
this.readOnlyMutableListeners.clearAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables all module handlers and bindings for all Blocks
|
||||
*/
|
||||
private enableModuleBindings(): void {
|
||||
/** Cut event */
|
||||
this.readOnlyMutableListeners.on(
|
||||
document,
|
||||
'cut',
|
||||
(e: ClipboardEvent) => this.Editor.BlockEvents.handleCommandX(e)
|
||||
);
|
||||
|
||||
this.blocks.forEach((block: Block) => {
|
||||
this.bindBlockEvents(block);
|
||||
});
|
||||
Listeners.on(block.holder, 'keydown', (event) => BlockEvents.keydown(event as KeyboardEvent), false);
|
||||
Listeners.on(block.holder, 'mousedown', (event: MouseEvent) => BlockEvents.mouseDown(event));
|
||||
Listeners.on(block.holder, 'keyup', (event) => BlockEvents.keyup(event));
|
||||
Listeners.on(block.holder, 'dragover', (event) => BlockEvents.dragOver(event as DragEvent));
|
||||
Listeners.on(block.holder, 'dragleave', (event) => BlockEvents.dragLeave(event as DragEvent));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,15 +17,6 @@ import { SanitizerConfig } from '../../../types/configs';
|
|||
*
|
||||
*/
|
||||
export default class BlockSelection extends Module {
|
||||
/**
|
||||
* Sometimes .anyBlockSelected can be called frequently,
|
||||
* for example at ui@selectionChange (to clear native browser selection in CBS)
|
||||
* We use cache to prevent multiple iterations through all the blocks
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private anyBlockSelectedCache: boolean | null = null;
|
||||
|
||||
/**
|
||||
* Sanitizer Config
|
||||
*
|
||||
|
@ -80,8 +71,6 @@ export default class BlockSelection extends Module {
|
|||
BlockManager.blocks.forEach((block) => {
|
||||
block.selected = state;
|
||||
});
|
||||
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,11 +81,7 @@ export default class BlockSelection extends Module {
|
|||
public get anyBlockSelected(): boolean {
|
||||
const { BlockManager } = this.Editor;
|
||||
|
||||
if (this.anyBlockSelectedCache === null) {
|
||||
this.anyBlockSelectedCache = BlockManager.blocks.some((block) => block.selected === true);
|
||||
}
|
||||
|
||||
return this.anyBlockSelectedCache;
|
||||
return BlockManager.blocks.some((block) => block.selected === true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,30 +132,15 @@ export default class BlockSelection extends Module {
|
|||
public prepare(): void {
|
||||
const { Shortcuts } = this.Editor;
|
||||
|
||||
this.selection = new SelectionUtils();
|
||||
|
||||
/**
|
||||
* CMD/CTRL+A selection shortcut
|
||||
*/
|
||||
/** Selection shortcut */
|
||||
Shortcuts.add({
|
||||
name: 'CMD+A',
|
||||
handler: (event) => {
|
||||
const { BlockManager, ReadOnly } = this.Editor;
|
||||
|
||||
/**
|
||||
* We use Editor's Block selection on CMD+A ShortCut instead of Browsers
|
||||
*/
|
||||
if (ReadOnly.isEnabled) {
|
||||
event.preventDefault();
|
||||
this.selectAllBlocks();
|
||||
|
||||
return;
|
||||
}
|
||||
const { BlockManager } = this.Editor;
|
||||
|
||||
/**
|
||||
* When one page consist of two or more EditorJS instances
|
||||
* Shortcut module tries to handle all events.
|
||||
* Thats why Editor's selection works inside the target Editor, but
|
||||
* Shortcut module tries to handle all events. Thats why Editor's selection works inside the target Editor, but
|
||||
* for others error occurs because nothing to select.
|
||||
*
|
||||
* Prevent such actions if focus is not inside the Editor
|
||||
|
@ -182,21 +152,8 @@ export default class BlockSelection extends Module {
|
|||
this.handleCommandA(event);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle read-only state
|
||||
*
|
||||
* - Remove all ranges
|
||||
* - Unselect all Blocks
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - "read only" state
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
SelectionUtils.get()
|
||||
.removeAllRanges();
|
||||
|
||||
this.allBlocksSelected = false;
|
||||
this.selection = new SelectionUtils();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -216,8 +173,6 @@ export default class BlockSelection extends Module {
|
|||
}
|
||||
|
||||
block.selected = false;
|
||||
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,18 +198,10 @@ export default class BlockSelection extends Module {
|
|||
if (this.anyBlockSelected && isKeyboard && isPrintableKey && !SelectionUtils.isSelectionExists) {
|
||||
const indexToInsert = BlockManager.removeSelectedBlocks();
|
||||
|
||||
BlockManager.insertDefaultBlockAtIndex(indexToInsert, true);
|
||||
BlockManager.insertInitialBlockAtIndex(indexToInsert, true);
|
||||
Caret.setToBlock(BlockManager.currentBlock);
|
||||
_.delay(() => {
|
||||
const eventKey = (reason as KeyboardEvent).key;
|
||||
|
||||
/**
|
||||
* If event.key length >1 that means key is special (e.g. Enter or Dead or Unidentifier).
|
||||
* So we use empty string
|
||||
*
|
||||
* @see https://developer.mozilla.org/ru/docs/Web/API/KeyboardEvent/key
|
||||
*/
|
||||
Caret.insertContentAtCaretPosition(eventKey.length > 1 ? '' : eventKey);
|
||||
Caret.insertContentAtCaretPosition((reason as KeyboardEvent).key);
|
||||
}, 20)();
|
||||
}
|
||||
|
||||
|
@ -343,19 +290,10 @@ export default class BlockSelection extends Module {
|
|||
|
||||
block.selected = true;
|
||||
|
||||
this.clearCache();
|
||||
|
||||
/** close InlineToolbar when we selected any Block */
|
||||
this.Editor.InlineToolbar.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear anyBlockSelected cache
|
||||
*/
|
||||
public clearCache(): void {
|
||||
this.anyBlockSelectedCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module destruction
|
||||
* De-registers Shortcut CMD+A
|
||||
|
|
|
@ -330,10 +330,10 @@ export default class Caret extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* If last block is empty and it is an defaultBlock, set to that.
|
||||
* If last block is empty and it is an initialBlock, set to that.
|
||||
* Otherwise, append new empty block and set to that
|
||||
*/
|
||||
if (this.Editor.Tools.isDefault(lastBlock.tool) && lastBlock.isEmpty) {
|
||||
if (this.Editor.Tools.isInitial(lastBlock.tool) && lastBlock.isEmpty) {
|
||||
this.setToBlock(lastBlock);
|
||||
} else {
|
||||
const newBlock = this.Editor.BlockManager.insertAtEnd();
|
||||
|
@ -355,30 +355,12 @@ export default class Caret extends Module {
|
|||
selectRange.deleteContents();
|
||||
|
||||
if (currentBlockInput) {
|
||||
if ($.isNativeInput(currentBlockInput)) {
|
||||
/**
|
||||
* If input is native text input we need to use it's value
|
||||
* Text before the caret stays in the input,
|
||||
* while text after the caret is returned as a fragment to be inserted after the block.
|
||||
*/
|
||||
const input = currentBlockInput as HTMLInputElement | HTMLTextAreaElement;
|
||||
const newFragment = document.createDocumentFragment();
|
||||
const range = selectRange.cloneRange();
|
||||
|
||||
const inputRemainingText = input.value.substring(0, input.selectionStart);
|
||||
const fragmentText = input.value.substring(input.selectionStart);
|
||||
range.selectNodeContents(currentBlockInput);
|
||||
range.setStart(selectRange.endContainer, selectRange.endOffset);
|
||||
|
||||
newFragment.textContent = fragmentText;
|
||||
input.value = inputRemainingText;
|
||||
|
||||
return newFragment;
|
||||
} else {
|
||||
const range = selectRange.cloneRange();
|
||||
|
||||
range.selectNodeContents(currentBlockInput);
|
||||
range.setStart(selectRange.endContainer, selectRange.endOffset);
|
||||
|
||||
return range.extractContents();
|
||||
}
|
||||
return range.extractContents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -401,15 +383,15 @@ export default class Caret extends Module {
|
|||
|
||||
if (!nextBlock && !nextInput) {
|
||||
/**
|
||||
* If there is no nextBlock and currentBlock is default, do not navigate
|
||||
* If there is no nextBlock and currentBlock is initial, do not navigate
|
||||
*/
|
||||
if (Tools.isDefault(currentBlock.tool)) {
|
||||
if (Tools.isInitial(currentBlock.tool)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no nextBlock, but currentBlock is not default,
|
||||
* insert new default block at the end and navigate to it
|
||||
* If there is no nextBlock, but currentBlock is not initial,
|
||||
* insert new initial block at the end and navigate to it
|
||||
*/
|
||||
nextBlock = BlockManager.insertAtEnd();
|
||||
}
|
||||
|
@ -523,13 +505,6 @@ export default class Caret extends Module {
|
|||
|
||||
Array.from(wrapper.childNodes).forEach((child: Node) => fragment.appendChild(child));
|
||||
|
||||
/**
|
||||
* If there is no child node, append empty one
|
||||
*/
|
||||
if (fragment.childNodes.length === 0) {
|
||||
fragment.appendChild(new Text(''));
|
||||
}
|
||||
|
||||
const lastChild = fragment.lastChild;
|
||||
|
||||
range.deleteContents();
|
||||
|
|
|
@ -17,19 +17,6 @@ export default class CrossBlockSelection extends Module {
|
|||
*/
|
||||
private lastSelectedBlock: Block;
|
||||
|
||||
/**
|
||||
* Module preparation
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
const { Listeners } = this.Editor;
|
||||
|
||||
Listeners.on(document, 'mousedown', (event: MouseEvent) => {
|
||||
this.enableCrossBlockSelection(event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up listeners
|
||||
*
|
||||
|
@ -64,7 +51,7 @@ export default class CrossBlockSelection extends Module {
|
|||
* @param {boolean} next - if true, toggle next block. Previous otherwise
|
||||
*/
|
||||
public toggleBlockSelectedState(next = true): void {
|
||||
const { BlockManager, BlockSelection } = this.Editor;
|
||||
const { BlockManager } = this.Editor;
|
||||
|
||||
if (!this.lastSelectedBlock) {
|
||||
this.lastSelectedBlock = this.firstSelectedBlock = BlockManager.currentBlock;
|
||||
|
@ -72,8 +59,6 @@ export default class CrossBlockSelection extends Module {
|
|||
|
||||
if (this.firstSelectedBlock === this.lastSelectedBlock) {
|
||||
this.firstSelectedBlock.selected = true;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
SelectionUtils.get().removeAllRanges();
|
||||
}
|
||||
|
||||
|
@ -86,12 +71,8 @@ export default class CrossBlockSelection extends Module {
|
|||
|
||||
if (this.lastSelectedBlock.selected !== nextBlock.selected) {
|
||||
nextBlock.selected = true;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
} else {
|
||||
this.lastSelectedBlock.selected = false;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
}
|
||||
|
||||
this.lastSelectedBlock = nextBlock;
|
||||
|
@ -139,34 +120,6 @@ export default class CrossBlockSelection extends Module {
|
|||
this.firstSelectedBlock = this.lastSelectedBlock = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Cross Block Selection
|
||||
*
|
||||
* @param {MouseEvent} event - mouse down event
|
||||
*/
|
||||
private enableCrossBlockSelection(event: MouseEvent): void {
|
||||
const { UI } = this.Editor;
|
||||
|
||||
/**
|
||||
* Each mouse down on must disable selectAll state
|
||||
*/
|
||||
if (!SelectionUtils.isCollapsed) {
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* If mouse down is performed inside the editor, we should watch CBS
|
||||
*/
|
||||
if (UI.nodes.redactor.contains(event.target as Node)) {
|
||||
this.watchSelection(event);
|
||||
} else {
|
||||
/**
|
||||
* Otherwise, clear selection
|
||||
*/
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse up event handler.
|
||||
* Removes the listeners
|
||||
|
@ -185,7 +138,7 @@ export default class CrossBlockSelection extends Module {
|
|||
* @param {MouseEvent} event - mouse over event
|
||||
*/
|
||||
private onMouseOver = (event: MouseEvent): void => {
|
||||
const { BlockManager, BlockSelection } = this.Editor;
|
||||
const { BlockManager } = this.Editor;
|
||||
|
||||
const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock;
|
||||
const targetBlock = BlockManager.getBlockByChildNode(event.target as Node);
|
||||
|
@ -204,8 +157,6 @@ export default class CrossBlockSelection extends Module {
|
|||
relatedBlock.selected = true;
|
||||
targetBlock.selected = true;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -213,8 +164,6 @@ export default class CrossBlockSelection extends Module {
|
|||
relatedBlock.selected = false;
|
||||
targetBlock.selected = false;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -231,7 +180,7 @@ export default class CrossBlockSelection extends Module {
|
|||
* @param {Block} lastBlock - last block in range
|
||||
*/
|
||||
private toggleBlocksSelectedState(firstBlock: Block, lastBlock: Block): void {
|
||||
const { BlockManager, BlockSelection } = this.Editor;
|
||||
const { BlockManager } = this.Editor;
|
||||
const fIndex = BlockManager.blocks.indexOf(firstBlock);
|
||||
const lIndex = BlockManager.blocks.indexOf(lastBlock);
|
||||
|
||||
|
@ -250,8 +199,6 @@ export default class CrossBlockSelection extends Module {
|
|||
block !== (shouldntSelectFirstBlock ? firstBlock : lastBlock)
|
||||
) {
|
||||
BlockManager.blocks[i].selected = !BlockManager.blocks[i].selected;
|
||||
|
||||
BlockSelection.clearCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,60 +14,30 @@ export default class DragNDrop extends Module {
|
|||
private isStartedAtEditor = false;
|
||||
|
||||
/**
|
||||
* Bind module. Enable all bindings if it is not read-only mode
|
||||
* Bind events
|
||||
*/
|
||||
public prepare(): void {
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.enableModuleBindings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle read-only state
|
||||
*
|
||||
* if state is true:
|
||||
* - disable all drag-n-drop event handlers
|
||||
*
|
||||
* if state is false:
|
||||
* - restore drag-n-drop event handlers
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - "read only" state
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (readOnlyEnabled) {
|
||||
this.disableModuleBindings();
|
||||
} else {
|
||||
this.enableModuleBindings();
|
||||
}
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add drag events listeners to editor zone
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private enableModuleBindings(): void {
|
||||
const { UI } = this.Editor;
|
||||
private bindEvents(): void {
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(UI.nodes.holder, 'drop', async (dropEvent: DragEvent) => {
|
||||
await this.processDrop(dropEvent);
|
||||
}, true);
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragstart', (dragEvent: DragEvent) => {
|
||||
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) {
|
||||
this.isStartedAtEditor = true;
|
||||
}
|
||||
|
||||
this.readOnlyMutableListeners.on(UI.nodes.holder, 'dragstart', () => {
|
||||
this.processDragStart();
|
||||
this.Editor.InlineToolbar.close();
|
||||
});
|
||||
|
||||
/**
|
||||
* Prevent default browser behavior to allow drop on non-contenteditable elements
|
||||
*/
|
||||
this.readOnlyMutableListeners.on(UI.nodes.holder, 'dragover', (dragEvent: DragEvent) => {
|
||||
this.processDragOver(dragEvent);
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind drag-n-drop event handlers
|
||||
*/
|
||||
private disableModuleBindings(): void {
|
||||
this.readOnlyMutableListeners.clearAll();
|
||||
/* Prevent default browser behavior to allow drop on non-contenteditable elements */
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragover', (e) => e.preventDefault(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,7 +45,7 @@ export default class DragNDrop extends Module {
|
|||
*
|
||||
* @param {DragEvent} dropEvent - drop event
|
||||
*/
|
||||
private async processDrop(dropEvent: DragEvent): Promise<void> {
|
||||
private processDrop = async (dropEvent: DragEvent): Promise<void> => {
|
||||
const {
|
||||
BlockManager,
|
||||
Caret,
|
||||
|
@ -108,24 +78,6 @@ export default class DragNDrop extends Module {
|
|||
this.Editor.Caret.setToBlock(targetBlock, Caret.positions.END);
|
||||
}
|
||||
|
||||
await Paste.processDataTransfer(dropEvent.dataTransfer, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drag start event
|
||||
*/
|
||||
private processDragStart(): void {
|
||||
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) {
|
||||
this.isStartedAtEditor = true;
|
||||
}
|
||||
|
||||
this.Editor.InlineToolbar.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} dragEvent - drag event
|
||||
*/
|
||||
private processDragOver(dragEvent: DragEvent): void {
|
||||
dragEvent.preventDefault();
|
||||
Paste.processDataTransfer(dropEvent.dataTransfer, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Module from '../__module';
|
||||
import * as _ from '../utils';
|
||||
|
||||
/**
|
||||
* Event listener information
|
||||
|
@ -7,11 +6,6 @@ import * as _ from '../utils';
|
|||
* @interface ListenerData
|
||||
*/
|
||||
export interface ListenerData {
|
||||
/**
|
||||
* Listener unique identifier
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Element where to listen to dispatched events
|
||||
*/
|
||||
|
@ -59,24 +53,20 @@ export default class Listeners extends Module {
|
|||
private allListeners: ListenerData[] = [];
|
||||
|
||||
/**
|
||||
* Assigns event listener on element and returns unique identifier
|
||||
* Assigns event listener on element
|
||||
*
|
||||
* @param {EventTarget} element - DOM element that needs to be listened
|
||||
* @param {string} eventType - event type
|
||||
* @param {Function} handler - method that will be fired on event
|
||||
* @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
public on(
|
||||
element: EventTarget,
|
||||
eventType: string,
|
||||
handler: (event: Event) => void,
|
||||
options: boolean | AddEventListenerOptions = false
|
||||
): string {
|
||||
const id = _.generateId('l');
|
||||
): void {
|
||||
const assignedEventData = {
|
||||
id,
|
||||
element,
|
||||
eventType,
|
||||
handler,
|
||||
|
@ -91,8 +81,6 @@ export default class Listeners extends Module {
|
|||
|
||||
this.allListeners.push(assignedEventData);
|
||||
element.addEventListener(eventType, handler, options);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,21 +110,6 @@ export default class Listeners extends Module {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes listener by id
|
||||
*
|
||||
* @param {string} id - listener identifier
|
||||
*/
|
||||
public offById(id: string): void {
|
||||
const listener = this.findById(id);
|
||||
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
listener.element.removeEventListener(listener.eventType, listener.handler, listener.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns first listener by passed params
|
||||
*
|
||||
|
@ -238,15 +211,4 @@ export default class Listeners extends Module {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns listener data found by id
|
||||
*
|
||||
* @param {string} id - listener identifier
|
||||
*
|
||||
* @returns {ListenerData}
|
||||
*/
|
||||
private findById(id: string): ListenerData {
|
||||
return this.allListeners.find((listener) => listener.id === id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ export default class ModificationsObserver extends Module {
|
|||
/**
|
||||
* Allows to temporary disable mutations handling
|
||||
*/
|
||||
private disabled = false;
|
||||
private disabled: boolean;
|
||||
|
||||
/**
|
||||
* Used to prevent several mutation callback execution
|
||||
|
@ -37,10 +37,7 @@ export default class ModificationsObserver extends Module {
|
|||
*/
|
||||
private mutationDebouncer = _.debounce(() => {
|
||||
this.updateNativeInputs();
|
||||
|
||||
if (typeof this.config.onChange === 'function') {
|
||||
this.config.onChange(this.Editor.API.methods);
|
||||
}
|
||||
this.config.onChange(this.Editor.API.methods);
|
||||
}, ModificationsObserver.DebounceTimer);
|
||||
|
||||
/**
|
||||
|
@ -59,7 +56,6 @@ export default class ModificationsObserver extends Module {
|
|||
}
|
||||
this.observer = null;
|
||||
this.nativeInputs.forEach((input) => this.Editor.Listeners.off(input, 'input', this.mutationDebouncer));
|
||||
this.mutationDebouncer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,22 +64,12 @@ export default class ModificationsObserver extends Module {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.enableModule();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read-only state
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - read only flag value
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (readOnlyEnabled) {
|
||||
this.disableModule();
|
||||
} else {
|
||||
this.enableModule();
|
||||
}
|
||||
/**
|
||||
* wait till Browser render Editor's Blocks
|
||||
*/
|
||||
window.setTimeout(() => {
|
||||
this.setObserver();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,7 +116,7 @@ export default class ModificationsObserver extends Module {
|
|||
* @param {MutationRecord[]} mutationList - list of mutations
|
||||
* @param {MutationObserver} observer - observer instance
|
||||
*/
|
||||
private mutationHandler(mutationList: MutationRecord[], observer: MutationObserver): void {
|
||||
private mutationHandler(mutationList: MutationRecord[], observer): void {
|
||||
/**
|
||||
* Skip mutations in stealth mode
|
||||
*/
|
||||
|
@ -182,25 +168,4 @@ export default class ModificationsObserver extends Module {
|
|||
|
||||
this.nativeInputs.forEach((input) => this.Editor.Listeners.on(input, 'input', this.mutationDebouncer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets observer and enables it
|
||||
*/
|
||||
private enableModule(): void {
|
||||
/**
|
||||
* wait till Browser render Editor's Blocks
|
||||
*/
|
||||
window.setTimeout(() => {
|
||||
this.setObserver();
|
||||
this.updateNativeInputs();
|
||||
this.enable();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables observer
|
||||
*/
|
||||
private disableModule(): void {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
PasteEventDetail
|
||||
} from '../../../types';
|
||||
import Block from '../block';
|
||||
import { SavedData } from '../../../types/data-formats';
|
||||
import { SavedData } from '../../types-internal/block-data';
|
||||
|
||||
/**
|
||||
* Tag substitute object.
|
||||
|
@ -141,25 +141,12 @@ export default class Paste extends Module {
|
|||
|
||||
/**
|
||||
* Set onPaste callback and collect tools` paste configurations
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
this.setCallback();
|
||||
this.processTools();
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.setCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read-only state
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - read only flag value
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (!readOnlyEnabled) {
|
||||
this.setCallback();
|
||||
} else {
|
||||
this.unsetCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,8 +237,8 @@ export default class Paste extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
const isCurrentBlockDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
|
||||
const needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
|
||||
const isCurrentBlockInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
|
||||
const needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;
|
||||
|
||||
dataToInsert.map(
|
||||
async (content, i) => this.insertBlock(content, i === 0 && needToReplaceCurrentBlock)
|
||||
|
@ -271,15 +258,6 @@ export default class Paste extends Module {
|
|||
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
|
||||
*/
|
||||
|
@ -298,7 +276,6 @@ export default class Paste extends Module {
|
|||
api: this.Editor.API.getMethodsForTool(name),
|
||||
config: {},
|
||||
data: {},
|
||||
readOnly: false,
|
||||
}) as BlockTool;
|
||||
|
||||
if (tool.pasteConfig === false) {
|
||||
|
@ -450,7 +427,7 @@ export default class Paste extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* If Tools is in list of errors, skip processing of paste event
|
||||
* If Tools is in list of exceptions, skip processing of paste event
|
||||
*/
|
||||
if (BlockManager.currentBlock && this.exceptionList.includes(BlockManager.currentBlock.name)) {
|
||||
return;
|
||||
|
@ -480,8 +457,8 @@ export default class Paste extends Module {
|
|||
);
|
||||
dataToInsert = dataToInsert.filter((data) => !!data);
|
||||
|
||||
const isCurrentBlockDefault = Tools.isDefault(BlockManager.currentBlock.tool);
|
||||
const needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
|
||||
const isCurrentBlockInitial = Tools.isInitial(BlockManager.currentBlock.tool);
|
||||
const needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;
|
||||
|
||||
dataToInsert.forEach(
|
||||
(data, i) => {
|
||||
|
@ -537,7 +514,7 @@ export default class Paste extends Module {
|
|||
*/
|
||||
private processHTML(innerHTML: string): PasteData[] {
|
||||
const { Tools, Sanitizer } = this.Editor;
|
||||
const initialTool = this.config.defaultBlock;
|
||||
const initialTool = this.config.initialBlock;
|
||||
const wrapper = $.make('DIV');
|
||||
|
||||
wrapper.innerHTML = innerHTML;
|
||||
|
@ -599,13 +576,13 @@ export default class Paste extends Module {
|
|||
* @returns {PasteData[]}
|
||||
*/
|
||||
private processPlain(plain: string): PasteData[] {
|
||||
const { defaultBlock } = this.config as {defaultBlock: string};
|
||||
const { initialBlock } = this.config as {initialBlock: string};
|
||||
|
||||
if (!plain) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tool = defaultBlock;
|
||||
const tool = initialBlock;
|
||||
|
||||
return plain
|
||||
.split(/\r?\n/)
|
||||
|
@ -645,7 +622,7 @@ export default class Paste extends Module {
|
|||
dataToInsert.tool !== currentBlock.name ||
|
||||
!$.containsOnlyInlineElements(dataToInsert.content.innerHTML)
|
||||
) {
|
||||
this.insertBlock(dataToInsert, currentBlock && Tools.isDefault(currentBlock.tool) && currentBlock.isEmpty);
|
||||
this.insertBlock(dataToInsert, currentBlock && Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -665,14 +642,14 @@ export default class Paste extends Module {
|
|||
const { BlockManager, Caret, Sanitizer, Tools } = this.Editor;
|
||||
const { content } = dataToInsert;
|
||||
|
||||
const currentBlockIsDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
|
||||
const currentBlockIsInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
|
||||
|
||||
if (currentBlockIsDefault && content.textContent.length < Paste.PATTERN_PROCESSING_MAX_LENGTH) {
|
||||
if (currentBlockIsInitial && 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) &&
|
||||
Tools.isInitial(BlockManager.currentBlock.tool) &&
|
||||
BlockManager.currentBlock.isEmpty;
|
||||
|
||||
const insertedBlock = BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock);
|
||||
|
@ -769,9 +746,9 @@ export default class Paste extends Module {
|
|||
let needToReplaceCurrentBlock = false;
|
||||
|
||||
if (i === 0) {
|
||||
const isCurrentBlockDefault = BlockManager.currentBlock && Tools.isDefault(BlockManager.currentBlock.tool);
|
||||
const isCurrentBlockInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
|
||||
|
||||
needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
|
||||
needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;
|
||||
}
|
||||
|
||||
BlockManager.insert({
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
import Module from '../__module';
|
||||
import { CriticalError } from '../errors/critical';
|
||||
|
||||
/**
|
||||
* @module ReadOnly
|
||||
*
|
||||
* Has one important method:
|
||||
* - {Function} toggleReadonly - Set read-only mode or toggle current state
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @typedef {ReadOnly} ReadOnly
|
||||
* @property {boolean} readOnlyEnabled - read-only state
|
||||
*/
|
||||
export default class ReadOnly extends Module {
|
||||
/**
|
||||
* Array of tools name which don't support read-only mode
|
||||
*/
|
||||
private toolsDontSupportReadOnly: string[] = [];
|
||||
|
||||
/**
|
||||
* Value to track read-only state
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
private readOnlyEnabled = false;
|
||||
|
||||
/**
|
||||
* Returns state of read only mode
|
||||
*/
|
||||
public get isEnabled(): boolean {
|
||||
return this.readOnlyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial state
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
const { Tools } = this.Editor;
|
||||
const { blockTools } = Tools;
|
||||
const toolsDontSupportReadOnly: string[] = [];
|
||||
|
||||
Object.entries(blockTools).forEach(([name, tool]) => {
|
||||
if (!Tools.isReadOnlySupported(tool)) {
|
||||
toolsDontSupportReadOnly.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
this.toolsDontSupportReadOnly = toolsDontSupportReadOnly;
|
||||
|
||||
if (this.config.readOnly && toolsDontSupportReadOnly.length > 0) {
|
||||
this.throwCriticalError();
|
||||
}
|
||||
|
||||
this.toggle(this.config.readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read-only mode or toggle current state
|
||||
* Call all Modules `toggleReadOnly` method and re-render Editor
|
||||
*
|
||||
* @param {boolean} state - (optional) read-only state or toggle
|
||||
*/
|
||||
public async toggle(state = !this.readOnlyEnabled): Promise<boolean> {
|
||||
if (state && this.toolsDontSupportReadOnly.length > 0) {
|
||||
this.throwCriticalError();
|
||||
}
|
||||
|
||||
const oldState = this.readOnlyEnabled;
|
||||
|
||||
this.readOnlyEnabled = state;
|
||||
|
||||
for (const name in this.Editor) {
|
||||
/**
|
||||
* Verify module has method `toggleReadOnly` method
|
||||
*/
|
||||
if (!this.Editor[name].toggleReadOnly) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* set or toggle read-only state
|
||||
*/
|
||||
this.Editor[name].toggleReadOnly(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* If new state equals old one, do not re-render blocks
|
||||
*/
|
||||
if (oldState === state) {
|
||||
return this.readOnlyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current Editor Blocks and render again
|
||||
*/
|
||||
const savedBlocks = await this.Editor.Saver.save();
|
||||
|
||||
await this.Editor.BlockManager.clear();
|
||||
await this.Editor.Renderer.render(savedBlocks.blocks);
|
||||
|
||||
return this.readOnlyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error about tools which don't support read-only mode
|
||||
*/
|
||||
private throwCriticalError(): never {
|
||||
throw new CriticalError(
|
||||
`To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(', ')} don't support read-only mode.`
|
||||
);
|
||||
}
|
||||
}
|
|
@ -96,17 +96,38 @@ export default class RectangleSelection extends Module {
|
|||
*/
|
||||
private overlayRectangle: HTMLDivElement;
|
||||
|
||||
/**
|
||||
* Listener identifiers
|
||||
*/
|
||||
private listenerIds: string[] = [];
|
||||
|
||||
/**
|
||||
* Module Preparation
|
||||
* Creating rect and hang handlers
|
||||
*/
|
||||
public prepare(): void {
|
||||
this.enableModuleBindings();
|
||||
const { Listeners } = this.Editor;
|
||||
const { container } = this.genHTML();
|
||||
|
||||
Listeners.on(container, 'mousedown', (event: MouseEvent) => {
|
||||
if (event.button !== this.MAIN_MOUSE_BUTTON) {
|
||||
return;
|
||||
}
|
||||
this.startSelection(event.pageX, event.pageY);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mousemove', (event: MouseEvent) => {
|
||||
this.changingRectangle(event);
|
||||
this.scrollByZones(event.clientY);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mouseleave', () => {
|
||||
this.clearSelection();
|
||||
this.endSelection();
|
||||
});
|
||||
|
||||
Listeners.on(window, 'scroll', (event) => {
|
||||
this.changingRectangle(event);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mouseup', () => {
|
||||
this.endSelection();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,78 +196,6 @@ export default class RectangleSelection extends Module {
|
|||
this.isRectSelectionActivated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Module necessary event handlers
|
||||
*/
|
||||
private enableModuleBindings(): void {
|
||||
const { Listeners } = this.Editor;
|
||||
const { container } = this.genHTML();
|
||||
|
||||
Listeners.on(container, 'mousedown', (mouseEvent: MouseEvent) => {
|
||||
this.processMouseDown(mouseEvent);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mousemove', (mouseEvent: MouseEvent) => {
|
||||
this.processMouseMove(mouseEvent);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mouseleave', () => {
|
||||
this.processMouseLeave();
|
||||
});
|
||||
|
||||
Listeners.on(window, 'scroll', (mouseEvent: MouseEvent) => {
|
||||
this.processScroll(mouseEvent);
|
||||
}, false);
|
||||
|
||||
Listeners.on(document.body, 'mouseup', () => {
|
||||
this.processMouseUp();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse down events
|
||||
*
|
||||
* @param {MouseEvent} mouseEvent - mouse event payload
|
||||
*/
|
||||
private processMouseDown(mouseEvent: MouseEvent): void {
|
||||
if (mouseEvent.button !== this.MAIN_MOUSE_BUTTON) {
|
||||
return;
|
||||
}
|
||||
this.startSelection(mouseEvent.pageX, mouseEvent.pageY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse move events
|
||||
*
|
||||
* @param {MouseEvent} mouseEvent - mouse event payload
|
||||
*/
|
||||
private processMouseMove(mouseEvent: MouseEvent): void {
|
||||
this.changingRectangle(mouseEvent);
|
||||
this.scrollByZones(mouseEvent.clientY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse leave
|
||||
*/
|
||||
private processMouseLeave(): void {
|
||||
this.clearSelection();
|
||||
this.endSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} mouseEvent - mouse event payload
|
||||
*/
|
||||
private processScroll(mouseEvent: MouseEvent): void {
|
||||
this.changingRectangle(mouseEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse up
|
||||
*/
|
||||
private processMouseUp(): void {
|
||||
this.endSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll If mouse in scroll zone
|
||||
*
|
||||
|
@ -321,7 +270,7 @@ export default class RectangleSelection extends Module {
|
|||
*
|
||||
* @param {MouseEvent} event - mouse event
|
||||
*/
|
||||
private changingRectangle(event: MouseEvent): void {
|
||||
private changingRectangle(event): void {
|
||||
if (!this.mousedown) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Module from '../__module';
|
||||
/* eslint-disable import/no-duplicates */
|
||||
import * as _ from '../utils';
|
||||
import { ChainData } from '../utils';
|
||||
import { BlockToolConstructable, OutputBlockData } from '../../../types';
|
||||
|
||||
/**
|
||||
|
@ -45,7 +47,7 @@ export default class Renderer extends Module {
|
|||
public async render(blocks: OutputBlockData[]): Promise<void> {
|
||||
const chainData = blocks.map((block) => ({ function: (): Promise<void> => this.insertBlock(block) }));
|
||||
|
||||
const sequence = await _.sequence(chainData as _.ChainData[]);
|
||||
const sequence = await _.sequence(chainData as ChainData[]);
|
||||
|
||||
this.Editor.UI.checkEmptiness();
|
||||
|
||||
|
@ -58,7 +60,6 @@ export default class Renderer extends Module {
|
|||
* Insert block to working zone
|
||||
*
|
||||
* @param {object} item - Block data to insert
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async insertBlock(item: OutputBlockData): Promise<void> {
|
||||
|
@ -90,7 +91,7 @@ export default class Renderer extends Module {
|
|||
const toolToolboxSettings = (Tools.unavailable[tool] as BlockToolConstructable).toolbox;
|
||||
const userToolboxSettings = Tools.getToolSettings(tool).toolbox;
|
||||
|
||||
stubData.title = toolToolboxSettings.title || (userToolboxSettings && userToolboxSettings.title) || stubData.title;
|
||||
stubData.title = toolToolboxSettings.title || userToolboxSettings.title || stubData.title;
|
||||
}
|
||||
|
||||
const stub = BlockManager.insert({
|
||||
|
|
|
@ -37,7 +37,7 @@ import * as _ from '../utils';
|
|||
|
||||
import HTMLJanitor from 'html-janitor';
|
||||
import { BlockToolData, InlineToolConstructable, SanitizerConfig } from '../../../types';
|
||||
import { SavedData } from '../../../types/data-formats';
|
||||
import { SavedData } from '../../types-internal/block-data';
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
import Module from '../__module';
|
||||
import { OutputData } from '../../../types';
|
||||
import { ValidatedData } from '../../../types/data-formats';
|
||||
import { ValidatedData } from '../../types-internal/block-data';
|
||||
import Block from '../block';
|
||||
import * as _ from '../utils';
|
||||
|
||||
|
@ -36,18 +36,16 @@ export default class Saver extends Module {
|
|||
*/
|
||||
ModificationsObserver.disable();
|
||||
|
||||
try {
|
||||
blocks.forEach((block: Block) => {
|
||||
chainData.push(this.getSavedData(block));
|
||||
});
|
||||
blocks.forEach((block: Block) => {
|
||||
chainData.push(this.getSavedData(block));
|
||||
});
|
||||
|
||||
const extractedData = await Promise.all(chainData);
|
||||
const sanitizedData = await Sanitizer.sanitizeBlocks(extractedData);
|
||||
const extractedData = await Promise.all(chainData);
|
||||
const sanitizedData = await Sanitizer.sanitizeBlocks(extractedData);
|
||||
|
||||
return this.makeOutput(sanitizedData);
|
||||
} finally {
|
||||
ModificationsObserver.enable();
|
||||
}
|
||||
ModificationsObserver.enable();
|
||||
|
||||
return this.makeOutput(sanitizedData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,10 +64,6 @@ export default class Shortcuts extends Module {
|
|||
public remove(shortcut: string): void {
|
||||
const index = this.registeredShortcuts.findIndex((shc) => shc.name === shortcut);
|
||||
|
||||
if (index === -1 || !this.registeredShortcuts[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.registeredShortcuts[index].remove();
|
||||
this.registeredShortcuts.splice(index, 1);
|
||||
}
|
||||
|
|
|
@ -4,15 +4,6 @@ import Flipper, { FlipperOptions } from '../../flipper';
|
|||
import * as _ from '../../utils';
|
||||
import SelectionUtils from '../../selection';
|
||||
|
||||
/**
|
||||
* HTML Elements that used for BlockSettings
|
||||
*/
|
||||
interface BlockSettingsNodes {
|
||||
wrapper: HTMLElement;
|
||||
toolSettings: HTMLElement;
|
||||
defaultSettings: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Settings
|
||||
*
|
||||
|
@ -24,7 +15,7 @@ interface BlockSettingsNodes {
|
|||
* | ...................... |
|
||||
* |________________________|
|
||||
*/
|
||||
export default class BlockSettings extends Module<BlockSettingsNodes> {
|
||||
export default class BlockSettings extends Module {
|
||||
/**
|
||||
* Module Events
|
||||
*
|
||||
|
@ -66,6 +57,15 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
return this.nodes.wrapper.classList.contains(this.CSS.wrapperOpened);
|
||||
}
|
||||
|
||||
/**
|
||||
* Block settings UI HTML elements
|
||||
*/
|
||||
public nodes: {[key: string]: HTMLElement} = {
|
||||
wrapper: null,
|
||||
toolSettings: null,
|
||||
defaultSettings: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* List of buttons
|
||||
*/
|
||||
|
@ -103,15 +103,6 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
this.enableFlipper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys module
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.flipper.deactivate();
|
||||
this.flipper = null;
|
||||
this.removeAllNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Block Settings pane
|
||||
*/
|
||||
|
@ -128,7 +119,6 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
* Highlight content of a Block we are working with
|
||||
*/
|
||||
this.Editor.BlockManager.currentBlock.selected = true;
|
||||
this.Editor.BlockSelection.clearCache();
|
||||
|
||||
/**
|
||||
* Fill Tool's settings
|
||||
|
|
|
@ -2,23 +2,15 @@ import Module from '../../__module';
|
|||
import $ from '../../dom';
|
||||
import { BlockToolConstructable } from '../../../../types';
|
||||
import * as _ from '../../utils';
|
||||
import { SavedData } from '../../../../types/data-formats';
|
||||
import { SavedData } from '../../../types-internal/block-data';
|
||||
import Flipper from '../../flipper';
|
||||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
|
||||
/**
|
||||
* HTML Elements used for ConversionToolbar
|
||||
*/
|
||||
interface ConversionToolbarNodes {
|
||||
wrapper: HTMLElement;
|
||||
tools: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Converter
|
||||
*/
|
||||
export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
||||
export default class ConversionToolbar extends Module {
|
||||
/**
|
||||
* CSS getter
|
||||
*/
|
||||
|
@ -37,6 +29,14 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML Elements used for UI
|
||||
*/
|
||||
public nodes: { [key: string]: HTMLElement } = {
|
||||
wrapper: null,
|
||||
tools: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Conversion Toolbar open/close state
|
||||
*
|
||||
|
@ -65,10 +65,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
* Create UI of Conversion Toolbar
|
||||
*/
|
||||
public make(): HTMLElement {
|
||||
this.nodes.wrapper = $.make('div', [
|
||||
ConversionToolbar.CSS.conversionToolbarWrapper,
|
||||
...(this.isRtl ? [ this.Editor.UI.CSS.editorRtlFix ] : []),
|
||||
]);
|
||||
this.nodes.wrapper = $.make('div', ConversionToolbar.CSS.conversionToolbarWrapper);
|
||||
this.nodes.tools = $.make('div', ConversionToolbar.CSS.conversionToolbarTools);
|
||||
|
||||
const label = $.make('div', ConversionToolbar.CSS.conversionToolbarLabel, {
|
||||
|
@ -91,15 +88,6 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
return this.nodes.wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates flipper and removes all nodes
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.flipper.deactivate();
|
||||
this.flipper = null;
|
||||
this.removeAllNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle conversion dropdown visibility
|
||||
*
|
||||
|
@ -161,7 +149,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
public hasTools(): boolean {
|
||||
const tools = Object.keys(this.tools); // available tools in array representation
|
||||
|
||||
return !(tools.length === 1 && tools.shift() === this.config.defaultBlock);
|
||||
return !(tools.length === 1 && tools.shift() === this.config.initialBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,7 +158,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
*
|
||||
* @param {string} replacingToolName - name of Tool which replaces current
|
||||
*/
|
||||
public async replaceWithBlock(replacingToolName: string): Promise<void> {
|
||||
public async replaceWithBlock(replacingToolName: string): Promise <void> {
|
||||
/**
|
||||
* At first, we get current Block data
|
||||
*
|
||||
|
@ -184,10 +172,10 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
|
||||
/**
|
||||
* When current Block name is equals to the replacing tool Name,
|
||||
* than convert this Block back to the default Block
|
||||
* than convert this Block back to the initial Block
|
||||
*/
|
||||
if (currentBlockName === replacingToolName) {
|
||||
replacingToolName = this.config.defaultBlock;
|
||||
replacingToolName = this.config.initialBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -276,15 +264,10 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
const toolToolboxSettings = toolClass[internalSettings.TOOLBOX];
|
||||
const conversionConfig = toolClass[internalSettings.CONVERSION_CONFIG];
|
||||
|
||||
const userSettings = this.Editor.Tools.USER_SETTINGS;
|
||||
const userToolboxSettings = this.Editor.Tools.getToolSettings(toolName)[userSettings.TOOLBOX];
|
||||
|
||||
const toolboxSettings = userToolboxSettings ?? toolToolboxSettings;
|
||||
|
||||
/**
|
||||
* Skip tools that don't pass 'toolbox' property
|
||||
*/
|
||||
if (_.isEmpty(toolboxSettings) || !toolboxSettings.icon) {
|
||||
if (_.isEmpty(toolToolboxSettings) || !toolToolboxSettings.icon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -295,7 +278,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
continue;
|
||||
}
|
||||
|
||||
this.addTool(toolName, toolboxSettings.icon, toolboxSettings.title);
|
||||
this.addTool(toolName, toolToolboxSettings.icon, toolToolboxSettings.title);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,21 +4,6 @@ import * as _ from '../../utils';
|
|||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
|
||||
/**
|
||||
* HTML Elements used for Toolbar UI
|
||||
*/
|
||||
interface ToolbarNodes {
|
||||
wrapper: HTMLElement;
|
||||
content: HTMLElement;
|
||||
actions: HTMLElement;
|
||||
|
||||
// Content Zone
|
||||
plusButton: HTMLElement;
|
||||
|
||||
// Actions Zone
|
||||
blockActionsButtons: HTMLElement;
|
||||
settingsToggler: HTMLElement;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* «Toolbar» is the node that moves up/down over current block
|
||||
|
@ -71,7 +56,23 @@ interface ToolbarNodes {
|
|||
* @property {Element} nodes.pluginSettings - Plugin Settings section of Settings Panel
|
||||
* @property {Element} nodes.defaultSettings - Default Settings section of Settings Panel
|
||||
*/
|
||||
export default class Toolbar extends Module<ToolbarNodes> {
|
||||
export default class Toolbar extends Module {
|
||||
/**
|
||||
* HTML Elements used for Toolbar UI
|
||||
*/
|
||||
public nodes: {[key: string]: HTMLElement} = {
|
||||
wrapper: null,
|
||||
content: null,
|
||||
actions: null,
|
||||
|
||||
// Content Zone
|
||||
plusButton: null,
|
||||
|
||||
// Actions Zone
|
||||
blockActionsButtons: null,
|
||||
settingsToggler: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* CSS styles
|
||||
*
|
||||
|
@ -98,76 +99,89 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the Toolbar opening state
|
||||
*
|
||||
* @returns {boolean}
|
||||
* Makes toolbar
|
||||
*/
|
||||
public get opened(): boolean {
|
||||
return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened);
|
||||
}
|
||||
public make(): void {
|
||||
this.nodes.wrapper = $.make('div', this.CSS.toolbar);
|
||||
|
||||
/**
|
||||
* Plus Button public methods
|
||||
*
|
||||
* @returns {{hide: function(): void, show: function(): void}}
|
||||
*/
|
||||
public get plusButton(): {hide: () => void; show: () => void} {
|
||||
return {
|
||||
hide: (): void => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
|
||||
show: (): void => {
|
||||
if (this.Editor.Toolbox.isEmpty) {
|
||||
return;
|
||||
}
|
||||
this.nodes.plusButton.classList.remove(this.CSS.plusButtonHidden);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Make Content Zone and Actions Zone
|
||||
*/
|
||||
['content', 'actions'].forEach((el) => {
|
||||
this.nodes[el] = $.make('div', this.CSS[el]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Block actions appearance manipulations
|
||||
*
|
||||
* @returns {{hide: function(): void, show: function(): void}}
|
||||
*/
|
||||
private get blockActions(): {hide: () => void; show: () => void} {
|
||||
return {
|
||||
hide: (): void => {
|
||||
this.nodes.actions.classList.remove(this.CSS.actionsOpened);
|
||||
},
|
||||
show: (): void => {
|
||||
this.nodes.actions.classList.add(this.CSS.actionsOpened);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Actions will be included to the toolbar content so we can align in to the right of the content
|
||||
*/
|
||||
$.append(this.nodes.wrapper, this.nodes.content);
|
||||
$.append(this.nodes.content, this.nodes.actions);
|
||||
|
||||
/**
|
||||
* Fill Content Zone:
|
||||
* - Plus Button
|
||||
* - Toolbox
|
||||
*/
|
||||
this.nodes.plusButton = $.make('div', this.CSS.plusButton);
|
||||
$.append(this.nodes.plusButton, $.svg('plus', 14, 14));
|
||||
$.append(this.nodes.content, this.nodes.plusButton);
|
||||
|
||||
this.Editor.Listeners.on(this.nodes.plusButton, 'click', () => this.plusButtonClicked(), false);
|
||||
|
||||
/**
|
||||
* Add events to show/hide tooltip for plus button
|
||||
*/
|
||||
const tooltipContent = $.make('div');
|
||||
|
||||
tooltipContent.appendChild(document.createTextNode(I18n.ui(I18nInternalNS.ui.toolbar.toolbox, 'Add')));
|
||||
tooltipContent.appendChild($.make('div', this.CSS.plusButtonShortcut, {
|
||||
textContent: '⇥ Tab',
|
||||
}));
|
||||
|
||||
this.Editor.Tooltip.onHover(this.nodes.plusButton, tooltipContent);
|
||||
|
||||
/**
|
||||
* Make a Toolbox
|
||||
*/
|
||||
this.Editor.Toolbox.make();
|
||||
|
||||
/**
|
||||
* Fill Actions Zone:
|
||||
* - Settings Toggler
|
||||
* - Remove Block Button
|
||||
* - Settings Panel
|
||||
*/
|
||||
this.nodes.blockActionsButtons = $.make('div', this.CSS.blockActionsButtons);
|
||||
this.nodes.settingsToggler = $.make('span', this.CSS.settingsToggler);
|
||||
const settingsIcon = $.svg('dots', 8, 8);
|
||||
|
||||
$.append(this.nodes.settingsToggler, settingsIcon);
|
||||
$.append(this.nodes.blockActionsButtons, this.nodes.settingsToggler);
|
||||
$.append(this.nodes.actions, this.nodes.blockActionsButtons);
|
||||
|
||||
this.Editor.Tooltip.onHover(
|
||||
this.nodes.settingsToggler,
|
||||
I18n.ui(I18nInternalNS.ui.blockTunes.toggler, 'Click to tune'),
|
||||
{
|
||||
placement: 'top',
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Make and append Settings Panel
|
||||
*/
|
||||
this.Editor.BlockSettings.make();
|
||||
$.append(this.nodes.actions, this.Editor.BlockSettings.nodes.wrapper);
|
||||
|
||||
/**
|
||||
* Append toolbar to the Editor
|
||||
*/
|
||||
$.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper);
|
||||
|
||||
/**
|
||||
* Module preparation method
|
||||
* Steps:
|
||||
* - Make Toolbar dependent components like BlockSettings, Toolbox and so on
|
||||
* - Make itself and append dependent nodes to itself
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
/**
|
||||
* Bind events on the Toolbar elements
|
||||
*/
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.drawUI();
|
||||
this.enableModuleBindings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles read-only mode
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - read-only mode
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (!readOnlyEnabled) {
|
||||
this.drawUI();
|
||||
this.enableModuleBindings();
|
||||
} else {
|
||||
this.destroy();
|
||||
this.disableModuleBindings();
|
||||
}
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,6 +251,15 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
}, 50)();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns toolbar opened state
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public get opened(): boolean {
|
||||
return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the Toolbar
|
||||
*/
|
||||
|
@ -250,81 +273,36 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Draws Toolbar elements
|
||||
* Plus Button public methods
|
||||
*
|
||||
* @returns {{hide: function(): void, show: function(): void}}
|
||||
*/
|
||||
private make(): void {
|
||||
this.nodes.wrapper = $.make('div', this.CSS.toolbar);
|
||||
public get plusButton(): {hide: () => void; show: () => void} {
|
||||
return {
|
||||
hide: (): void => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
|
||||
show: (): void => {
|
||||
if (this.Editor.Toolbox.isEmpty) {
|
||||
return;
|
||||
}
|
||||
this.nodes.plusButton.classList.remove(this.CSS.plusButtonHidden);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Make Content Zone and Actions Zone
|
||||
*/
|
||||
['content', 'actions'].forEach((el) => {
|
||||
this.nodes[el] = $.make('div', this.CSS[el]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Actions will be included to the toolbar content so we can align in to the right of the content
|
||||
*/
|
||||
$.append(this.nodes.wrapper, this.nodes.content);
|
||||
$.append(this.nodes.content, this.nodes.actions);
|
||||
|
||||
/**
|
||||
* Fill Content Zone:
|
||||
* - Plus Button
|
||||
* - Toolbox
|
||||
*/
|
||||
this.nodes.plusButton = $.make('div', this.CSS.plusButton);
|
||||
$.append(this.nodes.plusButton, $.svg('plus', 14, 14));
|
||||
$.append(this.nodes.content, this.nodes.plusButton);
|
||||
|
||||
this.readOnlyMutableListeners.on(this.nodes.plusButton, 'click', () => {
|
||||
this.plusButtonClicked();
|
||||
}, false);
|
||||
|
||||
/**
|
||||
* Add events to show/hide tooltip for plus button
|
||||
*/
|
||||
const tooltipContent = $.make('div');
|
||||
|
||||
tooltipContent.appendChild(document.createTextNode(I18n.ui(I18nInternalNS.ui.toolbar.toolbox, 'Add')));
|
||||
tooltipContent.appendChild($.make('div', this.CSS.plusButtonShortcut, {
|
||||
textContent: '⇥ Tab',
|
||||
}));
|
||||
|
||||
this.Editor.Tooltip.onHover(this.nodes.plusButton, tooltipContent);
|
||||
|
||||
/**
|
||||
* Fill Actions Zone:
|
||||
* - Settings Toggler
|
||||
* - Remove Block Button
|
||||
* - Settings Panel
|
||||
*/
|
||||
this.nodes.blockActionsButtons = $.make('div', this.CSS.blockActionsButtons);
|
||||
this.nodes.settingsToggler = $.make('span', this.CSS.settingsToggler);
|
||||
const settingsIcon = $.svg('dots', 8, 8);
|
||||
|
||||
$.append(this.nodes.settingsToggler, settingsIcon);
|
||||
$.append(this.nodes.blockActionsButtons, this.nodes.settingsToggler);
|
||||
$.append(this.nodes.actions, this.nodes.blockActionsButtons);
|
||||
|
||||
this.Editor.Tooltip.onHover(
|
||||
this.nodes.settingsToggler,
|
||||
I18n.ui(I18nInternalNS.ui.blockTunes.toggler, 'Click to tune'),
|
||||
{
|
||||
placement: 'top',
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Appending Toolbar components to itself
|
||||
*/
|
||||
$.append(this.nodes.content, this.Editor.Toolbox.nodes.toolbox);
|
||||
$.append(this.nodes.actions, this.Editor.BlockSettings.nodes.wrapper);
|
||||
|
||||
/**
|
||||
* Append toolbar to the Editor
|
||||
*/
|
||||
$.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper);
|
||||
/**
|
||||
* Block actions appearance manipulations
|
||||
*
|
||||
* @returns {{hide: function(): void, show: function(): void}}
|
||||
*/
|
||||
private get blockActions(): {hide: () => void; show: () => void} {
|
||||
return {
|
||||
hide: (): void => {
|
||||
this.nodes.actions.classList.remove(this.CSS.actionsOpened);
|
||||
},
|
||||
show: (): void => {
|
||||
this.nodes.actions.classList.add(this.CSS.actionsOpened);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -335,22 +313,14 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enable bindings
|
||||
* Bind events on the Toolbar Elements:
|
||||
* - Block Settings
|
||||
*/
|
||||
private enableModuleBindings(): void {
|
||||
private bindEvents(): void {
|
||||
/**
|
||||
* Settings toggler
|
||||
*/
|
||||
this.readOnlyMutableListeners.on(this.nodes.settingsToggler, 'click', () => {
|
||||
this.settingsTogglerClicked();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable bindings
|
||||
*/
|
||||
private disableModuleBindings(): void {
|
||||
this.readOnlyMutableListeners.clearAll();
|
||||
this.Editor.Listeners.on(this.nodes.settingsToggler, 'click', () => this.settingsTogglerClicked());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -363,37 +333,4 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
this.Editor.BlockSettings.open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws Toolbar UI
|
||||
*
|
||||
* Toolbar contains BlockSettings and Toolbox.
|
||||
* Thats why at first we draw its components and then Toolbar itself
|
||||
*/
|
||||
private drawUI(): void {
|
||||
/**
|
||||
* Make BlockSettings Panel
|
||||
*/
|
||||
this.Editor.BlockSettings.make();
|
||||
|
||||
/**
|
||||
* Make Toolbox
|
||||
*/
|
||||
this.Editor.Toolbox.make();
|
||||
|
||||
/**
|
||||
* Make Toolbar
|
||||
*/
|
||||
this.make();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all created and saved HTMLElements
|
||||
* It is used in Read-Only mode
|
||||
*/
|
||||
private destroy(): void {
|
||||
this.Editor.Toolbox.destroy();
|
||||
this.Editor.BlockSettings.destroy();
|
||||
this.removeAllNodes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,22 +7,6 @@ import Flipper from '../../flipper';
|
|||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
|
||||
/**
|
||||
* Inline Toolbar elements
|
||||
*/
|
||||
interface InlineToolbarNodes {
|
||||
wrapper: HTMLElement;
|
||||
togglerAndButtonsWrapper: HTMLElement;
|
||||
buttons: HTMLElement;
|
||||
conversionToggler: HTMLElement;
|
||||
conversionTogglerContent: HTMLElement;
|
||||
/**
|
||||
* Zone below the buttons where Tools can create additional actions by 'renderActions()' method
|
||||
* For example, input for the 'link' tool or textarea for the 'comment' tool
|
||||
*/
|
||||
actions: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline toolbar with actions that modifies selected text fragment
|
||||
*
|
||||
|
@ -30,7 +14,7 @@ interface InlineToolbarNodes {
|
|||
* | B i [link] [mark] |
|
||||
* |________________________|
|
||||
*/
|
||||
export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||
export default class InlineToolbar extends Module {
|
||||
/**
|
||||
* CSS styles
|
||||
*/
|
||||
|
@ -43,12 +27,12 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
buttonsWrapper: 'ce-inline-toolbar__buttons',
|
||||
actionsWrapper: 'ce-inline-toolbar__actions',
|
||||
inlineToolButton: 'ce-inline-tool',
|
||||
inlineToolButtonLast: 'ce-inline-tool--last',
|
||||
inputField: 'cdx-input',
|
||||
focusedButton: 'ce-inline-tool--focused',
|
||||
conversionToggler: 'ce-inline-toolbar__dropdown',
|
||||
conversionTogglerHidden: 'ce-inline-toolbar__dropdown--hidden',
|
||||
conversionTogglerContent: 'ce-inline-toolbar__dropdown-content',
|
||||
togglerAndButtonsWrapper: 'ce-inline-toolbar__toggler-and-button-wrapper',
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,13 +42,34 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
*/
|
||||
public opened = false;
|
||||
|
||||
/**
|
||||
* Inline Toolbar elements
|
||||
*/
|
||||
private nodes: {
|
||||
wrapper: HTMLElement;
|
||||
buttons: HTMLElement;
|
||||
conversionToggler: HTMLElement;
|
||||
conversionTogglerContent: HTMLElement;
|
||||
actions: HTMLElement;
|
||||
} = {
|
||||
wrapper: null,
|
||||
buttons: null,
|
||||
conversionToggler: null,
|
||||
conversionTogglerContent: null,
|
||||
/**
|
||||
* Zone below the buttons where Tools can create additional actions by 'renderActions()' method
|
||||
* For example, input for the 'link' tool or textarea for the 'comment' tool
|
||||
*/
|
||||
actions: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Margin above/below the Toolbar
|
||||
*/
|
||||
private readonly toolbarVerticalMargin: number = 5;
|
||||
|
||||
/**
|
||||
* Currently visible tools instances
|
||||
* Tools instances
|
||||
*/
|
||||
private toolsInstances: Map<string, InlineTool>;
|
||||
|
||||
|
@ -88,25 +93,76 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
private flipper: Flipper = null;
|
||||
|
||||
/**
|
||||
* Module preparation method
|
||||
* Inline Toolbar Tools
|
||||
*
|
||||
* @returns {Map<string, InlineTool>}
|
||||
*/
|
||||
public async prepare(): Promise<void> {
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.make();
|
||||
public get tools(): Map<string, InlineTool> {
|
||||
if (!this.toolsInstances || this.toolsInstances.size === 0) {
|
||||
const allTools = this.inlineTools;
|
||||
|
||||
this.toolsInstances = new Map();
|
||||
for (const tool in allTools) {
|
||||
if (Object.prototype.hasOwnProperty.call(allTools, tool)) {
|
||||
this.toolsInstances.set(tool, allTools[tool]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.toolsInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles read-only mode
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - read-only mode
|
||||
* Making DOM
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
if (!readOnlyEnabled) {
|
||||
this.make();
|
||||
} else {
|
||||
this.destroy();
|
||||
}
|
||||
public make(): void {
|
||||
this.nodes.wrapper = $.make('div', this.CSS.inlineToolbar);
|
||||
this.nodes.buttons = $.make('div', this.CSS.buttonsWrapper);
|
||||
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
|
||||
|
||||
// To prevent reset of a selection when click on the wrapper
|
||||
this.Editor.Listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
||||
const isClickedOnActionsWrapper = (event.target as Element).closest(`.${this.CSS.actionsWrapper}`);
|
||||
|
||||
// If click is on actions wrapper,
|
||||
// do not prevent default behaviour because actions might include interactive elements
|
||||
if (!isClickedOnActionsWrapper) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Append Inline Toolbar to the Editor
|
||||
*/
|
||||
$.append(this.nodes.wrapper, [this.nodes.buttons, this.nodes.actions]);
|
||||
$.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper);
|
||||
|
||||
/**
|
||||
* Add button that will allow switching block type
|
||||
*/
|
||||
this.addConversionToggler();
|
||||
|
||||
/**
|
||||
* Append Inline Toolbar Tools
|
||||
*/
|
||||
this.addTools();
|
||||
|
||||
/**
|
||||
* Prepare conversion toolbar.
|
||||
* If it has any conversion tool then it will be enabled in the Inline Toolbar
|
||||
*/
|
||||
this.prepareConversionToolbar();
|
||||
|
||||
/**
|
||||
* Recalculate initial width with all buttons
|
||||
*/
|
||||
this.recalculateWidth();
|
||||
|
||||
/**
|
||||
* Allow to leaf buttons by arrows / tab
|
||||
* Buttons will be filled on opening
|
||||
*/
|
||||
this.enableFlipper();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,6 +188,9 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
this.move();
|
||||
this.open();
|
||||
this.Editor.Toolbar.close();
|
||||
|
||||
/** Check Tools state for selected fragment */
|
||||
this.checkToolsState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,19 +243,8 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
* Hides Inline Toolbar
|
||||
*/
|
||||
public close(): void {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.Editor.ReadOnly.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed);
|
||||
this.toolsInstances.forEach((toolInstance) => {
|
||||
/**
|
||||
* @todo replace 'clear' with 'destroy'
|
||||
*/
|
||||
this.tools.forEach((toolInstance) => {
|
||||
if (typeof toolInstance.clear === 'function') {
|
||||
toolInstance.clear();
|
||||
}
|
||||
|
@ -212,19 +260,25 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
* Shows Inline Toolbar
|
||||
*/
|
||||
public open(): void {
|
||||
if (this.opened) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Filter inline-tools and show only allowed by Block's Tool
|
||||
*/
|
||||
this.addToolsFiltered();
|
||||
this.filterTools();
|
||||
|
||||
/**
|
||||
* Show Inline Toolbar
|
||||
*/
|
||||
this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed);
|
||||
|
||||
/**
|
||||
* Call 'clear' method for Inline Tools (for example, 'link' want to clear input)
|
||||
*/
|
||||
this.tools.forEach((toolInstance: InlineTool) => {
|
||||
if (typeof toolInstance.clear === 'function') {
|
||||
toolInstance.clear();
|
||||
}
|
||||
});
|
||||
|
||||
this.buttonsList = this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`);
|
||||
this.opened = true;
|
||||
|
||||
|
@ -260,154 +314,6 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
return this.nodes.wrapper.contains(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes UI and its components
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.flipper.deactivate();
|
||||
this.flipper = null;
|
||||
|
||||
this.Editor.ConversionToolbar.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns inline toolbar settings for a particular tool
|
||||
*
|
||||
* @param {string} toolName - user specified name of tool
|
||||
* @returns - array of ordered tool names or false
|
||||
*/
|
||||
private getInlineToolbarSettings(toolName): string[]|boolean {
|
||||
const toolSettings = this.Editor.Tools.getToolSettings(toolName);
|
||||
|
||||
/**
|
||||
* InlineToolbar property of a particular tool
|
||||
*/
|
||||
const settingsForTool = toolSettings[this.Editor.Tools.USER_SETTINGS.ENABLED_INLINE_TOOLS];
|
||||
|
||||
/**
|
||||
* Whether to enable IT for a particular tool is the decision of the editor user.
|
||||
* He can enable it by the inlineToolbar settings for this tool. To enable, he should pass true or strings[]
|
||||
*/
|
||||
const enabledForTool = settingsForTool === true || Array.isArray(settingsForTool);
|
||||
|
||||
/**
|
||||
* Disabled by user
|
||||
*/
|
||||
if (!enabledForTool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1st priority.
|
||||
*
|
||||
* If user pass the list of inline tools for the particular tool, return it.
|
||||
*/
|
||||
if (Array.isArray(settingsForTool)) {
|
||||
return settingsForTool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2nd priority.
|
||||
*
|
||||
* If user pass just 'true' for tool, get common inlineToolbar settings
|
||||
* - if common settings is an array, use it
|
||||
* - if common settings is 'true' or not specified, get default order
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common inlineToolbar settings got from the root of EditorConfig
|
||||
*/
|
||||
const commonInlineToolbarSettings = this.config.inlineToolbar;
|
||||
|
||||
/**
|
||||
* If common settings is an array, use it
|
||||
*/
|
||||
if (Array.isArray(commonInlineToolbarSettings)) {
|
||||
return commonInlineToolbarSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* If common settings is 'true' or not specified (will be set as true at core.ts), get the default order
|
||||
*/
|
||||
if (commonInlineToolbarSettings === true) {
|
||||
const defaultToolsOrder: string[] = Object.entries(this.Editor.Tools.available)
|
||||
.filter(([name, tool]) => {
|
||||
return tool[this.Editor.Tools.INTERNAL_SETTINGS.IS_INLINE];
|
||||
})
|
||||
.map(([name, tool]) => {
|
||||
return name;
|
||||
});
|
||||
|
||||
return defaultToolsOrder;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Making DOM
|
||||
*/
|
||||
private make(): void {
|
||||
this.nodes.wrapper = $.make('div', [
|
||||
this.CSS.inlineToolbar,
|
||||
...(this.isRtl ? [ this.Editor.UI.CSS.editorRtlFix ] : []),
|
||||
]);
|
||||
/**
|
||||
* Creates a different wrapper for toggler and buttons.
|
||||
*/
|
||||
this.nodes.togglerAndButtonsWrapper = $.make('div', this.CSS.togglerAndButtonsWrapper);
|
||||
this.nodes.buttons = $.make('div', this.CSS.buttonsWrapper);
|
||||
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
|
||||
|
||||
// To prevent reset of a selection when click on the wrapper
|
||||
this.Editor.Listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
||||
const isClickedOnActionsWrapper = (event.target as Element).closest(`.${this.CSS.actionsWrapper}`);
|
||||
|
||||
// If click is on actions wrapper,
|
||||
// do not prevent default behaviour because actions might include interactive elements
|
||||
if (!isClickedOnActionsWrapper) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Append the intermediary wrapper which contains toggler and buttons and button actions.
|
||||
*/
|
||||
$.append(this.nodes.wrapper, [this.nodes.togglerAndButtonsWrapper, this.nodes.actions]);
|
||||
/**
|
||||
* Append the inline toolbar to the editor.
|
||||
*/
|
||||
$.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper);
|
||||
|
||||
/**
|
||||
* Add button that will allow switching block type
|
||||
*/
|
||||
this.addConversionToggler();
|
||||
|
||||
/**
|
||||
* Wrapper for the inline tools
|
||||
* Will be appended after the Conversion Toolbar toggler
|
||||
*/
|
||||
$.append(this.nodes.togglerAndButtonsWrapper, this.nodes.buttons);
|
||||
|
||||
/**
|
||||
* Prepare conversion toolbar.
|
||||
* If it has any conversion tool then it will be enabled in the Inline Toolbar
|
||||
*/
|
||||
this.prepareConversionToolbar();
|
||||
|
||||
/**
|
||||
* Recalculate initial width with all buttons
|
||||
*/
|
||||
this.recalculateWidth();
|
||||
|
||||
/**
|
||||
* Allow to leaf buttons by arrows / tab
|
||||
* Buttons will be filled on opening
|
||||
*/
|
||||
this.enableFlipper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Need to show Inline Toolbar or not
|
||||
*/
|
||||
|
@ -452,12 +358,59 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* getInlineToolbarSettings could return an string[] (order of tools) or false (Inline Toolbar disabled).
|
||||
*/
|
||||
const inlineToolbarSettings = this.getInlineToolbarSettings(currentBlock.name);
|
||||
const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name);
|
||||
|
||||
return inlineToolbarSettings !== false;
|
||||
return toolSettings && toolSettings[this.Editor.Tools.USER_SETTINGS.ENABLED_INLINE_TOOLS];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show only allowed Tools
|
||||
*/
|
||||
private filterTools(): void {
|
||||
const currentSelection = SelectionUtils.get(),
|
||||
currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
|
||||
|
||||
const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name),
|
||||
inlineToolbarSettings = toolSettings && toolSettings[this.Editor.Tools.USER_SETTINGS.ENABLED_INLINE_TOOLS];
|
||||
|
||||
/**
|
||||
* All Inline Toolbar buttons
|
||||
*
|
||||
* @type {HTMLElement[]}
|
||||
*/
|
||||
const buttons = Array.from(this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`)) as HTMLElement[];
|
||||
|
||||
/**
|
||||
* Show previously hided
|
||||
*/
|
||||
buttons.forEach((button) => {
|
||||
button.hidden = false;
|
||||
button.classList.remove(this.CSS.inlineToolButtonLast);
|
||||
});
|
||||
|
||||
/**
|
||||
* Filter buttons if Block Tool pass config like inlineToolbar=['link']
|
||||
*/
|
||||
if (Array.isArray(inlineToolbarSettings)) {
|
||||
buttons.forEach((button) => {
|
||||
button.hidden = !inlineToolbarSettings.includes(button.dataset.tool);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick for removing right-margin from last visible button.
|
||||
* Current generation of CSS does not allow to filter :visible elements
|
||||
*/
|
||||
const lastVisibleButton = buttons.filter((button) => !button.hidden).pop();
|
||||
|
||||
if (lastVisibleButton) {
|
||||
lastVisibleButton.classList.add(this.CSS.inlineToolButtonLast);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate width because some buttons can be hidden
|
||||
*/
|
||||
this.recalculateWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -480,7 +433,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
this.nodes.conversionToggler.appendChild(this.nodes.conversionTogglerContent);
|
||||
this.nodes.conversionToggler.appendChild(icon);
|
||||
|
||||
this.nodes.togglerAndButtonsWrapper.appendChild(this.nodes.conversionToggler);
|
||||
this.nodes.buttons.appendChild(this.nodes.conversionToggler);
|
||||
|
||||
this.Editor.Listeners.on(this.nodes.conversionToggler, 'click', () => {
|
||||
this.Editor.ConversionToolbar.toggle((conversionToolbarOpened) => {
|
||||
|
@ -553,40 +506,12 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
*/
|
||||
|
||||
/**
|
||||
* Append only allowed Tools
|
||||
* Fill Inline Toolbar with Tools
|
||||
*/
|
||||
private addToolsFiltered(): void {
|
||||
const currentSelection = SelectionUtils.get();
|
||||
const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
|
||||
|
||||
/**
|
||||
* Clear buttons list
|
||||
*/
|
||||
this.nodes.buttons.innerHTML = '';
|
||||
this.nodes.actions.innerHTML = '';
|
||||
this.toolsInstances = new Map();
|
||||
|
||||
/**
|
||||
* Filter buttons if Block Tool pass config like inlineToolbar=['link']
|
||||
* Else filter them according to the default inlineToolbar property.
|
||||
*
|
||||
* For this moment, inlineToolbarOrder could not be 'false'
|
||||
* because this method will be called only if the Inline Toolbar is enabled
|
||||
*/
|
||||
const inlineToolbarOrder = this.getInlineToolbarSettings(currentBlock.name) as string[];
|
||||
|
||||
inlineToolbarOrder.forEach((toolName) => {
|
||||
const toolSettings = this.Editor.Tools.getToolSettings(toolName);
|
||||
const tool = this.Editor.Tools.constructInline(this.Editor.Tools.inline[toolName], toolName, toolSettings);
|
||||
|
||||
this.addTool(toolName, tool);
|
||||
tool.checkState(SelectionUtils.get());
|
||||
private addTools(): void {
|
||||
this.tools.forEach((toolInstance, toolName) => {
|
||||
this.addTool(toolName, toolInstance);
|
||||
});
|
||||
|
||||
/**
|
||||
* Recalculate width because some buttons can be hidden
|
||||
*/
|
||||
this.recalculateWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -612,7 +537,6 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
|
||||
button.dataset.tool = toolName;
|
||||
this.nodes.buttons.appendChild(button);
|
||||
this.toolsInstances.set(toolName, tool);
|
||||
|
||||
if (typeof tool.renderActions === 'function') {
|
||||
const actions = tool.renderActions();
|
||||
|
@ -741,7 +665,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
* Check Tools` state by selection
|
||||
*/
|
||||
private checkToolsState(): void {
|
||||
this.toolsInstances.forEach((toolInstance) => {
|
||||
this.tools.forEach((toolInstance) => {
|
||||
toolInstance.checkState(SelectionUtils.get());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,14 +7,6 @@ import { BlockToolAPI } from '../../block';
|
|||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
|
||||
/**
|
||||
* HTMLElements used for Toolbox UI
|
||||
*/
|
||||
interface ToolboxNodes {
|
||||
toolbox: HTMLElement;
|
||||
buttons: HTMLElement[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Toolbox
|
||||
* @classdesc Holder for Tools
|
||||
|
@ -25,15 +17,7 @@ interface ToolboxNodes {
|
|||
* @property {object} CSS - CSS class names
|
||||
*
|
||||
*/
|
||||
export default class Toolbox extends Module<ToolboxNodes> {
|
||||
/**
|
||||
* Current module HTML Elements
|
||||
*/
|
||||
public nodes = {
|
||||
toolbox: null,
|
||||
buttons: [],
|
||||
}
|
||||
|
||||
export default class Toolbox extends Module {
|
||||
/**
|
||||
* CSS styles
|
||||
*
|
||||
|
@ -68,6 +52,17 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
|||
*/
|
||||
public opened = false;
|
||||
|
||||
/**
|
||||
* HTMLElements used for Toolbox UI
|
||||
*/
|
||||
public nodes: {
|
||||
toolbox: HTMLElement;
|
||||
buttons: HTMLElement[];
|
||||
} = {
|
||||
toolbox: null,
|
||||
buttons: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* How many tools displayed in Toolbox
|
||||
*
|
||||
|
@ -87,20 +82,12 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
|||
*/
|
||||
public make(): void {
|
||||
this.nodes.toolbox = $.make('div', this.CSS.toolbox);
|
||||
$.append(this.Editor.Toolbar.nodes.content, this.nodes.toolbox);
|
||||
|
||||
this.addTools();
|
||||
this.enableFlipper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Module
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.flipper.deactivate();
|
||||
this.flipper = null;
|
||||
this.removeAllNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbox Tool's button click handler
|
||||
*
|
||||
|
@ -196,19 +183,12 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
|||
// return;
|
||||
// }
|
||||
|
||||
const userToolboxSettings = this.Editor.Tools.getToolSettings(toolName)[userSettings.TOOLBOX];
|
||||
|
||||
/**
|
||||
* Hide Toolbox button if Toolbox settings is false
|
||||
*/
|
||||
if ((userToolboxSettings ?? toolToolboxSettings) === false) {
|
||||
return;
|
||||
}
|
||||
const userToolboxSettings = this.Editor.Tools.getToolSettings(toolName)[userSettings.TOOLBOX] || {};
|
||||
|
||||
const button = $.make('li', [ this.CSS.toolboxButton ]);
|
||||
|
||||
button.dataset.tool = toolName;
|
||||
button.innerHTML = (userToolboxSettings && userToolboxSettings.icon) || toolToolboxSettings.icon;
|
||||
button.innerHTML = userToolboxSettings.icon || toolToolboxSettings.icon;
|
||||
|
||||
$.append(this.nodes.toolbox, button);
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ export default class Tools extends Module {
|
|||
/**
|
||||
* Returns available Tools
|
||||
*
|
||||
* @returns {object<Tool>}
|
||||
* @returns {Tool[]}
|
||||
*/
|
||||
public get available(): { [name: string]: ToolConstructable } {
|
||||
public get available(): {[name: string]: ToolConstructable} {
|
||||
return this.toolsAvailable;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @returns {Tool[]}
|
||||
*/
|
||||
public get unavailable(): { [name: string]: ToolConstructable } {
|
||||
public get unavailable(): {[name: string]: ToolConstructable} {
|
||||
return this.toolsUnavailable;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @returns {object} - object of Inline Tool's classes
|
||||
*/
|
||||
public get inline(): { [name: string]: InlineToolConstructable } {
|
||||
public get inline(): {[name: string]: ToolConstructable} {
|
||||
if (this._inlineTools) {
|
||||
return this._inlineTools;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ export default class Tools extends Module {
|
|||
/**
|
||||
* Return editor block tools
|
||||
*/
|
||||
public get blockTools(): { [name: string]: BlockToolConstructable } {
|
||||
public get blockTools(): {[name: string]: BlockToolConstructable} {
|
||||
const tools = Object.entries(this.available).filter(([, tool]) => {
|
||||
return !tool[this.INTERNAL_SETTINGS.IS_INLINE];
|
||||
});
|
||||
|
@ -134,7 +134,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
public get INTERNAL_SETTINGS(): { [name: string]: string } {
|
||||
public get INTERNAL_SETTINGS(): {[name: string]: string} {
|
||||
return {
|
||||
IS_ENABLED_LINE_BREAKS: 'enableLineBreaks',
|
||||
IS_INLINE: 'isInline',
|
||||
|
@ -143,7 +143,6 @@ export default class Tools extends Module {
|
|||
TOOLBOX: 'toolbox',
|
||||
SANITIZE_CONFIG: 'sanitize',
|
||||
CONVERSION_CONFIG: 'conversionConfig',
|
||||
IS_READ_ONLY_SUPPORTED: 'isReadOnlySupported',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,7 +151,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* return {object}
|
||||
*/
|
||||
public get USER_SETTINGS(): { [name: string]: string } {
|
||||
public get USER_SETTINGS(): {[name: string]: string} {
|
||||
return {
|
||||
SHORTCUT: 'shortcut',
|
||||
TOOLBOX: 'toolbox',
|
||||
|
@ -167,24 +166,24 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @type {object}
|
||||
*/
|
||||
public readonly toolsClasses: { [name: string]: ToolConstructable } = {};
|
||||
public readonly toolsClasses: {[name: string]: ToolConstructable} = {};
|
||||
|
||||
/**
|
||||
* Tools` classes available to use
|
||||
*/
|
||||
private readonly toolsAvailable: { [name: string]: ToolConstructable } = {};
|
||||
private readonly toolsAvailable: {[name: string]: ToolConstructable} = {};
|
||||
|
||||
/**
|
||||
* Tools` classes not available to use because of preparation failure
|
||||
*/
|
||||
private readonly toolsUnavailable: { [name: string]: ToolConstructable } = {};
|
||||
private readonly toolsUnavailable: {[name: string]: ToolConstructable} = {};
|
||||
|
||||
/**
|
||||
* Tools settings in a map {name: settings, ...}
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
private readonly toolsSettings: { [name: string]: ToolSettings } = {};
|
||||
private readonly toolsSettings: {[name: string]: ToolSettings} = {};
|
||||
|
||||
/**
|
||||
* Cache for the prepared inline tools
|
||||
|
@ -192,7 +191,7 @@ export default class Tools extends Module {
|
|||
* @type {null|object}
|
||||
* @private
|
||||
*/
|
||||
private _inlineTools: { [name: string]: ToolConstructable } = {};
|
||||
private _inlineTools: {[name: string]: ToolConstructable} = {};
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -301,9 +300,9 @@ export default class Tools extends Module {
|
|||
/**
|
||||
* to see how it works {@link '../utils.ts#sequence'}
|
||||
*/
|
||||
return _.sequence(sequenceData, (data: { toolName: string }) => {
|
||||
return _.sequence(sequenceData, (data: {toolName: string}) => {
|
||||
this.success(data);
|
||||
}, (data: { toolName: string }) => {
|
||||
}, (data: {toolName: string}) => {
|
||||
this.fallback(data);
|
||||
});
|
||||
}
|
||||
|
@ -313,7 +312,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @param {object} data - append tool to available list
|
||||
*/
|
||||
public success(data: { toolName: string }): void {
|
||||
public success(data: {toolName: string}): void {
|
||||
this.toolsAvailable[data.toolName] = this.toolsClasses[data.toolName];
|
||||
}
|
||||
|
||||
|
@ -322,7 +321,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @param {object} data - append tool to unavailable list
|
||||
*/
|
||||
public fallback(data: { toolName: string }): void {
|
||||
public fallback(data: {toolName: string}): void {
|
||||
this.toolsUnavailable[data.toolName] = this.toolsClasses[data.toolName];
|
||||
}
|
||||
|
||||
|
@ -335,11 +334,7 @@ export default class Tools extends Module {
|
|||
*
|
||||
* @returns {InlineTool} — instance
|
||||
*/
|
||||
public constructInline(
|
||||
tool: InlineToolConstructable,
|
||||
name: string,
|
||||
toolSettings: ToolSettings = {} as ToolSettings
|
||||
): InlineTool {
|
||||
public constructInline(tool: InlineToolConstructable, name: string, toolSettings: ToolSettings = {} as ToolSettings): InlineTool {
|
||||
const constructorOptions = {
|
||||
api: this.Editor.API.getMethodsForTool(name),
|
||||
config: (toolSettings[this.USER_SETTINGS.CONFIG] || {}) as ToolSettings,
|
||||
|
@ -350,14 +345,14 @@ export default class Tools extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if passed Tool is an instance of Default Block Tool
|
||||
* Check if passed Tool is an instance of Initial Block Tool
|
||||
*
|
||||
* @param {Tool} tool - Tool to check
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isDefault(tool): boolean {
|
||||
return tool instanceof this.available[this.config.defaultBlock];
|
||||
public isInitial(tool): boolean {
|
||||
return tool instanceof this.available[this.config.initialBlock];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,8 +366,8 @@ export default class Tools extends Module {
|
|||
const settings = this.toolsSettings[toolName];
|
||||
const config = settings[this.USER_SETTINGS.CONFIG] || {};
|
||||
|
||||
// Pass placeholder to default Block config
|
||||
if (toolName === this.config.defaultBlock && !config.placeholder) {
|
||||
// Pass placeholder to initial Block config
|
||||
if (toolName === this.config.initialBlock && !config.placeholder) {
|
||||
config.placeholder = this.config.placeholder;
|
||||
settings[this.USER_SETTINGS.CONFIG] = config;
|
||||
}
|
||||
|
@ -384,7 +379,7 @@ export default class Tools extends Module {
|
|||
* Returns internal tools
|
||||
* Includes Bold, Italic, Link and Paragraph
|
||||
*/
|
||||
public get internalTools(): { [toolName: string]: ToolConstructable | ToolSettings } {
|
||||
public get internalTools(): {[toolName: string]: ToolConstructable|ToolSettings} {
|
||||
return {
|
||||
bold: { class: BoldInlineTool },
|
||||
italic: { class: ItalicInlineTool },
|
||||
|
@ -397,40 +392,19 @@ export default class Tools extends Module {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if tool supports read-only mode
|
||||
*
|
||||
* @param tool - tool to check
|
||||
*/
|
||||
public isReadOnlySupported(tool: BlockToolConstructable): boolean {
|
||||
return tool[this.INTERNAL_SETTINGS.IS_READ_ONLY_SUPPORTED] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls each Tool reset method to clean up anything set by Tool
|
||||
*/
|
||||
public destroy(): void {
|
||||
Object.values(this.available).forEach(async tool => {
|
||||
if (_.isFunction(tool.reset)) {
|
||||
await tool.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds prepare function of plugins with user or default config
|
||||
*
|
||||
* @returns {Array} list of functions that needs to be fired sequentially
|
||||
*/
|
||||
private getListOfPrepareFunctions(): Array<{
|
||||
function: (data: { toolName: string; config: ToolConfig }) => void;
|
||||
data: { toolName: string; config: ToolConfig };
|
||||
function: (data: {toolName: string; config: ToolConfig}) => void;
|
||||
data: {toolName: string; config: ToolConfig};
|
||||
}> {
|
||||
const toolPreparationList: Array<{
|
||||
function: (data: { toolName: string; config: ToolConfig }) => void;
|
||||
data: { toolName: string; config: ToolConfig };
|
||||
}
|
||||
> = [];
|
||||
function: (data: {toolName: string; config: ToolConfig}) => void;
|
||||
data: {toolName: string; config: ToolConfig};}
|
||||
> = [];
|
||||
|
||||
for (const toolName in this.toolsClasses) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.toolsClasses, toolName)) {
|
||||
|
@ -444,7 +418,7 @@ export default class Tools extends Module {
|
|||
*/
|
||||
toolPreparationList.push({
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
function: typeof toolClass.prepare === 'function' ? toolClass.prepare : (): void => { },
|
||||
function: typeof toolClass.prepare === 'function' ? toolClass.prepare : (): void => {},
|
||||
data: {
|
||||
toolName,
|
||||
config: toolConfig,
|
||||
|
|
|
@ -17,16 +17,6 @@ import Selection from '../selection';
|
|||
import Block from '../block';
|
||||
import Flipper from '../flipper';
|
||||
|
||||
/**
|
||||
* HTML Elements used for UI
|
||||
*/
|
||||
interface UINodes {
|
||||
holder: HTMLElement;
|
||||
wrapper: HTMLElement;
|
||||
redactor: HTMLElement;
|
||||
loader: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
|
@ -45,7 +35,7 @@ interface UINodes {
|
|||
* @property {Element} nodes.wrapper - <codex-editor>
|
||||
* @property {Element} nodes.redactor - <ce-redactor>
|
||||
*/
|
||||
export default class UI extends Module<UINodes> {
|
||||
export default class UI extends Module {
|
||||
/**
|
||||
* Editor.js UI CSS class names
|
||||
*
|
||||
|
@ -53,7 +43,7 @@ export default class UI extends Module<UINodes> {
|
|||
*/
|
||||
public get CSS(): {
|
||||
editorWrapper: string; editorWrapperNarrow: string; editorZone: string; editorZoneHidden: string;
|
||||
editorLoader: string; editorEmpty: string; editorRtlFix: string;
|
||||
editorLoader: string; editorEmpty: string;
|
||||
} {
|
||||
return {
|
||||
editorWrapper: 'codex-editor',
|
||||
|
@ -62,7 +52,6 @@ export default class UI extends Module<UINodes> {
|
|||
editorZoneHidden: 'codex-editor__redactor--hidden',
|
||||
editorLoader: 'codex-editor__loader',
|
||||
editorEmpty: 'codex-editor--empty',
|
||||
editorRtlFix: 'codex-editor--rtl',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -101,6 +90,15 @@ export default class UI extends Module<UINodes> {
|
|||
*/
|
||||
public isMobile = false;
|
||||
|
||||
/**
|
||||
* HTML Elements used for UI
|
||||
*/
|
||||
public nodes: { [key: string]: HTMLElement } = {
|
||||
holder: null,
|
||||
wrapper: null,
|
||||
redactor: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache for center column rectangle info
|
||||
* Invalidates on window resize
|
||||
|
@ -147,7 +145,7 @@ export default class UI extends Module<UINodes> {
|
|||
/**
|
||||
* Make main UI elements
|
||||
*/
|
||||
this.make();
|
||||
await this.make();
|
||||
|
||||
/**
|
||||
* Loader for rendering process
|
||||
|
@ -157,47 +155,27 @@ export default class UI extends Module<UINodes> {
|
|||
/**
|
||||
* Append SVG sprite
|
||||
*/
|
||||
this.appendSVGSprite();
|
||||
await this.appendSVGSprite();
|
||||
|
||||
/**
|
||||
* Make toolbar
|
||||
*/
|
||||
await this.Editor.Toolbar.make();
|
||||
|
||||
/**
|
||||
* Make the Inline toolbar
|
||||
*/
|
||||
await this.Editor.InlineToolbar.make();
|
||||
|
||||
/**
|
||||
* Load and append CSS
|
||||
*/
|
||||
this.loadStyles();
|
||||
await this.loadStyles();
|
||||
|
||||
/**
|
||||
* Prepare with read-only state from config
|
||||
* Bind events for the UI elements
|
||||
*/
|
||||
if (!this.Editor.ReadOnly.isEnabled) {
|
||||
this.enableModuleBindings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle read-only state
|
||||
*
|
||||
* If readOnly is true:
|
||||
* - removes all listeners from main UI module elements
|
||||
*
|
||||
* if readOnly is false:
|
||||
* - enables all listeners to UI module elements
|
||||
*
|
||||
* @param {boolean} readOnlyEnabled - "read only" state
|
||||
*/
|
||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
||||
/**
|
||||
* Prepare components based on read-only state
|
||||
*/
|
||||
if (!readOnlyEnabled) {
|
||||
/**
|
||||
* Unbind all events
|
||||
*/
|
||||
this.enableModuleBindings();
|
||||
} else {
|
||||
/**
|
||||
* Bind events for the UI elements
|
||||
*/
|
||||
this.disableModuleBindings();
|
||||
}
|
||||
await this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,8 +239,10 @@ export default class UI extends Module<UINodes> {
|
|||
|
||||
/**
|
||||
* Makes Editor.js interface
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private make(): void {
|
||||
private async make(): Promise<void> {
|
||||
/**
|
||||
* Element where we need to append Editor.js
|
||||
*
|
||||
|
@ -273,10 +253,7 @@ export default class UI extends Module<UINodes> {
|
|||
/**
|
||||
* Create and save main UI elements
|
||||
*/
|
||||
this.nodes.wrapper = $.make('div', [
|
||||
this.CSS.editorWrapper,
|
||||
...(this.isRtl ? [ this.CSS.editorRtlFix ] : []),
|
||||
]);
|
||||
this.nodes.wrapper = $.make('div', this.CSS.editorWrapper);
|
||||
this.nodes.redactor = $.make('div', this.CSS.editorZone);
|
||||
|
||||
/**
|
||||
|
@ -330,48 +307,41 @@ export default class UI extends Module<UINodes> {
|
|||
/**
|
||||
* Bind events on the Editor.js interface
|
||||
*/
|
||||
private enableModuleBindings(): void {
|
||||
this.readOnlyMutableListeners.on(this.nodes.redactor, 'click', (event: MouseEvent) => {
|
||||
this.redactorClicked(event);
|
||||
}, false);
|
||||
private bindEvents(): void {
|
||||
this.Editor.Listeners.on(
|
||||
this.nodes.redactor,
|
||||
'click',
|
||||
(event) => this.redactorClicked(event as MouseEvent),
|
||||
false
|
||||
);
|
||||
this.Editor.Listeners.on(this.nodes.redactor,
|
||||
'mousedown',
|
||||
(event) => this.documentTouched(event as MouseEvent),
|
||||
true
|
||||
);
|
||||
this.Editor.Listeners.on(this.nodes.redactor,
|
||||
'touchstart',
|
||||
(event) => this.documentTouched(event as MouseEvent),
|
||||
true
|
||||
);
|
||||
|
||||
this.readOnlyMutableListeners.on(this.nodes.redactor, 'mousedown', (event: MouseEvent | TouchEvent) => {
|
||||
this.documentTouched(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(this.nodes.redactor, 'touchstart', (event: MouseEvent | TouchEvent) => {
|
||||
this.documentTouched(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(document, 'keydown', (event: KeyboardEvent) => {
|
||||
this.documentKeydown(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(document, 'click', (event: MouseEvent) => {
|
||||
this.documentClicked(event);
|
||||
}, true);
|
||||
this.Editor.Listeners.on(document, 'keydown', (event) => this.documentKeydown(event as KeyboardEvent), true);
|
||||
this.Editor.Listeners.on(document, 'click', (event) => this.documentClicked(event as MouseEvent), true);
|
||||
|
||||
/**
|
||||
* Handle selection change to manipulate Inline Toolbar appearance
|
||||
*/
|
||||
this.readOnlyMutableListeners.on(document, 'selectionchange', (event: Event) => {
|
||||
this.Editor.Listeners.on(document, 'selectionchange', (event: Event) => {
|
||||
this.selectionChanged(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(window, 'resize', () => {
|
||||
this.Editor.Listeners.on(window, 'resize', () => {
|
||||
this.resizeDebouncer();
|
||||
}, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind events on the Editor.js interface
|
||||
*/
|
||||
private disableModuleBindings(): void {
|
||||
this.readOnlyMutableListeners.clearAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize window handler
|
||||
*/
|
||||
|
@ -418,19 +388,10 @@ export default class UI extends Module<UINodes> {
|
|||
* @param {KeyboardEvent} event - keyboard event
|
||||
*/
|
||||
private defaultBehaviour(event: KeyboardEvent): void {
|
||||
const { currentBlock } = this.Editor.BlockManager;
|
||||
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`);
|
||||
const { currentBlock } = this.Editor.BlockManager;
|
||||
const isMetaKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
||||
|
||||
/**
|
||||
* When some block is selected, but the caret is not set inside the editor, treat such keydowns as keydown on selected block.
|
||||
*/
|
||||
if (currentBlock !== undefined && keyDownOnEditor === null) {
|
||||
this.Editor.BlockEvents.keydown(event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore keydowns on editor and meta keys
|
||||
*/
|
||||
|
@ -462,7 +423,7 @@ export default class UI extends Module<UINodes> {
|
|||
if (BlockSelection.anyBlockSelected && !Selection.isSelectionExists) {
|
||||
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
|
||||
|
||||
Caret.setToBlock(BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||
|
||||
/** Clear selection */
|
||||
BlockSelection.clearSelection(event);
|
||||
|
@ -517,6 +478,10 @@ export default class UI extends Module<UINodes> {
|
|||
* remove selected blocks
|
||||
*/
|
||||
if (BlockSelection.anyBlockSelected && !Selection.isSelectionExists) {
|
||||
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
|
||||
|
||||
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||
|
||||
/** Clear selection */
|
||||
BlockSelection.clearSelection(event);
|
||||
|
||||
|
@ -541,7 +506,7 @@ export default class UI extends Module<UINodes> {
|
|||
*/
|
||||
if (!this.someToolbarOpened && hasPointerToBlock && (event.target as HTMLElement).tagName === 'BODY') {
|
||||
/**
|
||||
* Insert the default typed Block
|
||||
* Insert initial typed Block
|
||||
*/
|
||||
const newBlock = this.Editor.BlockManager.insert();
|
||||
|
||||
|
@ -601,6 +566,13 @@ export default class UI extends Module<UINodes> {
|
|||
if (!this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted) {
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear Selection if user clicked somewhere
|
||||
*/
|
||||
if (!this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted) {
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -675,10 +647,8 @@ export default class UI extends Module<UINodes> {
|
|||
return;
|
||||
}
|
||||
|
||||
const stopPropagation = (): void => {
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
};
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
|
||||
/**
|
||||
* case when user clicks on anchor element
|
||||
|
@ -688,8 +658,6 @@ export default class UI extends Module<UINodes> {
|
|||
const ctrlKey = event.metaKey || event.ctrlKey;
|
||||
|
||||
if ($.isAnchor(element) && ctrlKey) {
|
||||
stopPropagation();
|
||||
|
||||
const href = element.getAttribute('href');
|
||||
const validUrl = _.getValidUrl(href);
|
||||
|
||||
|
@ -699,21 +667,17 @@ export default class UI extends Module<UINodes> {
|
|||
}
|
||||
|
||||
if (!this.Editor.BlockManager.currentBlock) {
|
||||
stopPropagation();
|
||||
|
||||
this.Editor.BlockManager.insert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the Plus Button if:
|
||||
* - Block is an default-block (Text)
|
||||
* - Block is an initial-block (Text)
|
||||
* - Block is empty
|
||||
*/
|
||||
const isDefaultBlock = this.Editor.Tools.isDefault(this.Editor.BlockManager.currentBlock.tool);
|
||||
|
||||
if (isDefaultBlock) {
|
||||
stopPropagation();
|
||||
const isInitialBlock = this.Editor.Tools.isInitial(this.Editor.BlockManager.currentBlock.tool);
|
||||
|
||||
if (isInitialBlock) {
|
||||
/**
|
||||
* Check isEmpty only for paragraphs to prevent unnecessary tree-walking on Tools with many nodes (for ex. Table)
|
||||
*/
|
||||
|
@ -732,16 +696,8 @@ export default class UI extends Module<UINodes> {
|
|||
* @param {Event} event - selection event
|
||||
*/
|
||||
private selectionChanged(event: Event): void {
|
||||
const { CrossBlockSelection, BlockSelection } = this.Editor;
|
||||
const focusedElement = Selection.anchorElement as Element;
|
||||
|
||||
if (CrossBlockSelection.isCrossBlockSelectionStarted) {
|
||||
// Removes all ranges when any Block is selected
|
||||
if (BlockSelection.anyBlockSelected) {
|
||||
Selection.get().removeAllRanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event can be fired on clicks at the Editor elements, for example, at the Inline Toolbar
|
||||
* We need to skip such firings
|
||||
|
@ -757,9 +713,6 @@ export default class UI extends Module<UINodes> {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo add debounce
|
||||
*/
|
||||
this.Editor.InlineToolbar.tryToShow(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4832182dcf3874cbaedcb789f682bd61782e49ad
|
||||
Subproject commit 306ed49135905d56ebb7d55f90f26fcd603ca7f1
|
|
@ -1,7 +1,7 @@
|
|||
import $ from '../../dom';
|
||||
import { API, BlockTool, BlockToolData, BlockToolConstructorOptions } from '../../../../types';
|
||||
|
||||
export interface StubData extends BlockToolData {
|
||||
export interface StubData extends BlockToolData{
|
||||
title: string;
|
||||
savedData: BlockToolData;
|
||||
}
|
||||
|
@ -11,11 +11,6 @@ export interface StubData extends BlockToolData {
|
|||
* It will store its data inside and pass it back with article saving
|
||||
*/
|
||||
export default class Stub implements BlockTool {
|
||||
/**
|
||||
* Notify core that tool supports read-only mode
|
||||
*/
|
||||
public static isReadOnlySupported = true;
|
||||
|
||||
/**
|
||||
* Stub styles
|
||||
*
|
||||
|
|
|
@ -183,7 +183,6 @@ export const logLabeled = _log.bind(window, true);
|
|||
export function isPrintableKey(keyCode: number): boolean {
|
||||
return (keyCode > 47 && keyCode < 58) || // number keys
|
||||
keyCode === 32 || keyCode === 13 || // Spacebar & return key(s)
|
||||
keyCode === 229 || // processing key input for certain languages — Chinese, Japanese, etc.
|
||||
(keyCode > 64 && keyCode < 91) || // letter keys
|
||||
(keyCode > 95 && keyCode < 112) || // Numpad keys
|
||||
(keyCode > 185 && keyCode < 193) || // ;=,-./` (in order)
|
||||
|
@ -557,30 +556,3 @@ export function getValidUrl(url: string): string {
|
|||
export function openTab(url: string): void {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random generated identifier
|
||||
*
|
||||
* @param {string} prefix - identifier prefix
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateId(prefix = ''): string {
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
return `${prefix}${(Math.floor(Math.random() * 1e8)).toString(16)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method for printing a warning about the usage of deprecated property or method.
|
||||
*
|
||||
* @param condition - condition for deprecation.
|
||||
* @param oldProperty - deprecated property.
|
||||
* @param newProperty - the property that should be used instead.
|
||||
*/
|
||||
export function deprecationAssert(condition: boolean, oldProperty: string, newProperty: string): void {
|
||||
const message = `«${oldProperty}» is deprecated and will be removed in the next major release. Please use the «${newProperty}» instead.`;
|
||||
|
||||
if (condition) {
|
||||
logLabeled(message, 'warn');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
will-change: transform, opacity;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 0 6px;
|
||||
|
||||
&--showed {
|
||||
opacity: 1;
|
||||
|
@ -38,6 +37,7 @@
|
|||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
|
@ -80,13 +80,6 @@
|
|||
word-spacing: -3px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
&__toggler-and-button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ce-inline-tool {
|
||||
|
@ -105,6 +98,10 @@
|
|||
height: 12px;
|
||||
}
|
||||
|
||||
&--last {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
&--link {
|
||||
.icon--unlink {
|
||||
display: none;
|
||||
|
|
|
@ -9,4 +9,3 @@
|
|||
@import './animations.css';
|
||||
@import './export.css';
|
||||
@import './stub.css';
|
||||
@import './rtl.css';
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
.codex-editor.codex-editor--rtl {
|
||||
direction: rtl;
|
||||
|
||||
.cdx-list {
|
||||
padding-left: 0;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.ce-toolbar {
|
||||
&__plus {
|
||||
right: calc(var(--toolbox-buttons-size) * -1);
|
||||
left: auto;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
right: auto;
|
||||
left: calc(var(--toolbox-buttons-size) * -1);
|
||||
|
||||
@media (--mobile){
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
padding-right: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ce-settings {
|
||||
left: 5px;
|
||||
right: auto;
|
||||
|
||||
&::before{
|
||||
right: auto;
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
&:not(:nth-child(3n+3)) {
|
||||
margin-left: 3px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ce-conversion-tool {
|
||||
&__icon {
|
||||
margin-right: 0px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.ce-inline-toolbar {
|
||||
&__dropdown {
|
||||
border-right: 0px solid transparent;
|
||||
border-left: 1px solid var(--color-gray-border);
|
||||
margin: 0 -6px 0 6px;
|
||||
|
||||
.icon--toggler-down {
|
||||
margin-left: 0px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.codex-editor--narrow.codex-editor--rtl {
|
||||
.ce-toolbar__plus {
|
||||
@media (--not-mobile) {
|
||||
left: 0px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.ce-toolbar__actions {
|
||||
@media (--not-mobile) {
|
||||
left: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +103,6 @@
|
|||
color: var(--grayText);
|
||||
cursor: pointer;
|
||||
background: var(--bg-light);
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-dark);
|
||||
|
|
|
@ -33,13 +33,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&--narrow&--rtl &__redactor {
|
||||
@media (--not-mobile) {
|
||||
margin-left: var(--narrow-mode-right-padding);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--narrow .ce-toolbar__actions {
|
||||
@media (--not-mobile) {
|
||||
right: -5px;
|
||||
|
|
20
src/types-internal/block-data.d.ts
vendored
Normal file
20
src/types-internal/block-data.d.ts
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {BlockToolData} from '../../types/tools';
|
||||
|
||||
/**
|
||||
* Tool's saved data
|
||||
*/
|
||||
export interface SavedData {
|
||||
tool: string;
|
||||
data: BlockToolData;
|
||||
time: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool's data after validation
|
||||
*/
|
||||
export interface ValidatedData {
|
||||
tool?: string;
|
||||
data?: BlockToolData;
|
||||
time?: number;
|
||||
isValid: boolean;
|
||||
}
|
4
src/types-internal/editor-modules.d.ts
vendored
4
src/types-internal/editor-modules.d.ts
vendored
|
@ -35,8 +35,6 @@ import InlineToolbarAPI from '../components/modules/api/inlineToolbar';
|
|||
import CrossBlockSelection from '../components/modules/crossBlockSelection';
|
||||
import ConversionToolbar from '../components/modules/toolbar/conversion';
|
||||
import TooltipAPI from '../components/modules/api/tooltip';
|
||||
import ReadOnly from '../components/modules/readonly';
|
||||
import ReadOnlyAPI from '../components/modules/api/readonly';
|
||||
import I18nAPI from '../components/modules/api/i18n';
|
||||
|
||||
export interface EditorModules {
|
||||
|
@ -77,7 +75,5 @@ export interface EditorModules {
|
|||
CrossBlockSelection: CrossBlockSelection;
|
||||
NotifierAPI: NotifierAPI;
|
||||
TooltipAPI: TooltipAPI;
|
||||
ReadOnly: ReadOnly;
|
||||
ReadOnlyAPI: ReadOnlyAPI;
|
||||
I18nAPI: I18nAPI;
|
||||
}
|
||||
|
|
2
types/api/block.d.ts
vendored
2
types/api/block.d.ts
vendored
|
@ -1,5 +1,5 @@
|
|||
import {BlockToolData, ToolConfig} from '../tools';
|
||||
import {SavedData} from '../data-formats';
|
||||
import {SavedData} from '../../src/types-internal/block-data';
|
||||
|
||||
/**
|
||||
* @interface BlockAPI Describes Block API methods and properties
|
||||
|
|
14
types/api/blocks.d.ts
vendored
14
types/api/blocks.d.ts
vendored
|
@ -13,13 +13,10 @@ export interface Blocks {
|
|||
|
||||
/**
|
||||
* Render passed data
|
||||
*
|
||||
* @param {OutputData} data - saved Block data
|
||||
* @param {boolean} readOnly - the flag that should be used to render a block in the read-only mode
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
* @param {OutputData} data
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
render(data: OutputData, readOnly: boolean): Promise<void>;
|
||||
render(data: OutputData): Promise<void>;
|
||||
|
||||
/**
|
||||
* Render passed HTML string
|
||||
|
@ -50,10 +47,11 @@ export interface Blocks {
|
|||
move(toIndex: number, fromIndex?: number): void;
|
||||
|
||||
/**
|
||||
* Returns Block API object by passed Block index
|
||||
* Returns Block holder by Block index
|
||||
* @param {number} index
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
getBlockByIndex(index: number): BlockAPI | void;
|
||||
getBlockByIndex(index: number): BlockAPI;
|
||||
|
||||
/**
|
||||
* Returns current Block index
|
||||
|
|
1
types/api/index.d.ts
vendored
1
types/api/index.d.ts
vendored
|
@ -11,5 +11,4 @@ export * from './notifier';
|
|||
export * from './tooltip';
|
||||
export * from './inline-toolbar';
|
||||
export * from './block';
|
||||
export * from './readonly';
|
||||
export * from './i18n';
|
||||
|
|
12
types/api/readonly.d.ts
vendored
12
types/api/readonly.d.ts
vendored
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* ReadOnly API
|
||||
*/
|
||||
export interface ReadOnly {
|
||||
/**
|
||||
* Set or toggle read-only state
|
||||
*
|
||||
* @param {Boolean|undefined} state - set or toggle state
|
||||
* @returns {Promise<boolean>} current value
|
||||
*/
|
||||
toggle: (state?: boolean) => Promise<boolean>;
|
||||
}
|
17
types/configs/editor-config.d.ts
vendored
17
types/configs/editor-config.d.ts
vendored
|
@ -25,13 +25,6 @@ export interface EditorConfig {
|
|||
* Name should be equal to one of Tool`s keys of passed tools
|
||||
* If not specified, Paragraph Tool will be used
|
||||
*/
|
||||
defaultBlock?: string;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This property will be deprecated in the next major release.
|
||||
* Use the 'defaultBlock' property instead.
|
||||
*/
|
||||
initialBlock?: string;
|
||||
|
||||
/**
|
||||
|
@ -70,11 +63,6 @@ export interface EditorConfig {
|
|||
*/
|
||||
logLevel?: LogLevels;
|
||||
|
||||
/**
|
||||
* Enable read-only mode
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
|
||||
/**
|
||||
* Internalization config
|
||||
*/
|
||||
|
@ -90,9 +78,4 @@ export interface EditorConfig {
|
|||
* @param {API} api - editor.js api
|
||||
*/
|
||||
onChange?(api: API): void;
|
||||
|
||||
/**
|
||||
* Defines default toolbar for all tools.
|
||||
*/
|
||||
inlineToolbar?: string[]|boolean;
|
||||
}
|
||||
|
|
7
types/configs/i18n-config.d.ts
vendored
7
types/configs/i18n-config.d.ts
vendored
|
@ -7,10 +7,5 @@ export interface I18nConfig {
|
|||
/**
|
||||
* Dictionary used for translation
|
||||
*/
|
||||
messages?: I18nDictionary;
|
||||
|
||||
/**
|
||||
* Text direction. If not set, uses ltr
|
||||
*/
|
||||
direction?: 'ltr' | 'rtl';
|
||||
messages: I18nDictionary;
|
||||
}
|
||||
|
|
20
types/data-formats/block-data.d.ts
vendored
20
types/data-formats/block-data.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
import {BlockToolData} from '../tools';
|
||||
|
||||
/**
|
||||
* Tool's saved data
|
||||
*/
|
||||
export interface SavedData {
|
||||
tool: string;
|
||||
data: BlockToolData;
|
||||
time: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool's data after validation
|
||||
*/
|
||||
export interface ValidatedData {
|
||||
tool?: string;
|
||||
data?: BlockToolData;
|
||||
time?: number;
|
||||
isValid: boolean;
|
||||
}
|
2
types/data-formats/index.d.ts
vendored
2
types/data-formats/index.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
export * from './block-data';
|
||||
export * from './output-data';
|
11
types/index.d.ts
vendored
11
types/index.d.ts
vendored
|
@ -5,13 +5,12 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
EditorConfig,
|
||||
I18nDictionary,
|
||||
Dictionary,
|
||||
DictValue,
|
||||
EditorConfig,
|
||||
I18nConfig,
|
||||
I18nDictionary,
|
||||
} from './configs';
|
||||
|
||||
import {
|
||||
Blocks,
|
||||
Caret,
|
||||
|
@ -19,7 +18,6 @@ import {
|
|||
InlineToolbar,
|
||||
Listeners,
|
||||
Notifier,
|
||||
ReadOnly,
|
||||
Sanitizer,
|
||||
Saver,
|
||||
Selection,
|
||||
|
@ -28,8 +26,7 @@ import {
|
|||
Tooltip,
|
||||
I18n,
|
||||
} from './api';
|
||||
|
||||
import { OutputData } from './data-formats';
|
||||
import {OutputData} from './data-formats/output-data';
|
||||
|
||||
/**
|
||||
* Interfaces used for development
|
||||
|
@ -91,7 +88,6 @@ export interface API {
|
|||
inlineToolbar: InlineToolbar;
|
||||
tooltip: Tooltip;
|
||||
i18n: I18n;
|
||||
readOnly: ReadOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,7 +108,6 @@ declare class EditorJS {
|
|||
public styles: Styles;
|
||||
public toolbar: Toolbar;
|
||||
public inlineToolbar: InlineToolbar;
|
||||
public readOnly: ReadOnly;
|
||||
constructor(configuration?: EditorConfig|string);
|
||||
|
||||
/**
|
||||
|
|
17
types/tools/block-tool.d.ts
vendored
17
types/tools/block-tool.d.ts
vendored
|
@ -16,11 +16,6 @@ export interface BlockTool extends BaseTool {
|
|||
*/
|
||||
sanitize?: SanitizerConfig;
|
||||
|
||||
/**
|
||||
* @param {boolean} readOnly - render HTML on readonly mode
|
||||
*/
|
||||
render(readOnly?: boolean): HTMLElement;
|
||||
|
||||
/**
|
||||
* Process Tool's element in DOM and return raw data
|
||||
* @param {HTMLElement} block - element created by {@link BlockTool#render} function
|
||||
|
@ -54,12 +49,6 @@ export interface BlockTool extends BaseTool {
|
|||
*/
|
||||
onPaste?(event: PasteEvent): void;
|
||||
|
||||
/**
|
||||
* Cleanup resources used by your tool here
|
||||
* Called when the editor is destroyed
|
||||
*/
|
||||
destroy?(): void;
|
||||
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
|
@ -93,7 +82,6 @@ export interface BlockToolConstructorOptions<D extends object = any, C extends o
|
|||
data: BlockToolData<D>;
|
||||
config?: ToolConfig<C>;
|
||||
block?: BlockAPI;
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
export interface BlockToolConstructable extends BaseToolConstructable {
|
||||
|
@ -122,11 +110,6 @@ export interface BlockToolConstructable extends BaseToolConstructable {
|
|||
*/
|
||||
conversionConfig?: ConversionConfig;
|
||||
|
||||
/**
|
||||
* Is Tool supports read-only mode, this property should return true
|
||||
*/
|
||||
isReadOnlySupported?: boolean;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
|
|
2
types/tools/inline-tool.d.ts
vendored
2
types/tools/inline-tool.d.ts
vendored
|
@ -31,8 +31,6 @@ export interface InlineTool extends BaseTool {
|
|||
|
||||
/**
|
||||
* Function called with Inline Toolbar closing
|
||||
* @deprecated 2020 10/02 - The new instance will be created each time the button is rendered. So clear is not needed.
|
||||
* Better to create the 'destroy' method in a future.
|
||||
*/
|
||||
clear?(): void;
|
||||
}
|
||||
|
|
3
types/tools/tool-settings.d.ts
vendored
3
types/tools/tool-settings.d.ts
vendored
|
@ -44,7 +44,6 @@ export interface ToolSettings {
|
|||
|
||||
/**
|
||||
* Tool's Toolbox settings
|
||||
* It will be hidden from Toolbox when false is specified.
|
||||
*/
|
||||
toolbox?: ToolboxConfig | false;
|
||||
toolbox?: ToolboxConfig;
|
||||
}
|
||||
|
|
5
types/tools/tool.d.ts
vendored
5
types/tools/tool.d.ts
vendored
|
@ -40,9 +40,4 @@ export interface BaseToolConstructable {
|
|||
* @param data
|
||||
*/
|
||||
prepare?(data: {toolName: string, config: ToolConfig}): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Tool`s reset method to clean up anything set by prepare. Can be async
|
||||
*/
|
||||
reset?(): void | Promise<void>;
|
||||
}
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -1230,9 +1230,8 @@ bluebird@^3.5.5:
|
|||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
|
||||
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||
version "4.11.9"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
|
||||
integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
|
||||
version "4.11.8"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||
|
||||
boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -1269,7 +1268,6 @@ braces@^3.0.1:
|
|||
brorand@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
|
||||
version "1.2.0"
|
||||
|
@ -2159,9 +2157,8 @@ electron-to-chromium@^1.3.413:
|
|||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.418.tgz#840021191f466b803a873e154113620c9f53cec6"
|
||||
|
||||
elliptic@^6.0.0:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
|
||||
integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
|
||||
dependencies:
|
||||
bn.js "^4.4.0"
|
||||
brorand "^1.0.1"
|
||||
|
@ -2930,7 +2927,6 @@ hash-base@^3.0.0:
|
|||
hash.js@^1.0.0, hash.js@^1.0.3:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
@ -2942,7 +2938,6 @@ hex-color-regex@^1.1.0:
|
|||
hmac-drbg@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
|
||||
dependencies:
|
||||
hash.js "^1.0.3"
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
@ -3094,7 +3089,6 @@ inflight@^1.0.4:
|
|||
inherits@2, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.1:
|
||||
version "2.0.1"
|
||||
|
@ -3796,12 +3790,10 @@ min-indent@^1.0.0:
|
|||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||
|
||||
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
|
|
Loading…
Reference in a new issue