[Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md
This commit is contained in:
Peter Savchenko 2020-04-11 21:07:30 +03:00 committed by GitHub
parent be6f9b78f2
commit f5e9a6648e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 1694 additions and 982 deletions

4
.eslintignore Normal file
View file

@ -0,0 +1,4 @@
node_modules
*.d.ts
src/components/tools/paragraph
src/polyfills.ts

106
.eslintrc
View file

@ -1,92 +1,18 @@
{ {
/** Enable ES6 features */ "extends": [
"parserOptions": { "codex"
"ecmaVersion": 2017, ],
"sourceType": "module" "rules": {
}, /**
"rules": { * Temporary suppress some errors. We need to fix them partially in next patches
*/
"arrow-spacing": [2, { "@typescript-eslint/explicit-function-return-type": ["warn"],
"before": true, "@typescript-eslint/explicit-member-accessibility": ["warn"],
"after": true "@typescript-eslint/member-ordering": ["warn"],
}], "@typescript-eslint/no-empty-function": ["warn"],
"no-prototype-builtins": ["warn"],
/** Variables */ "no-mixed-operators": ["warn"],
"no-catch-shadow": 2, "import/no-duplicates": ["warn"],
"no-delete-var": 2, "no-case-declarations": ["warn"]
"no-label-var": 2, }
"no-shadow-restricted-names": 2,
"no-shadow": 2,
"no-undef-init": 2,
"no-undef": 2,
"no-unused-vars": 0,
/** Style */
"array-bracket-spacing": [2, "never", {
"singleValue": true,
"objectsInArrays": true,
"arraysInArrays": true
}],
"quotes": [2, "single", {
"avoidEscape": true,
"allowTemplateLiterals": true
}],
"eqeqeq": 0,
"brace-style": [2, "1tbs"],
"comma-spacing": [2, {
"before": false,
"after": true
}],
"comma-style": [2, "last"],
"eol-last": 0,
"no-nested-ternary": 1,
"no-trailing-spaces": 2,
"no-mixed-spaces-and-tabs": 2,
"padded-blocks": [2, "never"],
"space-before-blocks": 1,
"space-before-function-paren": [1, {
"anonymous": "always",
"named": "never"
}],
"spaced-comment": [2, "always", {
"exceptions": ["-", "+"],
"markers": ["=", "!"]
}],
"semi": [2, "always"],
"indent": [2, 2, {
"SwitchCase": 1
}],
"camelcase": [2, {
"properties": "always"
}],
"newline-after-var": [1, "always"]
},
"globals":{
"document": true,
"module": true,
"require": true,
"window": true,
"console" : true,
"codex": true,
"VERSION" : true,
"Promise" : true,
"MutationObserver": true,
"FormData": true,
"XMLHttpRequest": true,
"ActiveXObject": true,
"RegExp": true,
"Module": true,
"Node": true,
"Element": true,
"DocumentFragment": true,
"Proxy": true,
"Symbol": true,
"$": true,
"_": true,
"setTimeout": true,
"process": true,
"__dirname": true,
"Map": true
}
} }

24
.github/workflows/eslint.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: ESLint CodeX
on: [pull_request]
jobs:
lint:
name: ESlint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.OS }}-build-${{ env.cache-name }}-
${{ runner.OS }}-build-
${{ runner.OS }}-
- run: yarn install
- run: yarn lint

2
dist/editor.js vendored

File diff suppressed because one or more lines are too long

View file

@ -25,6 +25,7 @@
* Editor.js * Editor.js
* *
* Short Description (눈_눈;) * Short Description (눈_눈;)
*
* @version 2.0 * @version 2.0
* *
* @licence Apache-2.0 * @licence Apache-2.0

View file

@ -1,5 +1,10 @@
# Changelog # Changelog
### 2.18
- `Improvements` - Deprecated TSLint replaced with ESLint, old config changed to [CodeX ESLint Config](https://github.com/codex-team/eslint-config).
- `Improvements` - Adjusted GitHub action for ESLint.
### 2.17 ### 2.17
- `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter - `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter

View file

@ -16,6 +16,9 @@
"build:win": "rimraf dist && yarn svg:win && yarn build:prod", "build:win": "rimraf dist && yarn svg:win && yarn build:prod",
"build:dev": "webpack --mode development --progress --display-error-details --display-entrypoints --watch", "build:dev": "webpack --mode development --progress --display-error-details --display-entrypoints --watch",
"build:prod": "webpack --mode production", "build:prod": "webpack --mode production",
"lint": "eslint src/ --ext .ts",
"lint:errors": "eslint src/ --ext .ts --quiet",
"lint:fix": "eslint src/ --ext .ts --fix",
"svg:win": "if not exist dist md dist && yarn svg", "svg:win": "if not exist dist md dist && yarn svg",
"svg": "svg-sprite-generate -d src/assets/ -o dist/sprite.svg", "svg": "svg-sprite-generate -d src/assets/ -o dist/sprite.svg",
"pull_tools": "git submodule update --init --recursive", "pull_tools": "git submodule update --init --recursive",
@ -43,7 +46,8 @@
"core-js": "3", "core-js": "3",
"css-loader": "^3.2.1", "css-loader": "^3.2.1",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"eslint": "^6.7.2", "eslint": "^6.8.0",
"eslint-config-codex": "^1.3.2",
"eslint-loader": "^3.0.3", "eslint-loader": "^3.0.3",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"html-janitor": "^2.0.4", "html-janitor": "^2.0.4",
@ -61,7 +65,6 @@
"terser-webpack-plugin": "^2.2.2", "terser-webpack-plugin": "^2.2.2",
"ts-loader": "^6.2.1", "ts-loader": "^6.2.1",
"tslint": "^5.14.0", "tslint": "^5.14.0",
"tslint-loader": "^3.5.4",
"typescript": "^3.7.3", "typescript": "^3.7.3",
"webpack": "^4.29.6", "webpack": "^4.29.6",
"webpack-cli": "^3.2.3" "webpack-cli": "^3.2.3"

View file

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import {EditorConfig} from '../types';
declare const VERSION: string; import { EditorConfig } from '../types';
/** /**
* Apply polyfills * Apply polyfills
@ -11,10 +10,13 @@ import '@babel/register';
import 'components/polyfills'; import 'components/polyfills';
import Core from './components/core'; import Core from './components/core';
declare const VERSION: string;
/** /**
* Editor.js * Editor.js
* *
* Short Description (_눈;) * Short Description (_눈;)
*
* @version 2.0 * @version 2.0
* *
* @licence Apache-2.0 * @licence Apache-2.0
@ -38,11 +40,9 @@ export default class EditorJS {
} }
/** /**
* @constructor * @param {EditorConfig|string|undefined} [configuration] - user configuration
*
* @param {EditorConfig|String|undefined} [configuration] - user configuration
*/ */
public constructor(configuration?: EditorConfig|string) { constructor(configuration?: EditorConfig|string) {
/** /**
* Set default onReady function * Set default onReady function
*/ */
@ -63,6 +63,7 @@ export default class EditorJS {
/** /**
* We need to export isReady promise in the constructor * We need to export isReady promise in the constructor
* as it can be used before other API methods are exported * as it can be used before other API methods are exported
*
* @type {Promise<void>} * @type {Promise<void>}
*/ */
this.isReady = editor.isReady.then(() => { this.isReady = editor.isReady.then(() => {

View file

@ -1,6 +1,6 @@
import {EditorModules} from '../types-internal/editor-modules'; import { EditorModules } from '../types-internal/editor-modules';
import {EditorConfig} from '../../types'; import { EditorConfig } from '../../types';
import {ModuleConfig} from '../types-internal/module-config'; import { ModuleConfig } from '../types-internal/module-config';
/** /**
* @abstract * @abstract
@ -8,28 +8,29 @@ import {ModuleConfig} from '../types-internal/module-config';
* @classdesc All modules inherits from this class. * @classdesc All modules inherits from this class.
* *
* @typedef {Module} Module * @typedef {Module} Module
* @property {Object} config - Editor user settings * @property {object} config - Editor user settings
* @property {EditorModules} Editor - List of Editor modules * @property {EditorModules} Editor - List of Editor modules
*/ */
export default class Module { export default class Module {
/** /**
* Editor modules list * Editor modules list
*
* @type {EditorModules} * @type {EditorModules}
*/ */
protected Editor: EditorModules; protected Editor: EditorModules;
/** /**
* Editor configuration object * Editor configuration object
*
* @type {EditorConfig} * @type {EditorConfig}
*/ */
protected config: EditorConfig; protected config: EditorConfig;
/** /**
* @constructor * @class
* @param {EditorConfig} * @param {EditorConfig}
*/ */
constructor({config}: ModuleConfig) { constructor({ config }: ModuleConfig) {
if (new.target === Module) { if (new.target === Module) {
throw new TypeError('Constructors for abstract class Module are not allowed.'); throw new TypeError('Constructors for abstract class Module are not allowed.');
} }
@ -39,6 +40,7 @@ export default class Module {
/** /**
* Editor modules setter * Editor modules setter
*
* @param {EditorModules} Editor * @param {EditorModules} Editor
*/ */
set state(Editor: EditorModules) { set state(Editor: EditorModules) {

View file

@ -4,13 +4,16 @@
* *
* @copyright <CodeX Team> 2018 * @copyright <CodeX Team> 2018
*/ */
import {API, BlockTune} from '../../../types'; import { API, BlockTune } from '../../../types';
import $ from '../dom'; import $ from '../dom';
/**
*
*/
export default class DeleteTune implements BlockTune { export default class DeleteTune implements BlockTune {
/** /**
* Property that contains Editor.js API methods * Property that contains Editor.js API methods
*
* @see {docs/api.md} * @see {docs/api.md}
*/ */
private readonly api: API; private readonly api: API;
@ -46,7 +49,7 @@ export default class DeleteTune implements BlockTune {
* *
* @param {{api: API}} api * @param {{api: API}} api
*/ */
constructor({api}) { constructor({ api }) {
this.api = api; this.api = api;
this.resetConfirmation = () => { this.resetConfirmation = () => {
@ -56,6 +59,7 @@ export default class DeleteTune implements BlockTune {
/** /**
* Create "Delete" button and add click event listener * Create "Delete" button and add click event listener
*
* @returns [Element} * @returns [Element}
*/ */
public render() { public render() {
@ -73,10 +77,10 @@ export default class DeleteTune implements BlockTune {
/** /**
* Delete block conditions passed * Delete block conditions passed
*
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
public handleClick(event: MouseEvent): void { public handleClick(event: MouseEvent): void {
/** /**
* if block is not waiting the confirmation, subscribe on block-settings-closing event to reset * if block is not waiting the confirmation, subscribe on block-settings-closing event to reset
* otherwise delete block * otherwise delete block
@ -90,9 +94,7 @@ export default class DeleteTune implements BlockTune {
* then reset confirmation state * then reset confirmation state
*/ */
this.api.events.on('block-settings-closed', this.resetConfirmation); this.api.events.on('block-settings-closed', this.resetConfirmation);
} else { } else {
/** /**
* Unsubscribe from block-settings closing event * Unsubscribe from block-settings closing event
*/ */
@ -111,10 +113,11 @@ export default class DeleteTune implements BlockTune {
/** /**
* change tune state * change tune state
*
* @param state
*/ */
private setConfirmation(state): void { private setConfirmation(state): void {
this.needConfirmation = state; this.needConfirmation = state;
this.nodes.button.classList.add(this.CSS.buttonConfirm); this.nodes.button.classList.add(this.CSS.buttonConfirm);
} }
} }

View file

@ -6,17 +6,22 @@
*/ */
import $ from '../dom'; import $ from '../dom';
import {API, BlockTune} from '../../../types'; import { API, BlockTune } from '../../../types';
/**
*
*/
export default class MoveDownTune implements BlockTune { export default class MoveDownTune implements BlockTune {
/** /**
* Property that contains Editor.js API methods * Property that contains Editor.js API methods
*
* @see {api.md} * @see {api.md}
*/ */
private readonly api: API; private readonly api: API;
/** /**
* Styles * Styles
*
* @type {{wrapper: string}} * @type {{wrapper: string}}
*/ */
private CSS = { private CSS = {
@ -30,7 +35,7 @@ export default class MoveDownTune implements BlockTune {
* *
* @param {{api: API}} api * @param {{api: API}} api
*/ */
public constructor({api}) { constructor({ api }) {
this.api = api; this.api = api;
} }
@ -39,12 +44,13 @@ export default class MoveDownTune implements BlockTune {
*/ */
public render() { public render() {
const moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); const moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
moveDownButton.appendChild($.svg('arrow-down', 14, 14)); moveDownButton.appendChild($.svg('arrow-down', 14, 14));
this.api.listeners.on( this.api.listeners.on(
moveDownButton, moveDownButton,
'click', 'click',
(event) => this.handleClick(event as MouseEvent, moveDownButton), (event) => this.handleClick(event as MouseEvent, moveDownButton),
false, false
); );
/** /**
@ -57,20 +63,21 @@ export default class MoveDownTune implements BlockTune {
/** /**
* Handle clicks on 'move down' button * Handle clicks on 'move down' button
*
* @param {MouseEvent} event * @param {MouseEvent} event
* @param {HTMLElement} button * @param {HTMLElement} button
*/ */
public handleClick(event: MouseEvent, button: HTMLElement) { public handleClick(event: MouseEvent, button: HTMLElement) {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
// If Block is last do nothing // If Block is last do nothing
if (currentBlockIndex === this.api.blocks.getBlocksCount() - 1) { if (currentBlockIndex === this.api.blocks.getBlocksCount() - 1) {
button.classList.add(this.CSS.animation); button.classList.add(this.CSS.animation);
window.setTimeout( () => { window.setTimeout(() => {
button.classList.remove(this.CSS.animation); button.classList.remove(this.CSS.animation);
}, 500); }, 500);
return; return;
} }
@ -84,9 +91,7 @@ export default class MoveDownTune implements BlockTune {
* Increment scroll by next block's height to save element onscreen-position * Increment scroll by next block's height to save element onscreen-position
*/ */
if (nextBlockCoords.top < window.innerHeight) { if (nextBlockCoords.top < window.innerHeight) {
scrollOffset = window.scrollY + nextBlockElement.offsetHeight; scrollOffset = window.scrollY + nextBlockElement.offsetHeight;
} }
window.scrollTo(0, scrollOffset); window.scrollTo(0, scrollOffset);

View file

@ -5,18 +5,22 @@
* @copyright <CodeX Team> 2018 * @copyright <CodeX Team> 2018
*/ */
import $ from '../dom'; import $ from '../dom';
import {API, BlockTune} from '../../../types'; import { API, BlockTune } from '../../../types';
/**
*
*/
export default class MoveUpTune implements BlockTune { export default class MoveUpTune implements BlockTune {
/** /**
* Property that contains Editor.js API methods * Property that contains Editor.js API methods
*
* @see {api.md} * @see {api.md}
*/ */
private readonly api: API; private readonly api: API;
/** /**
* Styles * Styles
*
* @type {{wrapper: string}} * @type {{wrapper: string}}
*/ */
private CSS = { private CSS = {
@ -30,22 +34,24 @@ export default class MoveUpTune implements BlockTune {
* *
* @param {{api: API}} api * @param {{api: API}} api
*/ */
public constructor({api}) { constructor({ api }) {
this.api = api; this.api = api;
} }
/** /**
* Create "MoveUp" button and add click event listener * Create "MoveUp" button and add click event listener
*
* @returns [HTMLElement} * @returns [HTMLElement}
*/ */
public render(): HTMLElement { public render(): HTMLElement {
const moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); const moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
moveUpButton.appendChild($.svg('arrow-up', 14, 14)); moveUpButton.appendChild($.svg('arrow-up', 14, 14));
this.api.listeners.on( this.api.listeners.on(
moveUpButton, moveUpButton,
'click', 'click',
(event) => this.handleClick(event as MouseEvent, moveUpButton), (event) => this.handleClick(event as MouseEvent, moveUpButton),
false, false
); );
/** /**
@ -58,19 +64,20 @@ export default class MoveUpTune implements BlockTune {
/** /**
* Move current block up * Move current block up
*
* @param {MouseEvent} event * @param {MouseEvent} event
* @param {HTMLElement} button * @param {HTMLElement} button
*/ */
public handleClick(event: MouseEvent, button: HTMLElement): void { public handleClick(event: MouseEvent, button: HTMLElement): void {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
if (currentBlockIndex === 0) { if (currentBlockIndex === 0) {
button.classList.add(this.CSS.animation); button.classList.add(this.CSS.animation);
window.setTimeout( () => { window.setTimeout(() => {
button.classList.remove(this.CSS.animation); button.classList.remove(this.CSS.animation);
}, 500); }, 500);
return; return;
} }
@ -86,7 +93,7 @@ export default class MoveUpTune implements BlockTune {
* than we scroll window to the difference between this offsets. * than we scroll window to the difference between this offsets.
*/ */
const currentBlockCoords = currentBlockElement.getBoundingClientRect(), const currentBlockCoords = currentBlockElement.getBoundingClientRect(),
previousBlockCoords = previousBlockElement.getBoundingClientRect(); previousBlockCoords = previousBlockElement.getBoundingClientRect();
let scrollUpOffset; let scrollUpOffset;

View file

@ -6,10 +6,10 @@ import {
BlockTune, BlockTune,
BlockTuneConstructable, BlockTuneConstructable,
SanitizerConfig, SanitizerConfig,
ToolConfig, ToolConfig
} from '../../types'; } from '../../types';
import {SavedData} from '../types-internal/block-data'; import { SavedData } from '../types-internal/block-data';
import $ from './dom'; import $ from './dom';
import * as _ from './utils'; import * as _ from './utils';
@ -18,7 +18,7 @@ import * as _ from './utils';
* @classdesc This class describes editor`s block, including block`s HTMLElement, data and tool * @classdesc This class describes editor`s block, including block`s HTMLElement, data and tool
* *
* @property {BlockTool} tool current block tool (Paragraph, for example) * @property {BlockTool} tool current block tool (Paragraph, for example)
* @property {Object} CSS block`s css classes * @property {object} CSS block`s css classes
* *
*/ */
@ -53,10 +53,10 @@ export enum BlockToolAPI {
* @property pluginsContent - HTML content that returns by Tool's render function * @property pluginsContent - HTML content that returns by Tool's render function
*/ */
export default class Block { export default class Block {
/** /**
* CSS classes for the Block * CSS classes for the Block
* @return {{wrapper: string, content: string}} *
* @returns {{wrapper: string, content: string}}
*/ */
static get CSS() { static get CSS() {
return { return {
@ -85,8 +85,8 @@ export default class Block {
const content = this.holder; const content = this.holder;
const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url']; const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url'];
const selector = '[contenteditable], textarea, input:not([type]), ' const selector = '[contenteditable], textarea, input:not([type]), ' +
+ allowedInputTypes.map((type) => `input[type="${type}"]`).join(', '); allowedInputTypes.map((type) => `input[type="${type}"]`).join(', ');
let inputs = _.array(content.querySelectorAll(selector)); let inputs = _.array(content.querySelectorAll(selector));
@ -178,7 +178,8 @@ export default class Block {
/** /**
* Returns Plugins content * Returns Plugins content
* @return {HTMLElement} *
* @returns {HTMLElement}
*/ */
get pluginsContent(): HTMLElement { get pluginsContent(): HTMLElement {
const blockContentNodes = this.holder.querySelector(`.${Block.CSS.content}`); const blockContentNodes = this.holder.querySelector(`.${Block.CSS.content}`);
@ -202,7 +203,8 @@ export default class Block {
/** /**
* Get Block's JSON data * Get Block's JSON data
* @return {Object} *
* @returns {object}
*/ */
get data(): BlockToolData { get data(): BlockToolData {
return this.save().then((savedObject) => { return this.save().then((savedObject) => {
@ -216,7 +218,8 @@ export default class Block {
/** /**
* Returns tool's sanitizer config * Returns tool's sanitizer config
* @return {object} *
* @returns {object}
*/ */
get sanitize(): SanitizerConfig { get sanitize(): SanitizerConfig {
return this.tool.sanitize; return this.tool.sanitize;
@ -225,7 +228,8 @@ export default class Block {
/** /**
* is block mergeable * is block mergeable
* We plugin have merge function then we call it mergable * We plugin have merge function then we call it mergable
* @return {boolean} *
* @returns {boolean}
*/ */
get mergeable(): boolean { get mergeable(): boolean {
return typeof this.tool.merge === 'function'; return typeof this.tool.merge === 'function';
@ -233,7 +237,8 @@ export default class Block {
/** /**
* Check block for emptiness * Check block for emptiness
* @return {Boolean} *
* @returns {boolean}
*/ */
get isEmpty(): boolean { get isEmpty(): boolean {
const emptyText = $.isEmpty(this.pluginsContent); const emptyText = $.isEmpty(this.pluginsContent);
@ -244,11 +249,13 @@ export default class Block {
/** /**
* Check if block has a media content such as images, iframes and other * Check if block has a media content such as images, iframes and other
* @return {Boolean} *
* @returns {boolean}
*/ */
get hasMedia(): boolean { get hasMedia(): boolean {
/** /**
* This tags represents media-content * This tags represents media-content
*
* @type {string[]} * @type {string[]}
*/ */
const mediaTags = [ const mediaTags = [
@ -267,7 +274,8 @@ export default class Block {
/** /**
* Set focused state * Set focused state
* @param {Boolean} state - 'true' to select, 'false' to remove selection *
* @param {boolean} state - 'true' to select, 'false' to remove selection
*/ */
set focused(state: boolean) { set focused(state: boolean) {
this.holder.classList.toggle(Block.CSS.focused, state); this.holder.classList.toggle(Block.CSS.focused, state);
@ -276,7 +284,8 @@ export default class Block {
/** /**
* Set selected state * Set selected state
* We don't need to mark Block as Selected when it is empty * We don't need to mark Block as Selected when it is empty
* @param {Boolean} state - 'true' to select, 'false' to remove selection *
* @param {boolean} state - 'true' to select, 'false' to remove selection
*/ */
set selected(state: boolean) { set selected(state: boolean) {
if (state) { if (state) {
@ -288,7 +297,8 @@ export default class Block {
/** /**
* Returns True if it is Selected * Returns True if it is Selected
* @return {boolean} *
* @returns {boolean}
*/ */
get selected(): boolean { get selected(): boolean {
return this.holder.classList.contains(Block.CSS.selected); return this.holder.classList.contains(Block.CSS.selected);
@ -296,7 +306,8 @@ export default class Block {
/** /**
* Set stretched state * Set stretched state
* @param {Boolean} state - 'true' to enable, 'false' to disable stretched statte *
* @param {boolean} state - 'true' to enable, 'false' to disable stretched statte
*/ */
set stretched(state: boolean) { set stretched(state: boolean) {
this.holder.classList.toggle(Block.CSS.wrapperStretched, state); this.holder.classList.toggle(Block.CSS.wrapperStretched, state);
@ -304,6 +315,7 @@ export default class Block {
/** /**
* Toggle drop target state * Toggle drop target state
*
* @param {boolean} state * @param {boolean} state
*/ */
public set dropTarget(state) { public set dropTarget(state) {
@ -342,6 +354,7 @@ export default class Block {
/** /**
* Cached inputs * Cached inputs
*
* @type {HTMLElement[]} * @type {HTMLElement[]}
*/ */
private cachedInputs: HTMLElement[] = []; private cachedInputs: HTMLElement[] = [];
@ -353,18 +366,21 @@ export default class Block {
/** /**
* Focused input index * Focused input index
*
* @type {number} * @type {number}
*/ */
private inputIndex = 0; private inputIndex = 0;
/** /**
* Mutation observer to handle DOM mutations * Mutation observer to handle DOM mutations
*
* @type {MutationObserver} * @type {MutationObserver}
*/ */
private mutationObserver: MutationObserver; private mutationObserver: MutationObserver;
/** /**
* Debounce Timer * Debounce Timer
*
* @type {number} * @type {number}
*/ */
private readonly modificationDebounceTimer = 450; private readonly modificationDebounceTimer = 450;
@ -387,19 +403,19 @@ export default class Block {
}, this.modificationDebounceTimer); }, this.modificationDebounceTimer);
/** /**
* @constructor * @class
* @param {String} toolName - Tool name that passed on initialization * @param {string} toolName - Tool name that passed on initialization
* @param {Object} toolInstance passed Tool`s instance that rendered the Block * @param {object} toolInstance passed Tool`s instance that rendered the Block
* @param {Object} toolClass Tool's class * @param {object} toolClass Tool's class
* @param {Object} settings - default settings * @param {object} settings - default settings
* @param {Object} apiMethods - Editor API * @param {object} apiMethods - Editor API
*/ */
constructor( constructor(
toolName: string, toolName: string,
toolInstance: BlockTool, toolInstance: BlockTool,
toolClass: BlockToolConstructable, toolClass: BlockToolConstructable,
settings: ToolConfig, settings: ToolConfig,
apiMethods: API, apiMethods: API
) { ) {
this.name = toolName; this.name = toolName;
this.tool = toolInstance; this.tool = toolInstance;
@ -421,8 +437,8 @@ export default class Block {
* *
* Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function * Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function
* *
* @param {String} methodName * @param {string} methodName
* @param {Object} params * @param {object} params
*/ */
public call(methodName: string, params?: object) { public call(methodName: string, params?: object) {
/** /**
@ -430,6 +446,7 @@ export default class Block {
*/ */
if (this.tool[methodName] && this.tool[methodName] instanceof Function) { if (this.tool[methodName] && this.tool[methodName] instanceof Function) {
try { try {
// eslint-disable-next-line no-useless-call
this.tool[methodName].call(this.tool, params); this.tool[methodName].call(this.tool, params);
} catch (e) { } catch (e) {
_.log(`Error during '${methodName}' call: ${e.message}`, 'error'); _.log(`Error during '${methodName}' call: ${e.message}`, 'error');
@ -439,15 +456,18 @@ export default class Block {
/** /**
* Call plugins merge method * Call plugins merge method
* @param {Object} data *
* @param {object} data
*/ */
public async mergeWith(data: BlockToolData): Promise<void> { public async mergeWith(data: BlockToolData): Promise<void> {
await this.tool.merge(data); await this.tool.merge(data);
} }
/** /**
* Extracts data from Block * Extracts data from Block
* Groups Tool's save processing time * Groups Tool's save processing time
* @return {Object} *
* @returns {object}
*/ */
public async save(): Promise<void|SavedData> { public async save(): Promise<void|SavedData> {
const extractedBlock = await this.tool.save(this.pluginsContent as HTMLElement); const extractedBlock = await this.tool.save(this.pluginsContent as HTMLElement);
@ -496,14 +516,15 @@ export default class Block {
/** /**
* Make an array with default settings * Make an array with default settings
* Each block has default tune instance that have states * Each block has default tune instance that have states
* @return {BlockTune[]} *
* @returns {BlockTune[]}
*/ */
public makeTunes(): BlockTune[] { public makeTunes(): BlockTune[] {
const tunesList = [MoveUpTune, DeleteTune, MoveDownTune]; const tunesList = [MoveUpTune, DeleteTune, MoveDownTune];
// Pluck tunes list and return tune instances with passed Editor API and settings // Pluck tunes list and return tune instances with passed Editor API and settings
return tunesList.map( (tune: BlockTuneConstructable) => { return tunesList.map((Tune: BlockTuneConstructable) => {
return new tune({ return new Tune({
api: this.api, api: this.api,
settings: this.settings, settings: this.settings,
}); });
@ -512,12 +533,13 @@ export default class Block {
/** /**
* Enumerates initialized tunes and returns fragment that can be appended to the toolbars area * Enumerates initialized tunes and returns fragment that can be appended to the toolbars area
* @return {DocumentFragment} *
* @returns {DocumentFragment}
*/ */
public renderTunes(): DocumentFragment { public renderTunes(): DocumentFragment {
const tunesElement = document.createDocumentFragment(); const tunesElement = document.createDocumentFragment();
this.tunes.forEach( (tune) => { this.tunes.forEach((tune) => {
$.append(tunesElement, tune.render()); $.append(tunesElement, tune.render());
}); });
@ -545,7 +567,7 @@ export default class Block {
subtree: true, subtree: true,
characterData: true, characterData: true,
attributes: true, attributes: true,
}, }
); );
} }
@ -558,15 +580,17 @@ export default class Block {
/** /**
* Make default Block wrappers and put Tool`s content there * Make default Block wrappers and put Tool`s content there
*
* @returns {HTMLDivElement} * @returns {HTMLDivElement}
*/ */
private compose(): HTMLDivElement { private compose(): HTMLDivElement {
const wrapper = $.make('div', Block.CSS.wrapper) as HTMLDivElement, const wrapper = $.make('div', Block.CSS.wrapper) as HTMLDivElement,
contentNode = $.make('div', Block.CSS.content), contentNode = $.make('div', Block.CSS.content),
pluginsContent = this.tool.render(); pluginsContent = this.tool.render();
contentNode.appendChild(pluginsContent); contentNode.appendChild(pluginsContent);
wrapper.appendChild(contentNode); wrapper.appendChild(contentNode);
return wrapper; return wrapper;
} }
} }

View file

@ -1,7 +1,7 @@
import * as _ from './utils'; import * as _ from './utils';
import $ from './dom'; import $ from './dom';
import Block, { BlockToolAPI } from './block'; import Block, { BlockToolAPI } from './block';
import {MoveEvent, MoveEventDetail} from '../../types/tools'; import { MoveEvent, MoveEventDetail } from '../../types/tools';
/** /**
* @class Blocks * @class Blocks
@ -16,7 +16,7 @@ export default class Blocks {
/** /**
* Get length of Block instances array * Get length of Block instances array
* *
* @returns {Number} * @returns {number}
*/ */
public get length(): number { public get length(): number {
return this.blocks.length; return this.blocks.length;
@ -47,17 +47,17 @@ export default class Blocks {
* blocks[0] = new Block(...) * blocks[0] = new Block(...)
* *
* @param {Blocks} instance Blocks instance * @param {Blocks} instance Blocks instance
* @param {Number|String} property block index or any Blocks class property to set * @param {number|string} property block index or any Blocks class property to set
* @param {Block} value value to set * @param {Block} value value to set
* @returns {Boolean} * @returns {boolean}
*/ */
public static set(instance: Blocks, property: number | string, value: Block | any) { public static set(instance: Blocks, property: number | string, value: Block | any) {
/** /**
* If property name is not a number (method or other property, access it via reflect * If property name is not a number (method or other property, access it via reflect
*/ */
if (isNaN(Number(property))) { if (isNaN(Number(property))) {
Reflect.set(instance, property, value); Reflect.set(instance, property, value);
return true; return true;
} }
@ -76,11 +76,10 @@ export default class Blocks {
* Proxy trap to implement array-like getter * Proxy trap to implement array-like getter
* *
* @param {Blocks} instance Blocks instance * @param {Blocks} instance Blocks instance
* @param {Number|String} property Blocks class property * @param {number|string} property Blocks class property
* @returns {Block|*} * @returns {Block|*}
*/ */
public static get(instance: Blocks, property: any | number) { public static get(instance: Blocks, property: any | number) {
/** /**
* If property is not a number, get it via Reflect object * If property is not a number, get it via Reflect object
*/ */
@ -105,7 +104,7 @@ export default class Blocks {
public workingArea: HTMLElement; public workingArea: HTMLElement;
/** /**
* @constructor * @class
* *
* @param {HTMLElement} workingArea editor`s working node * @param {HTMLElement} workingArea editor`s working node
*/ */
@ -126,8 +125,9 @@ export default class Blocks {
/** /**
* Swaps blocks with indexes first and second * Swaps blocks with indexes first and second
* @param {Number} first - first block index *
* @param {Number} second - second block index * @param {number} first - first block index
* @param {number} second - second block index
* @deprecated use 'move' instead * @deprecated use 'move' instead
*/ */
public swap(first: number, second: number): void { public swap(first: number, second: number): void {
@ -147,13 +147,15 @@ export default class Blocks {
/** /**
* Move a block from one to another index * Move a block from one to another index
* @param {Number} toIndex - new index of the block *
* @param {Number} fromIndex - block to move * @param {number} toIndex - new index of the block
* @param {number} fromIndex - block to move
*/ */
public move(toIndex: number, fromIndex: number): void { public move(toIndex: number, fromIndex: number): void {
/** /**
* cut out the block, move the DOM element and insert at the desired index * cut out the block, move the DOM element and insert at the desired index
* again (the shifting within the blocks array will happen automatically). * again (the shifting within the blocks array will happen automatically).
*
* @see https://stackoverflow.com/a/44932690/1238150 * @see https://stackoverflow.com/a/44932690/1238150
*/ */
const block = this.blocks.splice(fromIndex, 1)[0]; const block = this.blocks.splice(fromIndex, 1)[0];
@ -184,13 +186,14 @@ export default class Blocks {
/** /**
* Insert new Block at passed index * Insert new Block at passed index
* *
* @param {Number} index index to insert Block * @param {number} index index to insert Block
* @param {Block} block Block to insert * @param {Block} block Block to insert
* @param {Boolean} replace it true, replace block on given index * @param {boolean} replace it true, replace block on given index
*/ */
public insert(index: number, block: Block, replace: boolean = false): void { public insert(index: number, block: Block, replace = false): void {
if (!this.length) { if (!this.length) {
this.push(block); this.push(block);
return; return;
} }
@ -224,7 +227,8 @@ export default class Blocks {
/** /**
* Remove block * Remove block
* @param {Number|null} index *
* @param {number|null} index
*/ */
public remove(index: number): void { public remove(index: number): void {
if (isNaN(index)) { if (isNaN(index)) {
@ -266,7 +270,7 @@ export default class Blocks {
/** /**
* Get Block by index * Get Block by index
* *
* @param {Number} index Block index * @param {number} index Block index
* @returns {Block} * @returns {Block}
*/ */
public get(index: number): Block { public get(index: number): Block {
@ -277,7 +281,7 @@ export default class Blocks {
* Return index of passed Block * Return index of passed Block
* *
* @param {Block} block * @param {Block} block
* @returns {Number} * @returns {number}
*/ */
public indexOf(block: Block): number { public indexOf(block: Block): number {
return this.blocks.indexOf(block); return this.blocks.indexOf(block);
@ -303,13 +307,13 @@ export default class Blocks {
/** /**
* Composes Block event with passed type and details * Composes Block event with passed type and details
* *
* @param {String} type * @param {string} type
* @param {MoveEventDetail} detail * @param {MoveEventDetail} detail
*/ */
private composeBlockEvent(type: string, detail: MoveEventDetail): MoveEvent { private composeBlockEvent(type: string, detail: MoveEventDetail): MoveEvent {
return new CustomEvent(type, { return new CustomEvent(type, {
detail, detail,
}, }
) as MoveEvent; ) as MoveEvent;
} }
} }

View file

@ -1,8 +1,8 @@
import $ from './dom'; import $ from './dom';
import * as _ from './utils'; import * as _ from './utils';
import {EditorConfig, OutputData, SanitizerConfig} from '../../types'; import { EditorConfig, OutputData, SanitizerConfig } from '../../types';
import {EditorModules} from '../types-internal/editor-modules'; import { EditorModules } from '../types-internal/editor-modules';
import {LogLevels} from './utils'; import { LogLevels } from './utils';
/** /**
* @typedef {Core} Core - editor core class * @typedef {Core} Core - editor core class
@ -37,7 +37,6 @@ contextRequire.keys().forEach((filename) => {
* @type {Core} * @type {Core}
*/ */
export default class Core { export default class Core {
/** /**
* Editor configuration passed by user to the constructor * Editor configuration passed by user to the constructor
*/ */
@ -82,7 +81,7 @@ export default class Core {
await this.render(); await this.render();
if ((this.configuration as EditorConfig).autofocus) { if ((this.configuration as EditorConfig).autofocus) {
const {BlockManager, Caret} = this.moduleInstances; const { BlockManager, Caret } = this.moduleInstances;
Caret.setToBlock(BlockManager.blocks[0], Caret.positions.START); Caret.setToBlock(BlockManager.blocks[0], Caret.positions.START);
} }
@ -110,6 +109,7 @@ export default class Core {
/** /**
* Setting for configuration * Setting for configuration
*
* @param {EditorConfig|string|undefined} config * @param {EditorConfig|string|undefined} config
*/ */
set configuration(config: EditorConfig|string) { set configuration(config: EditorConfig|string) {
@ -134,6 +134,7 @@ export default class Core {
/** /**
* Place config into the class property * Place config into the class property
*
* @type {EditorConfig} * @type {EditorConfig}
*/ */
this.config = config; this.config = config;
@ -158,18 +159,20 @@ export default class Core {
/** /**
* Height of Editor's bottom area that allows to set focus on the last Block * Height of Editor's bottom area that allows to set focus on the last Block
*
* @type {number} * @type {number}
*/ */
this.config.minHeight = this.config.minHeight !== undefined ? this.config.minHeight : 300 ; this.config.minHeight = this.config.minHeight !== undefined ? this.config.minHeight : 300;
/** /**
* Initial block type * Initial block type
* Uses in case when there is no blocks passed * Uses in case when there is no blocks passed
*
* @type {{type: (*), data: {text: null}}} * @type {{type: (*), data: {text: null}}}
*/ */
const initialBlockData = { const initialBlockData = {
type : this.config.initialBlock, type: this.config.initialBlock,
data : {}, data: {},
}; };
this.config.placeholder = this.config.placeholder || false; this.config.placeholder = this.config.placeholder || false;
@ -200,6 +203,7 @@ export default class Core {
/** /**
* Returns private property * Returns private property
*
* @returns {EditorConfig} * @returns {EditorConfig}
*/ */
get configuration(): EditorConfig|string { get configuration(): EditorConfig|string {
@ -208,6 +212,7 @@ export default class Core {
/** /**
* Checks for required fields in Editor's config * Checks for required fields in Editor's config
*
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async validate(): Promise<void> { public async validate(): Promise<void> {
@ -250,7 +255,8 @@ export default class Core {
* Start Editor! * Start Editor!
* *
* Get list of modules that needs to be prepared and return a sequence (Promise) * Get list of modules that needs to be prepared and return a sequence (Promise)
* @return {Promise} *
* @returns {Promise}
*/ */
public async start() { public async start() {
const modulesToPrepare = [ const modulesToPrepare = [
@ -275,7 +281,7 @@ export default class Core {
} }
// _.log(`Preparing ${module} module`, 'timeEnd'); // _.log(`Preparing ${module} module`, 'timeEnd');
}), }),
Promise.resolve(), Promise.resolve()
); );
} }
@ -290,26 +296,26 @@ export default class Core {
* Make modules instances and save it to the @property this.moduleInstances * Make modules instances and save it to the @property this.moduleInstances
*/ */
private constructModules(): void { private constructModules(): void {
modules.forEach( (module) => { modules.forEach((module) => {
/** /**
* If module has non-default exports, passed object contains them all and default export as 'default' property * If module has non-default exports, passed object contains them all and default export as 'default' property
*/ */
const Module = typeof module === 'function' ? module : module.default; const Module = typeof module === 'function' ? module : module.default;
try { try {
/** /**
* We use class name provided by displayName property * We use class name provided by displayName property
* *
* On build, Babel will transform all Classes to the Functions so, name will always be 'Function' * On build, Babel will transform all Classes to the Functions so, name will always be 'Function'
* To prevent this, we use 'babel-plugin-class-display-name' plugin * To prevent this, we use 'babel-plugin-class-display-name' plugin
*
* @see https://www.npmjs.com/package/babel-plugin-class-display-name * @see https://www.npmjs.com/package/babel-plugin-class-display-name
*/ */
this.moduleInstances[Module.displayName] = new Module({ this.moduleInstances[Module.displayName] = new Module({
config : this.configuration, config: this.configuration,
}); });
} catch ( e ) { } catch (e) {
_.log(`Module ${Module.displayName} skipped because`, 'warn', e); _.log(`Module ${Module.displayName} skipped because`, 'warn', e);
} }
}); });
} }
@ -332,6 +338,7 @@ export default class Core {
/** /**
* Return modules without passed name * Return modules without passed name
*
* @param {string} name - module for witch modules difference should be calculated * @param {string} name - module for witch modules difference should be calculated
*/ */
private getModulesDiff(name: string): EditorModules { private getModulesDiff(name: string): EditorModules {

View file

@ -4,8 +4,9 @@
export default class Dom { export default class Dom {
/** /**
* Check if passed tag has no closed tag * Check if passed tag has no closed tag
*
* @param {HTMLElement} tag * @param {HTMLElement} tag
* @return {Boolean} * @returns {boolean}
*/ */
public static isSingleTag(tag: HTMLElement): boolean { public static isSingleTag(tag: HTMLElement): boolean {
return tag.tagName && [ return tag.tagName && [
@ -32,7 +33,7 @@ export default class Dom {
* Check if element is BR or WBR * Check if element is BR or WBR
* *
* @param {HTMLElement} element * @param {HTMLElement} element
* @return {boolean} * @returns {boolean}
*/ */
public static isLineBreakTag(element: HTMLElement) { public static isLineBreakTag(element: HTMLElement) {
return element && element.tagName && [ return element && element.tagName && [
@ -45,16 +46,16 @@ export default class Dom {
* Helper for making Elements with classname and attributes * Helper for making Elements with classname and attributes
* *
* @param {string} tagName - new Element tag name * @param {string} tagName - new Element tag name
* @param {array|string} classNames - list or name of CSS classname(s) * @param {Array|string} classNames - list or name of CSS classname(s)
* @param {Object} attributes - any attributes * @param {object} attributes - any attributes
* @return {HTMLElement} * @returns {HTMLElement}
*/ */
public static make(tagName: string, classNames: string|string[] = null, attributes: object = {}): HTMLElement { public static make(tagName: string, classNames: string|string[] = null, attributes: object = {}): HTMLElement {
const el = document.createElement(tagName); const el = document.createElement(tagName);
if ( Array.isArray(classNames) ) { if (Array.isArray(classNames)) {
el.classList.add(...classNames); el.classList.add(...classNames);
} else if ( classNames ) { } else if (classNames) {
el.classList.add(classNames); el.classList.add(classNames);
} }
@ -69,8 +70,9 @@ export default class Dom {
/** /**
* Creates Text Node with the passed content * Creates Text Node with the passed content
* @param {String} content - text content *
* @return {Text} * @param {string} content - text content
* @returns {Text}
*/ */
public static text(content: string): Text { public static text(content: string): Text {
return document.createTextNode(content); return document.createTextNode(content);
@ -78,12 +80,13 @@ export default class Dom {
/** /**
* Creates SVG icon linked to the sprite * Creates SVG icon linked to the sprite
*
* @param {string} name - name (id) of icon from sprite * @param {string} name - name (id) of icon from sprite
* @param {number} width * @param {number} width
* @param {number} height * @param {number} height
* @return {SVGElement} * @returns {SVGElement}
*/ */
public static svg(name: string, width: number = 14, height: number = 14): SVGElement { public static svg(name: string, width = 14, height = 14): SVGElement {
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.classList.add('icon', 'icon--' + name); icon.classList.add('icon', 'icon--' + name);
@ -102,10 +105,10 @@ export default class Dom {
*/ */
public static append( public static append(
parent: Element|DocumentFragment, parent: Element|DocumentFragment,
elements: Element|Element[]|DocumentFragment|Text|Text[], elements: Element|Element[]|DocumentFragment|Text|Text[]
): void { ): void {
if ( Array.isArray(elements) ) { if (Array.isArray(elements)) {
elements.forEach( (el) => parent.appendChild(el) ); elements.forEach((el) => parent.appendChild(el));
} else { } else {
parent.appendChild(elements); parent.appendChild(elements);
} }
@ -118,9 +121,9 @@ export default class Dom {
* @param {Element|Element[]} elements - element or elements list * @param {Element|Element[]} elements - element or elements list
*/ */
public static prepend(parent: Element, elements: Element|Element[]): void { public static prepend(parent: Element, elements: Element|Element[]): void {
if ( Array.isArray(elements) ) { if (Array.isArray(elements)) {
elements = elements.reverse(); elements = elements.reverse();
elements.forEach( (el) => parent.prepend(el) ); elements.forEach((el) => parent.prepend(el));
} else { } else {
parent.prepend(elements); parent.prepend(elements);
} }
@ -128,6 +131,7 @@ export default class Dom {
/** /**
* Swap two elements in parent * Swap two elements in parent
*
* @param {HTMLElement} el1 - from * @param {HTMLElement} el1 - from
* @param {HTMLElement} el2 - to * @param {HTMLElement} el2 - to
* @deprecated * @deprecated
@ -135,7 +139,7 @@ export default class Dom {
public static swap(el1: HTMLElement, el2: HTMLElement): void { public static swap(el1: HTMLElement, el2: HTMLElement): void {
// create marker element and insert it where el1 is // create marker element and insert it where el1 is
const temp = document.createElement('div'), const temp = document.createElement('div'),
parent = el1.parentNode; parent = el1.parentNode;
parent.insertBefore(temp, el1); parent.insertBefore(temp, el1);
@ -155,7 +159,7 @@ export default class Dom {
* Returns first match * Returns first match
* *
* @param {Element} el - element we searching inside. Default - DOM Document * @param {Element} el - element we searching inside. Default - DOM Document
* @param {String} selector - searching string * @param {string} selector - searching string
* *
* @returns {Element} * @returns {Element}
*/ */
@ -179,7 +183,7 @@ export default class Dom {
* Returns all matches * Returns all matches
* *
* @param {Element} el - element we searching inside. Default - DOM Document * @param {Element} el - element we searching inside. Default - DOM Document
* @param {String} selector - searching string * @param {string} selector - searching string
* @returns {NodeList} * @returns {NodeList}
*/ */
public static findAll(el: Element|Document = document, selector: string): NodeList { public static findAll(el: Element|Document = document, selector: string): NodeList {
@ -194,18 +198,19 @@ export default class Dom {
* *
* @param {Node} node - root Node. From this vertex we start Deep-first search * @param {Node} node - root Node. From this vertex we start Deep-first search
* {@link https://en.wikipedia.org/wiki/Depth-first_search} * {@link https://en.wikipedia.org/wiki/Depth-first_search}
* @param {Boolean} atLast - find last text node * @param {boolean} atLast - find last text node
* @return {Node} - it can be text Node or Element Node, so that caret will able to work with it * @returns {Node} - it can be text Node or Element Node, so that caret will able to work with it
*/ */
public static getDeepestNode(node: Node, atLast: boolean = false): Node { public static getDeepestNode(node: Node, atLast = false): Node {
/** /**
* Current function have two directions: * Current function have two directions:
* - starts from first child and every time gets first or nextSibling in special cases * - starts from first child and every time gets first or nextSibling in special cases
* - starts from last child and gets last or previousSibling * - starts from last child and gets last or previousSibling
*
* @type {string} * @type {string}
*/ */
const child = atLast ? 'lastChild' : 'firstChild', const child = atLast ? 'lastChild' : 'firstChild',
sibling = atLast ? 'previousSibling' : 'nextSibling'; sibling = atLast ? 'previousSibling' : 'nextSibling';
if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) { if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) {
let nodeChild = node[child] as Node; let nodeChild = node[child] as Node;
@ -245,7 +250,7 @@ export default class Dom {
/** /**
* Check if object is DOM node * Check if object is DOM node
* *
* @param {Object} node * @param {object} node
* @returns {boolean} * @returns {boolean}
*/ */
public static isElement(node: any): node is Element { public static isElement(node: any): node is Element {
@ -255,7 +260,7 @@ export default class Dom {
/** /**
* Check if object is DocumentFragmemt node * Check if object is DocumentFragmemt node
* *
* @param {Object} node * @param {object} node
* @returns {boolean} * @returns {boolean}
*/ */
public static isFragment(node: any): boolean { public static isFragment(node: any): boolean {
@ -264,8 +269,9 @@ export default class Dom {
/** /**
* Check if passed element is contenteditable * Check if passed element is contenteditable
*
* @param {HTMLElement} element * @param {HTMLElement} element
* @return {boolean} * @returns {boolean}
*/ */
public static isContentEditable(element: HTMLElement): boolean { public static isContentEditable(element: HTMLElement): boolean {
return element.contentEditable === 'true'; return element.contentEditable === 'true';
@ -273,8 +279,9 @@ export default class Dom {
/** /**
* Checks target if it is native input * Checks target if it is native input
* @param {Element|String|Node} target - HTML element or string *
* @return {Boolean} * @param {Element|string|Node} target - HTML element or string
* @returns {boolean}
*/ */
public static isNativeInput(target: any): boolean { public static isNativeInput(target: any): boolean {
const nativeInputs = [ const nativeInputs = [
@ -287,13 +294,16 @@ export default class Dom {
/** /**
* Checks if we can set caret * Checks if we can set caret
*
* @param {HTMLElement} target * @param {HTMLElement} target
* @return {boolean} * @returns {boolean}
*/ */
public static canSetCaret(target: HTMLElement): boolean { public static canSetCaret(target: HTMLElement): boolean {
let result = true; let result = true;
if (Dom.isNativeInput(target)) { if (Dom.isNativeInput(target)) {
const inputElement = target as HTMLInputElement; const inputElement = target as HTMLInputElement;
switch (inputElement.type) { switch (inputElement.type) {
case 'file': case 'file':
case 'checkbox': case 'checkbox':
@ -309,6 +319,7 @@ export default class Dom {
} else { } else {
result = Dom.isContentEditable(target); result = Dom.isContentEditable(target);
} }
return result; return result;
} }
@ -319,7 +330,7 @@ export default class Dom {
* If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method
* *
* @param {Node} node * @param {Node} node
* @return {Boolean} true if it is empty * @returns {boolean} true if it is empty
*/ */
public static isNodeEmpty(node: Node): boolean { public static isNodeEmpty(node: Node): boolean {
let nodeText; let nodeText;
@ -328,7 +339,7 @@ export default class Dom {
return false; return false;
} }
if ( this.isElement(node) && this.isNativeInput(node) ) { if (this.isElement(node) && this.isNativeInput(node)) {
nodeText = (node as HTMLInputElement).value; nodeText = (node as HTMLInputElement).value;
} else { } else {
nodeText = node.textContent.replace('\u200B', ''); nodeText = node.textContent.replace('\u200B', '');
@ -339,8 +350,9 @@ export default class Dom {
/** /**
* checks node if it is doesn't have any child nodes * checks node if it is doesn't have any child nodes
*
* @param {Node} node * @param {Node} node
* @return {boolean} * @returns {boolean}
*/ */
public static isLeaf(node: Node): boolean { public static isLeaf(node: Node): boolean {
if (!node) { if (!node) {
@ -357,11 +369,11 @@ export default class Dom {
* @description Pushes to stack all DOM leafs and checks for emptiness * @description Pushes to stack all DOM leafs and checks for emptiness
* *
* @param {Node} node * @param {Node} node
* @return {boolean} * @returns {boolean}
*/ */
public static isEmpty(node: Node): boolean { public static isEmpty(node: Node): boolean {
const treeWalker = [], const treeWalker = [],
leafs = []; leafs = [];
if (!node) { if (!node) {
return true; return true;
@ -378,21 +390,25 @@ export default class Dom {
treeWalker.push(node.firstChild); treeWalker.push(node.firstChild);
while ( treeWalker.length > 0 ) { while (treeWalker.length > 0) {
node = treeWalker.shift(); node = treeWalker.shift();
if (!node) { continue; } if (!node) {
continue;
}
if ( this.isLeaf(node) ) { if (this.isLeaf(node)) {
leafs.push(node); leafs.push(node);
} else { } else {
treeWalker.push(node.firstChild); treeWalker.push(node.firstChild);
} }
while ( node && node.nextSibling ) { while (node && node.nextSibling) {
node = node.nextSibling; node = node.nextSibling;
if (!node) { continue; } if (!node) {
continue;
}
treeWalker.push(node); treeWalker.push(node);
} }
@ -405,14 +421,14 @@ export default class Dom {
} }
} }
return leafs.every( (leaf) => this.isNodeEmpty(leaf) ); return leafs.every((leaf) => this.isNodeEmpty(leaf));
} }
/** /**
* Check if string contains html elements * Check if string contains html elements
* *
* @returns {boolean} * @returns {boolean}
* @param {String} str * @param {string} str
*/ */
public static isHTMLString(str: string): boolean { public static isHTMLString(str: string): boolean {
const wrapper = Dom.make('div'); const wrapper = Dom.make('div');
@ -491,7 +507,7 @@ export default class Dom {
* Check if passed content includes only inline elements * Check if passed content includes only inline elements
* *
* @param {string|HTMLElement} data - element or html string * @param {string|HTMLElement} data - element or html string
* @return {boolean} * @returns {boolean}
*/ */
public static containsOnlyInlineElements(data: string | HTMLElement): boolean { public static containsOnlyInlineElements(data: string | HTMLElement): boolean {
let wrapper: HTMLElement; let wrapper: HTMLElement;
@ -504,8 +520,8 @@ export default class Dom {
} }
const check = (element: HTMLElement) => { const check = (element: HTMLElement) => {
return !Dom.blockElements.includes(element.tagName.toLowerCase()) return !Dom.blockElements.includes(element.tagName.toLowerCase()) &&
&& Array.from(element.children).every(check); Array.from(element.children).every(check);
}; };
return Array.from(wrapper.children).every(check); return Array.from(wrapper.children).every(check);
@ -516,11 +532,11 @@ export default class Dom {
* *
* @param {HTMLElement} parent * @param {HTMLElement} parent
* *
* @return {HTMLElement[]} * @returns {HTMLElement[]}
*/ */
public static getDeepestBlockElements(parent: HTMLElement): HTMLElement[] { public static getDeepestBlockElements(parent: HTMLElement): HTMLElement[] {
if (Dom.containsOnlyInlineElements(parent)) { if (Dom.containsOnlyInlineElements(parent)) {
return [parent]; return [ parent ];
} }
return Array.from(parent.children).reduce((result, element) => { return Array.from(parent.children).reduce((result, element) => {
@ -528,17 +544,22 @@ export default class Dom {
}, []); }, []);
} }
/* /**
* Helper for get holder from {string} or return HTMLElement * Helper for get holder from {string} or return HTMLElement
*
* @param element * @param element
*/ */
public static getHolder(element: string | HTMLElement): HTMLElement { public static getHolder(element: string | HTMLElement): HTMLElement {
if (typeof element === 'string') { return document.getElementById(element); } if (typeof element === 'string') {
return document.getElementById(element);
}
return element; return element;
} }
/** /**
* Method checks passed Node if it is some extension Node * Method checks passed Node if it is some extension Node
*
* @param {Node} node - any node * @param {Node} node - any node
*/ */
public static isExtensionNode(node: Node): boolean { public static isExtensionNode(node: Node): boolean {

View file

@ -7,6 +7,7 @@ import Dom from './dom';
export default class DomIterator { export default class DomIterator {
/** /**
* This is a static property that defines iteration directions * This is a static property that defines iteration directions
*
* @type {{RIGHT: string, LEFT: string}} * @type {{RIGHT: string, LEFT: string}}
*/ */
public static directions = { public static directions = {
@ -22,9 +23,10 @@ export default class DomIterator {
/** /**
* Focused button index. * Focused button index.
* Default is -1 which means nothing is active * Default is -1 which means nothing is active
*
* @type {number} * @type {number}
*/ */
private cursor: number = -1; private cursor = -1;
/** /**
* Items to flip * Items to flip
@ -37,7 +39,7 @@ export default class DomIterator {
*/ */
constructor( constructor(
nodeList: HTMLElement[], nodeList: HTMLElement[],
focusedCssClass: string, focusedCssClass: string
) { ) {
this.items = nodeList || []; this.items = nodeList || [];
this.focusedCssClass = focusedCssClass; this.focusedCssClass = focusedCssClass;
@ -45,7 +47,8 @@ export default class DomIterator {
/** /**
* Returns Focused button Node * Returns Focused button Node
* @return {HTMLElement} *
* @returns {HTMLElement}
*/ */
public get currentItem(): HTMLElement { public get currentItem(): HTMLElement {
if (this.cursor === -1) { if (this.cursor === -1) {
@ -57,6 +60,7 @@ export default class DomIterator {
/** /**
* Sets items. Can be used when iterable items changed dynamically * Sets items. Can be used when iterable items changed dynamically
*
* @param {HTMLElement[]} nodeList * @param {HTMLElement[]} nodeList
*/ */
public setItems(nodeList: HTMLElement[]): void { public setItems(nodeList: HTMLElement[]): void {
@ -93,7 +97,7 @@ export default class DomIterator {
* Leafs nodes inside the target list from active element * Leafs nodes inside the target list from active element
* *
* @param {string} direction - leaf direction. Can be 'left' or 'right' * @param {string} direction - leaf direction. Can be 'left' or 'right'
* @return {Number} index of focused node * @returns {number} index of focused node
*/ */
private leafNodesAndReturnIndex(direction: string): number { private leafNodesAndReturnIndex(direction: string): number {
/** /**
@ -138,6 +142,7 @@ export default class DomIterator {
if (direction === DomIterator.directions.RIGHT) { if (direction === DomIterator.directions.RIGHT) {
/** /**
* If we go right then choose next (+1) Tool * If we go right then choose next (+1) Tool
*
* @type {number} * @type {number}
*/ */
focusedButtonIndex = (focusedButtonIndex + 1) % this.items.length; focusedButtonIndex = (focusedButtonIndex + 1) % this.items.length;
@ -145,6 +150,7 @@ export default class DomIterator {
/** /**
* If we go left then choose previous (-1) Tool * If we go left then choose previous (-1) Tool
* Before counting module we need to add length before because of "The JavaScript Modulo Bug" * Before counting module we need to add length before because of "The JavaScript Modulo Bug"
*
* @type {number} * @type {number}
*/ */
focusedButtonIndex = (this.items.length + focusedButtonIndex - 1) % this.items.length; focusedButtonIndex = (this.items.length + focusedButtonIndex - 1) % this.items.length;

View file

@ -36,21 +36,23 @@ export interface FlipperOptions {
* Flipper is a component that iterates passed items array by TAB or Arrows and clicks it by ENTER * Flipper is a component that iterates passed items array by TAB or Arrows and clicks it by ENTER
*/ */
export default class Flipper { export default class Flipper {
/** /**
* Instance of flipper iterator * Instance of flipper iterator
*
* @type {DomIterator|null} * @type {DomIterator|null}
*/ */
private readonly iterator: DomIterator = null; private readonly iterator: DomIterator = null;
/** /**
* Flag that defines activation status * Flag that defines activation status
*
* @type {boolean} * @type {boolean}
*/ */
private activated: boolean = false; private activated = false;
/** /**
* Flag that allows arrows usage to flip items * Flag that allows arrows usage to flip items
*
* @type {boolean} * @type {boolean}
*/ */
private readonly allowArrows: boolean = true; private readonly allowArrows: boolean = true;
@ -61,7 +63,7 @@ export default class Flipper {
private readonly activateCallback: () => void; private readonly activateCallback: () => void;
/** /**
* @constructor * @class
* *
* @param {FlipperOptions} options - different constructing settings * @param {FlipperOptions} options - different constructing settings
* @ * @
@ -129,6 +131,7 @@ export default class Flipper {
/** /**
* Active tab/arrows handling by flipper * Active tab/arrows handling by flipper
*
* @param {HTMLElement[]} items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically * @param {HTMLElement[]} items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically
*/ */
public activate(items?: HTMLElement[]): void { public activate(items?: HTMLElement[]): void {
@ -149,7 +152,8 @@ export default class Flipper {
/** /**
* Return current focused button * Return current focused button
* @return {HTMLElement|null} *
* @returns {HTMLElement|null}
*/ */
public get currentItem(): HTMLElement|null { public get currentItem(): HTMLElement|null {
return this.iterator.currentItem; return this.iterator.currentItem;
@ -165,6 +169,7 @@ export default class Flipper {
/** /**
* Drops flipper's iterator cursor * Drops flipper's iterator cursor
*
* @see DomIterator#dropCursor * @see DomIterator#dropCursor
*/ */
private dropCursor(): void { private dropCursor(): void {
@ -174,8 +179,9 @@ export default class Flipper {
/** /**
* This function is fired before handling flipper keycodes * This function is fired before handling flipper keycodes
* The result of this function defines if it is need to be handled or not * The result of this function defines if it is need to be handled or not
*
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
* @return {boolean} * @returns {boolean}
*/ */
private isEventReadyForHandling(event: KeyboardEvent): boolean { private isEventReadyForHandling(event: KeyboardEvent): boolean {
const handlingKeyCodeList = [ const handlingKeyCodeList = [
@ -188,7 +194,7 @@ export default class Flipper {
_.keyCodes.LEFT, _.keyCodes.LEFT,
_.keyCodes.RIGHT, _.keyCodes.RIGHT,
_.keyCodes.UP, _.keyCodes.UP,
_.keyCodes.DOWN, _.keyCodes.DOWN
); );
} }
@ -201,12 +207,13 @@ export default class Flipper {
/** /**
* When flipper is activated tab press will leaf the items * When flipper is activated tab press will leaf the items
*
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
private handleTabPress(event: KeyboardEvent): void { private handleTabPress(event: KeyboardEvent): void {
/** this property defines leaf direction */ /** this property defines leaf direction */
const shiftKey = event.shiftKey, const shiftKey = event.shiftKey,
direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT; direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT;
switch (direction) { switch (direction) {
case DomIterator.directions.RIGHT: case DomIterator.directions.RIGHT:
@ -234,6 +241,7 @@ export default class Flipper {
/** /**
* Enter press will click current item if flipper is activated * Enter press will click current item if flipper is activated
*
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
private handleEnterPress(event: KeyboardEvent): void { private handleEnterPress(event: KeyboardEvent): void {

View file

@ -1,5 +1,5 @@
import $ from '../dom'; import $ from '../dom';
import {API, InlineTool, SanitizerConfig} from '../../../types'; import { API, InlineTool, SanitizerConfig } from '../../../types';
/** /**
* Bold Tool * Bold Tool
@ -9,23 +9,23 @@ import {API, InlineTool, SanitizerConfig} from '../../../types';
* Makes selected text bolder * Makes selected text bolder
*/ */
export default class BoldInlineTool implements InlineTool { export default class BoldInlineTool implements InlineTool {
/** /**
* Specifies Tool as Inline Toolbar Tool * Specifies Tool as Inline Toolbar Tool
* *
* @return {boolean} * @returns {boolean}
*/ */
public static isInline = true; public static isInline = true;
/** /**
* Title for hover-tooltip * Title for hover-tooltip
*/ */
public static title: string = 'Bold'; public static title = 'Bold';
/** /**
* Sanitizer Rule * Sanitizer Rule
* Leave <b> tags * Leave <b> tags
* @return {object} *
* @returns {object}
*/ */
static get sanitize(): SanitizerConfig { static get sanitize(): SanitizerConfig {
return { return {
@ -62,11 +62,13 @@ export default class BoldInlineTool implements InlineTool {
this.nodes.button.type = 'button'; this.nodes.button.type = 'button';
this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier);
this.nodes.button.appendChild($.svg('bold', 12, 14)); this.nodes.button.appendChild($.svg('bold', 12, 14));
return this.nodes.button; return this.nodes.button;
} }
/** /**
* Wrap range with <b> tag * Wrap range with <b> tag
*
* @param {Range} range * @param {Range} range
*/ */
public surround(range: Range): void { public surround(range: Range): void {
@ -75,12 +77,14 @@ export default class BoldInlineTool implements InlineTool {
/** /**
* Check selection and set activated state to button if there are <b> tag * Check selection and set activated state to button if there are <b> tag
*
* @param {Selection} selection * @param {Selection} selection
*/ */
public checkState(selection: Selection): boolean { public checkState(selection: Selection): boolean {
const isActive = document.queryCommandState(this.commandName); const isActive = document.queryCommandState(this.commandName);
this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive);
return isActive; return isActive;
} }

View file

@ -1,5 +1,5 @@
import $ from '../dom'; import $ from '../dom';
import {InlineTool, SanitizerConfig} from '../../../types'; import { InlineTool, SanitizerConfig } from '../../../types';
/** /**
* Italic Tool * Italic Tool
@ -9,23 +9,23 @@ import {InlineTool, SanitizerConfig} from '../../../types';
* Style selected text with italic * Style selected text with italic
*/ */
export default class ItalicInlineTool implements InlineTool { export default class ItalicInlineTool implements InlineTool {
/** /**
* Specifies Tool as Inline Toolbar Tool * Specifies Tool as Inline Toolbar Tool
* *
* @return {boolean} * @returns {boolean}
*/ */
public static isInline = true; public static isInline = true;
/** /**
* Title for hover-tooltip * Title for hover-tooltip
*/ */
public static title: string = 'Italic'; public static title = 'Italic';
/** /**
* Sanitizer Rule * Sanitizer Rule
* Leave <i> tags * Leave <i> tags
* @return {object} *
* @returns {object}
*/ */
static get sanitize(): SanitizerConfig { static get sanitize(): SanitizerConfig {
return { return {
@ -62,11 +62,13 @@ export default class ItalicInlineTool implements InlineTool {
this.nodes.button.type = 'button'; this.nodes.button.type = 'button';
this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier);
this.nodes.button.appendChild($.svg('italic', 4, 11)); this.nodes.button.appendChild($.svg('italic', 4, 11));
return this.nodes.button; return this.nodes.button;
} }
/** /**
* Wrap range with <i> tag * Wrap range with <i> tag
*
* @param {Range} range * @param {Range} range
*/ */
public surround(range: Range): void { public surround(range: Range): void {
@ -75,12 +77,14 @@ export default class ItalicInlineTool implements InlineTool {
/** /**
* Check selection and set activated state to button if there are <i> tag * Check selection and set activated state to button if there are <i> tag
*
* @param {Selection} selection * @param {Selection} selection
*/ */
public checkState(selection: Selection): boolean { public checkState(selection: Selection): boolean {
const isActive = document.queryCommandState(this.commandName); const isActive = document.queryCommandState(this.commandName);
this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive);
return isActive; return isActive;
} }

View file

@ -2,8 +2,8 @@ import SelectionUtils from '../selection';
import $ from '../dom'; import $ from '../dom';
import * as _ from '../utils'; import * as _ from '../utils';
import {API, InlineTool, SanitizerConfig} from '../../../types'; import { API, InlineTool, SanitizerConfig } from '../../../types';
import {Notifier, Toolbar} from '../../../types/api'; import { Notifier, Toolbar } from '../../../types/api';
/** /**
* Link Tool * Link Tool
@ -13,23 +13,23 @@ import {Notifier, Toolbar} from '../../../types/api';
* Wrap selected text with <a> tag * Wrap selected text with <a> tag
*/ */
export default class LinkInlineTool implements InlineTool { export default class LinkInlineTool implements InlineTool {
/** /**
* Specifies Tool as Inline Toolbar Tool * Specifies Tool as Inline Toolbar Tool
* *
* @return {boolean} * @returns {boolean}
*/ */
public static isInline = true; public static isInline = true;
/** /**
* Title for hover-tooltip * Title for hover-tooltip
*/ */
public static title: string = 'Link'; public static title = 'Link';
/** /**
* Sanitizer Rule * Sanitizer Rule
* Leave <a> tags * Leave <a> tags
* @return {object} *
* @returns {object}
*/ */
static get sanitize(): SanitizerConfig { static get sanitize(): SanitizerConfig {
return { return {
@ -83,7 +83,7 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Input opening state * Input opening state
*/ */
private inputOpened: boolean = false; private inputOpened = false;
/** /**
* Available Toolbar methods (open/close) * Available Toolbar methods (open/close)
@ -103,7 +103,7 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* @param {{api: API}} - Editor.js API * @param {{api: API}} - Editor.js API
*/ */
constructor({api}) { constructor({ api }) {
this.toolbar = api.toolbar; this.toolbar = api.toolbar;
this.inlineToolbar = api.inlineToolbar; this.inlineToolbar = api.inlineToolbar;
this.notifier = api.notifier; this.notifier = api.notifier;
@ -119,6 +119,7 @@ export default class LinkInlineTool implements InlineTool {
this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier);
this.nodes.button.appendChild($.svg('link', 14, 10)); this.nodes.button.appendChild($.svg('link', 14, 10));
this.nodes.button.appendChild($.svg('unlink', 15, 11)); this.nodes.button.appendChild($.svg('unlink', 15, 11));
return this.nodes.button; return this.nodes.button;
} }
@ -134,11 +135,13 @@ export default class LinkInlineTool implements InlineTool {
this.enterPressed(event); this.enterPressed(event);
} }
}); });
return this.nodes.input; return this.nodes.input;
} }
/** /**
* Handle clicks on the Inline Toolbar icon * Handle clicks on the Inline Toolbar icon
*
* @param {Range} range * @param {Range} range
*/ */
public surround(range: Range): void { public surround(range: Range): void {
@ -168,6 +171,7 @@ export default class LinkInlineTool implements InlineTool {
this.closeActions(); this.closeActions();
this.checkState(); this.checkState();
this.toolbar.close(); this.toolbar.close();
return; return;
} }
} }
@ -177,6 +181,7 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Check selection and set activated state to button if there are <a> tag * Check selection and set activated state to button if there are <a> tag
*
* @param {Selection} selection * @param {Selection} selection
*/ */
public checkState(selection?: Selection): boolean { public checkState(selection?: Selection): boolean {
@ -191,6 +196,7 @@ export default class LinkInlineTool implements InlineTool {
* Fill input value with link href * Fill input value with link href
*/ */
const hrefAttr = anchorTag.getAttribute('href'); const hrefAttr = anchorTag.getAttribute('href');
this.nodes.input.value = hrefAttr !== 'null' ? hrefAttr : ''; this.nodes.input.value = hrefAttr !== 'null' ? hrefAttr : '';
this.selection.save(); this.selection.save();
@ -216,6 +222,9 @@ export default class LinkInlineTool implements InlineTool {
return 'CMD+K'; return 'CMD+K';
} }
/**
* Show/close link input
*/
private toggleActions(): void { private toggleActions(): void {
if (!this.inputOpened) { if (!this.inputOpened) {
this.openActions(true); this.openActions(true);
@ -227,7 +236,7 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* @param {boolean} needFocus - on link creation we need to focus input. On editing - nope. * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope.
*/ */
private openActions(needFocus: boolean = false): void { private openActions(needFocus = false): void {
this.nodes.input.classList.add(this.CSS.inputShowed); this.nodes.input.classList.add(this.CSS.inputShowed);
if (needFocus) { if (needFocus) {
this.nodes.input.focus(); this.nodes.input.focus();
@ -237,13 +246,15 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Close input * Close input
*
* @param {boolean} clearSavedSelection we don't need to clear saved selection * @param {boolean} clearSavedSelection we don't need to clear saved selection
* on toggle-clicks on the icon of opened Toolbar * on toggle-clicks on the icon of opened Toolbar
*/ */
private closeActions(clearSavedSelection: boolean = true): void { private closeActions(clearSavedSelection = true): void {
if (this.selection.isFakeBackgroundEnabled) { if (this.selection.isFakeBackgroundEnabled) {
// if actions is broken by other selection We need to save new selection // if actions is broken by other selection We need to save new selection
const currentSelection = new SelectionUtils(); const currentSelection = new SelectionUtils();
currentSelection.save(); currentSelection.save();
this.selection.restore(); this.selection.restore();
@ -263,6 +274,7 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Enter pressed on input * Enter pressed on input
*
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
private enterPressed(event: KeyboardEvent): void { private enterPressed(event: KeyboardEvent): void {
@ -276,13 +288,13 @@ export default class LinkInlineTool implements InlineTool {
} }
if (!this.validateURL(value)) { if (!this.validateURL(value)) {
this.notifier.show({ this.notifier.show({
message: 'Pasted link is not valid.', message: 'Pasted link is not valid.',
style: 'error', style: 'error',
}); });
_.log('Incorrect Link pasted', 'warn', value); _.log('Incorrect Link pasted', 'warn', value);
return; return;
} }
@ -305,8 +317,9 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Detects if passed string is URL * Detects if passed string is URL
*
* @param {string} str * @param {string} str
* @return {Boolean} * @returns {boolean}
*/ */
private validateURL(str: string): boolean { private validateURL(str: string): boolean {
/** /**
@ -319,17 +332,20 @@ export default class LinkInlineTool implements InlineTool {
* Process link before injection * Process link before injection
* - sanitize * - sanitize
* - add protocol for links like 'google.com' * - add protocol for links like 'google.com'
*
* @param {string} link - raw user input * @param {string} link - raw user input
*/ */
private prepareLink(link: string): string { private prepareLink(link: string): string {
link = link.trim(); link = link.trim();
link = this.addProtocol(link); link = this.addProtocol(link);
return link; return link;
} }
/** /**
* Add 'http' protocol to the links like 'vc.ru', 'google.com' * Add 'http' protocol to the links like 'vc.ru', 'google.com'
* @param {String} link *
* @param {string} link
*/ */
private addProtocol(link: string): string { private addProtocol(link: string): string {
/** /**
@ -345,9 +361,9 @@ export default class LinkInlineTool implements InlineTool {
* 2) Anchors looks like "#results" * 2) Anchors looks like "#results"
* 3) Protocol-relative URLs like "//google.com" * 3) Protocol-relative URLs like "//google.com"
*/ */
const isInternal = /^\/[^\/\s]/.test(link), const isInternal = /^\/[^/\s]/.test(link),
isAnchor = link.substring(0, 1) === '#', isAnchor = link.substring(0, 1) === '#',
isProtocolRelative = /^\/\/[^\/\s]/.test(link); isProtocolRelative = /^\/\/[^/\s]/.test(link);
if (!isInternal && !isAnchor && !isProtocolRelative) { if (!isInternal && !isAnchor && !isProtocolRelative) {
link = 'http://' + link; link = 'http://' + link;
@ -358,10 +374,10 @@ export default class LinkInlineTool implements InlineTool {
/** /**
* Inserts <a> tag with "href" * Inserts <a> tag with "href"
*
* @param {string} link - "href" value * @param {string} link - "href" value
*/ */
private insertLink(link: string): void { private insertLink(link: string): void {
/** /**
* Edit all link, not selected part * Edit all link, not selected part
*/ */

View file

@ -1,7 +1,7 @@
import Module from '../../__module'; import Module from '../../__module';
import {Blocks} from '../../../../types/api'; import { Blocks } from '../../../../types/api';
import {BlockToolData, OutputData, ToolConfig} from '../../../../types'; import { BlockToolData, OutputData, ToolConfig } from '../../../../types';
import * as _ from './../../utils'; import * as _ from './../../utils';
/** /**
@ -11,7 +11,8 @@ import * as _ from './../../utils';
export default class BlocksAPI extends Module { export default class BlocksAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Blocks} *
* @returns {Blocks}
*/ */
get methods(): Blocks { get methods(): Blocks {
return { return {
@ -24,7 +25,7 @@ export default class BlocksAPI extends Module {
getBlockByIndex: (index: number) => this.getBlockByIndex(index), getBlockByIndex: (index: number) => this.getBlockByIndex(index),
getCurrentBlockIndex: () => this.getCurrentBlockIndex(), getCurrentBlockIndex: () => this.getCurrentBlockIndex(),
getBlocksCount: () => this.getBlocksCount(), getBlocksCount: () => this.getBlocksCount(),
stretchBlock: (index: number, status: boolean = true) => this.stretchBlock(index, status), stretchBlock: (index: number, status = true) => this.stretchBlock(index, status),
insertNewBlock: () => this.insertNewBlock(), insertNewBlock: () => this.insertNewBlock(),
insert: this.insert, insert: this.insert,
}; };
@ -32,7 +33,8 @@ export default class BlocksAPI extends Module {
/** /**
* Returns Blocks count * Returns Blocks count
* @return {number} *
* @returns {number}
*/ */
public getBlocksCount(): number { public getBlocksCount(): number {
return this.Editor.BlockManager.blocks.length; return this.Editor.BlockManager.blocks.length;
@ -40,7 +42,8 @@ export default class BlocksAPI extends Module {
/** /**
* Returns current block index * Returns current block index
* @return {number} *
* @returns {number}
*/ */
public getCurrentBlockIndex(): number { public getCurrentBlockIndex(): number {
return this.Editor.BlockManager.currentBlockIndex; return this.Editor.BlockManager.currentBlockIndex;
@ -48,17 +51,20 @@ export default class BlocksAPI extends Module {
/** /**
* Returns Block holder by Block index * Returns Block holder by Block index
* @param {Number} index
* *
* @return {HTMLElement} * @param {number} index
*
* @returns {HTMLElement}
*/ */
public getBlockByIndex(index: number): HTMLElement { public getBlockByIndex(index: number): HTMLElement {
const block = this.Editor.BlockManager.getBlockByIndex(index); const block = this.Editor.BlockManager.getBlockByIndex(index);
return block.holder; return block.holder;
} }
/** /**
* Call Block Manager method that swap Blocks * Call Block Manager method that swap Blocks
*
* @param {number} fromIndex - position of first Block * @param {number} fromIndex - position of first Block
* @param {number} toIndex - position of second Block * @param {number} toIndex - position of second Block
* @deprecated use 'move' instead * @deprecated use 'move' instead
@ -75,7 +81,8 @@ export default class BlocksAPI extends Module {
/** /**
* Move block from one index to another * Move block from one index to another
* @param {Number} toIndex *
* @param {number} toIndex
* @param {number} fromIndex * @param {number} fromIndex
*/ */
public move(toIndex: number, fromIndex?: number): void { public move(toIndex: number, fromIndex?: number): void {
@ -90,6 +97,7 @@ export default class BlocksAPI extends Module {
/** /**
* Deletes Block * Deletes Block
*
* @param blockIndex * @param blockIndex
*/ */
public delete(blockIndex?: number): void { public delete(blockIndex?: number): void {
@ -121,29 +129,34 @@ export default class BlocksAPI extends Module {
/** /**
* Fills Editor with Blocks data * Fills Editor with Blocks data
*
* @param {OutputData} data Saved Editor data * @param {OutputData} data Saved Editor data
*/ */
public render(data: OutputData): Promise<void> { public render(data: OutputData): Promise<void> {
this.Editor.BlockManager.clear(); this.Editor.BlockManager.clear();
return this.Editor.Renderer.render(data.blocks); return this.Editor.Renderer.render(data.blocks);
} }
/** /**
* Render passed HTML string * Render passed HTML string
*
* @param {string} data * @param {string} data
* @return {Promise<void>} * @returns {Promise<void>}
*/ */
public renderFromHTML(data: string): Promise<void> { public renderFromHTML(data: string): Promise<void> {
this.Editor.BlockManager.clear(); this.Editor.BlockManager.clear();
return this.Editor.Paste.processText(data, true); return this.Editor.Paste.processText(data, true);
} }
/** /**
* Stretch Block's content * Stretch Block's content
*
* @param {number} index * @param {number} index
* @param {boolean} status - true to enable, false to disable * @param {boolean} status - true to enable, false to disable
*/ */
public stretchBlock(index: number, status: boolean = true): void { public stretchBlock(index: number, status = true): void {
const block = this.Editor.BlockManager.getBlockByIndex(index); const block = this.Editor.BlockManager.getBlockByIndex(index);
if (!block) { if (!block) {
@ -167,14 +180,14 @@ export default class BlocksAPI extends Module {
data: BlockToolData = {}, data: BlockToolData = {},
config: ToolConfig = {}, config: ToolConfig = {},
index?: number, index?: number,
needToFocus?: boolean, needToFocus?: boolean
): void => { ): void => {
this.Editor.BlockManager.insert( this.Editor.BlockManager.insert(
type, type,
data, data,
config, config,
index, index,
needToFocus, needToFocus
); );
} }

View file

@ -1,5 +1,5 @@
import Module from '../../__module'; import Module from '../../__module';
import {Caret} from '../../../../types/api'; import { Caret } from '../../../../types/api';
/** /**
* @class CaretAPI * @class CaretAPI
@ -8,7 +8,8 @@ import {Caret} from '../../../../types/api';
export default class CaretAPI extends Module { export default class CaretAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Caret} *
* @returns {Caret}
*/ */
get methods(): Caret { get methods(): Caret {
return { return {
@ -27,14 +28,15 @@ export default class CaretAPI extends Module {
* @param {string} position - position where to set caret * @param {string} position - position where to set caret
* @param {number} offset - caret offset * @param {number} offset - caret offset
* *
* @return {boolean} * @returns {boolean}
*/ */
private setToFirstBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset: number = 0): boolean => { private setToFirstBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset = 0): boolean => {
if (!this.Editor.BlockManager.firstBlock) { if (!this.Editor.BlockManager.firstBlock) {
return false; return false;
} }
this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock, position, offset); this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock, position, offset);
return true; return true;
} }
@ -44,14 +46,15 @@ export default class CaretAPI extends Module {
* @param {string} position - position where to set caret * @param {string} position - position where to set caret
* @param {number} offset - caret offset * @param {number} offset - caret offset
* *
* @return {boolean} * @returns {boolean}
*/ */
private setToLastBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset: number = 0): boolean => { private setToLastBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset = 0): boolean => {
if (!this.Editor.BlockManager.lastBlock) { if (!this.Editor.BlockManager.lastBlock) {
return false; return false;
} }
this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock, position, offset); this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock, position, offset);
return true; return true;
} }
@ -61,17 +64,18 @@ export default class CaretAPI extends Module {
* @param {string} position - position where to set caret * @param {string} position - position where to set caret
* @param {number} offset - caret offset * @param {number} offset - caret offset
* *
* @return {boolean} * @returns {boolean}
*/ */
private setToPreviousBlock = ( private setToPreviousBlock = (
position: string = this.Editor.Caret.positions.DEFAULT, position: string = this.Editor.Caret.positions.DEFAULT,
offset: number = 0, offset = 0
): boolean => { ): boolean => {
if (!this.Editor.BlockManager.previousBlock) { if (!this.Editor.BlockManager.previousBlock) {
return false; return false;
} }
this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock, position, offset); this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock, position, offset);
return true; return true;
} }
@ -81,14 +85,15 @@ export default class CaretAPI extends Module {
* @param {string} position - position where to set caret * @param {string} position - position where to set caret
* @param {number} offset - caret offset * @param {number} offset - caret offset
* *
* @return {boolean} * @returns {boolean}
*/ */
private setToNextBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset: number = 0): boolean => { private setToNextBlock = (position: string = this.Editor.Caret.positions.DEFAULT, offset = 0): boolean => {
if (!this.Editor.BlockManager.nextBlock) { if (!this.Editor.BlockManager.nextBlock) {
return false; return false;
} }
this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock, position, offset); this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock, position, offset);
return true; return true;
} }
@ -99,18 +104,19 @@ export default class CaretAPI extends Module {
* @param {string} position - position where to set caret * @param {string} position - position where to set caret
* @param {number} offset - caret offset * @param {number} offset - caret offset
* *
* @return {boolean} * @returns {boolean}
*/ */
private setToBlock = ( private setToBlock = (
index: number, index: number,
position: string = this.Editor.Caret.positions.DEFAULT, position: string = this.Editor.Caret.positions.DEFAULT,
offset: number = 0, offset = 0
): boolean => { ): boolean => {
if (!this.Editor.BlockManager.blocks[index]) { if (!this.Editor.BlockManager.blocks[index]) {
return false; return false;
} }
this.Editor.Caret.setToBlock(this.Editor.BlockManager.blocks[index], position, offset); this.Editor.Caret.setToBlock(this.Editor.BlockManager.blocks[index], position, offset);
return true; return true;
} }
@ -119,9 +125,9 @@ export default class CaretAPI extends Module {
* *
* @param {boolean} atEnd - if true, set Caret to the end of the Editor * @param {boolean} atEnd - if true, set Caret to the end of the Editor
* *
* @return {boolean} * @returns {boolean}
*/ */
private focus = (atEnd: boolean = false) => { private focus = (atEnd = false) => {
if (atEnd) { if (atEnd) {
return this.setToLastBlock(this.Editor.Caret.positions.END); return this.setToLastBlock(this.Editor.Caret.positions.END);
} }

View file

@ -1,5 +1,5 @@
import Module from '../../__module'; import Module from '../../__module';
import {Events} from '../../../../types/api'; import { Events } from '../../../../types/api';
/** /**
* @class EventsAPI * @class EventsAPI
@ -8,7 +8,8 @@ import {Events} from '../../../../types/api';
export default class EventsAPI extends Module { export default class EventsAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Events} *
* @returns {Events}
*/ */
get methods(): Events { get methods(): Events {
return { return {
@ -20,7 +21,8 @@ export default class EventsAPI extends Module {
/** /**
* Subscribe on Events * Subscribe on Events
* @param {String} eventName *
* @param {string} eventName
* @param {Function} callback * @param {Function} callback
*/ */
public on(eventName, callback): void { public on(eventName, callback): void {
@ -29,8 +31,9 @@ export default class EventsAPI extends Module {
/** /**
* Emit event with data * Emit event with data
* @param {String} eventName *
* @param {Object} data * @param {string} eventName
* @param {object} data
*/ */
public emit(eventName, data): void { public emit(eventName, data): void {
this.Editor.Events.emit(eventName, data); this.Editor.Events.emit(eventName, data);
@ -38,11 +41,11 @@ export default class EventsAPI extends Module {
/** /**
* Unsubscribe from Event * Unsubscribe from Event
* @param {String} eventName *
* @param {string} eventName
* @param {Function} callback * @param {Function} callback
*/ */
public off(eventName, callback): void { public off(eventName, callback): void {
this.Editor.Events.off(eventName, callback); this.Editor.Events.off(eventName, callback);
} }
} }

View file

@ -6,12 +6,15 @@
* if you cant to read more about how API works, please see docs * if you cant to read more about how API works, please see docs
*/ */
import Module from '../../__module'; import Module from '../../__module';
import {API as APIInterfaces} from '../../../../types'; import { API as APIInterfaces } from '../../../../types';
/** /**
* @class API * @class API
*/ */
export default class API extends Module { export default class API extends Module {
/**
* Editor.js Core API modules
*/
public get methods(): APIInterfaces { public get methods(): APIInterfaces {
return { return {
blocks: this.Editor.BlocksAPI.methods, blocks: this.Editor.BlocksAPI.methods,

View file

@ -8,7 +8,8 @@ import { InlineToolbar } from '../../../../types/api/inline-toolbar';
export default class InlineToolbarAPI extends Module { export default class InlineToolbarAPI extends Module {
/** /**
* Available methods * Available methods
* @return {InlineToolbar} *
* @returns {InlineToolbar}
*/ */
get methods(): InlineToolbar { get methods(): InlineToolbar {
return { return {

View file

@ -1,5 +1,5 @@
import Module from '../../__module'; import Module from '../../__module';
import {Listeners} from '../../../../types/api'; import { Listeners } from '../../../../types/api';
/** /**
* @class ListenersAPI * @class ListenersAPI
@ -8,7 +8,8 @@ import {Listeners} from '../../../../types/api';
export default class ListenersAPI extends Module { export default class ListenersAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Listeners} *
* @returns {Listeners}
*/ */
get methods(): Listeners { get methods(): Listeners {
return { return {

View file

@ -1,9 +1,11 @@
import Module from '../../__module'; import Module from '../../__module';
import {Notifier} from '../../../../types/api'; import { Notifier } from '../../../../types/api';
import {ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions} from 'codex-notifier'; import { ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions } from 'codex-notifier';
/**
*
*/
export default class NotifierAPI extends Module { export default class NotifierAPI extends Module {
/** /**
* Available methods * Available methods
*/ */
@ -13,7 +15,11 @@ export default class NotifierAPI extends Module {
}; };
} }
public show(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions) { /**
* Show notification
* @param options
*/
public show(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions): void {
return this.Editor.Notifier.show(options); return this.Editor.Notifier.show(options);
} }
} }

View file

@ -1,5 +1,5 @@
import Module from '../../__module'; import Module from '../../__module';
import {Sanitizer} from '../../../../types/api'; import { Sanitizer } from '../../../../types/api';
/** /**
* @class SanitizerAPI * @class SanitizerAPI
@ -8,7 +8,8 @@ import {Sanitizer} from '../../../../types/api';
export default class SanitizerAPI extends Module { export default class SanitizerAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Sanitizer} *
* @returns {Sanitizer}
*/ */
get methods(): Sanitizer { get methods(): Sanitizer {
return { return {
@ -16,8 +17,12 @@ export default class SanitizerAPI extends Module {
}; };
} }
/**
* Perform sanitizing of a string
* @param taintString - what to sanitize
* @param config - sanitizer config
*/
public clean(taintString, config) { public clean(taintString, config) {
return this.Editor.Sanitizer.clean(taintString, config); return this.Editor.Sanitizer.clean(taintString, config);
} }
} }

View file

@ -1,6 +1,6 @@
import Module from '../../__module'; import Module from '../../__module';
import {Saver} from '../../../../types/api'; import { Saver } from '../../../../types/api';
import {OutputData} from '../../../../types'; import { OutputData } from '../../../../types';
/** /**
* @class SaverAPI * @class SaverAPI
@ -9,7 +9,8 @@ import {OutputData} from '../../../../types';
export default class SaverAPI extends Module { export default class SaverAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Saver} *
* @returns {Saver}
*/ */
get methods(): Saver { get methods(): Saver {
return { return {

View file

@ -1,6 +1,6 @@
import Module from '../../__module'; import Module from '../../__module';
import SelectionUtils from '../../selection'; import SelectionUtils from '../../selection';
import {Selection as SelectionAPIInterface} from '../../../../types/api'; import { Selection as SelectionAPIInterface } from '../../../../types/api';
/** /**
* @class SelectionAPI * @class SelectionAPI
@ -9,9 +9,10 @@ import {Selection as SelectionAPIInterface} from '../../../../types/api';
export default class SelectionAPI extends Module { export default class SelectionAPI extends Module {
/** /**
* Available methods * Available methods
* @return {SelectionAPIInterface} *
* @returns {SelectionAPIInterface}
*/ */
get methods(): SelectionAPIInterface { public get methods(): SelectionAPIInterface {
return { return {
findParentTag: (tagName: string, className?: string) => this.findParentTag(tagName, className), findParentTag: (tagName: string, className?: string) => this.findParentTag(tagName, className),
expandToTag: (node: HTMLElement) => this.expandToTag(node), expandToTag: (node: HTMLElement) => this.expandToTag(node),
@ -20,9 +21,10 @@ export default class SelectionAPI extends Module {
/** /**
* Looks ahead from selection and find passed tag with class name * Looks ahead from selection and find passed tag with class name
*
* @param {string} tagName - tag to find * @param {string} tagName - tag to find
* @param {string} className - tag's class name * @param {string} className - tag's class name
* @return {HTMLElement|null} * @returns {HTMLElement|null}
*/ */
public findParentTag(tagName: string, className?: string): HTMLElement|null { public findParentTag(tagName: string, className?: string): HTMLElement|null {
return new SelectionUtils().findParentTag(tagName, className); return new SelectionUtils().findParentTag(tagName, className);
@ -30,10 +32,10 @@ export default class SelectionAPI extends Module {
/** /**
* Expand selection to passed tag * Expand selection to passed tag
*
* @param {HTMLElement} node - tag that should contain selection * @param {HTMLElement} node - tag that should contain selection
*/ */
public expandToTag(node: HTMLElement): void { public expandToTag(node: HTMLElement): void {
new SelectionUtils().expandToTag(node); new SelectionUtils().expandToTag(node);
} }
} }

View file

@ -1,11 +1,14 @@
import Module from '../../__module'; import Module from '../../__module';
import {Styles} from '../../../../types/api'; import { Styles } from '../../../../types/api';
/** /**
* *
*/ */
export default class StylesAPI extends Module { export default class StylesAPI extends Module {
get classes(): Styles { /**
* Exported classes
*/
public get classes(): Styles {
return { return {
/** /**
* Base Block styles * Base Block styles

View file

@ -1,5 +1,5 @@
import Module from '../../__module'; import Module from '../../__module';
import {Toolbar} from '../../../../types/api'; import { Toolbar } from '../../../../types/api';
/** /**
* @class ToolbarAPI * @class ToolbarAPI
@ -8,7 +8,8 @@ import {Toolbar} from '../../../../types/api';
export default class ToolbarAPI extends Module { export default class ToolbarAPI extends Module {
/** /**
* Available methods * Available methods
* @return {Toolbar} *
* @returns {Toolbar}
*/ */
get methods(): Toolbar { get methods(): Toolbar {
return { return {
@ -30,5 +31,4 @@ export default class ToolbarAPI extends Module {
public close(): void { public close(): void {
this.Editor.Toolbar.close(); this.Editor.Toolbar.close();
} }
} }

View file

@ -1,6 +1,6 @@
import Module from '../../__module'; import Module from '../../__module';
import { Tooltip } from '../../../../types/api'; import { Tooltip } from '../../../../types/api';
import {TooltipContent, TooltipOptions} from 'codex-tooltip'; import { TooltipContent, TooltipOptions } from 'codex-tooltip';
/** /**
* @class TooltipAPI * @class TooltipAPI
@ -13,13 +13,13 @@ export default class TooltipAPI extends Module {
get methods(): Tooltip { get methods(): Tooltip {
return { return {
show: (element: HTMLElement, show: (element: HTMLElement,
content: TooltipContent, content: TooltipContent,
options?: TooltipOptions, options?: TooltipOptions
) => this.show(element, content, options), ) => this.show(element, content, options),
hide: () => this.hide(), hide: () => this.hide(),
onHover: (element: HTMLElement, onHover: (element: HTMLElement,
content: TooltipContent, content: TooltipContent,
options?: TooltipOptions, options?: TooltipOptions
) => this.onHover(element, content, options), ) => this.onHover(element, content, options),
}; };
} }

View file

@ -6,10 +6,13 @@ import * as _ from '../utils';
import SelectionUtils from '../selection'; import SelectionUtils from '../selection';
import Flipper from '../flipper'; import Flipper from '../flipper';
/**
*
*/
export default class BlockEvents extends Module { export default class BlockEvents extends Module {
/** /**
* All keydowns on Block * All keydowns on Block
*
* @param {KeyboardEvent} event - keydown * @param {KeyboardEvent} event - keydown
*/ */
public keydown(event: KeyboardEvent): void { public keydown(event: KeyboardEvent): void {
@ -47,14 +50,12 @@ export default class BlockEvents extends Module {
case _.keyCodes.ESC: case _.keyCodes.ESC:
this.escapePressed(event); this.escapePressed(event);
break; break;
default:
this.defaultHandler();
break;
} }
} }
/** /**
* Fires on keydown before event processing * Fires on keydown before event processing
*
* @param {KeyboardEvent} event - keydown * @param {KeyboardEvent} event - keydown
*/ */
public beforeKeydownProcessing(event: KeyboardEvent): void { public beforeKeydownProcessing(event: KeyboardEvent): void {
@ -77,6 +78,7 @@ export default class BlockEvents extends Module {
/** /**
* Allow to use shortcuts with selected blocks * Allow to use shortcuts with selected blocks
*
* @type {boolean} * @type {boolean}
*/ */
const isShortcut = event.ctrlKey || event.metaKey || event.altKey || event.shiftKey; const isShortcut = event.ctrlKey || event.metaKey || event.altKey || event.shiftKey;
@ -92,9 +94,10 @@ export default class BlockEvents extends Module {
* Key up on Block: * Key up on Block:
* - shows Inline Toolbar if something selected * - shows Inline Toolbar if something selected
* - shows conversion toolbar with 85% of block selection * - shows conversion toolbar with 85% of block selection
*
* @param event
*/ */
public keyup(event): void { public keyup(event): void {
/** /**
* If shift key was pressed some special shortcut is used (eg. cross block selection via shift + arrows) * If shift key was pressed some special shortcut is used (eg. cross block selection via shift + arrows)
*/ */
@ -108,12 +111,6 @@ export default class BlockEvents extends Module {
this.Editor.UI.checkEmptiness(); this.Editor.UI.checkEmptiness();
} }
/**
* Mouse up on Block:
*/
public mouseUp(): void {
}
/** /**
* Set up mouse selection handlers * Set up mouse selection handlers
* *
@ -131,6 +128,7 @@ export default class BlockEvents extends Module {
/** /**
* Open Toolbox to leaf Tools * Open Toolbox to leaf Tools
*
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
public tabPressed(event): void { public tabPressed(event): void {
@ -226,6 +224,7 @@ export default class BlockEvents extends Module {
/** /**
* Copy and Delete selected Blocks * Copy and Delete selected Blocks
*
* @param {ClipboardEvent} event * @param {ClipboardEvent} event
*/ */
public handleCommandX(event: ClipboardEvent): void { public handleCommandX(event: ClipboardEvent): void {
@ -247,6 +246,7 @@ export default class BlockEvents extends Module {
/** /**
* ENTER pressed on block * ENTER pressed on block
*
* @param {KeyboardEvent} event - keydown * @param {KeyboardEvent} event - keydown
*/ */
private enter(event: KeyboardEvent): void { private enter(event: KeyboardEvent): void {
@ -314,6 +314,7 @@ export default class BlockEvents extends Module {
/** /**
* Handle backspace keydown on Block * Handle backspace keydown on Block
*
* @param {KeyboardEvent} event - keydown * @param {KeyboardEvent} event - keydown
*/ */
private backspace(event: KeyboardEvent): void { private backspace(event: KeyboardEvent): void {
@ -339,7 +340,7 @@ export default class BlockEvents extends Module {
Caret.setToBlock( Caret.setToBlock(
BlockManager.currentBlock, BlockManager.currentBlock,
index ? Caret.positions.END : Caret.positions.START, index ? Caret.positions.END : Caret.positions.START
); );
/** Close Toolbar */ /** Close Toolbar */
@ -347,6 +348,7 @@ export default class BlockEvents extends Module {
/** Clear selection */ /** Clear selection */
BlockSelection.clearSelection(event); BlockSelection.clearSelection(event);
return; return;
} }
@ -401,6 +403,7 @@ export default class BlockEvents extends Module {
Caret.setToBlock(BlockManager.currentBlock); Caret.setToBlock(BlockManager.currentBlock);
Toolbar.close(); Toolbar.close();
return; return;
} }
@ -413,7 +416,7 @@ export default class BlockEvents extends Module {
Caret.createShadow(targetBlock.pluginsContent); Caret.createShadow(targetBlock.pluginsContent);
BlockManager.mergeBlocks(targetBlock, blockToMerge) BlockManager.mergeBlocks(targetBlock, blockToMerge)
.then( () => { .then(() => {
/** Restore caret position after merge */ /** Restore caret position after merge */
Caret.restoreCaret(targetBlock.pluginsContent as HTMLElement); Caret.restoreCaret(targetBlock.pluginsContent as HTMLElement);
targetBlock.pluginsContent.normalize(); targetBlock.pluginsContent.normalize();
@ -423,6 +426,8 @@ export default class BlockEvents extends Module {
/** /**
* Handle right and down keyboard keys * Handle right and down keyboard keys
*
* @param event
*/ */
private arrowRightAndDown(event: KeyboardEvent): void { private arrowRightAndDown(event: KeyboardEvent): void {
const isFlipperCombination = Flipper.usedKeys.includes(event.keyCode) && const isFlipperCombination = Flipper.usedKeys.includes(event.keyCode) &&
@ -446,6 +451,7 @@ export default class BlockEvents extends Module {
if (event.shiftKey && event.keyCode === _.keyCodes.DOWN && shouldEnableCBS) { if (event.shiftKey && event.keyCode === _.keyCodes.DOWN && shouldEnableCBS) {
this.Editor.CrossBlockSelection.toggleBlockSelectedState(); this.Editor.CrossBlockSelection.toggleBlockSelectedState();
return; return;
} }
@ -474,6 +480,8 @@ export default class BlockEvents extends Module {
/** /**
* Handle left and up keyboard keys * Handle left and up keyboard keys
*
* @param event
*/ */
private arrowLeftAndUp(event: KeyboardEvent): void { private arrowLeftAndUp(event: KeyboardEvent): void {
/** /**
@ -498,6 +506,7 @@ export default class BlockEvents extends Module {
if (event.shiftKey && event.keyCode === _.keyCodes.UP && shouldEnableCBS) { if (event.shiftKey && event.keyCode === _.keyCodes.UP && shouldEnableCBS) {
this.Editor.CrossBlockSelection.toggleBlockSelectedState(false); this.Editor.CrossBlockSelection.toggleBlockSelectedState(false);
return; return;
} }
@ -524,20 +533,17 @@ export default class BlockEvents extends Module {
this.Editor.BlockSelection.clearSelection(event); this.Editor.BlockSelection.clearSelection(event);
} }
/**
* Default keydown handler
*/
private defaultHandler(): void {}
/** /**
* Cases when we need to close Toolbar * Cases when we need to close Toolbar
*
* @param event
*/ */
private needToolbarClosing(event) { private needToolbarClosing(event) {
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened), const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened),
blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened), blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened), inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened),
conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened), conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened),
flippingToolbarItems = event.keyCode === _.keyCodes.TAB; flippingToolbarItems = event.keyCode === _.keyCodes.TAB;
/** /**
* Do not close Toolbar in cases: * Do not close Toolbar in cases:
@ -545,12 +551,12 @@ export default class BlockEvents extends Module {
* 2. When Toolbar is opened and Tab leafs its Tools * 2. When Toolbar is opened and Tab leafs its Tools
* 3. When Toolbar's component is opened and some its item selected * 3. When Toolbar's component is opened and some its item selected
*/ */
return !(event.shiftKey return !(event.shiftKey ||
|| flippingToolbarItems flippingToolbarItems ||
|| toolboxItemSelected toolboxItemSelected ||
|| blockSettingsItemSelected blockSettingsItemSelected ||
|| inlineToolbarItemSelected inlineToolbarItemSelected ||
|| conversionToolbarItemSelected conversionToolbarItemSelected
); );
} }
@ -559,7 +565,7 @@ export default class BlockEvents extends Module {
*/ */
private activateToolbox(): void { private activateToolbox(): void {
if (!this.Editor.Toolbar.opened) { if (!this.Editor.Toolbar.opened) {
this.Editor.Toolbar.open(false , false); this.Editor.Toolbar.open(false, false);
this.Editor.Toolbar.plusButton.show(); this.Editor.Toolbar.plusButton.show();
} }

View file

@ -6,23 +6,23 @@
* *
* @version 2.0.0 * @version 2.0.0
*/ */
import Block, {BlockToolAPI} from '../block'; import Block, { BlockToolAPI } from '../block';
import Module from '../__module'; import Module from '../__module';
import $ from '../dom'; import $ from '../dom';
import * as _ from '../utils'; import * as _ from '../utils';
import Blocks from '../blocks'; import Blocks from '../blocks';
import {BlockTool, BlockToolConstructable, BlockToolData, PasteEvent, ToolConfig} from '../../../types'; import { BlockTool, BlockToolConstructable, BlockToolData, PasteEvent, ToolConfig } from '../../../types';
/** /**
* @typedef {BlockManager} BlockManager * @typedef {BlockManager} BlockManager
* @property {Number} currentBlockIndex - Index of current working block * @property {number} currentBlockIndex - Index of current working block
* @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks} * @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks}
*/ */
export default class BlockManager extends Module { export default class BlockManager extends Module {
/** /**
* Returns current Block index * Returns current Block index
* @return {number} *
* @returns {number}
*/ */
public get currentBlockIndex(): number { public get currentBlockIndex(): number {
return this._currentBlockIndex; return this._currentBlockIndex;
@ -30,6 +30,7 @@ export default class BlockManager extends Module {
/** /**
* Set current Block index and fire Block lifecycle callbacks * Set current Block index and fire Block lifecycle callbacks
*
* @param newIndex * @param newIndex
*/ */
public set currentBlockIndex(newIndex: number) { public set currentBlockIndex(newIndex: number) {
@ -46,7 +47,8 @@ export default class BlockManager extends Module {
/** /**
* returns first Block * returns first Block
* @return {Block} *
* @returns {Block}
*/ */
public get firstBlock(): Block { public get firstBlock(): Block {
return this._blocks[0]; return this._blocks[0];
@ -54,7 +56,8 @@ export default class BlockManager extends Module {
/** /**
* returns last Block * returns last Block
* @return {Block} *
* @returns {Block}
*/ */
public get lastBlock(): Block { public get lastBlock(): Block {
return this._blocks[this._blocks.length - 1]; return this._blocks[this._blocks.length - 1];
@ -63,7 +66,7 @@ export default class BlockManager extends Module {
/** /**
* Get current Block instance * Get current Block instance
* *
* @return {Block} * @returns {Block}
*/ */
public get currentBlock(): Block { public get currentBlock(): Block {
return this._blocks[this.currentBlockIndex]; return this._blocks[this.currentBlockIndex];
@ -71,7 +74,8 @@ export default class BlockManager extends Module {
/** /**
* Returns next Block instance * Returns next Block instance
* @return {Block|null} *
* @returns {Block|null}
*/ */
public get nextBlock(): Block { public get nextBlock(): Block {
const isLastBlock = this.currentBlockIndex === (this._blocks.length - 1); const isLastBlock = this.currentBlockIndex === (this._blocks.length - 1);
@ -107,7 +111,8 @@ export default class BlockManager extends Module {
/** /**
* Returns previous Block instance * Returns previous Block instance
* @return {Block|null} *
* @returns {Block|null}
*/ */
public get previousBlock(): Block { public get previousBlock(): Block {
const isFirstBlock = this.currentBlockIndex === 0; const isFirstBlock = this.currentBlockIndex === 0;
@ -142,7 +147,7 @@ export default class BlockManager extends Module {
* *
* @type {number} * @type {number}
*/ */
private _currentBlockIndex: number = -1; private _currentBlockIndex = -1;
/** /**
* Proxy for Blocks instance {@link Blocks} * Proxy for Blocks instance {@link Blocks}
@ -185,25 +190,25 @@ export default class BlockManager extends Module {
Listeners.on( Listeners.on(
document, document,
'copy', 'copy',
(e: ClipboardEvent) => BlockEvents.handleCommandC(e), (e: ClipboardEvent) => BlockEvents.handleCommandC(e)
); );
/** Copy and cut */ /** Copy and cut */
Listeners.on( Listeners.on(
document, document,
'cut', 'cut',
(e: ClipboardEvent) => BlockEvents.handleCommandX(e), (e: ClipboardEvent) => BlockEvents.handleCommandX(e)
); );
} }
/** /**
* Creates Block instance by tool name * Creates Block instance by tool name
* *
* @param {String} toolName - tools passed in editor config {@link EditorConfig#tools} * @param {string} toolName - tools passed in editor config {@link EditorConfig#tools}
* @param {Object} data - constructor params * @param {object} data - constructor params
* @param {Object} settings - block settings * @param {object} settings - block settings
* *
* @return {Block} * @returns {Block}
*/ */
public composeBlock(toolName: string, data: BlockToolData = {}, settings: ToolConfig = {}): Block { public composeBlock(toolName: string, data: BlockToolData = {}, settings: ToolConfig = {}): Block {
const toolInstance = this.Editor.Tools.construct(toolName, data) as BlockTool; const toolInstance = this.Editor.Tools.construct(toolName, data) as BlockTool;
@ -218,20 +223,20 @@ export default class BlockManager extends Module {
/** /**
* Insert new block into _blocks * Insert new block into _blocks
* *
* @param {String} toolName plugin name, by default method inserts initial block type * @param {string} toolName plugin name, by default method inserts initial block type
* @param {Object} data plugin data * @param {object} data plugin data
* @param {Object} settings - default settings * @param {object} settings - default settings
* @param {number} index - index where to insert new Block * @param {number} index - index where to insert new Block
* @param {boolean} needToFocus - flag shows if needed to update current Block index * @param {boolean} needToFocus - flag shows if needed to update current Block index
* *
* @return {Block} * @returns {Block}
*/ */
public insert( public insert(
toolName: string = this.config.initialBlock, toolName: string = this.config.initialBlock,
data: BlockToolData = {}, data: BlockToolData = {},
settings: ToolConfig = {}, settings: ToolConfig = {},
index: number = this.currentBlockIndex + 1, index: number = this.currentBlockIndex + 1,
needToFocus: boolean = true, needToFocus = true
): Block { ): Block {
const block = this.composeBlock(toolName, data, settings); const block = this.composeBlock(toolName, data, settings);
@ -254,7 +259,7 @@ export default class BlockManager extends Module {
public paste( public paste(
toolName: string, toolName: string,
pasteEvent: PasteEvent, pasteEvent: PasteEvent,
replace: boolean = false, replace = false
): Block { ): Block {
let block; let block;
@ -269,6 +274,7 @@ export default class BlockManager extends Module {
} catch (e) { } catch (e) {
_.log(`${toolName}: onPaste callback call is failed`, 'error', e); _.log(`${toolName}: onPaste callback call is failed`, 'error', e);
} }
return block; return block;
} }
@ -280,9 +286,9 @@ export default class BlockManager extends Module {
* *
* TODO: Remove method and use insert() with index instead (?) * TODO: Remove method and use insert() with index instead (?)
* *
* @return {Block} inserted Block * @returns {Block} inserted Block
*/ */
public insertInitialBlockAtIndex(index: number, needToFocus: boolean = false) { public insertInitialBlockAtIndex(index: number, needToFocus = false) {
const block = this.composeBlock(this.config.initialBlock, {}, {}); const block = this.composeBlock(this.config.initialBlock, {}, {});
this._blocks[index] = block; this._blocks[index] = block;
@ -298,7 +304,8 @@ export default class BlockManager extends Module {
/** /**
* Always inserts at the end * Always inserts at the end
* @return {Block} *
* @returns {Block}
*/ */
public insertAtEnd(): Block { public insertAtEnd(): Block {
/** /**
@ -314,10 +321,11 @@ export default class BlockManager extends Module {
/** /**
* Merge two blocks * Merge two blocks
*
* @param {Block} targetBlock - previous block will be append to this block * @param {Block} targetBlock - previous block will be append to this block
* @param {Block} blockToMerge - block that will be merged with target block * @param {Block} blockToMerge - block that will be merged with target block
* *
* @return {Promise} - the sequence that can be continued * @returns {Promise} - the sequence that can be continued
*/ */
public async mergeBlocks(targetBlock: Block, blockToMerge: Block): Promise<void> { public async mergeBlocks(targetBlock: Block, blockToMerge: Block): Promise<void> {
const blockToMergeIndex = this._blocks.indexOf(blockToMerge); const blockToMergeIndex = this._blocks.indexOf(blockToMerge);
@ -338,7 +346,8 @@ export default class BlockManager extends Module {
/** /**
* Remove block with passed index or remove last * Remove block with passed index or remove last
* @param {Number|null} index *
* @param {number|null} index
*/ */
public removeBlock(index?: number): void { public removeBlock(index?: number): void {
if (index === undefined) { if (index === undefined) {
@ -356,7 +365,6 @@ export default class BlockManager extends Module {
if (!this.blocks.length) { if (!this.blocks.length) {
this.currentBlockIndex = -1; this.currentBlockIndex = -1;
this.insert(); this.insert();
return;
} else if (index === 0) { } else if (index === 0) {
this.currentBlockIndex = 0; this.currentBlockIndex = 0;
} }
@ -365,7 +373,8 @@ export default class BlockManager extends Module {
/** /**
* Remove only selected Blocks * Remove only selected Blocks
* and returns first Block index where started removing... * and returns first Block index where started removing...
* @return number|undefined *
* @returns number|undefined
*/ */
public removeSelectedBlocks(): number|undefined { public removeSelectedBlocks(): number|undefined {
let firstSelectedBlockIndex; let firstSelectedBlockIndex;
@ -405,7 +414,7 @@ export default class BlockManager extends Module {
* 1. Extract content from Caret position to the Block`s end * 1. Extract content from Caret position to the Block`s end
* 2. Insert a new Block below current one with extracted content * 2. Insert a new Block below current one with extracted content
* *
* @return {Block} * @returns {Block}
*/ */
public split(): Block { public split(): Block {
const extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(); const extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition();
@ -422,6 +431,7 @@ export default class BlockManager extends Module {
/** /**
* Renew current Block * Renew current Block
*
* @type {Block} * @type {Block}
*/ */
return this.insert(this.config.initialBlock, data); return this.insert(this.config.initialBlock, data);
@ -430,16 +440,16 @@ export default class BlockManager extends Module {
/** /**
* Replace current working block * Replace current working block
* *
* @param {String} toolName plugin name * @param {string} toolName plugin name
* @param {BlockToolData} data plugin data * @param {BlockToolData} data plugin data
* @param {ToolConfig} settings plugin config * @param {ToolConfig} settings plugin config
* *
* @return {Block} * @returns {Block}
*/ */
public replace( public replace(
toolName: string = this.config.initialBlock, toolName: string = this.config.initialBlock,
data: BlockToolData = {}, data: BlockToolData = {},
settings: ToolConfig = {}, settings: ToolConfig = {}
): Block { ): Block {
const block = this.composeBlock(toolName, data, settings); const block = this.composeBlock(toolName, data, settings);
@ -450,8 +460,9 @@ export default class BlockManager extends Module {
/** /**
* Returns Block by passed index * Returns Block by passed index
* @param {Number} index *
* @return {Block} * @param {number} index
* @returns {Block}
*/ */
public getBlockByIndex(index): Block { public getBlockByIndex(index): Block {
return this._blocks[index]; return this._blocks[index];
@ -459,6 +470,7 @@ export default class BlockManager extends Module {
/** /**
* Get Block instance by html element * Get Block instance by html element
*
* @param {Node} element * @param {Node} element
* @returns {Block} * @returns {Block}
*/ */
@ -468,8 +480,8 @@ export default class BlockManager extends Module {
} }
const nodes = this._blocks.nodes, const nodes = this._blocks.nodes,
firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`), firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`),
index = nodes.indexOf(firstLevelBlock as HTMLElement); index = nodes.indexOf(firstLevelBlock as HTMLElement);
if (index >= 0) { if (index >= 0) {
return this._blocks[index]; return this._blocks[index];
@ -487,6 +499,7 @@ export default class BlockManager extends Module {
/** /**
* Mark current Block as selected * Mark current Block as selected
*
* @type {boolean} * @type {boolean}
*/ */
this.currentBlock.focused = true; this.currentBlock.focused = true;
@ -496,7 +509,9 @@ export default class BlockManager extends Module {
* Remove selection from all Blocks * Remove selection from all Blocks
*/ */
public clearFocused(): void { public clearFocused(): void {
this.blocks.forEach( (block) => block.focused = false); this.blocks.forEach((block) => {
block.focused = false;
});
} }
/** /**
@ -520,9 +535,11 @@ export default class BlockManager extends Module {
if (parentFirstLevelBlock) { if (parentFirstLevelBlock) {
/** /**
* Update current Block's index * Update current Block's index
*
* @type {number} * @type {number}
*/ */
this.currentBlockIndex = this._blocks.nodes.indexOf(parentFirstLevelBlock as HTMLElement); this.currentBlockIndex = this._blocks.nodes.indexOf(parentFirstLevelBlock as HTMLElement);
return this.currentBlock; return this.currentBlock;
} else { } else {
throw new Error('Can not find a Block from this child Node'); throw new Error('Can not find a Block from this child Node');
@ -533,7 +550,7 @@ export default class BlockManager extends Module {
* Return block which contents passed node * Return block which contents passed node
* *
* @param {Node} childNode * @param {Node} childNode
* @return {Block} * @returns {Block}
*/ */
public getBlockByChildNode(childNode: Node): Block { public getBlockByChildNode(childNode: Node): Block {
/** /**
@ -550,8 +567,9 @@ export default class BlockManager extends Module {
/** /**
* Swap Blocks Position * Swap Blocks Position
* @param {Number} fromIndex *
* @param {Number} toIndex * @param {number} fromIndex
* @param {number} toIndex
* @deprecated use 'move' instead * @deprecated use 'move' instead
*/ */
public swap(fromIndex, toIndex): void { public swap(fromIndex, toIndex): void {
@ -564,18 +582,21 @@ export default class BlockManager extends Module {
/** /**
* Move a block to a new index * Move a block to a new index
* @param {Number} toIndex *
* @param {Number} fromIndex * @param {number} toIndex
* @param {number} fromIndex
*/ */
public move(toIndex, fromIndex = this.currentBlockIndex): void { public move(toIndex, fromIndex = this.currentBlockIndex): void {
// make sure indexes are valid and within a valid range // make sure indexes are valid and within a valid range
if (isNaN(toIndex) || isNaN(fromIndex)) { if (isNaN(toIndex) || isNaN(fromIndex)) {
_.log(`Warning during 'move' call: incorrect indices provided.`, 'warn'); _.log(`Warning during 'move' call: incorrect indices provided.`, 'warn');
return; return;
} }
if (!this.validateIndex(toIndex) || !this.validateIndex(fromIndex)) { if (!this.validateIndex(toIndex) || !this.validateIndex(fromIndex)) {
_.log(`Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.`, 'warn'); _.log(`Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.`, 'warn');
return; return;
} }
@ -597,11 +618,12 @@ export default class BlockManager extends Module {
/** /**
* Clears Editor * Clears Editor
*
* @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render) * @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render)
* we don't need to add empty initial block * we don't need to add empty initial block
* 2) in api.blocks.clear we should add empty block * 2) in api.blocks.clear we should add empty block
*/ */
public clear(needAddInitialBlock: boolean = false): void { public clear(needAddInitialBlock = false): void {
this._blocks.removeAll(); this._blocks.removeAll();
this.dropPointer(); this.dropPointer();
@ -617,13 +639,13 @@ export default class BlockManager extends Module {
/** /**
* Bind Events * Bind Events
* @param {Object} block *
* @param {object} block
*/ */
private bindEvents(block: Block): void { private bindEvents(block: Block): void {
const {BlockEvents, Listeners} = this.Editor; const { BlockEvents, Listeners } = this.Editor;
Listeners.on(block.holder, 'keydown', (event) => BlockEvents.keydown(event as KeyboardEvent), true); Listeners.on(block.holder, 'keydown', (event) => BlockEvents.keydown(event as KeyboardEvent), true);
Listeners.on(block.holder, 'mouseup', (event) => BlockEvents.mouseUp());
Listeners.on(block.holder, 'mousedown', (event: MouseEvent) => BlockEvents.mouseDown(event)); Listeners.on(block.holder, 'mousedown', (event: MouseEvent) => BlockEvents.mouseDown(event));
Listeners.on(block.holder, 'keyup', (event) => BlockEvents.keyup(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, 'dragover', (event) => BlockEvents.dragOver(event as DragEvent));
@ -632,6 +654,7 @@ export default class BlockManager extends Module {
/** /**
* Validates that the given index is not lower than 0 or higher than the amount of blocks * Validates that the given index is not lower than 0 or higher than the amount of blocks
*
* @param {number} index - index of blocks array to validate * @param {number} index - index of blocks array to validate
*/ */
private validateIndex(index: number): boolean { private validateIndex(index: number): boolean {

View file

@ -12,11 +12,14 @@ import $ from '../dom';
import SelectionUtils from '../selection'; import SelectionUtils from '../selection';
/**
*
*/
export default class BlockSelection extends Module { export default class BlockSelection extends Module {
/** /**
* Sanitizer Config * Sanitizer Config
* @return {SanitizerConfig} *
* @returns {SanitizerConfig}
*/ */
private get sanitizerConfig() { private get sanitizerConfig() {
return { return {
@ -47,37 +50,43 @@ export default class BlockSelection extends Module {
/** /**
* Flag that identifies all Blocks selection * Flag that identifies all Blocks selection
* @return {boolean} *
* @returns {boolean}
*/ */
public get allBlocksSelected(): boolean { public get allBlocksSelected(): boolean {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
return BlockManager.blocks.every((block) => block.selected === true); return BlockManager.blocks.every((block) => block.selected === true);
} }
/** /**
* Set selected all blocks * Set selected all blocks
*
* @param {boolean} state * @param {boolean} state
*/ */
public set allBlocksSelected(state: boolean) { public set allBlocksSelected(state: boolean) {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
BlockManager.blocks.forEach((block) => block.selected = state); BlockManager.blocks.forEach((block) => {
block.selected = state;
});
} }
/** /**
* Flag that identifies any Block selection * Flag that identifies any Block selection
* @return {boolean} *
* @returns {boolean}
*/ */
public get anyBlockSelected(): boolean { public get anyBlockSelected(): boolean {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
return BlockManager.blocks.some((block) => block.selected === true); return BlockManager.blocks.some((block) => block.selected === true);
} }
/** /**
* Return selected Blocks array * Return selected Blocks array
* @return {Block[]} *
* @returns {Block[]}
*/ */
public get selectedBlocks(): Block[] { public get selectedBlocks(): Block[] {
return this.Editor.BlockManager.blocks.filter((block: Block) => block.selected); return this.Editor.BlockManager.blocks.filter((block: Block) => block.selected);
@ -86,26 +95,30 @@ export default class BlockSelection extends Module {
/** /**
* Flag used to define block selection * Flag used to define block selection
* First CMD+A defines it as true and then second CMD+A selects all Blocks * First CMD+A defines it as true and then second CMD+A selects all Blocks
*
* @type {boolean} * @type {boolean}
*/ */
private needToSelectAll: boolean = false; private needToSelectAll = false;
/** /**
* Flag used to define native input selection * Flag used to define native input selection
* In this case we allow double CMD+A to select Block * In this case we allow double CMD+A to select Block
*
* @type {boolean} * @type {boolean}
*/ */
private nativeInputSelected: boolean = false; private nativeInputSelected = false;
/** /**
* Flag identifies any input selection * Flag identifies any input selection
* That means we can select whole Block * That means we can select whole Block
*
* @type {boolean} * @type {boolean}
*/ */
private readyToBlockSelection: boolean = false; private readyToBlockSelection = false;
/** /**
* SelectionUtils instance * SelectionUtils instance
*
* @type {SelectionUtils} * @type {SelectionUtils}
*/ */
private selection: SelectionUtils; private selection: SelectionUtils;
@ -116,13 +129,14 @@ export default class BlockSelection extends Module {
* to select all and copy them * to select all and copy them
*/ */
public prepare(): void { public prepare(): void {
const {Shortcuts} = this.Editor; const { Shortcuts } = this.Editor;
/** Selection shortcut */ /** Selection shortcut */
Shortcuts.add({ Shortcuts.add({
name: 'CMD+A', name: 'CMD+A',
handler: (event) => { handler: (event) => {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
/** /**
* When one page consist of two or more EditorJS instances * 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
@ -143,10 +157,11 @@ export default class BlockSelection extends Module {
/** /**
* Remove selection of Block * Remove selection of Block
*
* @param {number?} index - Block index according to the BlockManager's indexes * @param {number?} index - Block index according to the BlockManager's indexes
*/ */
public unSelectBlockByIndex(index?) { public unSelectBlockByIndex(index?) {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
let block; let block;
@ -166,7 +181,7 @@ export default class BlockSelection extends Module {
* @param {boolean} restoreSelection - if true, restore saved selection * @param {boolean} restoreSelection - if true, restore saved selection
*/ */
public clearSelection(reason?: Event, restoreSelection = false) { public clearSelection(reason?: Event, restoreSelection = false) {
const {BlockManager, Caret, RectangleSelection} = this.Editor; const { BlockManager, Caret, RectangleSelection } = this.Editor;
this.needToSelectAll = false; this.needToSelectAll = false;
this.nativeInputSelected = false; this.nativeInputSelected = false;
@ -190,6 +205,7 @@ export default class BlockSelection extends Module {
if (!this.anyBlockSelected || RectangleSelection.isRectActivated()) { if (!this.anyBlockSelected || RectangleSelection.isRectActivated()) {
this.Editor.RectangleSelection.clearSelection(); this.Editor.RectangleSelection.clearSelection();
return; return;
} }
@ -210,7 +226,7 @@ export default class BlockSelection extends Module {
* *
* @param {ClipboardEvent} e - copy/cut event * @param {ClipboardEvent} e - copy/cut event
* *
* @return Promise<void> * @returns Promise<void>
*/ */
public async copySelectedBlocks(e: ClipboardEvent): Promise<void> { public async copySelectedBlocks(e: ClipboardEvent): Promise<void> {
/** /**
@ -221,19 +237,20 @@ export default class BlockSelection extends Module {
const fakeClipboard = $.make('div'); const fakeClipboard = $.make('div');
this.selectedBlocks.forEach((block) => { this.selectedBlocks.forEach((block) => {
/** /**
* Make <p> tag that holds clean HTML * Make <p> tag that holds clean HTML
*/ */
const cleanHTML = this.Editor.Sanitizer.clean(block.holder.innerHTML, this.sanitizerConfig); const cleanHTML = this.Editor.Sanitizer.clean(block.holder.innerHTML, this.sanitizerConfig);
const fragment = $.make('p'); const fragment = $.make('p');
fragment.innerHTML = cleanHTML; fragment.innerHTML = cleanHTML;
fakeClipboard.appendChild(fragment); fakeClipboard.appendChild(fragment);
}); });
const savedData = await Promise.all(this.selectedBlocks.map((block) => block.save())); const savedData = await Promise.all(this.selectedBlocks.map((block) => block.save()));
const textPlain = Array.from(fakeClipboard.childNodes).map((node) => node.textContent).join('\n\n'); const textPlain = Array.from(fakeClipboard.childNodes).map((node) => node.textContent)
.join('\n\n');
const textHTML = fakeClipboard.innerHTML; const textHTML = fakeClipboard.innerHTML;
e.clipboardData.setData('text/plain', textPlain); e.clipboardData.setData('text/plain', textPlain);
@ -243,10 +260,11 @@ export default class BlockSelection extends Module {
/** /**
* select Block * select Block
*
* @param {number?} index - Block index according to the BlockManager's indexes * @param {number?} index - Block index according to the BlockManager's indexes
*/ */
public selectBlockByIndex(index?) { public selectBlockByIndex(index?) {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
/** /**
* Remove previous focused Block's state * Remove previous focused Block's state
@ -284,6 +302,7 @@ export default class BlockSelection extends Module {
/** allow default selection on native inputs */ /** allow default selection on native inputs */
if ($.isNativeInput(event.target) && !this.readyToBlockSelection) { if ($.isNativeInput(event.target) && !this.readyToBlockSelection) {
this.readyToBlockSelection = true; this.readyToBlockSelection = true;
return; return;
} }
@ -296,11 +315,13 @@ export default class BlockSelection extends Module {
*/ */
if (inputs.length > 1 && !this.readyToBlockSelection) { if (inputs.length > 1 && !this.readyToBlockSelection) {
this.readyToBlockSelection = true; this.readyToBlockSelection = true;
return; return;
} }
if (inputs.length === 1 && !this.needToSelectAll) { if (inputs.length === 1 && !this.needToSelectAll) {
this.needToSelectAll = true; this.needToSelectAll = true;
return; return;
} }

View file

@ -19,14 +19,13 @@ import * as _ from '../utils';
* @typedef {Caret} Caret * @typedef {Caret} Caret
*/ */
export default class Caret extends Module { export default class Caret extends Module {
/** /**
* Allowed caret positions in input * Allowed caret positions in input
* *
* @static * @static
* @returns {{START: string, END: string, DEFAULT: string}} * @returns {{START: string, END: string, DEFAULT: string}}
*/ */
public get positions(): {START: string, END: string, DEFAULT: string} { public get positions(): {START: string; END: string; DEFAULT: string} {
return { return {
START: 'start', START: 'start',
END: 'end', END: 'end',
@ -45,7 +44,8 @@ export default class Caret extends Module {
/** /**
* Get's deepest first node and checks if offset is zero * Get's deepest first node and checks if offset is zero
* @return {boolean} *
* @returns {boolean}
*/ */
public get isAtStart(): boolean { public get isAtStart(): boolean {
const selection = Selection.get(); const selection = Selection.get();
@ -65,6 +65,7 @@ export default class Caret extends Module {
/** /**
* Workaround case when caret in the text like " |Hello!" * Workaround case when caret in the text like " |Hello!"
* selection.anchorOffset is 1, but real caret visible position is 0 * selection.anchorOffset is 1, but real caret visible position is 0
*
* @type {number} * @type {number}
*/ */
@ -84,6 +85,7 @@ export default class Caret extends Module {
* So we use child with focusOffset index as new anchorNode. * So we use child with focusOffset index as new anchorNode.
*/ */
let focusOffset = selection.focusOffset; let focusOffset = selection.focusOffset;
if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) { if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
if (focusNode.childNodes[focusOffset]) { if (focusNode.childNodes[focusOffset]) {
focusNode = focusNode.childNodes[focusOffset]; focusNode = focusNode.childNodes[focusOffset];
@ -106,6 +108,7 @@ export default class Caret extends Module {
const nothingAtLeft = leftSiblings.every((node) => { const nothingAtLeft = leftSiblings.every((node) => {
/** /**
* Workaround case when block starts with several <br>'s (created by SHIFT+ENTER) * Workaround case when block starts with several <br>'s (created by SHIFT+ENTER)
*
* @see https://github.com/codex-team/editor.js/issues/726 * @see https://github.com/codex-team/editor.js/issues/726
* We need to allow to delete such linebreaks, so in this case caret IS NOT AT START * We need to allow to delete such linebreaks, so in this case caret IS NOT AT START
*/ */
@ -133,7 +136,8 @@ export default class Caret extends Module {
/** /**
* Get's deepest last node and checks if offset is last node text length * Get's deepest last node and checks if offset is last node text length
* @return {boolean} *
* @returns {boolean}
*/ */
public get isAtEnd(): boolean { public get isAtEnd(): boolean {
const selection = Selection.get(); const selection = Selection.get();
@ -161,6 +165,7 @@ export default class Caret extends Module {
* So we use child with anchofocusOffset - 1 as new focusNode. * So we use child with anchofocusOffset - 1 as new focusNode.
*/ */
let focusOffset = selection.focusOffset; let focusOffset = selection.focusOffset;
if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) { if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
if (focusNode.childNodes[focusOffset - 1]) { if (focusNode.childNodes[focusOffset - 1]) {
focusNode = focusNode.childNodes[focusOffset - 1]; focusNode = focusNode.childNodes[focusOffset - 1];
@ -216,12 +221,12 @@ export default class Caret extends Module {
* - last found text node: sets at the end of the node. Also, you can customize the behaviour * - last found text node: sets at the end of the node. Also, you can customize the behaviour
* *
* @param {Block} block - Block class * @param {Block} block - Block class
* @param {String} position - position where to set caret. * @param {string} position - position where to set caret.
* If default - leave default behaviour and apply offset if it's passed * If default - leave default behaviour and apply offset if it's passed
* @param {Number} offset - caret offset regarding to the text node * @param {number} offset - caret offset regarding to the text node
*/ */
public setToBlock(block: Block, position: string = this.positions.DEFAULT, offset: number = 0): void { public setToBlock(block: Block, position: string = this.positions.DEFAULT, offset = 0): void {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
let element; let element;
switch (position) { switch (position) {
@ -255,7 +260,7 @@ export default class Caret extends Module {
/** /**
* @todo try to fix via Promises or use querySelectorAll to not to use timeout * @todo try to fix via Promises or use querySelectorAll to not to use timeout
*/ */
_.delay( () => { _.delay(() => {
this.set(nodeToSet as HTMLElement, offset); this.set(nodeToSet as HTMLElement, offset);
}, 20)(); }, 20)();
@ -267,12 +272,12 @@ export default class Caret extends Module {
* Set caret to the current input of current Block. * Set caret to the current input of current Block.
* *
* @param {HTMLElement} input - input where caret should be set * @param {HTMLElement} input - input where caret should be set
* @param {String} position - position of the caret. * @param {string} position - position of the caret.
* If default - leave default behaviour and apply offset if it's passed * If default - leave default behaviour and apply offset if it's passed
* @param {number} offset - caret offset regarding to the text node * @param {number} offset - caret offset regarding to the text node
*/ */
public setToInput(input: HTMLElement, position: string = this.positions.DEFAULT, offset: number = 0): void { public setToInput(input: HTMLElement, position: string = this.positions.DEFAULT, offset = 0): void {
const {currentBlock} = this.Editor.BlockManager; const { currentBlock } = this.Editor.BlockManager;
const nodeToSet = $.getDeepestNode(input); const nodeToSet = $.getDeepestNode(input);
switch (position) { switch (position) {
@ -297,12 +302,13 @@ export default class Caret extends Module {
/** /**
* Creates Document Range and sets caret to the element with offset * Creates Document Range and sets caret to the element with offset
*
* @param {HTMLElement} element - target node. * @param {HTMLElement} element - target node.
* @param {Number} offset - offset * @param {number} offset - offset
*/ */
public set(element: HTMLElement, offset: number = 0): void { public set(element: HTMLElement, offset = 0): void {
const range = document.createRange(), const range = document.createRange(),
selection = Selection.get(); selection = Selection.get();
/** if found deepest node is native input */ /** if found deepest node is native input */
if ($.isNativeInput(element)) { if ($.isNativeInput(element)) {
@ -312,6 +318,7 @@ export default class Caret extends Module {
element.focus(); element.focus();
(element as HTMLInputElement).selectionStart = (element as HTMLInputElement).selectionEnd = offset; (element as HTMLInputElement).selectionStart = (element as HTMLInputElement).selectionEnd = offset;
return; return;
} }
@ -322,14 +329,19 @@ export default class Caret extends Module {
selection.addRange(range); selection.addRange(range);
/** If new cursor position is not visible, scroll to it */ /** If new cursor position is not visible, scroll to it */
const {top, bottom} = element.nodeType === Node.ELEMENT_NODE const { top, bottom } = element.nodeType === Node.ELEMENT_NODE
? element.getBoundingClientRect() ? element.getBoundingClientRect()
: range.getBoundingClientRect(); : range.getBoundingClientRect();
const {innerHeight} = window; const { innerHeight } = window;
if (top < 0) { window.scrollBy(0, top); } if (top < 0) {
if (bottom > innerHeight) { window.scrollBy(0, bottom - innerHeight); } window.scrollBy(0, top);
}
if (bottom > innerHeight) {
window.scrollBy(0, bottom - innerHeight);
}
} }
/** /**
* Set Caret to the last Block * Set Caret to the last Block
* If last block is not empty, append another empty block * If last block is not empty, append another empty block
@ -371,6 +383,7 @@ export default class Caret extends Module {
range.selectNodeContents(currentBlockInput); range.selectNodeContents(currentBlockInput);
range.setStart(selectRange.endContainer, selectRange.endOffset); range.setStart(selectRange.endContainer, selectRange.endOffset);
return range.extractContents(); return range.extractContents();
} }
} }
@ -381,13 +394,13 @@ export default class Caret extends Module {
* Before moving caret, we should check if caret position is at the end of Plugins node * Before moving caret, we should check if caret position is at the end of Plugins node
* Using {@link Dom#getDeepestNode} to get a last node and match with current selection * Using {@link Dom#getDeepestNode} to get a last node and match with current selection
* *
* @param {Boolean} force - force navigation even if caret is not at the end * @param {boolean} force - force navigation even if caret is not at the end
* *
* @return {Boolean} * @returns {boolean}
*/ */
public navigateNext(force: boolean = false): boolean { public navigateNext(force = false): boolean {
const {currentBlock, nextContentfulBlock} = this.Editor.BlockManager; const { currentBlock, nextContentfulBlock } = this.Editor.BlockManager;
const {nextInput} = currentBlock; const { nextInput } = currentBlock;
if (!nextContentfulBlock && !nextInput) { if (!nextContentfulBlock && !nextInput) {
return false; return false;
@ -412,18 +425,18 @@ export default class Caret extends Module {
* Before moving caret, we should check if caret position is start of the Plugins node * Before moving caret, we should check if caret position is start of the Plugins node
* Using {@link Dom#getDeepestNode} to get a last node and match with current selection * Using {@link Dom#getDeepestNode} to get a last node and match with current selection
* *
* @param {Boolean} force - force navigation even if caret is not at the start * @param {boolean} force - force navigation even if caret is not at the start
* *
* @return {Boolean} * @returns {boolean}
*/ */
public navigatePrevious(force: boolean = false): boolean { public navigatePrevious(force = false): boolean {
const {currentBlock, previousContentfulBlock} = this.Editor.BlockManager; const { currentBlock, previousContentfulBlock } = this.Editor.BlockManager;
if (!currentBlock) { if (!currentBlock) {
return false; return false;
} }
const {previousInput} = currentBlock; const { previousInput } = currentBlock;
if (!previousContentfulBlock && !previousInput) { if (!previousContentfulBlock && !previousInput) {
return false; return false;
@ -432,10 +445,11 @@ export default class Caret extends Module {
if (force || this.isAtStart) { if (force || this.isAtStart) {
/** If previous Tool`s input exists, focus on it. Otherwise set caret to the previous Block */ /** If previous Tool`s input exists, focus on it. Otherwise set caret to the previous Block */
if (!previousInput) { if (!previousInput) {
this.setToBlock( previousContentfulBlock, this.positions.END ); this.setToBlock(previousContentfulBlock, this.positions.END);
} else { } else {
this.setToInput(previousInput, this.positions.END); this.setToInput(previousInput, this.positions.END);
} }
return true; return true;
} }
@ -444,6 +458,7 @@ export default class Caret extends Module {
/** /**
* Inserts shadow element after passed element where caret can be placed * Inserts shadow element after passed element where caret can be placed
*
* @param {Node} element * @param {Node} element
*/ */
public createShadow(element): void { public createShadow(element): void {
@ -455,6 +470,7 @@ export default class Caret extends Module {
/** /**
* Restores caret position * Restores caret position
*
* @param {HTMLElement} element * @param {HTMLElement} element
*/ */
public restoreCaret(element: HTMLElement): void { public restoreCaret(element: HTMLElement): void {
@ -519,16 +535,17 @@ export default class Caret extends Module {
* *
* @example * @example
* <div contenteditable> * <div contenteditable>
* <p></p> | * <p></p> |
* <p></p> | left first-level siblings * <p></p> | left first-level siblings
* <p></p> | * <p></p> |
* <blockquote><a><b>adaddad</b><a><blockquote> <-- passed node for example <b> * <blockquote><a><b>adaddad</b><a><blockquote> <-- passed node for example <b>
* <p></p> | * <p></p> |
* <p></p> | right first-level siblings * <p></p> | right first-level siblings
* <p></p> | * <p></p> |
* </div> * </div>
* * @returns {Element[]}
* @return {Element[]} * @param from
* @param direction
*/ */
private getHigherLevelSiblings(from: HTMLElement, direction?: string): HTMLElement[] { private getHigherLevelSiblings(from: HTMLElement, direction?: string): HTMLElement[] {
let current = from; let current = from;

View file

@ -3,6 +3,9 @@ import Block from '../block';
import SelectionUtils from '../selection'; import SelectionUtils from '../selection';
import * as _ from '../utils'; import * as _ from '../utils';
/**
*
*/
export default class CrossBlockSelection extends Module { export default class CrossBlockSelection extends Module {
/** /**
* Block where selection is started * Block where selection is started
@ -24,7 +27,7 @@ export default class CrossBlockSelection extends Module {
return; return;
} }
const {BlockManager, UI, Listeners} = this.Editor; const { BlockManager, UI, Listeners } = this.Editor;
this.firstSelectedBlock = BlockManager.getBlock(event.target as HTMLElement); this.firstSelectedBlock = BlockManager.getBlock(event.target as HTMLElement);
this.lastSelectedBlock = this.firstSelectedBlock; this.lastSelectedBlock = this.firstSelectedBlock;
@ -37,8 +40,8 @@ export default class CrossBlockSelection extends Module {
* return boolean is cross block selection started * return boolean is cross block selection started
*/ */
public get isCrossBlockSelectionStarted(): boolean { public get isCrossBlockSelectionStarted(): boolean {
return !!this.firstSelectedBlock return !!this.firstSelectedBlock &&
&& !!this.lastSelectedBlock; !!this.lastSelectedBlock;
} }
/** /**
@ -47,8 +50,8 @@ export default class CrossBlockSelection extends Module {
* *
* @param {boolean} next - if true, toggle next block. Previous otherwise * @param {boolean} next - if true, toggle next block. Previous otherwise
*/ */
public toggleBlockSelectedState(next: boolean = true): void { public toggleBlockSelectedState(next = true): void {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
if (!this.lastSelectedBlock) { if (!this.lastSelectedBlock) {
this.lastSelectedBlock = this.firstSelectedBlock = BlockManager.currentBlock; this.lastSelectedBlock = this.firstSelectedBlock = BlockManager.currentBlock;
@ -84,7 +87,7 @@ export default class CrossBlockSelection extends Module {
* @param {Event} reason - event caused clear of selection * @param {Event} reason - event caused clear of selection
*/ */
public clear(reason?: Event) { public clear(reason?: Event) {
const {BlockManager, BlockSelection, Caret} = this.Editor; const { BlockManager, BlockSelection, Caret } = this.Editor;
const fIndex = BlockManager.blocks.indexOf(this.firstSelectedBlock); const fIndex = BlockManager.blocks.indexOf(this.firstSelectedBlock);
const lIndex = BlockManager.blocks.indexOf(this.lastSelectedBlock); const lIndex = BlockManager.blocks.indexOf(this.lastSelectedBlock);
@ -121,8 +124,8 @@ export default class CrossBlockSelection extends Module {
* Mouse up event handler. * Mouse up event handler.
* Removes the listeners * Removes the listeners
*/ */
private onMouseUp = (): void => { private onMouseUp = (): void => {
const {Listeners} = this.Editor; const { Listeners } = this.Editor;
Listeners.off(document, 'mouseover', this.onMouseOver); Listeners.off(document, 'mouseover', this.onMouseOver);
Listeners.off(document, 'mouseup', this.onMouseUp); Listeners.off(document, 'mouseup', this.onMouseUp);
@ -135,7 +138,7 @@ export default class CrossBlockSelection extends Module {
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
private onMouseOver = (event: MouseEvent): void => { private onMouseOver = (event: MouseEvent): void => {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock; const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock;
const targetBlock = BlockManager.getBlockByChildNode(event.target as Node); const targetBlock = BlockManager.getBlockByChildNode(event.target as Node);
@ -153,12 +156,14 @@ export default class CrossBlockSelection extends Module {
relatedBlock.selected = true; relatedBlock.selected = true;
targetBlock.selected = true; targetBlock.selected = true;
return; return;
} }
if (targetBlock === this.firstSelectedBlock) { if (targetBlock === this.firstSelectedBlock) {
relatedBlock.selected = false; relatedBlock.selected = false;
targetBlock.selected = false; targetBlock.selected = false;
return; return;
} }
@ -175,7 +180,7 @@ export default class CrossBlockSelection extends Module {
* @param {Block} lastBlock * @param {Block} lastBlock
*/ */
private toggleBlocksSelectedState(firstBlock: Block, lastBlock: Block): void { private toggleBlocksSelectedState(firstBlock: Block, lastBlock: Block): void {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
const fIndex = BlockManager.blocks.indexOf(firstBlock); const fIndex = BlockManager.blocks.indexOf(firstBlock);
const lIndex = BlockManager.blocks.indexOf(lastBlock); const lIndex = BlockManager.blocks.indexOf(lastBlock);

View file

@ -1,8 +1,10 @@
import SelectionUtils from '../selection'; import SelectionUtils from '../selection';
import Module from '../__module'; import Module from '../__module';
/**
*
*/
export default class DragNDrop extends Module { export default class DragNDrop extends Module {
/** /**
* If drag has been started at editor, we save it * If drag has been started at editor, we save it
* *
@ -22,13 +24,13 @@ export default class DragNDrop extends Module {
/** /**
* Add drag events listeners to editor zone * Add drag events listeners to editor zone
*
* @private * @private
*/ */
private bindEvents(): void { private bindEvents(): void {
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true); this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true);
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragstart', (dragEvent: DragEvent) => { this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragstart', (dragEvent: DragEvent) => {
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) { if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) {
this.isStartedAtEditor = true; this.isStartedAtEditor = true;
} }
@ -54,7 +56,9 @@ export default class DragNDrop extends Module {
dropEvent.preventDefault(); dropEvent.preventDefault();
BlockManager.blocks.forEach((block) => block.dropTarget = false); BlockManager.blocks.forEach((block) => {
block.dropTarget = false;
});
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed && this.isStartedAtEditor) { if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed && this.isStartedAtEditor) {
document.execCommand('delete'); document.execCommand('delete');

View file

@ -11,12 +11,12 @@ import Module from '../__module';
* @version 1.0.0 * @version 1.0.0
* *
* @typedef {Events} Events * @typedef {Events} Events
* @property {Object} subscribers - all subscribers grouped by event name * @property {object} subscribers - all subscribers grouped by event name
*/ */
export default class Events extends Module { export default class Events extends Module {
/** /**
* Object with events` names as key and array of callback functions as value * Object with events` names as key and array of callback functions as value
*
* @type {{}} * @type {{}}
*/ */
private subscribers: {[name: string]: Array<(data?: any) => any>} = {}; private subscribers: {[name: string]: Array<(data?: any) => any>} = {};
@ -24,7 +24,7 @@ export default class Events extends Module {
/** /**
* Subscribe any event on callback * Subscribe any event on callback
* *
* @param {String} eventName - event name * @param {string} eventName - event name
* @param {Function} callback - subscriber * @param {Function} callback - subscriber
*/ */
public on(eventName: string, callback: (data: any) => any) { public on(eventName: string, callback: (data: any) => any) {
@ -39,7 +39,7 @@ export default class Events extends Module {
/** /**
* Subscribe any event on callback. Callback will be called once and be removed from subscribers array after call. * Subscribe any event on callback. Callback will be called once and be removed from subscribers array after call.
* *
* @param {String} eventName - event name * @param {string} eventName - event name
* @param {Function} callback - subscriber * @param {Function} callback - subscriber
*/ */
public once(eventName: string, callback: (data: any) => any) { public once(eventName: string, callback: (data: any) => any) {
@ -66,8 +66,8 @@ export default class Events extends Module {
/** /**
* Emit callbacks with passed data * Emit callbacks with passed data
* *
* @param {String} eventName - event name * @param {string} eventName - event name
* @param {Object} data - subscribers get this data when they were fired * @param {object} data - subscribers get this data when they were fired
*/ */
public emit(eventName: string, data?: any): void { public emit(eventName: string, data?: any): void {
if (!this.subscribers[eventName]) { if (!this.subscribers[eventName]) {
@ -77,7 +77,7 @@ export default class Events extends Module {
this.subscribers[eventName].reduce((previousData, currentHandler) => { this.subscribers[eventName].reduce((previousData, currentHandler) => {
const newData = currentHandler(previousData); const newData = currentHandler(previousData);
return newData ? newData : previousData; return newData || previousData;
}, data); }, data);
} }

View file

@ -43,9 +43,9 @@ export interface ListenerData {
* @property {Array} allListeners * @property {Array} allListeners
*/ */
export default class Listeners extends Module { export default class Listeners extends Module {
/** /**
* Stores all listeners data to find/remove/process it * Stores all listeners data to find/remove/process it
*
* @type {ListenerData[]} * @type {ListenerData[]}
*/ */
private allListeners: ListenerData[] = []; private allListeners: ListenerData[] = [];
@ -54,15 +54,15 @@ export default class Listeners extends Module {
* Assigns event listener on element * Assigns event listener on element
* *
* @param {EventTarget} element - DOM element that needs to be listened * @param {EventTarget} element - DOM element that needs to be listened
* @param {String} eventType - event type * @param {string} eventType - event type
* @param {Function} handler - method that will be fired on event * @param {Function} handler - method that will be fired on event
* @param {Boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
*/ */
public on( public on(
element: EventTarget, element: EventTarget,
eventType: string, eventType: string,
handler: (event: Event) => void, handler: (event: Event) => void,
options: boolean | AddEventListenerOptions = false, options: boolean | AddEventListenerOptions = false
): void { ): void {
const assignedEventData = { const assignedEventData = {
element, element,
@ -73,7 +73,9 @@ export default class Listeners extends Module {
const alreadyExist = this.findOne(element, eventType, handler); const alreadyExist = this.findOne(element, eventType, handler);
if (alreadyExist) { return; } if (alreadyExist) {
return;
}
this.allListeners.push(assignedEventData); this.allListeners.push(assignedEventData);
element.addEventListener(eventType, handler, options); element.addEventListener(eventType, handler, options);
@ -83,15 +85,15 @@ export default class Listeners extends Module {
* Removes event listener from element * Removes event listener from element
* *
* @param {EventTarget} element - DOM element that we removing listener * @param {EventTarget} element - DOM element that we removing listener
* @param {String} eventType - event type * @param {string} eventType - event type
* @param {Function} handler - remove handler, if element listens several handlers on the same event type * @param {Function} handler - remove handler, if element listens several handlers on the same event type
* @param {Boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
*/ */
public off( public off(
element: EventTarget, element: EventTarget,
eventType: string, eventType: string,
handler?: (event: Event) => void, handler?: (event: Event) => void,
options?: boolean | AddEventListenerOptions, options?: boolean | AddEventListenerOptions
): void { ): void {
const existingListeners = this.findAll(element, eventType, handler); const existingListeners = this.findAll(element, eventType, handler);
@ -104,14 +106,13 @@ export default class Listeners extends Module {
listener.element.removeEventListener(listener.eventType, listener.handler, listener.options); listener.element.removeEventListener(listener.eventType, listener.handler, listener.options);
} }
}); });
} }
/** /**
* @param {EventTarget} element * @param {EventTarget} element
* @param {String} eventType * @param {string} eventType
* @param {Function} handler * @param {Function} handler
* @return {ListenerData|null} * @returns {ListenerData|null}
*/ */
public findOne(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData { public findOne(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData {
const foundListeners = this.findAll(element, eventType, handler); const foundListeners = this.findAll(element, eventType, handler);
@ -121,18 +122,18 @@ export default class Listeners extends Module {
/** /**
* @param {EventTarget} element * @param {EventTarget} element
* @param {String} eventType * @param {string} eventType
* @param {Function} handler * @param {Function} handler
* @return {ListenerData[]} * @returns {ListenerData[]}
*/ */
public findAll(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData[] { public findAll(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData[] {
let found; let found;
const foundByEventTargets = element ? this.findByEventTarget(element) : []; const foundByEventTargets = element ? this.findByEventTarget(element) : [];
if (element && eventType && handler) { if (element && eventType && handler) {
found = foundByEventTargets.filter( (event) => event.eventType === eventType && event.handler === handler ); found = foundByEventTargets.filter((event) => event.eventType === eventType && event.handler === handler);
} else if (element && eventType) { } else if (element && eventType) {
found = foundByEventTargets.filter( (event) => event.eventType === eventType); found = foundByEventTargets.filter((event) => event.eventType === eventType);
} else { } else {
found = foundByEventTargets; found = foundByEventTargets;
} }
@ -144,7 +145,7 @@ export default class Listeners extends Module {
* Removes all listeners * Removes all listeners
*/ */
public removeAll(): void { public removeAll(): void {
this.allListeners.map( (current) => { this.allListeners.map((current) => {
current.element.removeEventListener(current.eventType, current.handler, current.options); current.element.removeEventListener(current.eventType, current.handler, current.options);
}); });
@ -153,6 +154,7 @@ export default class Listeners extends Module {
/** /**
* Search method: looks for listener by passed element * Search method: looks for listener by passed element
*
* @param {EventTarget} element - searching element * @param {EventTarget} element - searching element
* @returns {Array} listeners that found on element * @returns {Array} listeners that found on element
*/ */
@ -166,8 +168,9 @@ export default class Listeners extends Module {
/** /**
* Search method: looks for listener by passed event type * Search method: looks for listener by passed event type
* @param {String} eventType *
* @return {Array} listeners that found on element * @param {string} eventType
* @returns {Array} listeners that found on element
*/ */
private findByType(eventType: string): ListenerData[] { private findByType(eventType: string): ListenerData[] {
return this.allListeners.filter((listener) => { return this.allListeners.filter((listener) => {
@ -179,8 +182,9 @@ export default class Listeners extends Module {
/** /**
* Search method: looks for listener by passed handler * Search method: looks for listener by passed handler
*
* @param {Function} handler * @param {Function} handler
* @return {Array} listeners that found on element * @returns {Array} listeners that found on element
*/ */
private findByHandler(handler: (event: Event) => void): ListenerData[] { private findByHandler(handler: (event: Event) => void): ListenerData[] {
return this.allListeners.filter((listener) => { return this.allListeners.filter((listener) => {

View file

@ -9,10 +9,13 @@ import Module from '../__module';
import * as _ from '../utils'; import * as _ from '../utils';
import Block from '../block'; import Block from '../block';
/**
*
*/
export default class ModificationsObserver extends Module { export default class ModificationsObserver extends Module {
/** /**
* Debounce Timer * Debounce Timer
*
* @type {number} * @type {number}
*/ */
public static readonly DebounceTimer = 450; public static readonly DebounceTimer = 450;
@ -29,9 +32,10 @@ export default class ModificationsObserver extends Module {
/** /**
* Used to prevent several mutation callback execution * Used to prevent several mutation callback execution
*
* @type {Function} * @type {Function}
*/ */
private mutationDebouncer = _.debounce( () => { private mutationDebouncer = _.debounce(() => {
this.updateNativeInputs(); this.updateNativeInputs();
this.config.onChange(this.Editor.API.methods); this.config.onChange(this.Editor.API.methods);
}, ModificationsObserver.DebounceTimer); }, ModificationsObserver.DebounceTimer);
@ -56,13 +60,14 @@ export default class ModificationsObserver extends Module {
/** /**
* Preparation method * Preparation method
* @return {Promise<void>} *
* @returns {Promise<void>}
*/ */
public async prepare(): Promise<void> { public async prepare(): Promise<void> {
/** /**
* wait till Browser render Editor's Blocks * wait till Browser render Editor's Blocks
*/ */
window.setTimeout( () => { window.setTimeout(() => {
this.setObserver(); this.setObserver();
}, 1000); }, 1000);
} }
@ -90,7 +95,7 @@ export default class ModificationsObserver extends Module {
* so that User can handle outside from API * so that User can handle outside from API
*/ */
private setObserver(): void { private setObserver(): void {
const {UI} = this.Editor; const { UI } = this.Editor;
const observerOptions = { const observerOptions = {
childList: true, childList: true,
attributes: true, attributes: true,
@ -107,6 +112,7 @@ export default class ModificationsObserver extends Module {
/** /**
* MutationObserver events handler * MutationObserver events handler
*
* @param mutationList * @param mutationList
* @param observer * @param observer
*/ */
@ -141,7 +147,6 @@ export default class ModificationsObserver extends Module {
*/ */
if (!mutatedTarget.classList.contains(Block.CSS.wrapper)) { if (!mutatedTarget.classList.contains(Block.CSS.wrapper)) {
contentMutated = true; contentMutated = true;
return;
} }
break; break;
} }

View file

@ -5,13 +5,12 @@ import Module from '../__module';
* *
* @see https://github.com/codex-team/js-notifier * @see https://github.com/codex-team/js-notifier
*/ */
import notifier, {ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions} from 'codex-notifier'; import notifier, { ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions } from 'codex-notifier';
/** /**
* Notifier module * Notifier module
*/ */
export default class Notifier extends Module { export default class Notifier extends Module {
/** /**
* Show web notification * Show web notification
* *

View file

@ -7,7 +7,7 @@ import {
BlockToolData, BlockToolData,
PasteConfig, PasteConfig,
PasteEvent, PasteEvent,
PasteEventDetail, PasteEventDetail
} from '../../../types'; } from '../../../types';
import Block from '../block'; import Block from '../block';
@ -17,6 +17,7 @@ import Block from '../block';
interface TagSubstitute { interface TagSubstitute {
/** /**
* Name of related Tool * Name of related Tool
*
* @type {string} * @type {string}
*/ */
tool: string; tool: string;
@ -28,18 +29,21 @@ interface TagSubstitute {
interface PatternSubstitute { interface PatternSubstitute {
/** /**
* Pattern`s key * Pattern`s key
*
* @type {string} * @type {string}
*/ */
key: string; key: string;
/** /**
* Pattern regexp * Pattern regexp
*
* @type {RegExp} * @type {RegExp}
*/ */
pattern: RegExp; pattern: RegExp;
/** /**
* Name of related Tool * Name of related Tool
*
* @type {string} * @type {string}
*/ */
tool: string; tool: string;
@ -51,12 +55,14 @@ interface PatternSubstitute {
interface FilesSubstitution { interface FilesSubstitution {
/** /**
* Array of file extensions Tool can handle * Array of file extensions Tool can handle
*
* @type {string[]} * @type {string[]}
*/ */
extensions: string[]; extensions: string[];
/** /**
* Array of MIME types Tool can handle * Array of MIME types Tool can handle
*
* @type {string[]} * @type {string[]}
*/ */
mimeTypes: string[]; mimeTypes: string[];
@ -68,12 +74,14 @@ interface FilesSubstitution {
interface PasteData { interface PasteData {
/** /**
* Name of related Tool * Name of related Tool
*
* @type {string} * @type {string}
*/ */
tool: string; tool: string;
/** /**
* Pasted data. Processed and wrapped to HTML element * Pasted data. Processed and wrapped to HTML element
*
* @type {HTMLElement} * @type {HTMLElement}
*/ */
content: HTMLElement; content: HTMLElement;
@ -85,6 +93,7 @@ interface PasteData {
/** /**
* True if content should be inserted as new Block * True if content should be inserted as new Block
*
* @type {boolean} * @type {boolean}
*/ */
isBlock: boolean; isBlock: boolean;
@ -99,7 +108,6 @@ interface PasteData {
* @version 2.0.0 * @version 2.0.0
*/ */
export default class Paste extends Module { export default class Paste extends Module {
/** If string`s length is greater than this number we don't check paste patterns */ /** If string`s length is greater than this number we don't check paste patterns */
public static readonly PATTERN_PROCESSING_MAX_LENGTH = 450; public static readonly PATTERN_PROCESSING_MAX_LENGTH = 450;
@ -121,7 +129,7 @@ export default class Paste extends Module {
/** Files` substitutions parameters */ /** Files` substitutions parameters */
private toolsFiles: { private toolsFiles: {
[tool: string]: FilesSubstitution, [tool: string]: FilesSubstitution;
} = {}; } = {};
/** /**
@ -157,12 +165,13 @@ export default class Paste extends Module {
if (includesFiles) { if (includesFiles) {
await this.processFiles(dataTransfer.files); await this.processFiles(dataTransfer.files);
return; return;
} }
const editorJSData = dataTransfer.getData(this.MIME_TYPE); const editorJSData = dataTransfer.getData(this.MIME_TYPE);
const plainData = dataTransfer.getData('text/plain'); const plainData = dataTransfer.getData('text/plain');
let htmlData = dataTransfer.getData('text/html'); let htmlData = dataTransfer.getData('text/html');
/** /**
* If EditorJS json is passed, insert it * If EditorJS json is passed, insert it
@ -179,7 +188,7 @@ export default class Paste extends Module {
* If text was drag'n'dropped, wrap content with P tag to insert it as the new Block * If text was drag'n'dropped, wrap content with P tag to insert it as the new Block
*/ */
if (isDragNDrop && plainData.trim() && htmlData.trim()) { if (isDragNDrop && plainData.trim() && htmlData.trim()) {
htmlData = '<p>' + ( htmlData.trim() ? htmlData : plainData ) + '</p>'; htmlData = '<p>' + (htmlData.trim() ? htmlData : plainData) + '</p>';
} }
/** Add all tags that can be substituted to sanitizer configuration */ /** Add all tags that can be substituted to sanitizer configuration */
@ -189,7 +198,7 @@ export default class Paste extends Module {
return result; return result;
}, {}); }, {});
const customConfig = Object.assign({}, toolsTags, Sanitizer.getAllInlineToolsConfig(), {br: {}}); const customConfig = Object.assign({}, toolsTags, Sanitizer.getAllInlineToolsConfig(), { br: {} });
const cleanData = Sanitizer.clean(htmlData, customConfig); const cleanData = Sanitizer.clean(htmlData, customConfig);
@ -207,8 +216,8 @@ export default class Paste extends Module {
* @param {string} data - text to process. Can be HTML or plain. * @param {string} data - text to process. Can be HTML or plain.
* @param {boolean} isHTML - if passed string is HTML, this parameter should be true * @param {boolean} isHTML - if passed string is HTML, this parameter should be true
*/ */
public async processText(data: string, isHTML: boolean = false) { public async processText(data: string, isHTML = false) {
const {Caret, BlockManager, Tools} = this.Editor; const { Caret, BlockManager, Tools } = this.Editor;
const dataToInsert = isHTML ? this.processHTML(data) : this.processPlain(data); const dataToInsert = isHTML ? this.processHTML(data) : this.processPlain(data);
if (!dataToInsert.length) { if (!dataToInsert.length) {
@ -221,6 +230,7 @@ export default class Paste extends Module {
} else { } else {
this.processSingleBlock(dataToInsert.pop()); this.processSingleBlock(dataToInsert.pop());
} }
return; return;
} }
@ -228,7 +238,7 @@ export default class Paste extends Module {
const needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty; const needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;
dataToInsert.map( dataToInsert.map(
async (content, i) => this.insertBlock(content, i === 0 && needToReplaceCurrentBlock), async (content, i) => this.insertBlock(content, i === 0 && needToReplaceCurrentBlock)
); );
if (BlockManager.currentBlock) { if (BlockManager.currentBlock) {
@ -240,7 +250,7 @@ export default class Paste extends Module {
* Set onPaste callback handler * Set onPaste callback handler
*/ */
private setCallback(): void { private setCallback(): void {
const {Listeners} = this.Editor; const { Listeners } = this.Editor;
Listeners.on(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent); Listeners.on(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
} }
@ -270,6 +280,7 @@ export default class Paste extends Module {
if (tool.pasteConfig === false) { if (tool.pasteConfig === false) {
this.exceptionList.push(name); this.exceptionList.push(name);
return; return;
} }
@ -286,7 +297,7 @@ export default class Paste extends Module {
_.log( _.log(
`Paste handling for «${name}» Tool hasn't been set up because of the error`, `Paste handling for «${name}» Tool hasn't been set up because of the error`,
'warn', 'warn',
e, e
); );
} }
} }
@ -305,8 +316,9 @@ export default class Paste extends Module {
_.log( _.log(
`Paste handler for «${name}» Tool on «${tag}» tag is skipped ` + `Paste handler for «${name}» Tool on «${tag}» tag is skipped ` +
`because it is already used by «${this.toolsTags[tag].tool}» Tool.`, `because it is already used by «${this.toolsTags[tag].tool}» Tool.`,
'warn', 'warn'
); );
return; return;
} }
@ -325,9 +337,8 @@ export default class Paste extends Module {
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration * @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/ */
private getFilesConfig(name: string, toolPasteConfig: PasteConfig): void { private getFilesConfig(name: string, toolPasteConfig: PasteConfig): void {
const { files = {} } = toolPasteConfig;
const {files = {}} = toolPasteConfig; let { extensions, mimeTypes } = files;
let {extensions, mimeTypes} = files;
if (!extensions && !mimeTypes) { if (!extensions && !mimeTypes) {
return; return;
@ -347,6 +358,7 @@ export default class Paste extends Module {
mimeTypes = mimeTypes.filter((type) => { mimeTypes = mimeTypes.filter((type) => {
if (!_.isValidMimeType(type)) { if (!_.isValidMimeType(type)) {
_.log(`MIME type value «${type}» for the «${name}» Tool is not a valid MIME type`, 'warn'); _.log(`MIME type value «${type}» for the «${name}» Tool is not a valid MIME type`, 'warn');
return false; return false;
} }
@ -376,7 +388,7 @@ export default class Paste extends Module {
if (!(pattern instanceof RegExp)) { if (!(pattern instanceof RegExp)) {
_.log( _.log(
`Pattern ${pattern} for «${name}» Tool is skipped because it should be a Regexp instance.`, `Pattern ${pattern} for «${name}» Tool is skipped because it should be a Regexp instance.`,
'warn', 'warn'
); );
} }
@ -404,12 +416,11 @@ export default class Paste extends Module {
* @param {ClipboardEvent} event * @param {ClipboardEvent} event
*/ */
private handlePasteEvent = async (event: ClipboardEvent): Promise<void> => { private handlePasteEvent = async (event: ClipboardEvent): Promise<void> => {
const {BlockManager, Toolbar} = this.Editor; const { BlockManager, Toolbar } = this.Editor;
/** If target is native input or is not Block, use browser behaviour */ /** If target is native input or is not Block, use browser behaviour */
if ( if (
!BlockManager.currentBlock || !BlockManager.currentBlock || this.isNativeBehaviour(event.target) && !event.clipboardData.types.includes('Files')
this.isNativeBehaviour(event.target) && !event.clipboardData.types.includes('Files')
) { ) {
return; return;
} }
@ -434,14 +445,14 @@ export default class Paste extends Module {
* @param {FileList} items - pasted or dropped items * @param {FileList} items - pasted or dropped items
*/ */
private async processFiles(items: FileList) { private async processFiles(items: FileList) {
const {BlockManager, Tools} = this.Editor; const { BlockManager, Tools } = this.Editor;
let dataToInsert: Array<{type: string, event: PasteEvent}>; let dataToInsert: Array<{type: string; event: PasteEvent}>;
dataToInsert = await Promise.all( dataToInsert = await Promise.all(
Array Array
.from(items) .from(items)
.map((item) => this.processFile(item)), .map((item) => this.processFile(item))
); );
dataToInsert = dataToInsert.filter((data) => !!data); dataToInsert = dataToInsert.filter((data) => !!data);
@ -451,7 +462,7 @@ export default class Paste extends Module {
dataToInsert.forEach( dataToInsert.forEach(
(data, i) => { (data, i) => {
BlockManager.paste(data.type, data.event, i === 0 && needToReplaceCurrentBlock); BlockManager.paste(data.type, data.event, i === 0 && needToReplaceCurrentBlock);
}, }
); );
} }
@ -465,7 +476,7 @@ export default class Paste extends Module {
const foundConfig = Object const foundConfig = Object
.entries(this.toolsFiles) .entries(this.toolsFiles)
.find(([toolName, {mimeTypes, extensions}]) => { .find(([toolName, { mimeTypes, extensions } ]) => {
const [fileType, fileSubtype] = file.type.split('/'); const [fileType, fileSubtype] = file.type.split('/');
const foundExt = extensions.find((ext) => ext.toLowerCase() === extension.toLowerCase()); const foundExt = extensions.find((ext) => ext.toLowerCase() === extension.toLowerCase());
@ -482,7 +493,7 @@ export default class Paste extends Module {
return; return;
} }
const [tool] = foundConfig; const [ tool ] = foundConfig;
const pasteEvent = this.composePasteEvent('file', { const pasteEvent = this.composePasteEvent('file', {
file, file,
}); });
@ -500,7 +511,7 @@ export default class Paste extends Module {
* @returns {PasteData[]} * @returns {PasteData[]}
*/ */
private processHTML(innerHTML: string): PasteData[] { private processHTML(innerHTML: string): PasteData[] {
const {Tools, Sanitizer} = this.Editor; const { Tools, Sanitizer } = this.Editor;
const initialTool = this.config.initialBlock; const initialTool = this.config.initialBlock;
const wrapper = $.make('DIV'); const wrapper = $.make('DIV');
@ -530,7 +541,7 @@ export default class Paste extends Module {
break; break;
} }
const {tags} = Tools.blockTools[tool].pasteConfig as PasteConfig; const { tags } = Tools.blockTools[tool].pasteConfig as PasteConfig;
const toolTags = tags.reduce((result, tag) => { const toolTags = tags.reduce((result, tag) => {
result[tag.toLowerCase()] = {}; result[tag.toLowerCase()] = {};
@ -545,7 +556,12 @@ export default class Paste extends Module {
data: content, data: content,
}); });
return {content, isBlock, tool, event}; return {
content,
isBlock,
tool,
event,
};
}) })
.filter((data) => !$.isNodeEmpty(data.content) || $.isSingleTag(data.content)); .filter((data) => !$.isNodeEmpty(data.content) || $.isSingleTag(data.content));
} }
@ -557,8 +573,8 @@ export default class Paste extends Module {
* @returns {PasteData[]} * @returns {PasteData[]}
*/ */
private processPlain(plain: string): PasteData[] { private processPlain(plain: string): PasteData[] {
const {initialBlock} = this.config as {initialBlock: string}, const { initialBlock } = this.config as {initialBlock: string},
{Tools} = this.Editor; { Tools } = this.Editor;
if (!plain) { if (!plain) {
return []; return [];
@ -578,7 +594,12 @@ export default class Paste extends Module {
data: content, data: content,
}); });
return {content, tool, isBlock: false, event}; return {
content,
tool,
isBlock: false,
event,
};
}); });
} }
@ -588,8 +609,8 @@ export default class Paste extends Module {
* @param {PasteData} dataToInsert * @param {PasteData} dataToInsert
*/ */
private async processSingleBlock(dataToInsert: PasteData): Promise<void> { private async processSingleBlock(dataToInsert: PasteData): Promise<void> {
const {Caret, BlockManager, Tools} = this.Editor; const { Caret, BlockManager, Tools } = this.Editor;
const {currentBlock} = BlockManager; const { currentBlock } = BlockManager;
/** /**
* If pasted tool isn`t equal current Block or if pasted content contains block elements, insert it as new Block * If pasted tool isn`t equal current Block or if pasted content contains block elements, insert it as new Block
@ -600,6 +621,7 @@ export default class Paste extends Module {
!$.containsOnlyInlineElements(dataToInsert.content.innerHTML) !$.containsOnlyInlineElements(dataToInsert.content.innerHTML)
) { ) {
this.insertBlock(dataToInsert, currentBlock && Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty); this.insertBlock(dataToInsert, currentBlock && Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty);
return; return;
} }
@ -615,8 +637,8 @@ export default class Paste extends Module {
* @param {PasteData} dataToInsert * @param {PasteData} dataToInsert
*/ */
private async processInlinePaste(dataToInsert: PasteData): Promise<void> { private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
const {BlockManager, Caret, Sanitizer, Tools} = this.Editor; const { BlockManager, Caret, Sanitizer, Tools } = this.Editor;
const {content, tool} = dataToInsert; const { content, tool } = dataToInsert;
const currentBlockIsInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool); const currentBlockIsInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
@ -624,15 +646,14 @@ export default class Paste extends Module {
const blockData = await this.processPattern(content.textContent); const blockData = await this.processPattern(content.textContent);
if (blockData) { if (blockData) {
let insertedBlock; const needToReplaceCurrentBlock = BlockManager.currentBlock &&
Tools.isInitial(BlockManager.currentBlock.tool) &&
BlockManager.currentBlock.isEmpty;
const needToReplaceCurrentBlock = BlockManager.currentBlock const insertedBlock = BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock);
&& Tools.isInitial(BlockManager.currentBlock.tool)
&& BlockManager.currentBlock.isEmpty;
insertedBlock = BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock);
Caret.setToBlock(insertedBlock, Caret.positions.END); Caret.setToBlock(insertedBlock, Caret.positions.END);
return; return;
} }
} }
@ -644,7 +665,7 @@ export default class Paste extends Module {
document.execCommand( document.execCommand(
'insertHTML', 'insertHTML',
false, false,
Sanitizer.clean(content.innerHTML, currentToolSanitizeConfig), Sanitizer.clean(content.innerHTML, currentToolSanitizeConfig)
); );
} else { } else {
this.insertBlock(dataToInsert); this.insertBlock(dataToInsert);
@ -657,8 +678,8 @@ export default class Paste extends Module {
* @param {string} text * @param {string} text
* @returns Promise<{data: BlockToolData, tool: string}> * @returns Promise<{data: BlockToolData, tool: string}>
*/ */
private async processPattern(text: string): Promise<{event: PasteEvent, tool: string}> { private async processPattern(text: string): Promise<{event: PasteEvent; tool: string}> {
const pattern = this.toolsPatterns.find((substitute) => { const pattern = this.toolsPatterns.find((substitute) => {
const execResult = substitute.pattern.exec(text); const execResult = substitute.pattern.exec(text);
if (!execResult) { if (!execResult) {
@ -687,17 +708,18 @@ export default class Paste extends Module {
* Insert pasted Block content to Editor * Insert pasted Block content to Editor
* *
* @param {PasteData} data * @param {PasteData} data
* @param {Boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block * @param {boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block
* @returns {void} * @returns {void}
*/ */
private insertBlock(data: PasteData, canReplaceCurrentBlock: boolean = false): void { private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void {
const {BlockManager, Caret} = this.Editor; const { BlockManager, Caret } = this.Editor;
const {currentBlock} = BlockManager; const { currentBlock } = BlockManager;
let block: Block; let block: Block;
if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) { if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) {
block = BlockManager.paste(data.tool, data.event, true); block = BlockManager.paste(data.tool, data.event, true);
Caret.setToBlock(block, Caret.positions.END); Caret.setToBlock(block, Caret.positions.END);
return; return;
} }
@ -711,9 +733,9 @@ export default class Paste extends Module {
* *
* @param {object} blocks Blocks' data to insert * @param {object} blocks Blocks' data to insert
* *
* @return {void} * @returns {void}
*/ */
private insertEditorJSData(blocks: Array<{tool: string, data: BlockToolData}>): void { private insertEditorJSData(blocks: Array<{tool: string; data: BlockToolData}>): void {
const { BlockManager, Tools } = this.Editor; const { BlockManager, Tools } = this.Editor;
blocks.forEach(({ tool, data }, i) => { blocks.forEach(({ tool, data }, i) => {
@ -745,7 +767,7 @@ export default class Paste extends Module {
*/ */
private getNodes(wrapper: Node): Node[] { private getNodes(wrapper: Node): Node[] {
const children = Array.from(wrapper.childNodes), const children = Array.from(wrapper.childNodes),
tags = Object.keys(this.toolsTags); tags = Object.keys(this.toolsTags);
const reducer = (nodes: Node[], node: Node): Node[] => { const reducer = (nodes: Node[], node: Node): Node[] => {
if ($.isEmpty(node) && !$.isSingleTag(node as HTMLElement)) { if ($.isEmpty(node) && !$.isSingleTag(node as HTMLElement)) {
@ -769,30 +791,31 @@ export default class Paste extends Module {
case Node.ELEMENT_NODE: case Node.ELEMENT_NODE:
const element = node as HTMLElement; const element = node as HTMLElement;
const {tool = ''} = this.toolsTags[element.tagName] || {}; const { tool = '' } = this.toolsTags[element.tagName] || {};
const toolTags = this.tagsByTool[tool] || []; const toolTags = this.tagsByTool[tool] || [];
const isSubstitutable = tags.includes(element.tagName); const isSubstitutable = tags.includes(element.tagName);
const isBlockElement = $.blockElements.includes(element.tagName.toLowerCase()); const isBlockElement = $.blockElements.includes(element.tagName.toLowerCase());
const containsAnotherToolTags = Array const containsAnotherToolTags = Array
.from(element.children) .from(element.children)
.some( .some(
({tagName}) => tags.includes(tagName) && !toolTags.includes(tagName), ({ tagName }) => tags.includes(tagName) && !toolTags.includes(tagName)
); );
const containsBlockElements = Array.from(element.children).some( const containsBlockElements = Array.from(element.children).some(
({tagName}) => $.blockElements.includes(tagName.toLowerCase()), ({ tagName }) => $.blockElements.includes(tagName.toLowerCase())
); );
/** Append inline elements to previous fragment */ /** Append inline elements to previous fragment */
if (!isBlockElement && !isSubstitutable && !containsAnotherToolTags) { if (!isBlockElement && !isSubstitutable && !containsAnotherToolTags) {
destNode.appendChild(element); destNode.appendChild(element);
return [...nodes, destNode]; return [...nodes, destNode];
} }
if ( if (
(isSubstitutable && !containsAnotherToolTags) || (isSubstitutable && !containsAnotherToolTags) ||
(isBlockElement && !containsBlockElements && !containsAnotherToolTags ) (isBlockElement && !containsBlockElements && !containsAnotherToolTags)
) { ) {
return [...nodes, destNode, element]; return [...nodes, destNode, element];
} }
@ -803,6 +826,7 @@ export default class Paste extends Module {
*/ */
case Node.TEXT_NODE: case Node.TEXT_NODE:
destNode.appendChild(node); destNode.appendChild(node);
return [...nodes, destNode]; return [...nodes, destNode];
default: default:

View file

@ -11,10 +11,14 @@ import $ from '../dom';
import SelectionUtils from '../selection'; import SelectionUtils from '../selection';
import Block from '../block'; import Block from '../block';
/**
*
*/
export default class RectangleSelection extends Module { export default class RectangleSelection extends Module {
/** /**
* CSS classes for the Block * CSS classes for the Block
* @return {{wrapper: string, content: string}} *
* @returns {{wrapper: string, content: string}}
*/ */
static get CSS() { static get CSS() {
return { return {
@ -28,9 +32,10 @@ export default class RectangleSelection extends Module {
/** /**
* Using the selection rectangle * Using the selection rectangle
*
* @type {boolean} * @type {boolean}
*/ */
private isRectSelectionActivated: boolean = false; private isRectSelectionActivated = false;
/** /**
* Speed of Scrolling * Speed of Scrolling
@ -56,12 +61,12 @@ export default class RectangleSelection extends Module {
/** /**
* Mouse is clamped * Mouse is clamped
*/ */
private mousedown: boolean = false; private mousedown = false;
/** /**
* Is scrolling now * Is scrolling now
*/ */
private isScrolling: boolean = false; private isScrolling = false;
/** /**
* Mouse is in scroll zone * Mouse is in scroll zone
@ -71,10 +76,10 @@ export default class RectangleSelection extends Module {
/** /**
* Coords of rect * Coords of rect
*/ */
private startX: number = 0; private startX = 0;
private startY: number = 0; private startY = 0;
private mouseX: number = 0; private mouseX = 0;
private mouseY: number = 0; private mouseY = 0;
/** /**
* Selected blocks * Selected blocks
@ -96,8 +101,8 @@ export default class RectangleSelection extends Module {
* Creating rect and hang handlers * Creating rect and hang handlers
*/ */
public prepare(): void { public prepare(): void {
const {Listeners} = this.Editor; const { Listeners } = this.Editor;
const {container} = this.genHTML(); const { container } = this.genHTML();
Listeners.on(container, 'mousedown', (event: MouseEvent) => { Listeners.on(container, 'mousedown', (event: MouseEvent) => {
if (event.button !== this.MAIN_MOUSE_BUTTON) { if (event.button !== this.MAIN_MOUSE_BUTTON) {
@ -127,6 +132,7 @@ export default class RectangleSelection extends Module {
/** /**
* Init rect params * Init rect params
*
* @param {number} pageX - X coord of mouse * @param {number} pageX - X coord of mouse
* @param {number} pageY - Y coord of mouse * @param {number} pageY - Y coord of mouse
*/ */
@ -152,7 +158,7 @@ export default class RectangleSelection extends Module {
]; ];
const startsInsideEditor = elemWhereSelectionStart.closest('.' + this.Editor.UI.CSS.editorWrapper); const startsInsideEditor = elemWhereSelectionStart.closest('.' + this.Editor.UI.CSS.editorWrapper);
const startsInSelectorToAvoid = selectorsToAvoid.some(((selector) => !!elemWhereSelectionStart.closest(selector))); const startsInSelectorToAvoid = selectorsToAvoid.some((selector) => !!elemWhereSelectionStart.closest(selector));
/** /**
* If selection starts outside of the editor or inside the blocks or on Editor UI elements, do not handle it * If selection starts outside of the editor or inside the blocks or on Editor UI elements, do not handle it
@ -192,6 +198,7 @@ export default class RectangleSelection extends Module {
/** /**
* Scroll If mouse in scroll zone * Scroll If mouse in scroll zone
*
* @param {number} clientY - Y coord of mouse * @param {number} clientY - Y coord of mouse
*/ */
private scrollByZones(clientY) { private scrollByZones(clientY) {
@ -205,6 +212,7 @@ export default class RectangleSelection extends Module {
if (!this.inScrollZone) { if (!this.inScrollZone) {
this.isScrolling = false; this.isScrolling = false;
return; return;
} }
@ -214,8 +222,11 @@ export default class RectangleSelection extends Module {
} }
} }
/**
*
*/
private genHTML() { private genHTML() {
const {UI} = this.Editor; const { UI } = this.Editor;
const container = UI.nodes.holder.querySelector('.' + UI.CSS.editorWrapper); const container = UI.nodes.holder.querySelector('.' + UI.CSS.editorWrapper);
const overlay = $.make('div', RectangleSelection.CSS.overlay, {}); const overlay = $.make('div', RectangleSelection.CSS.overlay, {});
@ -227,6 +238,7 @@ export default class RectangleSelection extends Module {
container.appendChild(overlay); container.appendChild(overlay);
this.overlayRectangle = overlayRectangle as HTMLDivElement; this.overlayRectangle = overlayRectangle as HTMLDivElement;
return { return {
container, container,
overlay, overlay,
@ -235,6 +247,7 @@ export default class RectangleSelection extends Module {
/** /**
* Activates scrolling if blockSelection is active and mouse is in scroll zone * Activates scrolling if blockSelection is active and mouse is in scroll zone
*
* @param {number} speed - speed of scrolling * @param {number} speed - speed of scrolling
*/ */
private scrollVertical(speed) { private scrollVertical(speed) {
@ -242,6 +255,7 @@ export default class RectangleSelection extends Module {
return; return;
} }
const lastOffset = window.pageYOffset; const lastOffset = window.pageYOffset;
window.scrollBy(0, speed); window.scrollBy(0, speed);
this.mouseY += window.pageYOffset - lastOffset; this.mouseY += window.pageYOffset - lastOffset;
setTimeout(() => { setTimeout(() => {
@ -251,6 +265,7 @@ export default class RectangleSelection extends Module {
/** /**
* Handles the change in the rectangle and its effect * Handles the change in the rectangle and its effect
*
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
private changingRectangle(event) { private changingRectangle(event) {
@ -263,11 +278,12 @@ export default class RectangleSelection extends Module {
this.mouseY = event.pageY; this.mouseY = event.pageY;
} }
const {rightPos, leftPos, index} = this.genInfoForMouseSelection(); const { rightPos, leftPos, index } = this.genInfoForMouseSelection();
// There is not new block in selection // There is not new block in selection
const rectIsOnRighSideOfredactor = this.startX > rightPos && this.mouseX > rightPos; const rectIsOnRighSideOfredactor = this.startX > rightPos && this.mouseX > rightPos;
const rectISOnLeftSideOfRedactor = this.startX < leftPos && this.mouseX < leftPos; const rectISOnLeftSideOfRedactor = this.startX < leftPos && this.mouseX < leftPos;
this.rectCrossesBlocks = !(rectIsOnRighSideOfredactor || rectISOnLeftSideOfRedactor); this.rectCrossesBlocks = !(rectIsOnRighSideOfredactor || rectISOnLeftSideOfRedactor);
if (!this.isRectSelectionActivated) { if (!this.isRectSelectionActivated) {
@ -346,7 +362,8 @@ export default class RectangleSelection extends Module {
/** /**
* Collects information needed to determine the behavior of the rectangle * Collects information needed to determine the behavior of the rectangle
* @return {number} index - index next Block, leftPos - start of left border of Block, rightPos - right border *
* @returns {number} index - index next Block, leftPos - start of left border of Block, rightPos - right border
*/ */
private genInfoForMouseSelection() { private genInfoForMouseSelection() {
const widthOfRedactor = document.body.offsetWidth; const widthOfRedactor = document.body.offsetWidth;
@ -355,6 +372,7 @@ export default class RectangleSelection extends Module {
const elementUnderMouse = document.elementFromPoint(centerOfRedactor, Y); const elementUnderMouse = document.elementFromPoint(centerOfRedactor, Y);
const blockInCurrentPos = this.Editor.BlockManager.getBlockByChildNode(elementUnderMouse); const blockInCurrentPos = this.Editor.BlockManager.getBlockByChildNode(elementUnderMouse);
let index; let index;
if (blockInCurrentPos !== undefined) { if (blockInCurrentPos !== undefined) {
index = this.Editor.BlockManager.blocks.findIndex((block) => block.holder === blockInCurrentPos.holder); index = this.Editor.BlockManager.blocks.findIndex((block) => block.holder === blockInCurrentPos.holder);
} }
@ -372,6 +390,7 @@ export default class RectangleSelection extends Module {
/** /**
* Select block with index index * Select block with index index
*
* @param index - index of block in redactor * @param index - index of block in redactor
*/ */
private addBlockInSelection(index) { private addBlockInSelection(index) {
@ -383,6 +402,7 @@ export default class RectangleSelection extends Module {
/** /**
* Adds a block to the selection and determines which blocks should be selected * Adds a block to the selection and determines which blocks should be selected
*
* @param {object} index - index of new block in the reactor * @param {object} index - index of new block in the reactor
*/ */
private trySelectNextBlock(index) { private trySelectNextBlock(index) {
@ -409,6 +429,7 @@ export default class RectangleSelection extends Module {
for (ind; ind <= index; ind++) { for (ind; ind <= index; ind++) {
this.addBlockInSelection(ind); this.addBlockInSelection(ind);
} }
return; return;
} }
@ -417,6 +438,7 @@ export default class RectangleSelection extends Module {
for (let ind = this.stackOfSelected[sizeStack - 1] - 1; ind >= index; ind--) { for (let ind = this.stackOfSelected[sizeStack - 1] - 1; ind >= index; ind--) {
this.addBlockInSelection(ind); this.addBlockInSelection(ind);
} }
return; return;
} }
@ -443,6 +465,5 @@ export default class RectangleSelection extends Module {
this.stackOfSelected.pop(); this.stackOfSelected.pop();
i--; i--;
} }
return;
} }
} }

View file

@ -1,8 +1,9 @@
import Module from '../__module'; import Module from '../__module';
/* eslint-disable import/no-duplicates */
import * as _ from '../utils'; import * as _ from '../utils';
import {ChainData} from '../utils'; import { ChainData } from '../utils';
import {BlockToolData} from '../../../types'; import { BlockToolData } from '../../../types';
import {BlockToolConstructable} from '../../../types/tools'; import { BlockToolConstructable } from '../../../types/tools';
/** /**
* Editor.js Renderer Module * Editor.js Renderer Module
@ -14,9 +15,9 @@ import {BlockToolConstructable} from '../../../types/tools';
*/ */
export default class Renderer extends Module { export default class Renderer extends Module {
/** /**
* @typedef {Object} RendererBlocks * @typedef {object} RendererBlocks
* @property {String} type - tool name * @property {string} type - tool name
* @property {Object} data - tool data * @property {object} data - tool data
*/ */
/** /**
@ -41,10 +42,11 @@ export default class Renderer extends Module {
/** /**
* Make plugin blocks from array of plugin`s data * Make plugin blocks from array of plugin`s data
*
* @param {RendererBlocks[]} blocks * @param {RendererBlocks[]} blocks
*/ */
public async render(blocks: BlockToolData[]): Promise<void> { public async render(blocks: BlockToolData[]): Promise<void> {
const chainData = blocks.map((block) => ({function: () => this.insertBlock(block)})); const chainData = blocks.map((block) => ({ function: () => this.insertBlock(block) }));
const sequence = await _.sequence(chainData as ChainData[]); const sequence = await _.sequence(chainData as ChainData[]);
@ -58,7 +60,7 @@ export default class Renderer extends Module {
* Add plugin instance to BlockManager * Add plugin instance to BlockManager
* Insert block to working zone * Insert block to working zone
* *
* @param {Object} item * @param {object} item
* @returns {Promise<void>} * @returns {Promise<void>}
* @private * @private
*/ */
@ -76,7 +78,6 @@ export default class Renderer extends Module {
throw Error(error); throw Error(error);
} }
} else { } else {
/** If Tool is unavailable, create stub Block for it */ /** If Tool is unavailable, create stub Block for it */
const stubData = { const stubData = {
savedData: { savedData: {

View file

@ -20,8 +20,8 @@ import Module from '../__module';
import * as _ from '../utils'; import * as _ from '../utils';
/** /**
* @typedef {Object} SanitizerConfig * @typedef {object} SanitizerConfig
* @property {Object} tags - define tags restrictions * @property {object} tags - define tags restrictions
* *
* @example * @example
* *
@ -36,8 +36,11 @@ import * as _ from '../utils';
*/ */
import HTMLJanitor from 'html-janitor'; import HTMLJanitor from 'html-janitor';
import {BlockToolData, InlineToolConstructable, SanitizerConfig} from '../../../types'; import { BlockToolData, InlineToolConstructable, SanitizerConfig } from '../../../types';
/**
*
*/
export default class Sanitizer extends Module { export default class Sanitizer extends Module {
/** /**
* Memoize tools config * Memoize tools config
@ -54,12 +57,12 @@ export default class Sanitizer extends Module {
* *
* Enumerate blocks and clean data * Enumerate blocks and clean data
* *
* @param blocksData
* @param {{tool, data: BlockToolData}[]} blocksData[] * @param {{tool, data: BlockToolData}[]} blocksData[]
*/ */
public sanitizeBlocks( public sanitizeBlocks(
blocksData: Array<{tool: string, data: BlockToolData}>, blocksData: Array<{tool: string; data: BlockToolData}>
): Array<{tool: string, data: BlockToolData}> { ): Array<{tool: string; data: BlockToolData}> {
return blocksData.map((block) => { return blocksData.map((block) => {
const toolConfig = this.composeToolConfig(block.tool); const toolConfig = this.composeToolConfig(block.tool);
@ -105,6 +108,7 @@ export default class Sanitizer extends Module {
if (typeof dataToSanitize === 'string') { if (typeof dataToSanitize === 'string') {
return this.cleanOneItem(dataToSanitize, rules); return this.cleanOneItem(dataToSanitize, rules);
} }
return dataToSanitize; return dataToSanitize;
} }
} }
@ -116,10 +120,9 @@ export default class Sanitizer extends Module {
* @param {string} taintString - taint string * @param {string} taintString - taint string
* @param {SanitizerConfig} customConfig - allowed tags * @param {SanitizerConfig} customConfig - allowed tags
* *
* @return {string} clean HTML * @returns {string} clean HTML
*/ */
public clean(taintString: string, customConfig: SanitizerConfig = {} as SanitizerConfig): string { public clean(taintString: string, customConfig: SanitizerConfig = {} as SanitizerConfig): string {
const sanitizerConfig = { const sanitizerConfig = {
tags: customConfig, tags: customConfig,
}; };
@ -128,6 +131,7 @@ export default class Sanitizer extends Module {
* API client can use custom config to manage sanitize process * API client can use custom config to manage sanitize process
*/ */
const sanitizerInstance = this.createHTMLJanitorInstance(sanitizerConfig); const sanitizerInstance = this.createHTMLJanitorInstance(sanitizerConfig);
return sanitizerInstance.clean(taintString); return sanitizerInstance.clean(taintString);
} }
@ -136,7 +140,7 @@ export default class Sanitizer extends Module {
* *
* @param {string} toolName * @param {string} toolName
* @param {SanitizerConfig} toolRules * @param {SanitizerConfig} toolRules
* @return {SanitizerConfig} * @returns {SanitizerConfig}
*/ */
public composeToolConfig(toolName: string): SanitizerConfig { public composeToolConfig(toolName: string): SanitizerConfig {
/** /**
@ -160,9 +164,11 @@ export default class Sanitizer extends Module {
const toolRules = toolClass.sanitize; const toolRules = toolClass.sanitize;
const toolConfig = {} as SanitizerConfig; const toolConfig = {} as SanitizerConfig;
for (const fieldName in toolRules) { for (const fieldName in toolRules) {
if (toolRules.hasOwnProperty(fieldName)) { if (toolRules.hasOwnProperty(fieldName)) {
const rule = toolRules[fieldName]; const rule = toolRules[fieldName];
if (typeof rule === 'object') { if (typeof rule === 'object') {
toolConfig[fieldName] = Object.assign({}, baseConfig, rule); toolConfig[fieldName] = Object.assign({}, baseConfig, rule);
} else { } else {
@ -179,9 +185,11 @@ export default class Sanitizer extends Module {
* Returns Sanitizer config * Returns Sanitizer config
* When Tool's "inlineToolbar" value is True, get all sanitizer rules from all tools, * When Tool's "inlineToolbar" value is True, get all sanitizer rules from all tools,
* otherwise get only enabled * otherwise get only enabled
*
* @param name
*/ */
public getInlineToolsConfig(name: string): SanitizerConfig { public getInlineToolsConfig(name: string): SanitizerConfig {
const {Tools} = this.Editor; const { Tools } = this.Editor;
const toolsConfig = Tools.getToolSettings(name); const toolsConfig = Tools.getToolSettings(name);
const enableInlineTools = toolsConfig.inlineToolbar || []; const enableInlineTools = toolsConfig.inlineToolbar || [];
@ -196,10 +204,10 @@ export default class Sanitizer extends Module {
/** /**
* getting only enabled * getting only enabled
*/ */
(enableInlineTools as string[]).map( (inlineToolName) => { (enableInlineTools as string[]).map((inlineToolName) => {
config = Object.assign( config = Object.assign(
config, config,
Tools.inline[inlineToolName][Tools.INTERNAL_SETTINGS.SANITIZE_CONFIG], Tools.inline[inlineToolName][Tools.INTERNAL_SETTINGS.SANITIZE_CONFIG]
) as SanitizerConfig; ) as SanitizerConfig;
}); });
} }
@ -217,7 +225,7 @@ export default class Sanitizer extends Module {
* Return general config for all inline tools * Return general config for all inline tools
*/ */
public getAllInlineToolsConfig(): SanitizerConfig { public getAllInlineToolsConfig(): SanitizerConfig {
const {Tools} = this.Editor; const { Tools } = this.Editor;
if (this.inlineToolsConfigCache) { if (this.inlineToolsConfigCache) {
return this.inlineToolsConfigCache; return this.inlineToolsConfigCache;
@ -226,7 +234,7 @@ export default class Sanitizer extends Module {
const config: SanitizerConfig = {} as SanitizerConfig; const config: SanitizerConfig = {} as SanitizerConfig;
Object.entries(Tools.inline) Object.entries(Tools.inline)
.forEach( ([name, inlineTool]: [string, InlineToolConstructable]) => { .forEach(([name, inlineTool]: [string, InlineToolConstructable]) => {
Object.assign(config, inlineTool[Tools.INTERNAL_SETTINGS.SANITIZE_CONFIG]); Object.assign(config, inlineTool[Tools.INTERNAL_SETTINGS.SANITIZE_CONFIG]);
}); });
@ -237,18 +245,20 @@ export default class Sanitizer extends Module {
/** /**
* Clean array * Clean array
* @param {array} array - [1, 2, {}, []] *
* @param {Array} array - [1, 2, {}, []]
* @param {object} ruleForItem * @param {object} ruleForItem
*/ */
private cleanArray(array: any[], ruleForItem: SanitizerConfig): any[] { private cleanArray(array: any[], ruleForItem: SanitizerConfig): any[] {
return array.map( (arrayItem) => this.deepSanitize(arrayItem, ruleForItem)); return array.map((arrayItem) => this.deepSanitize(arrayItem, ruleForItem));
} }
/** /**
* Clean object * Clean object
*
* @param {object} object - {level: 0, text: 'adada', items: [1,2,3]}} * @param {object} object - {level: 0, text: 'adada', items: [1,2,3]}}
* @param {object} rules - { b: true } or true|false * @param {object} rules - { b: true } or true|false
* @return {object} * @returns {object}
*/ */
private cleanObject(object: any, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): any { private cleanObject(object: any, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): any {
const cleanData = {}; const cleanData = {};
@ -269,13 +279,14 @@ export default class Sanitizer extends Module {
cleanData[fieldName] = this.deepSanitize(currentIterationItem, ruleForItem as SanitizerConfig); cleanData[fieldName] = this.deepSanitize(currentIterationItem, ruleForItem as SanitizerConfig);
} }
return cleanData; return cleanData;
} }
/** /**
* @param {string} taintString * @param {string} taintString
* @param {SanitizerConfig|boolean} rule * @param {SanitizerConfig|boolean} rule
* @return {string} * @returns {string}
*/ */
private cleanOneItem(taintString: string, rule: SanitizerConfig|boolean): string { private cleanOneItem(taintString: string, rule: SanitizerConfig|boolean): string {
if (typeof rule === 'object') { if (typeof rule === 'object') {
@ -291,6 +302,7 @@ export default class Sanitizer extends Module {
* Check if passed item is a HTML Janitor rule: * Check if passed item is a HTML Janitor rule:
* { a : true }, {}, false, true, function(){} correct rules * { a : true }, {}, false, true, function(){} correct rules
* undefined, null, 0, 1, 2 not a rules * undefined, null, 0, 1, 2 not a rules
*
* @param config * @param config
*/ */
private isRule(config: SanitizerConfig): boolean { private isRule(config: SanitizerConfig): boolean {
@ -311,6 +323,7 @@ export default class Sanitizer extends Module {
if (config) { if (config) {
return new HTMLJanitor(config); return new HTMLJanitor(config);
} }
return null; return null;
} }
} }

View file

@ -6,8 +6,8 @@
* @version 2.0.0 * @version 2.0.0
*/ */
import Module from '../__module'; import Module from '../__module';
import {OutputData} from '../../../types'; import { OutputData } from '../../../types';
import {ValidatedData} from '../../types-internal/block-data'; import { ValidatedData } from '../../types-internal/block-data';
import Block from '../block'; import Block from '../block';
import * as _ from '../utils'; import * as _ from '../utils';
@ -18,17 +18,18 @@ declare const VERSION: string;
* *
* @typedef {Saver} Saver * @typedef {Saver} Saver
* @property {Element} html - Editor HTML content * @property {Element} html - Editor HTML content
* @property {String} json - Editor JSON output * @property {string} json - Editor JSON output
*/ */
export default class Saver extends Module { export default class Saver extends Module {
/** /**
* Composes new chain of Promises to fire them alternatelly * Composes new chain of Promises to fire them alternatelly
* @return {OutputData} *
* @returns {OutputData}
*/ */
public async save(): Promise<OutputData> { public async save(): Promise<OutputData> {
const {BlockManager, Sanitizer, ModificationsObserver} = this.Editor; const { BlockManager, Sanitizer, ModificationsObserver } = this.Editor;
const blocks = BlockManager.blocks, const blocks = BlockManager.blocks,
chainData = []; chainData = [];
/** /**
* Disable modifications observe while saving * Disable modifications observe while saving
@ -36,7 +37,7 @@ export default class Saver extends Module {
ModificationsObserver.disable(); ModificationsObserver.disable();
blocks.forEach((block: Block) => { blocks.forEach((block: Block) => {
chainData.push(this.getSavedData(block)); chainData.push(this.getSavedData(block));
}); });
const extractedData = await Promise.all(chainData); const extractedData = await Promise.all(chainData);
@ -49,20 +50,25 @@ export default class Saver extends Module {
/** /**
* Saves and validates * Saves and validates
*
* @param {Block} block - Editor's Tool * @param {Block} block - Editor's Tool
* @return {ValidatedData} - Tool's validated data * @returns {ValidatedData} - Tool's validated data
*/ */
private async getSavedData(block: Block): Promise<ValidatedData> { private async getSavedData(block: Block): Promise<ValidatedData> {
const blockData = await block.save(); const blockData = await block.save();
const isValid = blockData && await block.validate(blockData.data); const isValid = blockData && await block.validate(blockData.data);
return {...blockData, isValid}; return {
...blockData,
isValid,
};
} }
/** /**
* Creates output object with saved data, time and version of editor * Creates output object with saved data, time and version of editor
*
* @param {ValidatedData} allExtractedData * @param {ValidatedData} allExtractedData
* @return {OutputData} * @returns {OutputData}
*/ */
private makeOutput(allExtractedData): OutputData { private makeOutput(allExtractedData): OutputData {
let totalTime = 0; let totalTime = 0;
@ -70,7 +76,7 @@ export default class Saver extends Module {
_.log('[Editor.js saving]:', 'groupCollapsed'); _.log('[Editor.js saving]:', 'groupCollapsed');
allExtractedData.forEach(({tool, data, time, isValid}) => { allExtractedData.forEach(({ tool, data, time, isValid }) => {
totalTime += time; totalTime += time;
/** /**
@ -85,12 +91,14 @@ export default class Saver extends Module {
} else { } else {
_.log(`Block «${tool}» skipped because saved data is invalid`); _.log(`Block «${tool}» skipped because saved data is invalid`);
_.log(undefined, 'groupEnd'); _.log(undefined, 'groupEnd');
return; return;
} }
/** If it was stub Block, get original data */ /** If it was stub Block, get original data */
if (tool === this.Editor.Tools.stubTool) { if (tool === this.Editor.Tools.stubTool) {
blocks.push(data); blocks.push(data);
return; return;
} }

View file

@ -1,5 +1,10 @@
import Shortcut from '@codexteam/shortcuts'; import Shortcut from '@codexteam/shortcuts';
/**
* Contains keyboard and mouse events binded on each Block by Block Manager
*/
import Module from '../__module';
/** /**
* ShortcutData interface * ShortcutData interface
* Each shortcut must have name and handler * Each shortcut must have name and handler
@ -20,11 +25,6 @@ export interface ShortcutData {
handler(event): void; handler(event): void;
} }
/**
* Contains keyboard and mouse events binded on each Block by Block Manager
*/
import Module from '../__module';
/** /**
* @class Shortcut * @class Shortcut
* @classdesc Allows to register new shortcut * @classdesc Allows to register new shortcut
@ -34,12 +34,14 @@ import Module from '../__module';
export default class Shortcuts extends Module { export default class Shortcuts extends Module {
/** /**
* All registered shortcuts * All registered shortcuts
*
* @type {Shortcut[]} * @type {Shortcut[]}
*/ */
private registeredShortcuts: Shortcut[] = []; private registeredShortcuts: Shortcut[] = [];
/** /**
* Register shortcut * Register shortcut
*
* @param {ShortcutData} shortcut * @param {ShortcutData} shortcut
*/ */
public add(shortcut: ShortcutData): void { public add(shortcut: ShortcutData): void {
@ -54,6 +56,7 @@ export default class Shortcuts extends Module {
/** /**
* Remove shortcut * Remove shortcut
*
* @param {ShortcutData} shortcut * @param {ShortcutData} shortcut
*/ */
public remove(shortcut: string): void { public remove(shortcut: string): void {

View file

@ -1,6 +1,6 @@
import Module from '../../__module'; import Module from '../../__module';
import $ from '../../dom'; import $ from '../../dom';
import Flipper, {FlipperOptions} from '../../flipper'; import Flipper, { FlipperOptions } from '../../flipper';
import * as _ from '../../utils'; import * as _ from '../../utils';
/** /**
@ -15,12 +15,12 @@ import * as _ from '../../utils';
* |________________________| * |________________________|
*/ */
export default class BlockSettings extends Module { export default class BlockSettings extends Module {
/** /**
* Module Events * Module Events
* @return {{opened: string, closed: string}} *
* @returns {{opened: string, closed: string}}
*/ */
public get events(): {opened: string, closed: string} { public get events(): {opened: string; closed: string} {
return { return {
opened: 'block-settings-opened', opened: 'block-settings-opened',
closed: 'block-settings-closed', closed: 'block-settings-closed',
@ -29,7 +29,8 @@ export default class BlockSettings extends Module {
/** /**
* Block Settings CSS * Block Settings CSS
* @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}} *
* @returns {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
*/ */
public get CSS() { public get CSS() {
return { return {
@ -41,13 +42,14 @@ export default class BlockSettings extends Module {
button: 'ce-settings__button', button: 'ce-settings__button',
focusedButton : 'ce-settings__button--focused', focusedButton: 'ce-settings__button--focused',
focusedButtonAnimated: 'ce-settings__button--focused-animated', focusedButtonAnimated: 'ce-settings__button--focused-animated',
}; };
} }
/** /**
* Is Block Settings opened or not * Is Block Settings opened or not
*
* @returns {boolean} * @returns {boolean}
*/ */
public get opened(): boolean { public get opened(): boolean {
@ -70,6 +72,7 @@ export default class BlockSettings extends Module {
/** /**
* Instance of class that responses for leafing buttons by arrows/tab * Instance of class that responses for leafing buttons by arrows/tab
*
* @type {Flipper|null} * @type {Flipper|null}
*/ */
private flipper: Flipper = null; private flipper: Flipper = null;
@ -79,7 +82,7 @@ export default class BlockSettings extends Module {
* - Tool's Settings * - Tool's Settings
* - Default Settings [Move, Remove, etc] * - Default Settings [Move, Remove, etc]
* *
* @return {Element} * @returns {Element}
*/ */
public make(): void { public make(): void {
this.nodes.wrapper = $.make('div', this.CSS.wrapper); this.nodes.wrapper = $.make('div', this.CSS.wrapper);
@ -145,7 +148,8 @@ export default class BlockSettings extends Module {
/** /**
* Returns Tools Settings and Default Settings * Returns Tools Settings and Default Settings
* @return {HTMLElement[]} *
* @returns {HTMLElement[]}
*/ */
public get blockTunesButtons(): HTMLElement[] { public get blockTunesButtons(): HTMLElement[] {
/** /**
@ -198,7 +202,7 @@ export default class BlockSettings extends Module {
* Restoring focus on current Block after settings clicked. * Restoring focus on current Block after settings clicked.
* For example, when H3 changed to H2 DOM Elements replaced, so we need to focus a new one * For example, when H3 changed to H2 DOM Elements replaced, so we need to focus a new one
*/ */
_.delay( () => { _.delay(() => {
this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock); this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock);
}, 10)(); }, 10)();
}, },

View file

@ -1,8 +1,8 @@
import Module from '../../__module'; import Module from '../../__module';
import $ from '../../dom'; import $ from '../../dom';
import {BlockToolConstructable} from '../../../../types'; import { BlockToolConstructable } from '../../../../types';
import * as _ from '../../utils'; import * as _ from '../../utils';
import {SavedData} from '../../../types-internal/block-data'; import { SavedData } from '../../../types-internal/block-data';
import Block from '../../block'; import Block from '../../block';
import Flipper from '../../flipper'; import Flipper from '../../flipper';
@ -23,8 +23,8 @@ export default class ConversionToolbar extends Module {
conversionToolHidden: 'ce-conversion-tool--hidden', conversionToolHidden: 'ce-conversion-tool--hidden',
conversionToolIcon: 'ce-conversion-tool__icon', conversionToolIcon: 'ce-conversion-tool__icon',
conversionToolFocused : 'ce-conversion-tool--focused', conversionToolFocused: 'ce-conversion-tool--focused',
conversionToolActive : 'ce-conversion-tool--active', conversionToolActive: 'ce-conversion-tool--active',
}; };
} }
@ -38,9 +38,10 @@ export default class ConversionToolbar extends Module {
/** /**
* Conversion Toolbar open/close state * Conversion Toolbar open/close state
*
* @type {boolean} * @type {boolean}
*/ */
public opened: boolean = false; public opened = false;
/** /**
* Available tools * Available tools
@ -49,6 +50,7 @@ export default class ConversionToolbar extends Module {
/** /**
* Instance of class that responses for leafing buttons by arrows/tab * Instance of class that responses for leafing buttons by arrows/tab
*
* @type {Flipper|null} * @type {Flipper|null}
*/ */
private flipper: Flipper = null; private flipper: Flipper = null;
@ -87,7 +89,8 @@ export default class ConversionToolbar extends Module {
/** /**
* Toggle conversion dropdown visibility * Toggle conversion dropdown visibility
* @param {function} [togglingCallback] callback that will accept opening state *
* @param {Function} [togglingCallback] callback that will accept opening state
*/ */
public toggle(togglingCallback?: (openedState: boolean) => void): void { public toggle(togglingCallback?: (openedState: boolean) => void): void {
if (!this.opened) { if (!this.opened) {
@ -157,6 +160,7 @@ export default class ConversionToolbar extends Module {
public async replaceWithBlock(replacingToolName: string): Promise <void> { public async replaceWithBlock(replacingToolName: string): Promise <void> {
/** /**
* At first, we get current Block data * At first, we get current Block data
*
* @type {BlockToolConstructable} * @type {BlockToolConstructable}
*/ */
const currentBlockClass = this.Editor.BlockManager.currentBlock.class; const currentBlockClass = this.Editor.BlockManager.currentBlock.class;
@ -175,6 +179,7 @@ export default class ConversionToolbar extends Module {
/** /**
* Getting a class of replacing Tool * Getting a class of replacing Tool
*
* @type {BlockToolConstructable} * @type {BlockToolConstructable}
*/ */
const replacingTool = this.Editor.Tools.toolsClasses[replacingToolName] as BlockToolConstructable; const replacingTool = this.Editor.Tools.toolsClasses[replacingToolName] as BlockToolConstructable;
@ -186,7 +191,7 @@ export default class ConversionToolbar extends Module {
* *
* In both cases returning value must be a string * In both cases returning value must be a string
*/ */
let exportData: string = ''; let exportData = '';
const exportProp = currentBlockClass[INTERNAL_SETTINGS.CONVERSION_CONFIG].export; const exportProp = currentBlockClass[INTERNAL_SETTINGS.CONVERSION_CONFIG].export;
if (typeof exportProp === 'function') { if (typeof exportProp === 'function') {
@ -196,6 +201,7 @@ export default class ConversionToolbar extends Module {
} else { } else {
_.log('Conversion «export» property must be a string or function. ' + _.log('Conversion «export» property must be a string or function. ' +
'String means key of saved data object to export. Function should export processed string to export.'); 'String means key of saved data object to export. Function should export processed string to export.');
return; return;
} }
@ -204,7 +210,7 @@ export default class ConversionToolbar extends Module {
*/ */
const cleaned: string = this.Editor.Sanitizer.clean( const cleaned: string = this.Editor.Sanitizer.clean(
exportData, exportData,
replacingTool.sanitize, replacingTool.sanitize
); );
/** /**
@ -222,6 +228,7 @@ export default class ConversionToolbar extends Module {
} else { } else {
_.log('Conversion «import» property must be a string or function. ' + _.log('Conversion «import» property must be a string or function. ' +
'String means key of tool data to import. Function accepts a imported string and return composed tool data.'); 'String means key of tool data to import. Function accepts a imported string and return composed tool data.');
return; return;
} }
@ -273,6 +280,10 @@ export default class ConversionToolbar extends Module {
/** /**
* Add tool to the Conversion Toolbar * Add tool to the Conversion Toolbar
*
* @param toolName
* @param toolIcon
* @param title
*/ */
private addTool(toolName: string, toolIcon: string, title: string): void { private addTool(toolName: string, toolIcon: string, title: string): void {
const tool = $.make('div', [ ConversionToolbar.CSS.conversionTool ]); const tool = $.make('div', [ ConversionToolbar.CSS.conversionTool ]);

View file

@ -42,7 +42,7 @@ import * as _ from '../../utils';
* @classdesc Toolbar module * @classdesc Toolbar module
* *
* @typedef {Toolbar} Toolbar * @typedef {Toolbar} Toolbar
* @property {Object} nodes * @property {object} nodes
* @property {Element} nodes.wrapper - Toolbar main element * @property {Element} nodes.wrapper - Toolbar main element
* @property {Element} nodes.content - Zone with Plus button and toolbox. * @property {Element} nodes.content - Zone with Plus button and toolbox.
* @property {Element} nodes.actions - Zone with Block Settings and Remove Button * @property {Element} nodes.actions - Zone with Block Settings and Remove Button
@ -59,21 +59,22 @@ export default class Toolbar extends Module {
* HTML Elements used for Toolbar UI * HTML Elements used for Toolbar UI
*/ */
public nodes: {[key: string]: HTMLElement} = { public nodes: {[key: string]: HTMLElement} = {
wrapper : null, wrapper: null,
content : null, content: null,
actions : null, actions: null,
// Content Zone // Content Zone
plusButton : null, plusButton: null,
// Actions Zone // Actions Zone
blockActionsButtons: null, blockActionsButtons: null,
settingsToggler : null, settingsToggler: null,
}; };
/** /**
* CSS styles * CSS styles
* @return {Object} *
* @returns {object}
*/ */
public get CSS() { public get CSS() {
return { return {
@ -104,7 +105,7 @@ export default class Toolbar extends Module {
/** /**
* Make Content Zone and Actions Zone * Make Content Zone and Actions Zone
*/ */
['content', 'actions'].forEach( (el) => { ['content', 'actions'].forEach((el) => {
this.nodes[el] = $.make('div', this.CSS[el]); this.nodes[el] = $.make('div', this.CSS[el]);
}); });
@ -149,7 +150,7 @@ export default class Toolbar extends Module {
* - Settings Panel * - Settings Panel
*/ */
this.nodes.blockActionsButtons = $.make('div', this.CSS.blockActionsButtons); this.nodes.blockActionsButtons = $.make('div', this.CSS.blockActionsButtons);
this.nodes.settingsToggler = $.make('span', this.CSS.settingsToggler); this.nodes.settingsToggler = $.make('span', this.CSS.settingsToggler);
const settingsIcon = $.svg('dots', 8, 8); const settingsIcon = $.svg('dots', 8, 8);
$.append(this.nodes.settingsToggler, settingsIcon); $.append(this.nodes.settingsToggler, settingsIcon);
@ -179,9 +180,10 @@ export default class Toolbar extends Module {
/** /**
* Move Toolbar to the Current Block * Move Toolbar to the Current Block
* @param {Boolean} forceClose - force close Toolbar Settings and Toolbar *
* @param {boolean} forceClose - force close Toolbar Settings and Toolbar
*/ */
public move(forceClose: boolean = true): void { public move(forceClose = true): void {
if (forceClose) { if (forceClose) {
/** Close Toolbox when we move toolbar */ /** Close Toolbox when we move toolbar */
this.Editor.Toolbox.close(); this.Editor.Toolbox.close();
@ -222,6 +224,7 @@ export default class Toolbar extends Module {
/** /**
* Open Toolbar with Plus Button and Actions * Open Toolbar with Plus Button and Actions
*
* @param {boolean} withBlockActions - by default, Toolbar opens with Block Actions. * @param {boolean} withBlockActions - by default, Toolbar opens with Block Actions.
* This flag allows to open Toolbar without Actions. * This flag allows to open Toolbar without Actions.
* @param {boolean} needToCloseToolbox - by default, Toolbar will be moved with opening * @param {boolean} needToCloseToolbox - by default, Toolbar will be moved with opening
@ -229,7 +232,7 @@ export default class Toolbar extends Module {
* with closing Toolbox and Block Settings * with closing Toolbox and Block Settings
* This flag allows to open Toolbar with Toolbox * This flag allows to open Toolbar with Toolbox
*/ */
public open(withBlockActions: boolean = true, needToCloseToolbox: boolean = true): void { public open(withBlockActions = true, needToCloseToolbox = true): void {
_.delay(() => { _.delay(() => {
this.move(needToCloseToolbox); this.move(needToCloseToolbox);
this.nodes.wrapper.classList.add(this.CSS.toolbarOpened); this.nodes.wrapper.classList.add(this.CSS.toolbarOpened);
@ -244,7 +247,8 @@ export default class Toolbar extends Module {
/** /**
* returns toolbar opened state * returns toolbar opened state
* @return {Boolean} *
* @returns {boolean}
*/ */
public get opened(): boolean { public get opened(): boolean {
return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened); return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened);
@ -264,9 +268,10 @@ export default class Toolbar extends Module {
/** /**
* Plus Button public methods * Plus Button public methods
* @return {{hide: function(): void, show: function(): void}} *
* @returns {{hide: function(): void, show: function(): void}}
*/ */
public get plusButton(): {hide: () => void, show: () => void} { public get plusButton(): {hide: () => void; show: () => void} {
return { return {
hide: () => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden), hide: () => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
show: () => { show: () => {
@ -280,14 +285,15 @@ export default class Toolbar extends Module {
/** /**
* Block actions appearance manipulations * Block actions appearance manipulations
* @return {{hide: function(): void, show: function(): void}} *
* @returns {{hide: function(): void, show: function(): void}}
*/ */
private get blockActions(): {hide: () => void, show: () => void} { private get blockActions(): {hide: () => void; show: () => void} {
return { return {
hide: () => { hide: () => {
this.nodes.actions.classList.remove(this.CSS.actionsOpened); this.nodes.actions.classList.remove(this.CSS.actionsOpened);
}, },
show : () => { show: () => {
this.nodes.actions.classList.add(this.CSS.actionsOpened); this.nodes.actions.classList.add(this.CSS.actionsOpened);
}, },
}; };
@ -295,6 +301,7 @@ export default class Toolbar extends Module {
/** /**
* Handler for Plus Button * Handler for Plus Button
*
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
private plusButtonClicked(): void { private plusButtonClicked(): void {

View file

@ -3,7 +3,7 @@ import $ from '../../dom';
import SelectionUtils from '../../selection'; import SelectionUtils from '../../selection';
import * as _ from '../../utils'; import * as _ from '../../utils';
import {InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings} from '../../../../types'; import { InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings } from '../../../../types';
import Flipper from '../../flipper'; import Flipper from '../../flipper';
/** /**
@ -14,7 +14,6 @@ import Flipper from '../../flipper';
* |________________________| * |________________________|
*/ */
export default class InlineToolbar extends Module { export default class InlineToolbar extends Module {
/** /**
* CSS styles * CSS styles
*/ */
@ -37,19 +36,20 @@ export default class InlineToolbar extends Module {
/** /**
* State of inline toolbar * State of inline toolbar
*
* @type {boolean} * @type {boolean}
*/ */
public opened: boolean = false; public opened = false;
/** /**
* Inline Toolbar elements * Inline Toolbar elements
*/ */
private nodes: { private nodes: {
wrapper: HTMLElement, wrapper: HTMLElement;
buttons: HTMLElement, buttons: HTMLElement;
conversionToggler: HTMLElement, conversionToggler: HTMLElement;
conversionTogglerContent: HTMLElement, conversionTogglerContent: HTMLElement;
actions: HTMLElement, actions: HTMLElement;
} = { } = {
wrapper: null, wrapper: null,
buttons: null, buttons: null,
@ -74,15 +74,17 @@ export default class InlineToolbar extends Module {
/** /**
* Buttons List * Buttons List
*
* @type {NodeList} * @type {NodeList}
*/ */
private buttonsList: NodeList = null; private buttonsList: NodeList = null;
/** /**
* Cache for Inline Toolbar width * Cache for Inline Toolbar width
*
* @type {number} * @type {number}
*/ */
private width: number = 0; private width = 0;
/** /**
* Instance of class that responses for leafing buttons by arrows/tab * Instance of class that responses for leafing buttons by arrows/tab
@ -169,14 +171,16 @@ export default class InlineToolbar extends Module {
/** /**
* Shows Inline Toolbar if something is selected * Shows Inline Toolbar if something is selected
*
* @param {boolean} [needToClose] - pass true to close toolbar if it is not allowed. * @param {boolean} [needToClose] - pass true to close toolbar if it is not allowed.
* Avoid to use it just for closing IT, better call .close() clearly. * Avoid to use it just for closing IT, better call .close() clearly.
*/ */
public tryToShow(needToClose: boolean = false): void { public tryToShow(needToClose = false): void {
if (!this.allowedToShow()) { if (!this.allowedToShow()) {
if (needToClose) { if (needToClose) {
this.close(); this.close();
} }
return; return;
} }
@ -196,11 +200,11 @@ export default class InlineToolbar extends Module {
const wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect(); const wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect();
const newCoords = { const newCoords = {
x: selectionRect.x - wrapperOffset.left, x: selectionRect.x - wrapperOffset.left,
y: selectionRect.y y: selectionRect.y +
+ selectionRect.height selectionRect.height -
// + window.scrollY // + window.scrollY
- wrapperOffset.top wrapperOffset.top +
+ this.toolbarVerticalMargin, this.toolbarVerticalMargin,
}; };
/** /**
@ -222,12 +226,12 @@ export default class InlineToolbar extends Module {
*/ */
this.nodes.wrapper.classList.toggle( this.nodes.wrapper.classList.toggle(
this.CSS.inlineToolbarLeftOriented, this.CSS.inlineToolbarLeftOriented,
realLeftCoord < this.Editor.UI.contentRect.left, realLeftCoord < this.Editor.UI.contentRect.left
); );
this.nodes.wrapper.classList.toggle( this.nodes.wrapper.classList.toggle(
this.CSS.inlineToolbarRightOriented, this.CSS.inlineToolbarRightOriented,
realRightCoord > this.Editor.UI.contentRect.right, realRightCoord > this.Editor.UI.contentRect.right
); );
this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px'; this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px';
@ -331,7 +335,7 @@ export default class InlineToolbar extends Module {
return false; return false;
} }
const target = !$.isElement(currentSelection.anchorNode ) const target = !$.isElement(currentSelection.anchorNode)
? currentSelection.anchorNode.parentElement ? currentSelection.anchorNode.parentElement
: currentSelection.anchorNode; : currentSelection.anchorNode;
@ -363,13 +367,14 @@ export default class InlineToolbar extends Module {
*/ */
private filterTools(): void { private filterTools(): void {
const currentSelection = SelectionUtils.get(), const currentSelection = SelectionUtils.get(),
currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement); currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name), const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name),
inlineToolbarSettings = toolSettings && toolSettings[this.Editor.Tools.USER_SETTINGS.ENABLED_INLINE_TOOLS]; inlineToolbarSettings = toolSettings && toolSettings[this.Editor.Tools.USER_SETTINGS.ENABLED_INLINE_TOOLS];
/** /**
* All Inline Toolbar buttons * All Inline Toolbar buttons
*
* @type {HTMLElement[]} * @type {HTMLElement[]}
*/ */
const buttons = Array.from(this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`)) as HTMLElement[]; const buttons = Array.from(this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`)) as HTMLElement[];
@ -458,7 +463,7 @@ export default class InlineToolbar extends Module {
* Changes Conversion Dropdown content for current block's Tool * Changes Conversion Dropdown content for current block's Tool
*/ */
private setConversionTogglerContent(): void { private setConversionTogglerContent(): void {
const {BlockManager, Tools} = this.Editor; const { BlockManager, Tools } = this.Editor;
const toolName = BlockManager.currentBlock.name; const toolName = BlockManager.currentBlock.name;
/** /**
@ -478,11 +483,11 @@ export default class InlineToolbar extends Module {
const userToolboxSettings = toolSettings.toolbox || {}; const userToolboxSettings = toolSettings.toolbox || {};
this.nodes.conversionTogglerContent.innerHTML = this.nodes.conversionTogglerContent.innerHTML =
userToolboxSettings.icon userToolboxSettings.icon ||
|| toolboxSettings.icon toolboxSettings.icon ||
|| userToolboxSettings.title userToolboxSettings.title ||
|| toolboxSettings.title toolboxSettings.title ||
|| _.capitalize(toolName); _.capitalize(toolName);
} }
/** /**
@ -510,6 +515,9 @@ export default class InlineToolbar extends Module {
/** /**
* Add tool button and activate clicks * Add tool button and activate clicks
*
* @param toolName
* @param tool
*/ */
private addTool(toolName: string, tool: InlineTool): void { private addTool(toolName: string, tool: InlineTool): void {
const { const {
@ -522,6 +530,7 @@ export default class InlineToolbar extends Module {
if (!button) { if (!button) {
_.log('Render method must return an instance of Node', 'warn', toolName); _.log('Render method must return an instance of Node', 'warn', toolName);
return; return;
} }
@ -530,6 +539,7 @@ export default class InlineToolbar extends Module {
if (typeof tool.renderActions === 'function') { if (typeof tool.renderActions === 'function') {
const actions = tool.renderActions(); const actions = tool.renderActions();
this.nodes.actions.appendChild(actions); this.nodes.actions.appendChild(actions);
} }
@ -558,7 +568,7 @@ export default class InlineToolbar extends Module {
return (toolClass as ToolSettings).class[Tools.INTERNAL_SETTINGS.IS_INLINE]; return (toolClass as ToolSettings).class[Tools.INTERNAL_SETTINGS.IS_INLINE];
}) })
.map(([name]: [string, InlineToolConstructable | ToolSettings]) => name); .map(([ name ]: [string, InlineToolConstructable | ToolSettings]) => name);
/** /**
* 1) For internal tools, check public getter 'shortcut' * 1) For internal tools, check public getter 'shortcut'
@ -592,11 +602,11 @@ export default class InlineToolbar extends Module {
placement: 'top', placement: 'top',
hidingDelay: 100, hidingDelay: 100,
}); });
} }
/** /**
* Enable Tool shortcut with Editor Shortcuts Module * Enable Tool shortcut with Editor Shortcuts Module
*
* @param {InlineTool} tool - Tool instance * @param {InlineTool} tool - Tool instance
* @param {string} shortcut - shortcut according to the ShortcutData Module format * @param {string} shortcut - shortcut according to the ShortcutData Module format
*/ */
@ -604,7 +614,7 @@ export default class InlineToolbar extends Module {
this.Editor.Shortcuts.add({ this.Editor.Shortcuts.add({
name: shortcut, name: shortcut,
handler: (event) => { handler: (event) => {
const {currentBlock} = this.Editor.BlockManager; const { currentBlock } = this.Editor.BlockManager;
/** /**
* Editor is not focused * Editor is not focused
@ -618,7 +628,7 @@ export default class InlineToolbar extends Module {
* it can be used by tools like «Mention» that works without selection: * it can be used by tools like «Mention» that works without selection:
* Example: by SHIFT+@ show dropdown and insert selected username * Example: by SHIFT+@ show dropdown and insert selected username
*/ */
// if (SelectionUtils.isCollapsed) return; // if (SelectionUtils.isCollapsed) return;
const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name); const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name);
@ -634,6 +644,7 @@ export default class InlineToolbar extends Module {
/** /**
* Inline Tool button clicks * Inline Tool button clicks
*
* @param {InlineTool} tool - Tool's instance * @param {InlineTool} tool - Tool's instance
*/ */
private toolClicked(tool: InlineTool): void { private toolClicked(tool: InlineTool): void {

View file

@ -1,32 +1,32 @@
import Module from '../../__module'; import Module from '../../__module';
import $ from '../../dom'; import $ from '../../dom';
import * as _ from '../../utils'; import * as _ from '../../utils';
import {BlockToolConstructable} from '../../../../types'; import { BlockToolConstructable } from '../../../../types';
import Flipper from '../../flipper'; import Flipper from '../../flipper';
import {BlockToolAPI} from '../../block'; import { BlockToolAPI } from '../../block';
/** /**
* @class Toolbox * @class Toolbox
* @classdesc Holder for Tools * @classdesc Holder for Tools
* *
* @typedef {Toolbox} Toolbox * @typedef {Toolbox} Toolbox
* @property {Boolean} opened - opening state * @property {boolean} opened - opening state
* @property {Object} nodes - Toolbox nodes * @property {object} nodes - Toolbox nodes
* @property {Object} CSS - CSS class names * @property {object} CSS - CSS class names
* *
*/ */
export default class Toolbox extends Module { export default class Toolbox extends Module {
/** /**
* CSS styles * CSS styles
* @return {{toolbox: string, toolboxButton string, toolboxButtonActive: string, *
* @returns {{toolbox: string, toolboxButton string, toolboxButtonActive: string,
* toolboxOpened: string, tooltip: string, tooltipShown: string, tooltipShortcut: string}} * toolboxOpened: string, tooltip: string, tooltipShown: string, tooltipShortcut: string}}
*/ */
get CSS() { get CSS() {
return { return {
toolbox: 'ce-toolbox', toolbox: 'ce-toolbox',
toolboxButton: 'ce-toolbox__button', toolboxButton: 'ce-toolbox__button',
toolboxButtonActive : 'ce-toolbox__button--active', toolboxButtonActive: 'ce-toolbox__button--active',
toolboxOpened: 'ce-toolbox--opened', toolboxOpened: 'ce-toolbox--opened',
openedToolbarHolderModifier: 'codex-editor--toolbox-opened', openedToolbarHolderModifier: 'codex-editor--toolbox-opened',
@ -37,7 +37,8 @@ export default class Toolbox extends Module {
/** /**
* Returns True if Toolbox is Empty and nothing to show * Returns True if Toolbox is Empty and nothing to show
* @return {boolean} *
* @returns {boolean}
*/ */
public get isEmpty(): boolean { public get isEmpty(): boolean {
return this.displayedToolsCount === 0; return this.displayedToolsCount === 0;
@ -45,16 +46,17 @@ export default class Toolbox extends Module {
/** /**
* Opening state * Opening state
*
* @type {boolean} * @type {boolean}
*/ */
public opened: boolean = false; public opened = false;
/** /**
* HTMLElements used for Toolbox UI * HTMLElements used for Toolbox UI
*/ */
public nodes: { public nodes: {
toolbox: HTMLElement, toolbox: HTMLElement;
buttons: HTMLElement[], buttons: HTMLElement[];
} = { } = {
toolbox: null, toolbox: null,
buttons: [], buttons: [],
@ -62,12 +64,14 @@ export default class Toolbox extends Module {
/** /**
* How many tools displayed in Toolbox * How many tools displayed in Toolbox
*
* @type {number} * @type {number}
*/ */
private displayedToolsCount: number = 0; private displayedToolsCount = 0;
/** /**
* Instance of class that responses for leafing buttons by arrows/tab * Instance of class that responses for leafing buttons by arrows/tab
*
* @type {Flipper|null} * @type {Flipper|null}
*/ */
private flipper: Flipper = null; private flipper: Flipper = null;
@ -140,7 +144,7 @@ export default class Toolbox extends Module {
for (const toolName in tools) { for (const toolName in tools) {
if (tools.hasOwnProperty(toolName)) { if (tools.hasOwnProperty(toolName)) {
this.addTool(toolName, tools[toolName] as BlockToolConstructable); this.addTool(toolName, tools[toolName] as BlockToolConstructable);
} }
} }
} }
@ -166,6 +170,7 @@ export default class Toolbox extends Module {
if (toolToolboxSettings && !toolToolboxSettings.icon) { if (toolToolboxSettings && !toolToolboxSettings.icon) {
_.log('Toolbar icon is missed. Tool %o skipped', 'warn', toolName); _.log('Toolbar icon is missed. Tool %o skipped', 'warn', toolName);
return; return;
} }
@ -222,8 +227,8 @@ export default class Toolbox extends Module {
/** /**
* Draw tooltip for toolbox tools * Draw tooltip for toolbox tools
* *
* @param {String} toolName - toolbox tool name * @param {string} toolName - toolbox tool name
* @return { HTMLElement } * @returns { HTMLElement }
*/ */
private drawTooltip(toolName: string): HTMLElement { private drawTooltip(toolName: string): HTMLElement {
const toolSettings = this.Editor.Tools.getToolSettings(toolName); const toolSettings = this.Editor.Tools.getToolSettings(toolName);
@ -251,9 +256,10 @@ export default class Toolbox extends Module {
/** /**
* Enable shortcut Block Tool implemented shortcut * Enable shortcut Block Tool implemented shortcut
*
* @param {BlockToolConstructable} tool - Tool class * @param {BlockToolConstructable} tool - Tool class
* @param {String} toolName - Tool name * @param {string} toolName - Tool name
* @param {String} shortcut - shortcut according to the ShortcutData Module format * @param {string} shortcut - shortcut according to the ShortcutData Module format
*/ */
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string) { private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string) {
this.Editor.Shortcuts.add({ this.Editor.Shortcuts.add({
@ -270,6 +276,7 @@ export default class Toolbox extends Module {
*/ */
private enableFlipper(): void { private enableFlipper(): void {
const tools = Array.from(this.nodes.toolbox.childNodes) as HTMLElement[]; const tools = Array.from(this.nodes.toolbox.childNodes) as HTMLElement[];
this.flipper = new Flipper({ this.flipper = new Flipper({
items: tools, items: tools,
focusedItemClass: this.CSS.toolboxButtonActive, focusedItemClass: this.CSS.toolboxButtonActive,
@ -281,14 +288,14 @@ export default class Toolbox extends Module {
* Can be called when button clicked on Toolbox or by ShortcutData * Can be called when button clicked on Toolbox or by ShortcutData
* *
* @param {BlockToolConstructable} tool - Tool Class * @param {BlockToolConstructable} tool - Tool Class
* @param {String} toolName - Tool name * @param {string} toolName - Tool name
*/ */
private insertNewBlock(tool: BlockToolConstructable, toolName: string) { private insertNewBlock(tool: BlockToolConstructable, toolName: string) {
const {BlockManager, Caret} = this.Editor; const { BlockManager, Caret } = this.Editor;
/** /**
* @type {Block} * @type {Block}
*/ */
const {currentBlock} = BlockManager; const { currentBlock } = BlockManager;
let newBlock; let newBlock;

View file

@ -7,7 +7,7 @@ import {
InlineToolConstructable, Tool, InlineToolConstructable, Tool,
ToolConfig, ToolConfig,
ToolConstructable, ToolConstructable,
ToolSettings, ToolSettings
} from '../../../types'; } from '../../../types';
import BoldInlineTool from '../inline-tools/inline-tool-bold'; import BoldInlineTool from '../inline-tools/inline-tool-bold';
import ItalicInlineTool from '../inline-tools/inline-tool-italic'; import ItalicInlineTool from '../inline-tools/inline-tool-italic';
@ -31,17 +31,18 @@ import Stub from '../tools/stub';
* @property {EditorConfig} config - Editor config * @property {EditorConfig} config - Editor config
*/ */
export default class Tools extends Module { export default class Tools extends Module {
/** /**
* Name of Stub Tool * Name of Stub Tool
* Stub Tool is used to substitute unavailable block Tools and store their data * Stub Tool is used to substitute unavailable block Tools and store their data
*
* @type {string} * @type {string}
*/ */
public stubTool = 'stub'; public stubTool = 'stub';
/** /**
* Returns available Tools * Returns available Tools
* @return {Tool[]} *
* @returns {Tool[]}
*/ */
public get available(): {[name: string]: ToolConstructable} { public get available(): {[name: string]: ToolConstructable} {
return this.toolsAvailable; return this.toolsAvailable;
@ -49,7 +50,8 @@ export default class Tools extends Module {
/** /**
* Returns unavailable Tools * Returns unavailable Tools
* @return {Tool[]} *
* @returns {Tool[]}
*/ */
public get unavailable(): {[name: string]: ToolConstructable} { public get unavailable(): {[name: string]: ToolConstructable} {
return this.toolsUnavailable; return this.toolsUnavailable;
@ -57,14 +59,15 @@ export default class Tools extends Module {
/** /**
* Return Tools for the Inline Toolbar * Return Tools for the Inline Toolbar
* @return {Object} - object of Inline Tool's classes *
* @returns {object} - object of Inline Tool's classes
*/ */
public get inline(): {[name: string]: ToolConstructable} { public get inline(): {[name: string]: ToolConstructable} {
if (this._inlineTools) { if (this._inlineTools) {
return this._inlineTools; return this._inlineTools;
} }
const tools = Object.entries(this.available).filter( ([name, tool]) => { const tools = Object.entries(this.available).filter(([name, tool]) => {
if (!tool[this.INTERNAL_SETTINGS.IS_INLINE]) { if (!tool[this.INTERNAL_SETTINGS.IS_INLINE]) {
return false; return false;
} }
@ -73,14 +76,15 @@ export default class Tools extends Module {
* Some Tools validation * Some Tools validation
*/ */
const inlineToolRequiredMethods = ['render', 'surround', 'checkState']; const inlineToolRequiredMethods = ['render', 'surround', 'checkState'];
const notImplementedMethods = inlineToolRequiredMethods.filter( (method) => !this.constructInline(tool)[method]); const notImplementedMethods = inlineToolRequiredMethods.filter((method) => !this.constructInline(tool)[method]);
if (notImplementedMethods.length) { if (notImplementedMethods.length) {
_.log( _.log(
`Incorrect Inline Tool: ${tool.name}. Some of required methods is not implemented %o`, `Incorrect Inline Tool: ${tool.name}. Some of required methods is not implemented %o`,
'warn', 'warn',
notImplementedMethods, notImplementedMethods
); );
return false; return false;
} }
@ -92,7 +96,9 @@ export default class Tools extends Module {
*/ */
const result = {}; const result = {};
tools.forEach(([name, tool]) => result[name] = tool); tools.forEach(([name, tool]) => {
result[name] = tool;
});
/** /**
* Cache prepared Tools * Cache prepared Tools
@ -107,7 +113,7 @@ export default class Tools extends Module {
*/ */
public get blockTools(): {[name: string]: BlockToolConstructable} { public get blockTools(): {[name: string]: BlockToolConstructable} {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const tools = Object.entries(this.available).filter( ([name, tool]) => { const tools = Object.entries(this.available).filter(([name, tool]) => {
return !tool[this.INTERNAL_SETTINGS.IS_INLINE]; return !tool[this.INTERNAL_SETTINGS.IS_INLINE];
}); });
@ -116,7 +122,9 @@ export default class Tools extends Module {
*/ */
const result = {}; const result = {};
tools.forEach(([name, tool]) => result[name] = tool); tools.forEach(([name, tool]) => {
result[name] = tool;
});
return result; return result;
} }
@ -124,7 +132,7 @@ export default class Tools extends Module {
/** /**
* Constant for available Tools internal settings provided by Tool developer * Constant for available Tools internal settings provided by Tool developer
* *
* @return {object} * @returns {object}
*/ */
public get INTERNAL_SETTINGS() { public get INTERNAL_SETTINGS() {
return { return {
@ -155,7 +163,8 @@ export default class Tools extends Module {
/** /**
* Map {name: Class, ...} where: * Map {name: Class, ...} where:
* name block type name in JSON. Got from EditorConfig.tools keys * name block type name in JSON. Got from EditorConfig.tools keys
* @type {Object} *
* @type {object}
*/ */
public readonly toolsClasses: {[name: string]: ToolConstructable} = {}; public readonly toolsClasses: {[name: string]: ToolConstructable} = {};
@ -171,24 +180,26 @@ export default class Tools extends Module {
/** /**
* Tools settings in a map {name: settings, ...} * Tools settings in a map {name: settings, ...}
* @type {Object} *
* @type {object}
*/ */
private readonly toolsSettings: {[name: string]: ToolSettings} = {}; private readonly toolsSettings: {[name: string]: ToolSettings} = {};
/** /**
* Cache for the prepared inline tools * Cache for the prepared inline tools
*
* @type {null|object} * @type {null|object}
* @private * @private
*/ */
private _inlineTools: {[name: string]: ToolConstructable} = {}; private _inlineTools: {[name: string]: ToolConstructable} = {};
/** /**
* @constructor * @class
* *
* @param {EditorConfig} config * @param {EditorConfig} config
*/ */
constructor({config}) { constructor({ config }) {
super({config}); super({ config });
this.toolsClasses = {}; this.toolsClasses = {};
@ -197,14 +208,16 @@ export default class Tools extends Module {
/** /**
* Available tools list * Available tools list
* {name: Class, ...} * {name: Class, ...}
* @type {Object} *
* @type {object}
*/ */
this.toolsAvailable = {}; this.toolsAvailable = {};
/** /**
* Tools that rejected a prepare method * Tools that rejected a prepare method
* {name: Class, ... } * {name: Class, ... }
* @type {Object} *
* @type {object}
*/ */
this.toolsUnavailable = {}; this.toolsUnavailable = {};
@ -213,7 +226,8 @@ export default class Tools extends Module {
/** /**
* Creates instances via passed or default configuration * Creates instances via passed or default configuration
* @return {Promise} *
* @returns {Promise}
*/ */
public prepare() { public prepare() {
this.validateTools(); this.validateTools();
@ -238,12 +252,14 @@ export default class Tools extends Module {
if (typeof this.config.tools[toolName] === 'object') { if (typeof this.config.tools[toolName] === 'object') {
/** /**
* Save Tool's class from 'class' field * Save Tool's class from 'class' field
*
* @type {Tool} * @type {Tool}
*/ */
this.toolsClasses[toolName] = (this.config.tools[toolName] as ToolSettings).class; this.toolsClasses[toolName] = (this.config.tools[toolName] as ToolSettings).class;
/** /**
* Save Tool's settings * Save Tool's settings
*
* @type {ToolSettings} * @type {ToolSettings}
*/ */
this.toolsSettings[toolName] = this.config.tools[toolName] as ToolSettings; this.toolsSettings[toolName] = this.config.tools[toolName] as ToolSettings;
@ -255,15 +271,17 @@ export default class Tools extends Module {
} else { } else {
/** /**
* Save Tool's class * Save Tool's class
*
* @type {Tool} * @type {Tool}
*/ */
this.toolsClasses[toolName] = this.config.tools[toolName] as ToolConstructable; this.toolsClasses[toolName] = this.config.tools[toolName] as ToolConstructable;
/** /**
* Set empty settings for Block by default * Set empty settings for Block by default
*
* @type {{}} * @type {{}}
*/ */
this.toolsSettings[toolName] = {class: this.config.tools[toolName] as ToolConstructable}; this.toolsSettings[toolName] = { class: this.config.tools[toolName] as ToolConstructable };
} }
} }
@ -306,12 +324,12 @@ export default class Tools extends Module {
/** /**
* Return Tool`s instance * Return Tool`s instance
* *
* @param {String} tool tool name * @param {string} tool tool name
* @param {BlockToolData} data initial data * @param {BlockToolData} data initial data
* @return {BlockTool} * @returns {BlockTool}
*/ */
public construct(tool, data) { public construct(tool, data) {
const plugin = this.toolsClasses[tool]; const Plugin = this.toolsClasses[tool];
/** /**
* Configuration to be passed to the Tool's constructor * Configuration to be passed to the Tool's constructor
@ -332,7 +350,7 @@ export default class Tools extends Module {
data, data,
}; };
return new plugin(constructorOptions); return new Plugin(constructorOptions);
} }
/** /**
@ -340,7 +358,7 @@ export default class Tools extends Module {
* *
* @param {InlineTool} tool * @param {InlineTool} tool
* @param {ToolSettings} toolSettings * @param {ToolSettings} toolSettings
* @return {InlineTool} instance * @returns {InlineTool} instance
*/ */
public constructInline(tool: InlineToolConstructable, toolSettings: ToolSettings = {} as ToolSettings): InlineTool { public constructInline(tool: InlineToolConstructable, toolSettings: ToolSettings = {} as ToolSettings): InlineTool {
/** /**
@ -351,13 +369,15 @@ export default class Tools extends Module {
config: (toolSettings[this.USER_SETTINGS.CONFIG] || {}) as ToolSettings, config: (toolSettings[this.USER_SETTINGS.CONFIG] || {}) as ToolSettings,
}; };
// eslint-disable-next-line new-cap
return new tool(constructorOptions) as InlineTool; return new tool(constructorOptions) as InlineTool;
} }
/** /**
* Check if passed Tool is an instance of Initial Block Tool * Check if passed Tool is an instance of Initial Block Tool
*
* @param {Tool} tool - Tool to check * @param {Tool} tool - Tool to check
* @return {Boolean} * @returns {boolean}
*/ */
public isInitial(tool) { public isInitial(tool) {
return tool instanceof this.available[this.config.initialBlock]; return tool instanceof this.available[this.config.initialBlock];
@ -365,8 +385,9 @@ export default class Tools extends Module {
/** /**
* Return Tool's config by name * Return Tool's config by name
*
* @param {string} toolName * @param {string} toolName
* @return {ToolSettings} * @returns {ToolSettings}
*/ */
public getToolSettings(toolName): ToolSettings { public getToolSettings(toolName): ToolSettings {
return this.toolsSettings[toolName]; return this.toolsSettings[toolName];
@ -374,15 +395,16 @@ export default class Tools extends Module {
/** /**
* Binds prepare function of plugins with user or default config * Binds prepare function of plugins with user or default config
* @return {Array} list of functions that needs to be fired sequentially *
* @returns {Array} list of functions that needs to be fired sequentially
*/ */
private getListOfPrepareFunctions(): Array<{ private getListOfPrepareFunctions(): Array<{
function: (data: {toolName: string, config: ToolConfig}) => void, function: (data: {toolName: string; config: ToolConfig}) => void;
data: {toolName: string, config: ToolConfig}, data: {toolName: string; config: ToolConfig};
}> { }> {
const toolPreparationList: Array<{ const toolPreparationList: Array<{
function: (data: {toolName: string, config: ToolConfig}) => void, function: (data: {toolName: string; config: ToolConfig}) => void;
data: {toolName: string, config: ToolConfig}} data: {toolName: string; config: ToolConfig};}
> = []; > = [];
for (const toolName in this.toolsClasses) { for (const toolName in this.toolsClasses) {
@ -426,7 +448,7 @@ export default class Tools extends Module {
if (!_.isFunction(tool) && !_.isFunction((tool as ToolSettings).class)) { if (!_.isFunction(tool) && !_.isFunction((tool as ToolSettings).class)) {
throw Error( throw Error(
`Tool «${toolName}» must be a constructor function or an object with function in the «class» property`, `Tool «${toolName}» must be a constructor function or an object with function in the «class» property`
); );
} }
} }
@ -439,14 +461,14 @@ export default class Tools extends Module {
*/ */
get internalTools() { get internalTools() {
return { return {
bold: {class: BoldInlineTool}, bold: { class: BoldInlineTool },
italic: {class: ItalicInlineTool}, italic: { class: ItalicInlineTool },
link: {class: LinkInlineTool}, link: { class: LinkInlineTool },
paragraph: { paragraph: {
class: Paragraph, class: Paragraph,
inlineToolbar: true, inlineToolbar: true,
}, },
stub: {class: Stub}, stub: { class: Stub },
}; };
} }
} }

View file

@ -4,7 +4,7 @@ import Module from '../__module';
* Use external module CodeX Tooltip * Use external module CodeX Tooltip
*/ */
import CodeXTooltips, { TooltipContent, TooltipOptions } from 'codex-tooltip'; import CodeXTooltips, { TooltipContent, TooltipOptions } from 'codex-tooltip';
import {ModuleConfig} from '../../types-internal/module-config'; import { ModuleConfig } from '../../types-internal/module-config';
/** /**
* Tooltip * Tooltip
@ -12,19 +12,19 @@ import {ModuleConfig} from '../../types-internal/module-config';
* Decorates any tooltip module like adapter * Decorates any tooltip module like adapter
*/ */
export default class Tooltip extends Module { export default class Tooltip extends Module {
/** /**
* Tooltips lib: CodeX Tooltips * Tooltips lib: CodeX Tooltips
*
* @see https://github.com/codex-team/codex.tooltips * @see https://github.com/codex-team/codex.tooltips
*/ */
private lib: CodeXTooltips = new CodeXTooltips(); private lib: CodeXTooltips = new CodeXTooltips();
/** /**
* @constructor * @class
* @param {EditorConfig} * @param {EditorConfig}
*/ */
constructor({config}: ModuleConfig) { constructor({ config }: ModuleConfig) {
super({config}); super({ config });
} }
/** /**

View file

@ -35,22 +35,21 @@ import Flipper from '../flipper';
* @property {Element} nodes.redactor - <ce-redactor> * @property {Element} nodes.redactor - <ce-redactor>
*/ */
export default class UI extends Module { export default class UI extends Module {
/** /**
* Editor.js UI CSS class names * Editor.js UI CSS class names
* @return {{editorWrapper: string, editorZone: string}} * @return {{editorWrapper: string, editorZone: string}}
*/ */
public get CSS(): { public get CSS(): {
editorWrapper: string, editorWrapperNarrow: string, editorZone: string, editorZoneHidden: string, editorWrapper: string; editorWrapperNarrow: string; editorZone: string; editorZoneHidden: string;
editorLoader: string, editorEmpty: string, editorLoader: string; editorEmpty: string;
} { } {
return { return {
editorWrapper : 'codex-editor', editorWrapper: 'codex-editor',
editorWrapperNarrow : 'codex-editor--narrow', editorWrapperNarrow: 'codex-editor--narrow',
editorZone : 'codex-editor__redactor', editorZone: 'codex-editor__redactor',
editorZoneHidden : 'codex-editor__redactor--hidden', editorZoneHidden: 'codex-editor__redactor--hidden',
editorLoader : 'codex-editor__loader', editorLoader: 'codex-editor__loader',
editorEmpty : 'codex-editor--empty', editorEmpty: 'codex-editor--empty',
}; };
} }
@ -85,7 +84,7 @@ export default class UI extends Module {
* Flag that became true on mobile viewport * Flag that became true on mobile viewport
* @type {boolean} * @type {boolean}
*/ */
public isMobile: boolean = false; public isMobile = false;
/** /**
* HTML Elements used for UI * HTML Elements used for UI
@ -177,7 +176,7 @@ export default class UI extends Module {
* Check if Editor is empty and set CSS class to wrapper * Check if Editor is empty and set CSS class to wrapper
*/ */
public checkEmptiness(): void { public checkEmptiness(): void {
const {BlockManager} = this.Editor; const { BlockManager } = this.Editor;
this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, BlockManager.isEditorEmpty); this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, BlockManager.isEditorEmpty);
} }
@ -199,9 +198,10 @@ export default class UI extends Module {
public get someFlipperButtonFocused(): boolean { public get someFlipperButtonFocused(): boolean {
return Object.entries(this.Editor).filter(([moduleName, moduleClass]) => { return Object.entries(this.Editor).filter(([moduleName, moduleClass]) => {
return moduleClass.flipper instanceof Flipper; return moduleClass.flipper instanceof Flipper;
}).some(([moduleName, moduleClass]) => { })
return moduleClass.flipper.currentItem; .some(([moduleName, moduleClass]) => {
}); return moduleClass.flipper.currentItem;
});
} }
/** /**
@ -244,7 +244,7 @@ export default class UI extends Module {
/** /**
* Create and save main UI elements * Create and save main UI elements
*/ */
this.nodes.wrapper = $.make('div', this.CSS.editorWrapper); this.nodes.wrapper = $.make('div', this.CSS.editorWrapper);
this.nodes.redactor = $.make('div', this.CSS.editorZone); this.nodes.redactor = $.make('div', this.CSS.editorZone);
/** /**
@ -261,7 +261,6 @@ export default class UI extends Module {
this.nodes.wrapper.appendChild(this.nodes.redactor); this.nodes.wrapper.appendChild(this.nodes.redactor);
this.nodes.holder.appendChild(this.nodes.wrapper); this.nodes.holder.appendChild(this.nodes.wrapper);
} }
/** /**
@ -294,17 +293,17 @@ export default class UI extends Module {
this.nodes.redactor, this.nodes.redactor,
'click', 'click',
(event) => this.redactorClicked(event as MouseEvent), (event) => this.redactorClicked(event as MouseEvent),
false, false
); );
this.Editor.Listeners.on(this.nodes.redactor, this.Editor.Listeners.on(this.nodes.redactor,
'mousedown', 'mousedown',
(event) => this.documentTouched(event as MouseEvent), (event) => this.documentTouched(event as MouseEvent),
true, true
); );
this.Editor.Listeners.on(this.nodes.redactor, this.Editor.Listeners.on(this.nodes.redactor,
'touchstart', 'touchstart',
(event) => this.documentTouched(event as MouseEvent), (event) => this.documentTouched(event as MouseEvent),
true, true
); );
this.Editor.Listeners.on(document, 'keydown', (event) => this.documentKeydown(event as KeyboardEvent), true); this.Editor.Listeners.on(document, 'keydown', (event) => this.documentKeydown(event as KeyboardEvent), true);
@ -363,7 +362,7 @@ export default class UI extends Module {
*/ */
private defaultBehaviour(event: KeyboardEvent): void { private defaultBehaviour(event: KeyboardEvent): void {
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`); const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`);
const {currentBlock} = this.Editor.BlockManager; const { currentBlock } = this.Editor.BlockManager;
const isMetaKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; const isMetaKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
/** /**
@ -388,10 +387,11 @@ export default class UI extends Module {
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
*/ */
private backspacePressed(event: KeyboardEvent): void { private backspacePressed(event: KeyboardEvent): void {
const {BlockManager, BlockSelection, Caret} = this.Editor; const { BlockManager, BlockSelection, Caret } = this.Editor;
if (BlockSelection.anyBlockSelected) { if (BlockSelection.anyBlockSelected) {
const selectionPositionIndex = BlockManager.removeSelectedBlocks(); const selectionPositionIndex = BlockManager.removeSelectedBlocks();
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START); Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
/** Clear selection */ /** Clear selection */
@ -418,6 +418,7 @@ export default class UI extends Module {
if (BlockSelection.anyBlockSelected) { if (BlockSelection.anyBlockSelected) {
const selectionPositionIndex = BlockManager.removeSelectedBlocks(); const selectionPositionIndex = BlockManager.removeSelectedBlocks();
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START); Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
/** Clear selection */ /** Clear selection */
@ -431,6 +432,7 @@ export default class UI extends Module {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
event.stopPropagation(); event.stopPropagation();
return; return;
} }
@ -596,6 +598,7 @@ export default class UI extends Module {
const validUrl = _.getValidUrl(href); const validUrl = _.getValidUrl(href);
_.openTab(validUrl); _.openTab(validUrl);
return; return;
} }
@ -635,7 +638,6 @@ export default class UI extends Module {
* We need to skip such firings * We need to skip such firings
*/ */
if (!focusedElement || !focusedElement.closest(`.${Block.CSS.content}`)) { if (!focusedElement || !focusedElement.closest(`.${Block.CSS.content}`)) {
/** /**
* If new selection is not on Inline Toolbar, we need to close it * If new selection is not on Inline Toolbar, we need to close it
*/ */

View file

@ -26,7 +26,7 @@ if (!Element.prototype.matches) {
Element.prototype.msMatchesSelector || Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector || Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector ||
function(s) { function (s) {
const matches = (this.document || this.ownerDocument).querySelectorAll(s); const matches = (this.document || this.ownerDocument).querySelectorAll(s);
let i = matches.length; let i = matches.length;
@ -46,7 +46,8 @@ if (!Element.prototype.matches) {
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill} * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill}
*/ */
if (!Element.prototype.closest) { if (!Element.prototype.closest) {
Element.prototype.closest = function(s) { Element.prototype.closest = function (s) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let el = this; let el = this;
if (!document.documentElement.contains(el)) { if (!document.documentElement.contains(el)) {

View file

@ -30,15 +30,16 @@ interface Document {
/** /**
* Working with selection * Working with selection
*
* @typedef {SelectionUtils} SelectionUtils * @typedef {SelectionUtils} SelectionUtils
*/ */
export default class SelectionUtils { export default class SelectionUtils {
/** /**
* Editor styles * Editor styles
* @return {{editorWrapper: string, editorZone: string}} *
* @returns {{editorWrapper: string, editorZone: string}}
*/ */
static get CSS(): { editorWrapper: string, editorZone: string } { static get CSS(): { editorWrapper: string; editorZone: string } {
return { return {
editorWrapper: 'codex-editor', editorWrapper: 'codex-editor',
editorZone: 'codex-editor__redactor', editorZone: 'codex-editor__redactor',
@ -48,7 +49,8 @@ export default class SelectionUtils {
/** /**
* Returns selected anchor * Returns selected anchor
* {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode} * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode}
* @return {Node|null} *
* @returns {Node|null}
*/ */
static get anchorNode(): Node | null { static get anchorNode(): Node | null {
const selection = window.getSelection(); const selection = window.getSelection();
@ -58,7 +60,8 @@ export default class SelectionUtils {
/** /**
* Returns selected anchor element * Returns selected anchor element
* @return {Element|null} *
* @returns {Element|null}
*/ */
static get anchorElement(): Element | null { static get anchorElement(): Element | null {
const selection = window.getSelection(); const selection = window.getSelection();
@ -83,7 +86,8 @@ export default class SelectionUtils {
/** /**
* Returns selection offset according to the anchor node * Returns selection offset according to the anchor node
* {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset} * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset}
* @return {Number|null} *
* @returns {number|null}
*/ */
static get anchorOffset(): number | null { static get anchorOffset(): number | null {
const selection = window.getSelection(); const selection = window.getSelection();
@ -93,7 +97,8 @@ export default class SelectionUtils {
/** /**
* Is current selection range collapsed * Is current selection range collapsed
* @return {boolean|null} *
* @returns {boolean|null}
*/ */
static get isCollapsed(): boolean | null { static get isCollapsed(): boolean | null {
const selection = window.getSelection(); const selection = window.getSelection();
@ -103,7 +108,8 @@ export default class SelectionUtils {
/** /**
* Check current selection if it is at Editor's zone * Check current selection if it is at Editor's zone
* @return {boolean} *
* @returns {boolean}
*/ */
static get isAtEditor(): boolean { static get isAtEditor(): boolean {
const selection = SelectionUtils.get(); const selection = SelectionUtils.get();
@ -118,6 +124,7 @@ export default class SelectionUtils {
} }
let editorZone = null; let editorZone = null;
if (selectedNode) { if (selectedNode) {
editorZone = selectedNode.closest(`.${SelectionUtils.CSS.editorZone}`); editorZone = selectedNode.closest(`.${SelectionUtils.CSS.editorZone}`);
} }
@ -130,7 +137,8 @@ export default class SelectionUtils {
/** /**
* Return first range * Return first range
* @return {Range|null} *
* @returns {Range|null}
*/ */
static get range(): Range { static get range(): Range {
const selection = window.getSelection(); const selection = window.getSelection();
@ -140,11 +148,12 @@ export default class SelectionUtils {
/** /**
* Calculates position and size of selected text * Calculates position and size of selected text
* @return {{x, y, width, height, top?, left?, bottom?, right?}} *
* @returns {{x, y, width, height, top?, left?, bottom?, right?}}
*/ */
static get rect(): DOMRect | ClientRect { static get rect(): DOMRect | ClientRect {
let sel: Selection | MSSelection = (document as Document).selection, let sel: Selection | MSSelection = (document as Document).selection,
range: TextRange | Range; range: TextRange | Range;
let rect = { let rect = {
x: 0, x: 0,
@ -166,6 +175,7 @@ export default class SelectionUtils {
if (!window.getSelection) { if (!window.getSelection) {
_.log('Method window.getSelection is not supported', 'warn'); _.log('Method window.getSelection is not supported', 'warn');
return rect; return rect;
} }
@ -173,6 +183,7 @@ export default class SelectionUtils {
if (sel.rangeCount === null || isNaN(sel.rangeCount)) { if (sel.rangeCount === null || isNaN(sel.rangeCount)) {
_.log('Method SelectionUtils.rangeCount is not supported', 'warn'); _.log('Method SelectionUtils.rangeCount is not supported', 'warn');
return rect; return rect;
} }
@ -210,6 +221,7 @@ export default class SelectionUtils {
/** /**
* Returns selected text as String * Returns selected text as String
*
* @returns {string} * @returns {string}
*/ */
static get text(): string { static get text(): string {
@ -219,7 +231,8 @@ export default class SelectionUtils {
/** /**
* Returns window SelectionUtils * Returns window SelectionUtils
* {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection} * {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection}
* @return {Selection} *
* @returns {Selection}
*/ */
public static get(): Selection { public static get(): Selection {
return window.getSelection(); return window.getSelection();
@ -230,6 +243,7 @@ export default class SelectionUtils {
/** /**
* This property can store SelectionUtils's range for restoring later * This property can store SelectionUtils's range for restoring later
*
* @type {Range|null} * @type {Range|null}
*/ */
public savedSelectionRange: Range = null; public savedSelectionRange: Range = null;
@ -237,7 +251,7 @@ export default class SelectionUtils {
/** /**
* Fake background is active * Fake background is active
* *
* @return {boolean} * @returns {boolean}
*/ */
public isFakeBackgroundEnabled = false; public isFakeBackgroundEnabled = false;
@ -312,10 +326,10 @@ export default class SelectionUtils {
/** /**
* Looks ahead to find passed tag from current selection * Looks ahead to find passed tag from current selection
* *
* @param {String} tagName - tag to found * @param {string} tagName - tag to found
* @param {String} [className] - tag's class name * @param {string} [className] - tag's class name
* @param {Number} [searchDepth] - count of tags that can be included. For better performance. * @param {number} [searchDepth] - count of tags that can be included. For better performance.
* @return {HTMLElement|null} * @returns {HTMLElement|null}
*/ */
public findParentTag(tagName: string, className?: string, searchDepth = 10): HTMLElement | null { public findParentTag(tagName: string, className?: string, searchDepth = 10): HTMLElement | null {
const selection = window.getSelection(); const selection = window.getSelection();

View file

@ -1,9 +1,13 @@
import $ from '../../dom'; import $ from '../../dom';
import {BlockTool, BlockToolData} from '../../../../types'; import { BlockTool, BlockToolData } from '../../../../types';
/**
*
*/
export default class Stub implements BlockTool { export default class Stub implements BlockTool {
/** /**
* Stub styles * Stub styles
*
* @type {{wrapper: string; info: string; title: string; subtitle: string}} * @type {{wrapper: string; info: string; title: string; subtitle: string}}
*/ */
private CSS = { private CSS = {
@ -33,7 +37,12 @@ export default class Stub implements BlockTool {
*/ */
private readonly savedData: BlockToolData; private readonly savedData: BlockToolData;
constructor({data, config, api}) { /**
* @param data
* @param config
* @param api
*/
constructor({ data, config, api }) {
this.title = data.title || 'Error'; this.title = data.title || 'Error';
this.subtitle = 'The block can not be displayed correctly.'; this.subtitle = 'The block can not be displayed correctly.';
this.savedData = data.savedData; this.savedData = data.savedData;
@ -43,7 +52,8 @@ export default class Stub implements BlockTool {
/** /**
* Returns stub holder * Returns stub holder
* @return {HTMLElement} *
* @returns {HTMLElement}
*/ */
public render(): HTMLElement { public render(): HTMLElement {
return this.wrapper; return this.wrapper;
@ -51,7 +61,8 @@ export default class Stub implements BlockTool {
/** /**
* Return original Tool data * Return original Tool data
* @return {BlockToolData} *
* @returns {BlockToolData}
*/ */
public save(): BlockToolData { public save(): BlockToolData {
return this.savedData; return this.savedData;
@ -59,7 +70,8 @@ export default class Stub implements BlockTool {
/** /**
* Create Tool html markup * Create Tool html markup
* @return {HTMLElement} *
* @returns {HTMLElement}
*/ */
private make(): HTMLElement { private make(): HTMLElement {
const wrapper = $.make('div', this.CSS.wrapper); const wrapper = $.make('div', this.CSS.wrapper);

View file

@ -20,8 +20,8 @@ export enum LogLevels {
declare const VERSION: string; declare const VERSION: string;
/** /**
* @typedef {Object} ChainData * @typedef {object} ChainData
* @property {Object} data - data that will be passed to the success or fallback * @property {object} data - data that will be passed to the success or fallback
* @property {Function} function - function's that must be called asynchronically * @property {Function} function - function's that must be called asynchronically
*/ */
export interface ChainData { export interface ChainData {
@ -35,7 +35,8 @@ export interface ChainData {
/** /**
* Returns basic keycodes as constants * Returns basic keycodes as constants
* @return {{}} *
* @returns {{}}
*/ */
export const keyCodes = { export const keyCodes = {
BACKSPACE: 8, BACKSPACE: 8,
@ -73,17 +74,15 @@ export const mouseButtons = {
* @param {string} type - logging type 'log'|'warn'|'error'|'info' * @param {string} type - logging type 'log'|'warn'|'error'|'info'
* @param {*} [args] - argument to log with a message * @param {*} [args] - argument to log with a message
* @param {string} style - additional styling to message * @param {string} style - additional styling to message
* @param labeled
*/ */
function _log( function _log(
labeled: boolean, labeled: boolean,
msg: string, msg: string,
type: string = 'log', type = 'log',
args?: any, args?: any,
style: string = 'color: inherit', style = 'color: inherit'
): void { ): void {
if (!('console' in window) || !window.console[type]) {
if ( !('console' in window) || !window.console[ type ] ) {
return; return;
} }
@ -156,7 +155,7 @@ _log.logLevel = LogLevels.VERBOSE;
* *
* @param {LogLevels} logLevel - log level to set * @param {LogLevels} logLevel - log level to set
*/ */
export function setLogLevel(logLevel: LogLevels) { export function setLogLevel(logLevel: LogLevels): void {
_log.logLevel = logLevel; _log.logLevel = logLevel;
} }
@ -172,31 +171,34 @@ export const logLabeled = _log.bind(window, true);
/** /**
* Returns true if passed key code is printable (a-Z, 0-9, etc) character. * Returns true if passed key code is printable (a-Z, 0-9, etc) character.
*
* @param {number} keyCode * @param {number} keyCode
* @return {boolean} * @returns {boolean}
*/ */
export function isPrintableKey( keyCode: number ): boolean { export function isPrintableKey(keyCode: number): boolean {
return (keyCode > 47 && keyCode < 58) || // number keys return (keyCode > 47 && keyCode < 58) || // number keys
keyCode === 32 || keyCode === 13 || // Spacebar & return key(s) keyCode === 32 || keyCode === 13 || // Spacebar & return key(s)
(keyCode > 64 && keyCode < 91) || // letter keys (keyCode > 64 && keyCode < 91) || // letter keys
(keyCode > 95 && keyCode < 112) || // Numpad keys (keyCode > 95 && keyCode < 112) || // Numpad keys
(keyCode > 185 && keyCode < 193) || // ;=,-./` (in order) (keyCode > 185 && keyCode < 193) || // ;=,-./` (in order)
(keyCode > 218 && keyCode < 223); // [\]' (in order) (keyCode > 218 && keyCode < 223); // [\]' (in order)
} }
/** /**
* Fires a promise sequence asyncronically * Fires a promise sequence asynchronously
* *
* @param {ChainData[]} chains - list or ChainData's * @param {ChainData[]} chains - list or ChainData's
* @param {Function} success - success callback * @param {Function} success - success callback
* @param {Function} fallback - callback that fires in case of errors * @param {Function} fallback - callback that fires in case of errors
* *
* @return {Promise} * @returns {Promise}
*/ */
export async function sequence( export async function sequence(
chains: ChainData[], chains: ChainData[],
success: (data: any) => void = () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function
fallback: (data: any) => void = () => {}, success: (data: any) => void = (): void => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
fallback: (data: any) => void = (): void => {},
): Promise<void> { ): Promise<void> {
/** /**
* Decorator * Decorator
@ -206,12 +208,12 @@ export async function sequence(
* @param {Function} successCallback * @param {Function} successCallback
* @param {Function} fallbackCallback * @param {Function} fallbackCallback
* *
* @return {Promise} * @returns {Promise}
*/ */
async function waitNextBlock( async function waitNextBlock(
chainData: ChainData, chainData: ChainData,
successCallback: (data: any) => void, successCallback: (data: any) => void,
fallbackCallback: (data: any) => void, fallbackCallback: (data: any) => void
): Promise<void> { ): Promise<void> {
try { try {
await chainData.function(chainData.data); await chainData.function(chainData.data);
@ -228,8 +230,9 @@ export async function sequence(
* reduce current element will not be able to continue while can't get * reduce current element will not be able to continue while can't get
* a resolved Promise * a resolved Promise
*/ */
return await chains.reduce(async (previousValue, currentValue) => { return chains.reduce(async (previousValue, currentValue) => {
await previousValue; await previousValue;
return waitNextBlock(currentValue, success, fallback); return waitNextBlock(currentValue, success, fallback);
}, Promise.resolve()); }, Promise.resolve());
} }
@ -239,7 +242,7 @@ export async function sequence(
* *
* @param {ArrayLike} collection * @param {ArrayLike} collection
* *
* @return {Array} * @returns {Array}
*/ */
export function array(collection: ArrayLike<any>): any[] { export function array(collection: ArrayLike<any>): any[] {
return Array.prototype.slice.call(collection); return Array.prototype.slice.call(collection);
@ -247,8 +250,9 @@ export function array(collection: ArrayLike<any>): any[] {
/** /**
* Check if passed variable is a function * Check if passed variable is a function
*
* @param {*} fn * @param {*} fn
* @return {boolean} * @returns {boolean}
*/ */
export function isFunction(fn: any): boolean { export function isFunction(fn: any): boolean {
return typeof fn === 'function'; return typeof fn === 'function';
@ -256,8 +260,9 @@ export function isFunction(fn: any): boolean {
/** /**
* Check if passed function is a class * Check if passed function is a class
* @param {function} fn *
* @return {boolean} * @param {Function} fn
* @returns {boolean}
*/ */
export function isClass(fn: any): boolean { export function isClass(fn: any): boolean {
return typeof fn === 'function' && /^\s*class\s+/.test(fn.toString()); return typeof fn === 'function' && /^\s*class\s+/.test(fn.toString());
@ -266,8 +271,8 @@ export function isClass(fn: any): boolean {
/** /**
* Checks if object is empty * Checks if object is empty
* *
* @param {Object} object * @param {object} object
* @return {boolean} * @returns {boolean}
*/ */
export function isEmpty(object: object): boolean { export function isEmpty(object: object): boolean {
if (!object) { if (!object) {
@ -279,8 +284,9 @@ export function isEmpty(object: object): boolean {
/** /**
* Check if passed object is a Promise * Check if passed object is a Promise
*
* @param {*} object - object to check * @param {*} object - object to check
* @return {Boolean} * @returns {boolean}
*/ */
export function isPromise(object: any): boolean { export function isPromise(object: any): boolean {
return Promise.resolve(object) === object; return Promise.resolve(object) === object;
@ -290,12 +296,14 @@ export function isPromise(object: any): boolean {
* Delays method execution * Delays method execution
* *
* @param {Function} method * @param {Function} method
* @param {Number} timeout * @param {number} timeout
*/ */
export function delay(method: (...args: any[]) => any, timeout: number) { export function delay(method: (...args: any[]) => any, timeout: number) {
return function() { return function() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this, const context = this,
args = arguments; // eslint-disable-next-line prefer-rest-params
args = arguments;
window.setTimeout(() => method.apply(context, args), timeout); window.setTimeout(() => method.apply(context, args), timeout);
}; };
@ -305,7 +313,7 @@ export function delay(method: (...args: any[]) => any, timeout: number) {
* Get file extension * Get file extension
* *
* @param {File} file * @param {File} file
* @return string * @returns string
*/ */
export function getFileExtension(file: File): string { export function getFileExtension(file: File): string {
return file.name.split('.').pop(); return file.name.split('.').pop();
@ -315,7 +323,7 @@ export function getFileExtension(file: File): string {
* Check if string is MIME type * Check if string is MIME type
* *
* @param {string} type * @param {string} type
* @return boolean * @returns boolean
*/ */
export function isValidMimeType(type: string): boolean { export function isValidMimeType(type: string): boolean {
return /^[-\w]+\/([-+\w]+|\*)$/.test(type); return /^[-\w]+\/([-+\w]+|\*)$/.test(type);
@ -328,17 +336,20 @@ export function isValidMimeType(type: string): boolean {
* Note that this method returns Function and declared variable need to be called * Note that this method returns Function and declared variable need to be called
* *
* @param {Function} func - function that we're throttling * @param {Function} func - function that we're throttling
* @param {Number} wait - time in milliseconds * @param {number} wait - time in milliseconds
* @param {Boolean} immediate - call now * @param {boolean} immediate - call now
* @return {Function} * @returns {Function}
*/ */
export function debounce(func: () => void, wait?: number , immediate?: boolean): () => void { export function debounce(func: () => void, wait?: number, immediate?: boolean): () => void {
let timeout; let timeout;
return () => { return (): void => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this, const context = this,
args = arguments; // eslint-disable-next-line prefer-rest-params
args = arguments;
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const later = () => { const later = () => {
timeout = null; timeout = null;
if (!immediate) { if (!immediate) {
@ -358,9 +369,10 @@ export function debounce(func: () => void, wait?: number , immediate?: boolean):
/** /**
* Copies passed text to the clipboard * Copies passed text to the clipboard
* @param text *
* @param text - text to copy
*/ */
export function copyTextToClipboard(text) { export function copyTextToClipboard(text): void {
const el = Dom.make('div', 'codex-editor-clipboard', { const el = Dom.make('div', 'codex-editor-clipboard', {
innerHTML: text, innerHTML: text,
}); });
@ -369,6 +381,7 @@ export function copyTextToClipboard(text) {
const selection = window.getSelection(); const selection = window.getSelection();
const range = document.createRange(); const range = document.createRange();
range.selectNode(el); range.selectNode(el);
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
@ -380,8 +393,6 @@ export function copyTextToClipboard(text) {
/** /**
* Returns object with os name as key and boolean as value. Shows current user OS * Returns object with os name as key and boolean as value. Shows current user OS
*
* @return {[key: string]: boolean}
*/ */
export function getUserOS(): {[key: string]: boolean} { export function getUserOS(): {[key: string]: boolean} {
const OS = { const OS = {
@ -395,6 +406,7 @@ export function getUserOS(): {[key: string]: boolean} {
if (userOS) { if (userOS) {
OS[userOS] = true; OS[userOS] = true;
return OS; return OS;
} }
@ -403,8 +415,9 @@ export function getUserOS(): {[key: string]: boolean} {
/** /**
* Capitalizes first letter of the string * Capitalizes first letter of the string
*
* @param {string} text * @param {string} text
* @return {string} * @returns {string}
*/ */
export function capitalize(text: string): string { export function capitalize(text: string): string {
return text[0].toUpperCase() + text.slice(1); return text[0].toUpperCase() + text.slice(1);
@ -412,14 +425,17 @@ export function capitalize(text: string): string {
/** /**
* Merge to objects recursively * Merge to objects recursively
*
* @param {object} target * @param {object} target
* @param {object[]} sources * @param {object[]} sources
* @return {object} * @returns {object}
*/ */
export function deepMerge(target, ...sources) { export function deepMerge(target, ...sources): {[key: string]: any} {
const isObject = (item) => item && typeOf(item) === 'object'; const isObject = (item) => item && typeOf(item) === 'object';
if (!sources.length) { return target; } if (!sources.length) {
return target;
}
const source = sources.shift(); const source = sources.shift();
if (isObject(target) && isObject(source)) { if (isObject(target) && isObject(source)) {
@ -444,9 +460,10 @@ export function deepMerge(target, ...sources) {
* *
* Note! This is a simple solution, it can give false-positive results. * Note! This is a simple solution, it can give false-positive results.
* To detect touch devices more carefully, use 'touchstart' event listener * To detect touch devices more carefully, use 'touchstart' event listener
*
* @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
* *
* @return {boolean} * @returns {boolean}
*/ */
export const isTouchSupported: boolean = 'ontouchstart' in document.documentElement; export const isTouchSupported: boolean = 'ontouchstart' in document.documentElement;
@ -461,6 +478,7 @@ export function typeOf(object: any): string {
/** /**
* Make shortcut command more human-readable * Make shortcut command more human-readable
*
* @param {string} shortcut string like 'CMD+B' * @param {string} shortcut string like 'CMD+B'
*/ */
export function beautifyShortcut(shortcut: string): string { export function beautifyShortcut(shortcut: string): string {
@ -493,7 +511,7 @@ export function beautifyShortcut(shortcut: string): string {
* If url has `one slash`, then it concatenates with window location origin * If url has `one slash`, then it concatenates with window location origin
* or when url has `two lack` it appends only protocol * or when url has `two lack` it appends only protocol
* *
* @param {String} url * @param {string} url
*/ */
export function getValidUrl(url: string): string { export function getValidUrl(url: string): string {
try { try {
@ -514,7 +532,7 @@ export function getValidUrl(url: string): string {
/** /**
* Opens new Tab with passed URL * Opens new Tab with passed URL
* *
* @param {String} url - URL address to redirect * @param {string} url - URL address to redirect
*/ */
export function openTab(url: string): void { export function openTab(url: string): void {
window.open(url, '_blank'); window.open(url, '_blank');

View file

@ -9,58 +9,60 @@
module.exports = (env, argv) => { module.exports = (env, argv) => {
const path = require('path'); const path = require('path');
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const {LicenseWebpackPlugin} = require('license-webpack-plugin'); const { LicenseWebpackPlugin } = require('license-webpack-plugin');
const pkg = require('./package.json'); const pkg = require('./package.json');
/** /**
* Environment * Environment
*
* @type {any} * @type {any}
*/ */
const NODE_ENV = argv.mode || 'development'; const NODE_ENV = argv.mode || 'development';
const VERSION = process.env.VERSION || pkg.version; const VERSION = process.env.VERSION || pkg.version;
/** /**
* Plugins for bundle * Plugins for bundle
*
* @type {webpack} * @type {webpack}
*/ */
const webpack = require('webpack'); const webpack = require('webpack');
return { return {
entry: { entry: {
'editor': ['@babel/polyfill/noConflict', './src/codex.ts'] editor: ['@babel/polyfill/noConflict', './src/codex.ts'],
}, },
output: { output: {
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
filename: '[name].js', filename: '[name].js',
library: [ 'EditorJS' ], library: [ 'EditorJS' ],
libraryTarget: 'umd' libraryTarget: 'umd',
}, },
watchOptions: { watchOptions: {
aggregateTimeout: 50 aggregateTimeout: 50,
}, },
/** /**
* Tell webpack what directories should be searched when resolving modules. * Tell webpack what directories should be searched when resolving modules.
*/ */
resolve: { resolve: {
modules: [path.join(__dirname, 'src'), 'node_modules'], modules: [path.join(__dirname, 'src'), 'node_modules'],
extensions: ['.js', '.ts'] extensions: ['.js', '.ts'],
}, },
plugins: [ plugins: [
/** Pass variables into modules */ /** Pass variables into modules */
new webpack.DefinePlugin({ new webpack.DefinePlugin({
NODE_ENV: JSON.stringify(NODE_ENV), NODE_ENV: JSON.stringify(NODE_ENV),
VERSION: JSON.stringify(VERSION) VERSION: JSON.stringify(VERSION),
}), }),
new webpack.BannerPlugin({ new webpack.BannerPlugin({
banner: `Editor.js\n\n@version ${VERSION}\n\n@licence Apache-2.0\n@author CodeX <https://codex.so>\n\n@uses html-janitor\n@licence Apache-2.0 (https://github.com/guardian/html-janitor/blob/master/LICENSE)` banner: `Editor.js\n\n@version ${VERSION}\n\n@licence Apache-2.0\n@author CodeX <https://codex.so>\n\n@uses html-janitor\n@licence Apache-2.0 (https://github.com/guardian/html-janitor/blob/master/LICENSE)`,
}), }),
new LicenseWebpackPlugin() new LicenseWebpackPlugin(),
], ],
module: { module: {
@ -72,35 +74,29 @@ module.exports = (env, argv) => {
loader: 'babel-loader', loader: 'babel-loader',
options: { options: {
cacheDirectory: true, cacheDirectory: true,
} },
}, },
{ {
loader: 'ts-loader' loader: 'ts-loader',
}, },
{ ],
loader: 'tslint-loader',
options: {
fix: true
}
}
]
}, },
{ {
test: /\.css$/, test: /\.css$/,
exclude: /node_modules/, exclude: /node_modules/,
use: [ use: [
'postcss-loader' 'postcss-loader',
] ],
}, },
{ {
test: /\.(svg)$/, test: /\.(svg)$/,
use: [ use: [
{ {
loader: 'raw-loader', loader: 'raw-loader',
} },
] ],
} },
] ],
}, },
devtool: NODE_ENV === 'development' ? 'source-map' : false, devtool: NODE_ENV === 'development' ? 'source-map' : false,
@ -109,9 +105,9 @@ module.exports = (env, argv) => {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
cache: true, cache: true,
parallel: true parallel: true,
}) }),
] ],
} },
}; };
}; };

476
yarn.lock
View file

@ -2,7 +2,13 @@
# yarn lockfile v1 # yarn lockfile v1
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": "@babel/code-frame@^7.0.0":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
dependencies:
"@babel/highlight" "^7.8.3"
"@babel/code-frame@^7.5.5":
version "7.5.5" version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
dependencies: dependencies:
@ -200,6 +206,10 @@
dependencies: dependencies:
"@babel/types" "^7.7.4" "@babel/types" "^7.7.4"
"@babel/helper-validator-identifier@^7.9.0":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
"@babel/helper-wrap-function@^7.7.4": "@babel/helper-wrap-function@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace"
@ -217,12 +227,12 @@
"@babel/traverse" "^7.7.4" "@babel/traverse" "^7.7.4"
"@babel/types" "^7.7.4" "@babel/types" "^7.7.4"
"@babel/highlight@^7.0.0": "@babel/highlight@^7.0.0", "@babel/highlight@^7.8.3":
version "7.5.0" version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.9.0"
chalk "^2.0.0" chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.7.4": "@babel/parser@^7.7.4":
@ -670,6 +680,10 @@
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
"@types/events@*": "@types/events@*":
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
@ -682,6 +696,10 @@
"@types/minimatch" "*" "@types/minimatch" "*"
"@types/node" "*" "@types/node" "*"
"@types/json-schema@^7.0.3":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
"@types/minimatch@*": "@types/minimatch@*":
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -753,6 +771,45 @@
"@types/webpack-sources" "*" "@types/webpack-sources" "*"
source-map "^0.6.0" source-map "^0.6.0"
"@typescript-eslint/eslint-plugin@^2.12.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.27.0.tgz#e479cdc4c9cf46f96b4c287755733311b0d0ba4b"
dependencies:
"@typescript-eslint/experimental-utils" "2.27.0"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@2.27.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a"
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "2.27.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@^2.12.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.27.0.tgz#d91664335b2c46584294e42eb4ff35838c427287"
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "2.27.0"
"@typescript-eslint/typescript-estree" "2.27.0"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@2.27.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz#a288e54605412da8b81f1660b56c8b2e42966ce8"
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^6.3.0"
tsutils "^3.17.1"
"@webassemblyjs/ast@1.8.5": "@webassemblyjs/ast@1.8.5":
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@ -893,17 +950,17 @@ abbrev@1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
acorn-jsx@^5.1.0: acorn-jsx@^5.2.0:
version "5.1.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
acorn@^6.2.1: acorn@^6.2.1:
version "6.4.1" version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
acorn@^7.1.0: acorn@^7.1.1:
version "7.1.0" version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
aggregate-error@^3.0.0: aggregate-error@^3.0.0:
version "3.0.1" version "3.0.1"
@ -929,7 +986,7 @@ ajv@^5.0.0:
fast-json-stable-stringify "^2.0.0" fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0" json-schema-traverse "^0.3.0"
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2: ajv@^6.1.0:
version "6.10.2" version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
dependencies: dependencies:
@ -938,15 +995,24 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2:
json-schema-traverse "^0.4.1" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" uri-js "^4.2.2"
ajv@^6.10.0, ajv@^6.10.2:
version "6.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
alphanum-sort@^1.0.0: alphanum-sort@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
ansi-escapes@^4.2.1: ansi-escapes@^4.2.1:
version "4.3.0" version "4.3.1"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
dependencies: dependencies:
type-fest "^0.8.1" type-fest "^0.11.0"
ansi-regex@^2.0.0: ansi-regex@^2.0.0:
version "2.1.1" version "2.1.1"
@ -1017,6 +1083,14 @@ array-find-index@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
array-includes@^3.0.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0"
is-string "^1.0.5"
array-union@^1.0.2: array-union@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@ -1031,6 +1105,13 @@ array-unique@^0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
array.prototype.flat@^1.2.1:
version "1.2.3"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
arrify@^1.0.1: arrify@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@ -1578,6 +1659,10 @@ commander@^2.12.1, commander@^2.20.0, commander@^2.8.1:
version "2.20.3" version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
comment-parser@^0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.2.tgz#baf6d99b42038678b81096f15b630d18142f4b8a"
commondir@^1.0.1: commondir@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@ -1611,6 +1696,10 @@ constants-browserify@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
contains-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
convert-source-map@^1.7.0: convert-source-map@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
@ -1902,7 +1991,7 @@ cyclist@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
debug@^2.2.0, debug@^2.3.3: debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies: dependencies:
@ -2005,6 +2094,13 @@ dir-glob@^2.2.2:
dependencies: dependencies:
path-type "^3.0.0" path-type "^3.0.0"
doctrine@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
dependencies:
esutils "^2.0.2"
isarray "^1.0.0"
doctrine@^3.0.0: doctrine@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
@ -2152,7 +2248,7 @@ errno@^0.1.3, errno@~0.1.7:
dependencies: dependencies:
prr "~1.0.1" prr "~1.0.1"
error-ex@^1.3.1: error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
dependencies: dependencies:
@ -2173,6 +2269,22 @@ es-abstract@^1.12.0, es-abstract@^1.5.1:
string.prototype.trimleft "^2.1.0" string.prototype.trimleft "^2.1.0"
string.prototype.trimright "^2.1.0" string.prototype.trimright "^2.1.0"
es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5:
version "1.17.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.1.5"
is-regex "^1.0.5"
object-inspect "^1.7.0"
object-keys "^1.1.1"
object.assign "^4.1.0"
string.prototype.trimleft "^2.1.1"
string.prototype.trimright "^2.1.1"
es-to-primitive@^1.2.1: es-to-primitive@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@ -2189,6 +2301,30 @@ escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
eslint-config-codex@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/eslint-config-codex/-/eslint-config-codex-1.3.2.tgz#063007f215832fd8a967e7e53084f480ba24596c"
dependencies:
"@typescript-eslint/eslint-plugin" "^2.12.0"
"@typescript-eslint/parser" "^2.12.0"
eslint-config-standard "14.1.0"
eslint-plugin-import "2.19.1"
eslint-plugin-jsdoc "^22.1.0"
eslint-plugin-node "10.0.0"
eslint-plugin-promise "4.2.1"
eslint-plugin-standard "4.0.1"
eslint-config-standard@14.1.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz#b23da2b76fe5a2eba668374f246454e7058f15d4"
eslint-import-resolver-node@^0.3.2:
version "0.3.3"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404"
dependencies:
debug "^2.6.9"
resolve "^1.13.1"
eslint-loader@^3.0.3: eslint-loader@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-3.0.3.tgz#e018e3d2722381d982b1201adb56819c73b480ca" resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-3.0.3.tgz#e018e3d2722381d982b1201adb56819c73b480ca"
@ -2199,6 +2335,68 @@ eslint-loader@^3.0.3:
object-hash "^2.0.1" object-hash "^2.0.1"
schema-utils "^2.6.1" schema-utils "^2.6.1"
eslint-module-utils@^2.4.1:
version "2.6.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
dependencies:
debug "^2.6.9"
pkg-dir "^2.0.0"
eslint-plugin-es@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz#0f5f5da5f18aa21989feebe8a73eadefb3432976"
dependencies:
eslint-utils "^1.4.2"
regexpp "^3.0.0"
eslint-plugin-import@2.19.1:
version "2.19.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz#5654e10b7839d064dd0d46cd1b88ec2133a11448"
dependencies:
array-includes "^3.0.3"
array.prototype.flat "^1.2.1"
contains-path "^0.1.0"
debug "^2.6.9"
doctrine "1.5.0"
eslint-import-resolver-node "^0.3.2"
eslint-module-utils "^2.4.1"
has "^1.0.3"
minimatch "^3.0.4"
object.values "^1.1.0"
read-pkg-up "^2.0.0"
resolve "^1.12.0"
eslint-plugin-jsdoc@^22.1.0:
version "22.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.1.0.tgz#dadfa62653fc0d87f900d810307f5ed07ef6ecd5"
dependencies:
comment-parser "^0.7.2"
debug "^4.1.1"
jsdoctypeparser "^6.1.0"
lodash "^4.17.15"
regextras "^0.7.0"
semver "^6.3.0"
spdx-expression-parse "^3.0.0"
eslint-plugin-node@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz#fd1adbc7a300cf7eb6ac55cf4b0b6fc6e577f5a6"
dependencies:
eslint-plugin-es "^2.0.0"
eslint-utils "^1.4.2"
ignore "^5.1.1"
minimatch "^3.0.4"
resolve "^1.10.1"
semver "^6.1.0"
eslint-plugin-promise@4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
eslint-plugin-standard@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4"
eslint-scope@^4.0.3: eslint-scope@^4.0.3:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
@ -2213,19 +2411,25 @@ eslint-scope@^5.0.0:
esrecurse "^4.1.0" esrecurse "^4.1.0"
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-utils@^1.4.3: eslint-utils@^1.4.2, eslint-utils@^1.4.3:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
dependencies: dependencies:
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
eslint-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-visitor-keys@^1.1.0: eslint-visitor-keys@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
eslint@^6.7.2: eslint@^6.8.0:
version "6.7.2" version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
dependencies: dependencies:
"@babel/code-frame" "^7.0.0" "@babel/code-frame" "^7.0.0"
ajv "^6.10.0" ajv "^6.10.0"
@ -2266,11 +2470,11 @@ eslint@^6.7.2:
v8-compile-cache "^2.0.3" v8-compile-cache "^2.0.3"
espree@^6.1.2: espree@^6.1.2:
version "6.1.2" version "6.2.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
dependencies: dependencies:
acorn "^7.1.0" acorn "^7.1.1"
acorn-jsx "^5.1.0" acorn-jsx "^5.2.0"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
esprima@^4.0.0: esprima@^4.0.0:
@ -2278,10 +2482,10 @@ esprima@^4.0.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
esquery@^1.0.1: esquery@^1.0.1:
version "1.0.1" version "1.2.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe"
dependencies: dependencies:
estraverse "^4.0.0" estraverse "^5.0.0"
esrecurse@^4.1.0: esrecurse@^4.1.0:
version "4.2.1" version "4.2.1"
@ -2289,10 +2493,14 @@ esrecurse@^4.1.0:
dependencies: dependencies:
estraverse "^4.1.0" estraverse "^4.1.0"
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: estraverse@^4.1.0, estraverse@^4.1.1:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
estraverse@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22"
esutils@^2.0.2: esutils@^2.0.2:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@ -2399,6 +2607,10 @@ fast-deep-equal@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
fast-glob@^2.2.6: fast-glob@^2.2.6:
version "2.2.7" version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
@ -2411,8 +2623,8 @@ fast-glob@^2.2.6:
micromatch "^3.1.10" micromatch "^3.1.10"
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
fast-levenshtein@~2.0.6: fast-levenshtein@~2.0.6:
version "2.0.6" version "2.0.6"
@ -2423,8 +2635,8 @@ figgy-pudding@^3.5.1:
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
figures@^3.0.0: figures@^3.0.0:
version "3.1.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
dependencies: dependencies:
escape-string-regexp "^1.0.5" escape-string-regexp "^1.0.5"
@ -2480,7 +2692,7 @@ find-up@^1.0.0:
path-exists "^2.0.0" path-exists "^2.0.0"
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
find-up@^2.0.0: find-up@^2.0.0, find-up@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
dependencies: dependencies:
@ -2517,8 +2729,8 @@ flat-cache@^2.0.1:
write "1.0.3" write "1.0.3"
flatted@^2.0.0: flatted@^2.0.0:
version "2.0.1" version "2.0.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
flatten@^1.0.2: flatten@^1.0.2:
version "1.0.3" version "1.0.3"
@ -2635,8 +2847,8 @@ glob-parent@^3.1.0:
path-dirname "^1.0.0" path-dirname "^1.0.0"
glob-parent@^5.0.0: glob-parent@^5.0.0:
version "5.1.0" version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
@ -2644,7 +2856,7 @@ glob-to-regexp@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
dependencies: dependencies:
@ -2692,8 +2904,8 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
globals@^12.1.0: globals@^12.1.0:
version "12.3.0" version "12.4.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
dependencies: dependencies:
type-fest "^0.8.1" type-fest "^0.8.1"
@ -2884,7 +3096,7 @@ ignore@^4.0.3, ignore@^4.0.6:
version "4.0.6" version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
ignore@^5.1.4: ignore@^5.1.1, ignore@^5.1.4:
version "5.1.4" version "5.1.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
@ -2969,21 +3181,21 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
inquirer@^7.0.0: inquirer@^7.0.0:
version "7.0.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
dependencies: dependencies:
ansi-escapes "^4.2.1" ansi-escapes "^4.2.1"
chalk "^2.4.2" chalk "^3.0.0"
cli-cursor "^3.1.0" cli-cursor "^3.1.0"
cli-width "^2.0.0" cli-width "^2.0.0"
external-editor "^3.0.3" external-editor "^3.0.3"
figures "^3.0.0" figures "^3.0.0"
lodash "^4.17.15" lodash "^4.17.15"
mute-stream "0.0.8" mute-stream "0.0.8"
run-async "^2.2.0" run-async "^2.4.0"
rxjs "^6.4.0" rxjs "^6.5.3"
string-width "^4.1.0" string-width "^4.1.0"
strip-ansi "^5.1.0" strip-ansi "^6.0.0"
through "^2.3.6" through "^2.3.6"
interpret@1.2.0: interpret@1.2.0:
@ -3057,6 +3269,10 @@ is-callable@^1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
is-callable@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
is-color-stop@^1.0.0: is-color-stop@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
@ -3186,6 +3402,12 @@ is-regex@^1.0.4:
dependencies: dependencies:
has "^1.0.1" has "^1.0.1"
is-regex@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
dependencies:
has "^1.0.3"
is-regexp@^2.0.0: is-regexp@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d"
@ -3198,6 +3420,10 @@ is-stream@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
is-string@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
is-svg@^3.0.0: is-svg@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
@ -3274,6 +3500,10 @@ js-yaml@^3.13.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
jsdoctypeparser@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz#acfb936c26300d98f1405cb03e20b06748e512a8"
jsesc@^2.5.1: jsesc@^2.5.1:
version "2.5.2" version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@ -3368,6 +3598,15 @@ lines-and-columns@^1.1.6:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
load-json-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
dependencies:
graceful-fs "^4.1.2"
parse-json "^2.2.0"
pify "^2.0.0"
strip-bom "^3.0.0"
load-json-file@^4.0.0: load-json-file@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
@ -3668,6 +3907,10 @@ minimist@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
minipass-collect@^1.0.2: minipass-collect@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
@ -3727,12 +3970,18 @@ mixin-deep@^1.2.0:
for-in "^1.0.2" for-in "^1.0.2"
is-extendable "^1.0.1" is-extendable "^1.0.1"
mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@~0.5.1:
version "0.5.1" version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies: dependencies:
minimist "0.0.8" minimist "0.0.8"
mkdirp@^0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
dependencies:
minimist "^1.2.5"
move-concurrently@^1.0.1: move-concurrently@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -4134,6 +4383,12 @@ parse-entities@^1.0.2, parse-entities@^1.1.0:
is-decimal "^1.0.0" is-decimal "^1.0.0"
is-hexadecimal "^1.0.0" is-hexadecimal "^1.0.0"
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
dependencies:
error-ex "^1.2.0"
parse-json@^4.0.0: parse-json@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@ -4192,6 +4447,12 @@ path-parse@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
dependencies:
pify "^2.0.0"
path-type@^3.0.0: path-type@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@ -4216,7 +4477,7 @@ picomatch@^2.0.5:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5"
pify@^2.3.0: pify@^2.0.0, pify@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@ -4250,6 +4511,12 @@ pkg-dir@^1.0.0:
dependencies: dependencies:
find-up "^1.0.0" find-up "^1.0.0"
pkg-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
dependencies:
find-up "^2.1.0"
pkg-dir@^3.0.0: pkg-dir@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@ -5053,6 +5320,13 @@ read-cache@^1.0.0:
dependencies: dependencies:
pify "^2.3.0" pify "^2.3.0"
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
dependencies:
find-up "^2.0.0"
read-pkg "^2.0.0"
read-pkg-up@^3.0.0: read-pkg-up@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
@ -5060,6 +5334,14 @@ read-pkg-up@^3.0.0:
find-up "^2.0.0" find-up "^2.0.0"
read-pkg "^3.0.0" read-pkg "^3.0.0"
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
dependencies:
load-json-file "^2.0.0"
normalize-package-data "^2.3.2"
path-type "^2.0.0"
read-pkg@^3.0.0: read-pkg@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
@ -5143,6 +5425,10 @@ regexpp@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
regexpp@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
regexpu-core@^4.6.0: regexpu-core@^4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6"
@ -5154,6 +5440,10 @@ regexpu-core@^4.6.0:
unicode-match-property-ecmascript "^1.0.4" unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.1.0" unicode-match-property-value-ecmascript "^1.1.0"
regextras@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.0.tgz#2298bef8cfb92b1b7e3b9b12aa8f69547b7d71e4"
regjsgen@^0.5.0: regjsgen@^0.5.0:
version "0.5.1" version "0.5.1"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
@ -5270,6 +5560,12 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1:
dependencies: dependencies:
path-parse "^1.0.6" path-parse "^1.0.6"
resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
dependencies:
path-parse "^1.0.6"
restore-cursor@^3.1.0: restore-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@ -5295,7 +5591,7 @@ rimraf@2.6.3:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
rimraf@^2.4.4, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1:
version "2.7.1" version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
dependencies: dependencies:
@ -5314,9 +5610,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0" hash-base "^3.0.0"
inherits "^2.0.1" inherits "^2.0.1"
run-async@^2.2.0: run-async@^2.4.0:
version "2.3.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
dependencies: dependencies:
is-promise "^2.1.0" is-promise "^2.1.0"
@ -5326,9 +5622,9 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies: dependencies:
aproba "^1.1.1" aproba "^1.1.1"
rxjs@^6.4.0: rxjs@^6.5.3:
version "6.5.3" version "6.5.5"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
@ -5379,7 +5675,7 @@ schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0:
version "6.3.0" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@ -5425,10 +5721,14 @@ shebang-regex@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
signal-exit@^3.0.0, signal-exit@^3.0.2: signal-exit@^3.0.0:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
signal-exit@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
simple-swizzle@^0.2.2: simple-swizzle@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@ -5634,6 +5934,13 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
string.prototype.trimend@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimleft@^2.1.0: string.prototype.trimleft@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
@ -5641,6 +5948,14 @@ string.prototype.trimleft@^2.1.0:
define-properties "^1.1.3" define-properties "^1.1.3"
function-bind "^1.1.1" function-bind "^1.1.1"
string.prototype.trimleft@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimstart "^1.0.0"
string.prototype.trimright@^2.1.0: string.prototype.trimright@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
@ -5648,6 +5963,21 @@ string.prototype.trimright@^2.1.0:
define-properties "^1.1.3" define-properties "^1.1.3"
function-bind "^1.1.1" function-bind "^1.1.1"
string.prototype.trimright@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimend "^1.0.0"
string.prototype.trimstart@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string_decoder@^1.0.0, string_decoder@^1.1.1: string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@ -5710,8 +6040,8 @@ strip-indent@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
strip-json-comments@^3.0.1: strip-json-comments@^3.0.1:
version "3.0.1" version "3.1.0"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180"
strip-json-comments@~2.0.1: strip-json-comments@~2.0.1:
version "2.0.1" version "2.0.1"
@ -5998,19 +6328,13 @@ ts-loader@^6.2.1:
micromatch "^4.0.0" micromatch "^4.0.0"
semver "^6.0.0" semver "^6.0.0"
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: tslib@^1.8.0, tslib@^1.8.1:
version "1.10.0" version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
tslint-loader@^3.5.4: tslib@^1.9.0:
version "3.5.4" version "1.11.1"
resolved "https://registry.yarnpkg.com/tslint-loader/-/tslint-loader-3.5.4.tgz#052af7f0772434451ea1b247bb55407f878a4c40" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
dependencies:
loader-utils "^1.0.2"
mkdirp "^0.5.1"
object-assign "^4.1.1"
rimraf "^2.4.4"
semver "^5.3.0"
tslint@^5.14.0: tslint@^5.14.0:
version "5.20.1" version "5.20.1"
@ -6036,6 +6360,12 @@ tsutils@^2.29.0:
dependencies: dependencies:
tslib "^1.8.1" tslib "^1.8.1"
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
dependencies:
tslib "^1.8.1"
tty-browserify@0.0.0: tty-browserify@0.0.0:
version "0.0.0" version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@ -6046,6 +6376,10 @@ type-check@~0.3.2:
dependencies: dependencies:
prelude-ls "~1.1.2" prelude-ls "~1.1.2"
type-fest@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
type-fest@^0.8.1: type-fest@^0.8.1:
version "0.8.1" version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"