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

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
*
* Short Description (눈_눈;)
*
* @version 2.0
*
* @licence Apache-2.0

View file

@ -1,5 +1,10 @@
# 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
- `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:dev": "webpack --mode development --progress --display-error-details --display-entrypoints --watch",
"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": "svg-sprite-generate -d src/assets/ -o dist/sprite.svg",
"pull_tools": "git submodule update --init --recursive",
@ -43,7 +46,8 @@
"core-js": "3",
"css-loader": "^3.2.1",
"cssnano": "^4.1.10",
"eslint": "^6.7.2",
"eslint": "^6.8.0",
"eslint-config-codex": "^1.3.2",
"eslint-loader": "^3.0.3",
"extract-text-webpack-plugin": "^3.0.2",
"html-janitor": "^2.0.4",
@ -61,7 +65,6 @@
"terser-webpack-plugin": "^2.2.2",
"ts-loader": "^6.2.1",
"tslint": "^5.14.0",
"tslint-loader": "^3.5.4",
"typescript": "^3.7.3",
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3"

View file

@ -1,7 +1,6 @@
'use strict';
import {EditorConfig} from '../types';
declare const VERSION: string;
import { EditorConfig } from '../types';
/**
* Apply polyfills
@ -11,10 +10,13 @@ import '@babel/register';
import 'components/polyfills';
import Core from './components/core';
declare const VERSION: string;
/**
* Editor.js
*
* Short Description (_눈;)
*
* @version 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
*/
@ -63,6 +63,7 @@ export default class EditorJS {
/**
* We need to export isReady promise in the constructor
* as it can be used before other API methods are exported
*
* @type {Promise<void>}
*/
this.isReady = editor.isReady.then(() => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,8 @@
import $ from './dom';
import * as _ from './utils';
import {EditorConfig, OutputData, SanitizerConfig} from '../../types';
import {EditorModules} from '../types-internal/editor-modules';
import {LogLevels} from './utils';
import { EditorConfig, OutputData, SanitizerConfig } from '../../types';
import { EditorModules } from '../types-internal/editor-modules';
import { LogLevels } from './utils';
/**
* @typedef {Core} Core - editor core class
@ -37,7 +37,6 @@ contextRequire.keys().forEach((filename) => {
* @type {Core}
*/
export default class Core {
/**
* Editor configuration passed by user to the constructor
*/
@ -82,7 +81,7 @@ export default class Core {
await this.render();
if ((this.configuration as EditorConfig).autofocus) {
const {BlockManager, Caret} = this.moduleInstances;
const { BlockManager, Caret } = this.moduleInstances;
Caret.setToBlock(BlockManager.blocks[0], Caret.positions.START);
}
@ -110,6 +109,7 @@ export default class Core {
/**
* Setting for configuration
*
* @param {EditorConfig|string|undefined} config
*/
set configuration(config: EditorConfig|string) {
@ -134,6 +134,7 @@ export default class Core {
/**
* Place config into the class property
*
* @type {EditorConfig}
*/
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
*
* @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
* Uses in case when there is no blocks passed
*
* @type {{type: (*), data: {text: null}}}
*/
const initialBlockData = {
type : this.config.initialBlock,
data : {},
type: this.config.initialBlock,
data: {},
};
this.config.placeholder = this.config.placeholder || false;
@ -200,6 +203,7 @@ export default class Core {
/**
* Returns private property
*
* @returns {EditorConfig}
*/
get configuration(): EditorConfig|string {
@ -208,6 +212,7 @@ export default class Core {
/**
* Checks for required fields in Editor's config
*
* @returns {Promise<void>}
*/
public async validate(): Promise<void> {
@ -250,7 +255,8 @@ export default class Core {
* Start Editor!
*
* Get list of modules that needs to be prepared and return a sequence (Promise)
* @return {Promise}
*
* @returns {Promise}
*/
public async start() {
const modulesToPrepare = [
@ -275,7 +281,7 @@ export default class Core {
}
// _.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
*/
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
*/
const Module = typeof module === 'function' ? module : module.default;
try {
/**
* We use class name provided by displayName property
*
* 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
*
* @see https://www.npmjs.com/package/babel-plugin-class-display-name
*/
this.moduleInstances[Module.displayName] = new Module({
config : this.configuration,
config: this.configuration,
});
} catch ( e ) {
_.log(`Module ${Module.displayName} skipped because`, 'warn', e);
} catch (e) {
_.log(`Module ${Module.displayName} skipped because`, 'warn', e);
}
});
}
@ -332,6 +338,7 @@ export default class Core {
/**
* Return modules without passed name
*
* @param {string} name - module for witch modules difference should be calculated
*/
private getModulesDiff(name: string): EditorModules {

View file

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

View file

@ -7,6 +7,7 @@ import Dom from './dom';
export default class DomIterator {
/**
* This is a static property that defines iteration directions
*
* @type {{RIGHT: string, LEFT: string}}
*/
public static directions = {
@ -22,9 +23,10 @@ export default class DomIterator {
/**
* Focused button index.
* Default is -1 which means nothing is active
*
* @type {number}
*/
private cursor: number = -1;
private cursor = -1;
/**
* Items to flip
@ -37,7 +39,7 @@ export default class DomIterator {
*/
constructor(
nodeList: HTMLElement[],
focusedCssClass: string,
focusedCssClass: string
) {
this.items = nodeList || [];
this.focusedCssClass = focusedCssClass;
@ -45,7 +47,8 @@ export default class DomIterator {
/**
* Returns Focused button Node
* @return {HTMLElement}
*
* @returns {HTMLElement}
*/
public get currentItem(): HTMLElement {
if (this.cursor === -1) {
@ -57,6 +60,7 @@ export default class DomIterator {
/**
* Sets items. Can be used when iterable items changed dynamically
*
* @param {HTMLElement[]} nodeList
*/
public setItems(nodeList: HTMLElement[]): void {
@ -93,7 +97,7 @@ export default class DomIterator {
* Leafs nodes inside the target list from active element
*
* @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 {
/**
@ -138,6 +142,7 @@ export default class DomIterator {
if (direction === DomIterator.directions.RIGHT) {
/**
* If we go right then choose next (+1) Tool
*
* @type {number}
*/
focusedButtonIndex = (focusedButtonIndex + 1) % this.items.length;
@ -145,6 +150,7 @@ export default class DomIterator {
/**
* If we go left then choose previous (-1) Tool
* Before counting module we need to add length before because of "The JavaScript Modulo Bug"
*
* @type {number}
*/
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
*/
export default class Flipper {
/**
* Instance of flipper iterator
*
* @type {DomIterator|null}
*/
private readonly iterator: DomIterator = null;
/**
* Flag that defines activation status
*
* @type {boolean}
*/
private activated: boolean = false;
private activated = false;
/**
* Flag that allows arrows usage to flip items
*
* @type {boolean}
*/
private readonly allowArrows: boolean = true;
@ -61,7 +63,7 @@ export default class Flipper {
private readonly activateCallback: () => void;
/**
* @constructor
* @class
*
* @param {FlipperOptions} options - different constructing settings
* @
@ -129,6 +131,7 @@ export default class Flipper {
/**
* Active tab/arrows handling by flipper
*
* @param {HTMLElement[]} items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically
*/
public activate(items?: HTMLElement[]): void {
@ -149,7 +152,8 @@ export default class Flipper {
/**
* Return current focused button
* @return {HTMLElement|null}
*
* @returns {HTMLElement|null}
*/
public get currentItem(): HTMLElement|null {
return this.iterator.currentItem;
@ -165,6 +169,7 @@ export default class Flipper {
/**
* Drops flipper's iterator cursor
*
* @see DomIterator#dropCursor
*/
private dropCursor(): void {
@ -174,8 +179,9 @@ export default class Flipper {
/**
* This function is fired before handling flipper keycodes
* The result of this function defines if it is need to be handled or not
*
* @param {KeyboardEvent} event
* @return {boolean}
* @returns {boolean}
*/
private isEventReadyForHandling(event: KeyboardEvent): boolean {
const handlingKeyCodeList = [
@ -188,7 +194,7 @@ export default class Flipper {
_.keyCodes.LEFT,
_.keyCodes.RIGHT,
_.keyCodes.UP,
_.keyCodes.DOWN,
_.keyCodes.DOWN
);
}
@ -201,12 +207,13 @@ export default class Flipper {
/**
* When flipper is activated tab press will leaf the items
*
* @param {KeyboardEvent} event
*/
private handleTabPress(event: KeyboardEvent): void {
/** this property defines leaf direction */
const shiftKey = event.shiftKey,
direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT;
direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT;
switch (direction) {
case DomIterator.directions.RIGHT:
@ -234,6 +241,7 @@ export default class Flipper {
/**
* Enter press will click current item if flipper is activated
*
* @param {KeyboardEvent} event
*/
private handleEnterPress(event: KeyboardEvent): void {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,11 @@
import Module from '../../__module';
import {Notifier} from '../../../../types/api';
import {ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions} from 'codex-notifier';
import { Notifier } from '../../../../types/api';
import { ConfirmNotifierOptions, NotifierOptions, PromptNotifierOptions } from 'codex-notifier';
/**
*
*/
export default class NotifierAPI extends Module {
/**
* 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);
}
}

View file

@ -1,5 +1,5 @@
import Module from '../../__module';
import {Sanitizer} from '../../../../types/api';
import { Sanitizer } from '../../../../types/api';
/**
* @class SanitizerAPI
@ -8,7 +8,8 @@ import {Sanitizer} from '../../../../types/api';
export default class SanitizerAPI extends Module {
/**
* Available methods
* @return {Sanitizer}
*
* @returns {Sanitizer}
*/
get methods(): Sanitizer {
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) {
return this.Editor.Sanitizer.clean(taintString, config);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,14 +19,13 @@ import * as _ from '../utils';
* @typedef {Caret} Caret
*/
export default class Caret extends Module {
/**
* Allowed caret positions in input
*
* @static
* @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 {
START: 'start',
END: 'end',
@ -45,7 +44,8 @@ export default class Caret extends Module {
/**
* Get's deepest first node and checks if offset is zero
* @return {boolean}
*
* @returns {boolean}
*/
public get isAtStart(): boolean {
const selection = Selection.get();
@ -65,6 +65,7 @@ export default class Caret extends Module {
/**
* Workaround case when caret in the text like " |Hello!"
* selection.anchorOffset is 1, but real caret visible position is 0
*
* @type {number}
*/
@ -84,6 +85,7 @@ export default class Caret extends Module {
* So we use child with focusOffset index as new anchorNode.
*/
let focusOffset = selection.focusOffset;
if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
if (focusNode.childNodes[focusOffset]) {
focusNode = focusNode.childNodes[focusOffset];
@ -106,6 +108,7 @@ export default class Caret extends Module {
const nothingAtLeft = leftSiblings.every((node) => {
/**
* Workaround case when block starts with several <br>'s (created by SHIFT+ENTER)
*
* @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
*/
@ -133,7 +136,8 @@ export default class Caret extends Module {
/**
* Get's deepest last node and checks if offset is last node text length
* @return {boolean}
*
* @returns {boolean}
*/
public get isAtEnd(): boolean {
const selection = Selection.get();
@ -161,6 +165,7 @@ export default class Caret extends Module {
* So we use child with anchofocusOffset - 1 as new focusNode.
*/
let focusOffset = selection.focusOffset;
if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
if (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
*
* @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
* @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 {
const {BlockManager} = this.Editor;
public setToBlock(block: Block, position: string = this.positions.DEFAULT, offset = 0): void {
const { BlockManager } = this.Editor;
let element;
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
*/
_.delay( () => {
_.delay(() => {
this.set(nodeToSet as HTMLElement, offset);
}, 20)();
@ -267,12 +272,12 @@ export default class Caret extends Module {
* Set caret to the current input of current Block.
*
* @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
* @param {number} offset - caret offset regarding to the text node
*/
public setToInput(input: HTMLElement, position: string = this.positions.DEFAULT, offset: number = 0): void {
const {currentBlock} = this.Editor.BlockManager;
public setToInput(input: HTMLElement, position: string = this.positions.DEFAULT, offset = 0): void {
const { currentBlock } = this.Editor.BlockManager;
const nodeToSet = $.getDeepestNode(input);
switch (position) {
@ -297,12 +302,13 @@ export default class Caret extends Module {
/**
* Creates Document Range and sets caret to the element with offset
*
* @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(),
selection = Selection.get();
selection = Selection.get();
/** if found deepest node is native input */
if ($.isNativeInput(element)) {
@ -312,6 +318,7 @@ export default class Caret extends Module {
element.focus();
(element as HTMLInputElement).selectionStart = (element as HTMLInputElement).selectionEnd = offset;
return;
}
@ -322,14 +329,19 @@ export default class Caret extends Module {
selection.addRange(range);
/** 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()
: range.getBoundingClientRect();
const {innerHeight} = window;
const { innerHeight } = window;
if (top < 0) { window.scrollBy(0, top); }
if (bottom > innerHeight) { window.scrollBy(0, bottom - innerHeight); }
if (top < 0) {
window.scrollBy(0, top);
}
if (bottom > innerHeight) {
window.scrollBy(0, bottom - innerHeight);
}
}
/**
* Set Caret to the last 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.setStart(selectRange.endContainer, selectRange.endOffset);
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
* 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 {
const {currentBlock, nextContentfulBlock} = this.Editor.BlockManager;
const {nextInput} = currentBlock;
public navigateNext(force = false): boolean {
const { currentBlock, nextContentfulBlock } = this.Editor.BlockManager;
const { nextInput } = currentBlock;
if (!nextContentfulBlock && !nextInput) {
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
* 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 {
const {currentBlock, previousContentfulBlock} = this.Editor.BlockManager;
public navigatePrevious(force = false): boolean {
const { currentBlock, previousContentfulBlock } = this.Editor.BlockManager;
if (!currentBlock) {
return false;
}
const {previousInput} = currentBlock;
const { previousInput } = currentBlock;
if (!previousContentfulBlock && !previousInput) {
return false;
@ -432,10 +445,11 @@ export default class Caret extends Module {
if (force || this.isAtStart) {
/** If previous Tool`s input exists, focus on it. Otherwise set caret to the previous Block */
if (!previousInput) {
this.setToBlock( previousContentfulBlock, this.positions.END );
this.setToBlock(previousContentfulBlock, this.positions.END);
} else {
this.setToInput(previousInput, this.positions.END);
}
return true;
}
@ -444,6 +458,7 @@ export default class Caret extends Module {
/**
* Inserts shadow element after passed element where caret can be placed
*
* @param {Node} element
*/
public createShadow(element): void {
@ -455,6 +470,7 @@ export default class Caret extends Module {
/**
* Restores caret position
*
* @param {HTMLElement} element
*/
public restoreCaret(element: HTMLElement): void {
@ -519,16 +535,17 @@ export default class Caret extends Module {
*
* @example
* <div contenteditable>
* <p></p> |
* <p></p> | left first-level siblings
* <p></p> |
* <blockquote><a><b>adaddad</b><a><blockquote> <-- passed node for example <b>
* <p></p> |
* <p></p> | right first-level siblings
* <p></p> |
* <p></p> |
* <p></p> | left first-level siblings
* <p></p> |
* <blockquote><a><b>adaddad</b><a><blockquote> <-- passed node for example <b>
* <p></p> |
* <p></p> | right first-level siblings
* <p></p> |
* </div>
*
* @return {Element[]}
* @returns {Element[]}
* @param from
* @param direction
*/
private getHigherLevelSiblings(from: HTMLElement, direction?: string): HTMLElement[] {
let current = from;

View file

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

View file

@ -1,8 +1,10 @@
import SelectionUtils from '../selection';
import Module from '../__module';
/**
*
*/
export default class DragNDrop extends Module {
/**
* 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
*
* @private
*/
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, 'dragstart', (dragEvent: DragEvent) => {
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) {
this.isStartedAtEditor = true;
}
@ -54,7 +56,9 @@ export default class DragNDrop extends Module {
dropEvent.preventDefault();
BlockManager.blocks.forEach((block) => block.dropTarget = false);
BlockManager.blocks.forEach((block) => {
block.dropTarget = false;
});
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed && this.isStartedAtEditor) {
document.execCommand('delete');

View file

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

View file

@ -43,9 +43,9 @@ export interface ListenerData {
* @property {Array} allListeners
*/
export default class Listeners extends Module {
/**
* Stores all listeners data to find/remove/process it
*
* @type {ListenerData[]}
*/
private allListeners: ListenerData[] = [];
@ -54,15 +54,15 @@ export default class Listeners extends Module {
* Assigns event listener on element
*
* @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 {Boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
* @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
*/
public on(
element: EventTarget,
eventType: string,
handler: (event: Event) => void,
options: boolean | AddEventListenerOptions = false,
options: boolean | AddEventListenerOptions = false
): void {
const assignedEventData = {
element,
@ -73,7 +73,9 @@ export default class Listeners extends Module {
const alreadyExist = this.findOne(element, eventType, handler);
if (alreadyExist) { return; }
if (alreadyExist) {
return;
}
this.allListeners.push(assignedEventData);
element.addEventListener(eventType, handler, options);
@ -83,15 +85,15 @@ export default class Listeners extends Module {
* Removes event listener from element
*
* @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 {Boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
* @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once}
*/
public off(
element: EventTarget,
eventType: string,
handler?: (event: Event) => void,
options?: boolean | AddEventListenerOptions,
options?: boolean | AddEventListenerOptions
): void {
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);
}
});
}
/**
* @param {EventTarget} element
* @param {String} eventType
* @param {string} eventType
* @param {Function} handler
* @return {ListenerData|null}
* @returns {ListenerData|null}
*/
public findOne(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData {
const foundListeners = this.findAll(element, eventType, handler);
@ -121,18 +122,18 @@ export default class Listeners extends Module {
/**
* @param {EventTarget} element
* @param {String} eventType
* @param {string} eventType
* @param {Function} handler
* @return {ListenerData[]}
* @returns {ListenerData[]}
*/
public findAll(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData[] {
let found;
const foundByEventTargets = element ? this.findByEventTarget(element) : [];
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) {
found = foundByEventTargets.filter( (event) => event.eventType === eventType);
found = foundByEventTargets.filter((event) => event.eventType === eventType);
} else {
found = foundByEventTargets;
}
@ -144,7 +145,7 @@ export default class Listeners extends Module {
* Removes all listeners
*/
public removeAll(): void {
this.allListeners.map( (current) => {
this.allListeners.map((current) => {
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
*
* @param {EventTarget} element - searching 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
* @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[] {
return this.allListeners.filter((listener) => {
@ -179,8 +182,9 @@ export default class Listeners extends Module {
/**
* Search method: looks for listener by passed 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[] {
return this.allListeners.filter((listener) => {

View file

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

View file

@ -5,13 +5,12 @@ import Module from '../__module';
*
* @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
*/
export default class Notifier extends Module {
/**
* Show web notification
*

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
import Module from '../../__module';
import $ from '../../dom';
import Flipper, {FlipperOptions} from '../../flipper';
import Flipper, { FlipperOptions } from '../../flipper';
import * as _ from '../../utils';
/**
@ -15,12 +15,12 @@ import * as _ from '../../utils';
* |________________________|
*/
export default class BlockSettings extends Module {
/**
* 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 {
opened: 'block-settings-opened',
closed: 'block-settings-closed',
@ -29,7 +29,8 @@ export default class BlockSettings extends Module {
/**
* Block Settings CSS
* @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
*
* @returns {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
*/
public get CSS() {
return {
@ -41,13 +42,14 @@ export default class BlockSettings extends Module {
button: 'ce-settings__button',
focusedButton : 'ce-settings__button--focused',
focusedButton: 'ce-settings__button--focused',
focusedButtonAnimated: 'ce-settings__button--focused-animated',
};
}
/**
* Is Block Settings opened or not
*
* @returns {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
*
* @type {Flipper|null}
*/
private flipper: Flipper = null;
@ -79,7 +82,7 @@ export default class BlockSettings extends Module {
* - Tool's Settings
* - Default Settings [Move, Remove, etc]
*
* @return {Element}
* @returns {Element}
*/
public make(): void {
this.nodes.wrapper = $.make('div', this.CSS.wrapper);
@ -145,7 +148,8 @@ export default class BlockSettings extends Module {
/**
* Returns Tools Settings and Default Settings
* @return {HTMLElement[]}
*
* @returns {HTMLElement[]}
*/
public get blockTunesButtons(): HTMLElement[] {
/**
@ -198,7 +202,7 @@ export default class BlockSettings extends Module {
* 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
*/
_.delay( () => {
_.delay(() => {
this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock);
}, 10)();
},

View file

@ -1,8 +1,8 @@
import Module from '../../__module';
import $ from '../../dom';
import {BlockToolConstructable} from '../../../../types';
import { BlockToolConstructable } from '../../../../types';
import * as _ from '../../utils';
import {SavedData} from '../../../types-internal/block-data';
import { SavedData } from '../../../types-internal/block-data';
import Block from '../../block';
import Flipper from '../../flipper';
@ -23,8 +23,8 @@ export default class ConversionToolbar extends Module {
conversionToolHidden: 'ce-conversion-tool--hidden',
conversionToolIcon: 'ce-conversion-tool__icon',
conversionToolFocused : 'ce-conversion-tool--focused',
conversionToolActive : 'ce-conversion-tool--active',
conversionToolFocused: 'ce-conversion-tool--focused',
conversionToolActive: 'ce-conversion-tool--active',
};
}
@ -38,9 +38,10 @@ export default class ConversionToolbar extends Module {
/**
* Conversion Toolbar open/close state
*
* @type {boolean}
*/
public opened: boolean = false;
public opened = false;
/**
* Available tools
@ -49,6 +50,7 @@ export default class ConversionToolbar extends Module {
/**
* Instance of class that responses for leafing buttons by arrows/tab
*
* @type {Flipper|null}
*/
private flipper: Flipper = null;
@ -87,7 +89,8 @@ export default class ConversionToolbar extends Module {
/**
* 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 {
if (!this.opened) {
@ -157,6 +160,7 @@ export default class ConversionToolbar extends Module {
public async replaceWithBlock(replacingToolName: string): Promise <void> {
/**
* At first, we get current Block data
*
* @type {BlockToolConstructable}
*/
const currentBlockClass = this.Editor.BlockManager.currentBlock.class;
@ -175,6 +179,7 @@ export default class ConversionToolbar extends Module {
/**
* Getting a class of replacing Tool
*
* @type {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
*/
let exportData: string = '';
let exportData = '';
const exportProp = currentBlockClass[INTERNAL_SETTINGS.CONVERSION_CONFIG].export;
if (typeof exportProp === 'function') {
@ -196,6 +201,7 @@ export default class ConversionToolbar extends Module {
} else {
_.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.');
return;
}
@ -204,7 +210,7 @@ export default class ConversionToolbar extends Module {
*/
const cleaned: string = this.Editor.Sanitizer.clean(
exportData,
replacingTool.sanitize,
replacingTool.sanitize
);
/**
@ -222,6 +228,7 @@ export default class ConversionToolbar extends Module {
} else {
_.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.');
return;
}
@ -273,6 +280,10 @@ export default class ConversionToolbar extends Module {
/**
* Add tool to the Conversion Toolbar
*
* @param toolName
* @param toolIcon
* @param title
*/
private addTool(toolName: string, toolIcon: string, title: string): void {
const tool = $.make('div', [ ConversionToolbar.CSS.conversionTool ]);

View file

@ -42,7 +42,7 @@ import * as _ from '../../utils';
* @classdesc Toolbar module
*
* @typedef {Toolbar} Toolbar
* @property {Object} nodes
* @property {object} nodes
* @property {Element} nodes.wrapper - Toolbar main element
* @property {Element} nodes.content - Zone with Plus button and toolbox.
* @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
*/
public nodes: {[key: string]: HTMLElement} = {
wrapper : null,
content : null,
actions : null,
wrapper: null,
content: null,
actions: null,
// Content Zone
plusButton : null,
plusButton: null,
// Actions Zone
blockActionsButtons: null,
settingsToggler : null,
settingsToggler: null,
};
/**
* CSS styles
* @return {Object}
*
* @returns {object}
*/
public get CSS() {
return {
@ -104,7 +105,7 @@ export default class Toolbar extends Module {
/**
* Make Content Zone and Actions Zone
*/
['content', 'actions'].forEach( (el) => {
['content', 'actions'].forEach((el) => {
this.nodes[el] = $.make('div', this.CSS[el]);
});
@ -149,7 +150,7 @@ export default class Toolbar extends Module {
* - Settings Panel
*/
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);
$.append(this.nodes.settingsToggler, settingsIcon);
@ -179,9 +180,10 @@ export default class Toolbar extends Module {
/**
* 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) {
/** Close Toolbox when we move toolbar */
this.Editor.Toolbox.close();
@ -222,6 +224,7 @@ export default class Toolbar extends Module {
/**
* Open Toolbar with Plus Button and Actions
*
* @param {boolean} withBlockActions - by default, Toolbar opens with Block Actions.
* This flag allows to open Toolbar without Actions.
* @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
* 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(() => {
this.move(needToCloseToolbox);
this.nodes.wrapper.classList.add(this.CSS.toolbarOpened);
@ -244,7 +247,8 @@ export default class Toolbar extends Module {
/**
* returns toolbar opened state
* @return {Boolean}
*
* @returns {boolean}
*/
public get opened(): boolean {
return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened);
@ -264,9 +268,10 @@ export default class Toolbar extends Module {
/**
* 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 {
hide: () => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
show: () => {
@ -280,14 +285,15 @@ export default class Toolbar extends Module {
/**
* 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 {
hide: () => {
this.nodes.actions.classList.remove(this.CSS.actionsOpened);
},
show : () => {
show: () => {
this.nodes.actions.classList.add(this.CSS.actionsOpened);
},
};
@ -295,6 +301,7 @@ export default class Toolbar extends Module {
/**
* Handler for Plus Button
*
* @param {MouseEvent} event
*/
private plusButtonClicked(): void {

View file

@ -3,7 +3,7 @@ import $ from '../../dom';
import SelectionUtils from '../../selection';
import * as _ from '../../utils';
import {InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings} from '../../../../types';
import { InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings } from '../../../../types';
import Flipper from '../../flipper';
/**
@ -14,7 +14,6 @@ import Flipper from '../../flipper';
* |________________________|
*/
export default class InlineToolbar extends Module {
/**
* CSS styles
*/
@ -37,19 +36,20 @@ export default class InlineToolbar extends Module {
/**
* State of inline toolbar
*
* @type {boolean}
*/
public opened: boolean = false;
public opened = false;
/**
* Inline Toolbar elements
*/
private nodes: {
wrapper: HTMLElement,
buttons: HTMLElement,
conversionToggler: HTMLElement,
conversionTogglerContent: HTMLElement,
actions: HTMLElement,
wrapper: HTMLElement;
buttons: HTMLElement;
conversionToggler: HTMLElement;
conversionTogglerContent: HTMLElement;
actions: HTMLElement;
} = {
wrapper: null,
buttons: null,
@ -74,15 +74,17 @@ export default class InlineToolbar extends Module {
/**
* Buttons List
*
* @type {NodeList}
*/
private buttonsList: NodeList = null;
/**
* Cache for Inline Toolbar width
*
* @type {number}
*/
private width: number = 0;
private width = 0;
/**
* 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
*
* @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.
*/
public tryToShow(needToClose: boolean = false): void {
public tryToShow(needToClose = false): void {
if (!this.allowedToShow()) {
if (needToClose) {
this.close();
}
return;
}
@ -196,11 +200,11 @@ export default class InlineToolbar extends Module {
const wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect();
const newCoords = {
x: selectionRect.x - wrapperOffset.left,
y: selectionRect.y
+ selectionRect.height
y: selectionRect.y +
selectionRect.height -
// + window.scrollY
- wrapperOffset.top
+ this.toolbarVerticalMargin,
wrapperOffset.top +
this.toolbarVerticalMargin,
};
/**
@ -222,12 +226,12 @@ export default class InlineToolbar extends Module {
*/
this.nodes.wrapper.classList.toggle(
this.CSS.inlineToolbarLeftOriented,
realLeftCoord < this.Editor.UI.contentRect.left,
realLeftCoord < this.Editor.UI.contentRect.left
);
this.nodes.wrapper.classList.toggle(
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';
@ -331,7 +335,7 @@ export default class InlineToolbar extends Module {
return false;
}
const target = !$.isElement(currentSelection.anchorNode )
const target = !$.isElement(currentSelection.anchorNode)
? currentSelection.anchorNode.parentElement
: currentSelection.anchorNode;
@ -363,13 +367,14 @@ export default class InlineToolbar extends Module {
*/
private filterTools(): void {
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),
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
*
* @type {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
*/
private setConversionTogglerContent(): void {
const {BlockManager, Tools} = this.Editor;
const { BlockManager, Tools } = this.Editor;
const toolName = BlockManager.currentBlock.name;
/**
@ -478,11 +483,11 @@ export default class InlineToolbar extends Module {
const userToolboxSettings = toolSettings.toolbox || {};
this.nodes.conversionTogglerContent.innerHTML =
userToolboxSettings.icon
|| toolboxSettings.icon
|| userToolboxSettings.title
|| toolboxSettings.title
|| _.capitalize(toolName);
userToolboxSettings.icon ||
toolboxSettings.icon ||
userToolboxSettings.title ||
toolboxSettings.title ||
_.capitalize(toolName);
}
/**
@ -510,6 +515,9 @@ export default class InlineToolbar extends Module {
/**
* Add tool button and activate clicks
*
* @param toolName
* @param tool
*/
private addTool(toolName: string, tool: InlineTool): void {
const {
@ -522,6 +530,7 @@ export default class InlineToolbar extends Module {
if (!button) {
_.log('Render method must return an instance of Node', 'warn', toolName);
return;
}
@ -530,6 +539,7 @@ export default class InlineToolbar extends Module {
if (typeof tool.renderActions === 'function') {
const actions = tool.renderActions();
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];
})
.map(([name]: [string, InlineToolConstructable | ToolSettings]) => name);
.map(([ name ]: [string, InlineToolConstructable | ToolSettings]) => name);
/**
* 1) For internal tools, check public getter 'shortcut'
@ -592,11 +602,11 @@ export default class InlineToolbar extends Module {
placement: 'top',
hidingDelay: 100,
});
}
/**
* Enable Tool shortcut with Editor Shortcuts Module
*
* @param {InlineTool} tool - Tool instance
* @param {string} shortcut - shortcut according to the ShortcutData Module format
*/
@ -604,7 +614,7 @@ export default class InlineToolbar extends Module {
this.Editor.Shortcuts.add({
name: shortcut,
handler: (event) => {
const {currentBlock} = this.Editor.BlockManager;
const { currentBlock } = this.Editor.BlockManager;
/**
* 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:
* 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);
@ -634,6 +644,7 @@ export default class InlineToolbar extends Module {
/**
* Inline Tool button clicks
*
* @param {InlineTool} tool - Tool's instance
*/
private toolClicked(tool: InlineTool): void {

View file

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

View file

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

View file

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

View file

@ -35,22 +35,21 @@ import Flipper from '../flipper';
* @property {Element} nodes.redactor - <ce-redactor>
*/
export default class UI extends Module {
/**
* Editor.js UI CSS class names
* @return {{editorWrapper: string, editorZone: string}}
*/
public get CSS(): {
editorWrapper: string, editorWrapperNarrow: string, editorZone: string, editorZoneHidden: string,
editorLoader: string, editorEmpty: string,
} {
editorWrapper: string; editorWrapperNarrow: string; editorZone: string; editorZoneHidden: string;
editorLoader: string; editorEmpty: string;
} {
return {
editorWrapper : 'codex-editor',
editorWrapperNarrow : 'codex-editor--narrow',
editorZone : 'codex-editor__redactor',
editorZoneHidden : 'codex-editor__redactor--hidden',
editorLoader : 'codex-editor__loader',
editorEmpty : 'codex-editor--empty',
editorWrapper: 'codex-editor',
editorWrapperNarrow: 'codex-editor--narrow',
editorZone: 'codex-editor__redactor',
editorZoneHidden: 'codex-editor__redactor--hidden',
editorLoader: 'codex-editor__loader',
editorEmpty: 'codex-editor--empty',
};
}
@ -85,7 +84,7 @@ export default class UI extends Module {
* Flag that became true on mobile viewport
* @type {boolean}
*/
public isMobile: boolean = false;
public isMobile = false;
/**
* 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
*/
public checkEmptiness(): void {
const {BlockManager} = this.Editor;
const { BlockManager } = this.Editor;
this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, BlockManager.isEditorEmpty);
}
@ -199,9 +198,10 @@ export default class UI extends Module {
public get someFlipperButtonFocused(): boolean {
return Object.entries(this.Editor).filter(([moduleName, moduleClass]) => {
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
*/
this.nodes.wrapper = $.make('div', this.CSS.editorWrapper);
this.nodes.wrapper = $.make('div', this.CSS.editorWrapper);
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.holder.appendChild(this.nodes.wrapper);
}
/**
@ -294,17 +293,17 @@ export default class UI extends Module {
this.nodes.redactor,
'click',
(event) => this.redactorClicked(event as MouseEvent),
false,
false
);
this.Editor.Listeners.on(this.nodes.redactor,
'mousedown',
(event) => this.documentTouched(event as MouseEvent),
true,
true
);
this.Editor.Listeners.on(this.nodes.redactor,
'touchstart',
(event) => this.documentTouched(event as MouseEvent),
true,
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 {
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;
/**
@ -388,10 +387,11 @@ export default class UI extends Module {
* @param {KeyboardEvent} event
*/
private backspacePressed(event: KeyboardEvent): void {
const {BlockManager, BlockSelection, Caret} = this.Editor;
const { BlockManager, BlockSelection, Caret } = this.Editor;
if (BlockSelection.anyBlockSelected) {
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
/** Clear selection */
@ -418,6 +418,7 @@ export default class UI extends Module {
if (BlockSelection.anyBlockSelected) {
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);
/** Clear selection */
@ -431,6 +432,7 @@ export default class UI extends Module {
event.preventDefault();
event.stopImmediatePropagation();
event.stopPropagation();
return;
}
@ -596,6 +598,7 @@ export default class UI extends Module {
const validUrl = _.getValidUrl(href);
_.openTab(validUrl);
return;
}
@ -635,7 +638,6 @@ export default class UI extends Module {
* We need to skip such firings
*/
if (!focusedElement || !focusedElement.closest(`.${Block.CSS.content}`)) {
/**
* 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.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
function (s) {
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
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}
*/
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;
if (!document.documentElement.contains(el)) {

View file

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

View file

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

View file

@ -20,8 +20,8 @@ export enum LogLevels {
declare const VERSION: string;
/**
* @typedef {Object} ChainData
* @property {Object} data - data that will be passed to the success or fallback
* @typedef {object} ChainData
* @property {object} data - data that will be passed to the success or fallback
* @property {Function} function - function's that must be called asynchronically
*/
export interface ChainData {
@ -35,7 +35,8 @@ export interface ChainData {
/**
* Returns basic keycodes as constants
* @return {{}}
*
* @returns {{}}
*/
export const keyCodes = {
BACKSPACE: 8,
@ -73,17 +74,15 @@ export const mouseButtons = {
* @param {string} type - logging type 'log'|'warn'|'error'|'info'
* @param {*} [args] - argument to log with a message
* @param {string} style - additional styling to message
* @param labeled
*/
function _log(
labeled: boolean,
msg: string,
type: string = 'log',
type = 'log',
args?: any,
style: string = 'color: inherit',
style = 'color: inherit'
): void {
if ( !('console' in window) || !window.console[ type ] ) {
if (!('console' in window) || !window.console[type]) {
return;
}
@ -156,7 +155,7 @@ _log.logLevel = LogLevels.VERBOSE;
*
* @param {LogLevels} logLevel - log level to set
*/
export function setLogLevel(logLevel: LogLevels) {
export function setLogLevel(logLevel: LogLevels): void {
_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.
*
* @param {number} keyCode
* @return {boolean}
* @returns {boolean}
*/
export function isPrintableKey( keyCode: number ): boolean {
return (keyCode > 47 && keyCode < 58) || // number keys
keyCode === 32 || keyCode === 13 || // Spacebar & return key(s)
(keyCode > 64 && keyCode < 91) || // letter keys
(keyCode > 95 && keyCode < 112) || // Numpad keys
export function isPrintableKey(keyCode: number): boolean {
return (keyCode > 47 && keyCode < 58) || // number keys
keyCode === 32 || keyCode === 13 || // Spacebar & return key(s)
(keyCode > 64 && keyCode < 91) || // letter keys
(keyCode > 95 && keyCode < 112) || // Numpad keys
(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 {Function} success - success callback
* @param {Function} fallback - callback that fires in case of errors
*
* @return {Promise}
* @returns {Promise}
*/
export async function sequence(
chains: ChainData[],
success: (data: any) => void = () => {},
fallback: (data: any) => void = () => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
success: (data: any) => void = (): void => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
fallback: (data: any) => void = (): void => {},
): Promise<void> {
/**
* Decorator
@ -206,12 +208,12 @@ export async function sequence(
* @param {Function} successCallback
* @param {Function} fallbackCallback
*
* @return {Promise}
* @returns {Promise}
*/
async function waitNextBlock(
chainData: ChainData,
successCallback: (data: any) => void,
fallbackCallback: (data: any) => void,
fallbackCallback: (data: any) => void
): Promise<void> {
try {
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
* a resolved Promise
*/
return await chains.reduce(async (previousValue, currentValue) => {
return chains.reduce(async (previousValue, currentValue) => {
await previousValue;
return waitNextBlock(currentValue, success, fallback);
}, Promise.resolve());
}
@ -239,7 +242,7 @@ export async function sequence(
*
* @param {ArrayLike} collection
*
* @return {Array}
* @returns {Array}
*/
export function array(collection: ArrayLike<any>): any[] {
return Array.prototype.slice.call(collection);
@ -247,8 +250,9 @@ export function array(collection: ArrayLike<any>): any[] {
/**
* Check if passed variable is a function
*
* @param {*} fn
* @return {boolean}
* @returns {boolean}
*/
export function isFunction(fn: any): boolean {
return typeof fn === 'function';
@ -256,8 +260,9 @@ export function isFunction(fn: any): boolean {
/**
* Check if passed function is a class
* @param {function} fn
* @return {boolean}
*
* @param {Function} fn
* @returns {boolean}
*/
export function isClass(fn: any): boolean {
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
*
* @param {Object} object
* @return {boolean}
* @param {object} object
* @returns {boolean}
*/
export function isEmpty(object: object): boolean {
if (!object) {
@ -279,8 +284,9 @@ export function isEmpty(object: object): boolean {
/**
* Check if passed object is a Promise
*
* @param {*} object - object to check
* @return {Boolean}
* @returns {boolean}
*/
export function isPromise(object: any): boolean {
return Promise.resolve(object) === object;
@ -290,12 +296,14 @@ export function isPromise(object: any): boolean {
* Delays method execution
*
* @param {Function} method
* @param {Number} timeout
* @param {number} timeout
*/
export function delay(method: (...args: any[]) => any, timeout: number) {
return function() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this,
args = arguments;
// eslint-disable-next-line prefer-rest-params
args = arguments;
window.setTimeout(() => method.apply(context, args), timeout);
};
@ -305,7 +313,7 @@ export function delay(method: (...args: any[]) => any, timeout: number) {
* Get file extension
*
* @param {File} file
* @return string
* @returns string
*/
export function getFileExtension(file: File): string {
return file.name.split('.').pop();
@ -315,7 +323,7 @@ export function getFileExtension(file: File): string {
* Check if string is MIME type
*
* @param {string} type
* @return boolean
* @returns boolean
*/
export function isValidMimeType(type: string): boolean {
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
*
* @param {Function} func - function that we're throttling
* @param {Number} wait - time in milliseconds
* @param {Boolean} immediate - call now
* @return {Function}
* @param {number} wait - time in milliseconds
* @param {boolean} immediate - call now
* @returns {Function}
*/
export function debounce(func: () => void, wait?: number , immediate?: boolean): () => void {
export function debounce(func: () => void, wait?: number, immediate?: boolean): () => void {
let timeout;
return () => {
return (): void => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
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 = () => {
timeout = null;
if (!immediate) {
@ -358,9 +369,10 @@ export function debounce(func: () => void, wait?: number , immediate?: boolean):
/**
* 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', {
innerHTML: text,
});
@ -369,6 +381,7 @@ export function copyTextToClipboard(text) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNode(el);
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
*
* @return {[key: string]: boolean}
*/
export function getUserOS(): {[key: string]: boolean} {
const OS = {
@ -395,6 +406,7 @@ export function getUserOS(): {[key: string]: boolean} {
if (userOS) {
OS[userOS] = true;
return OS;
}
@ -403,8 +415,9 @@ export function getUserOS(): {[key: string]: boolean} {
/**
* Capitalizes first letter of the string
*
* @param {string} text
* @return {string}
* @returns {string}
*/
export function capitalize(text: string): string {
return text[0].toUpperCase() + text.slice(1);
@ -412,14 +425,17 @@ export function capitalize(text: string): string {
/**
* Merge to objects recursively
*
* @param {object} target
* @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';
if (!sources.length) { return target; }
if (!sources.length) {
return target;
}
const source = sources.shift();
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.
* To detect touch devices more carefully, use 'touchstart' event listener
*
* @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
*
* @return {boolean}
* @returns {boolean}
*/
export const isTouchSupported: boolean = 'ontouchstart' in document.documentElement;
@ -461,6 +478,7 @@ export function typeOf(object: any): string {
/**
* Make shortcut command more human-readable
*
* @param {string} shortcut string like 'CMD+B'
*/
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
* or when url has `two lack` it appends only protocol
*
* @param {String} url
* @param {string} url
*/
export function getValidUrl(url: string): string {
try {
@ -514,7 +532,7 @@ export function getValidUrl(url: string): string {
/**
* 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 {
window.open(url, '_blank');

View file

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

476
yarn.lock
View file

@ -2,7 +2,13 @@
# 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"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
dependencies:
@ -200,6 +206,10 @@
dependencies:
"@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":
version "7.7.4"
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/types" "^7.7.4"
"@babel/highlight@^7.0.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
"@babel/highlight@^7.0.0", "@babel/highlight@^7.8.3":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
dependencies:
"@babel/helper-validator-identifier" "^7.9.0"
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.7.4":
@ -670,6 +680,10 @@
version "1.1.1"
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@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
@ -682,6 +696,10 @@
"@types/minimatch" "*"
"@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@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -753,6 +771,45 @@
"@types/webpack-sources" "*"
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":
version "1.8.5"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@ -893,17 +950,17 @@ abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
acorn-jsx@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384"
acorn-jsx@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
acorn@^6.2.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
acorn@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c"
acorn@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
aggregate-error@^3.0.0:
version "3.0.1"
@ -929,7 +986,7 @@ ajv@^5.0.0:
fast-json-stable-stringify "^2.0.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"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
dependencies:
@ -938,15 +995,24 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2:
json-schema-traverse "^0.4.1"
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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
ansi-escapes@^4.2.1:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d"
version "4.3.1"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
dependencies:
type-fest "^0.8.1"
type-fest "^0.11.0"
ansi-regex@^2.0.0:
version "2.1.1"
@ -1017,6 +1083,14 @@ array-find-index@^1.0.1:
version "1.0.2"
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:
version "1.0.2"
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"
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:
version "1.0.1"
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"
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:
version "1.0.1"
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"
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:
version "1.7.0"
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"
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"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@ -2005,6 +2094,13 @@ dir-glob@^2.2.2:
dependencies:
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:
version "3.0.0"
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:
prr "~1.0.1"
error-ex@^1.3.1:
error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
dependencies:
@ -2173,6 +2269,22 @@ es-abstract@^1.12.0, es-abstract@^1.5.1:
string.prototype.trimleft "^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:
version "1.2.1"
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"
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:
version "3.0.3"
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"
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:
version "4.0.3"
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"
estraverse "^4.1.1"
eslint-utils@^1.4.3:
eslint-utils@^1.4.2, eslint-utils@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
dependencies:
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:
version "1.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
eslint@^6.7.2:
version "6.7.2"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1"
eslint@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.10.0"
@ -2266,11 +2470,11 @@ eslint@^6.7.2:
v8-compile-cache "^2.0.3"
espree@^6.1.2:
version "6.1.2"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d"
version "6.2.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
dependencies:
acorn "^7.1.0"
acorn-jsx "^5.1.0"
acorn "^7.1.1"
acorn-jsx "^5.2.0"
eslint-visitor-keys "^1.1.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"
esquery@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
version "1.2.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe"
dependencies:
estraverse "^4.0.0"
estraverse "^5.0.0"
esrecurse@^4.1.0:
version "4.2.1"
@ -2289,10 +2493,14 @@ esrecurse@^4.1.0:
dependencies:
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"
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:
version "2.0.3"
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"
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:
version "2.2.7"
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"
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
fast-levenshtein@~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"
figures@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec"
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
dependencies:
escape-string-regexp "^1.0.5"
@ -2480,7 +2692,7 @@ find-up@^1.0.0:
path-exists "^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"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
dependencies:
@ -2517,8 +2729,8 @@ flat-cache@^2.0.1:
write "1.0.3"
flatted@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
version "2.0.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
flatten@^1.0.2:
version "1.0.3"
@ -2635,8 +2847,8 @@ glob-parent@^3.1.0:
path-dirname "^1.0.0"
glob-parent@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
dependencies:
is-glob "^4.0.1"
@ -2644,7 +2856,7 @@ glob-to-regexp@^0.3.0:
version "0.3.0"
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"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
dependencies:
@ -2692,8 +2904,8 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
globals@^12.1.0:
version "12.3.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13"
version "12.4.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
dependencies:
type-fest "^0.8.1"
@ -2884,7 +3096,7 @@ ignore@^4.0.3, ignore@^4.0.6:
version "4.0.6"
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"
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"
inquirer@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a"
version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
dependencies:
ansi-escapes "^4.2.1"
chalk "^2.4.2"
chalk "^3.0.0"
cli-cursor "^3.1.0"
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.15"
mute-stream "0.0.8"
run-async "^2.2.0"
rxjs "^6.4.0"
run-async "^2.4.0"
rxjs "^6.5.3"
string-width "^4.1.0"
strip-ansi "^5.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
interpret@1.2.0:
@ -3057,6 +3269,10 @@ is-callable@^1.1.4:
version "1.1.4"
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:
version "1.1.0"
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:
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:
version "2.1.0"
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"
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:
version "3.0.0"
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"
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:
version "2.5.2"
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"
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:
version "4.0.0"
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"
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:
version "1.0.2"
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"
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"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
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:
version "1.0.1"
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-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:
version "4.0.0"
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"
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:
version "3.0.0"
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"
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"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@ -4250,6 +4511,12 @@ pkg-dir@^1.0.0:
dependencies:
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:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@ -5053,6 +5320,13 @@ read-cache@^1.0.0:
dependencies:
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:
version "3.0.0"
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"
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:
version "3.0.0"
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"
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:
version "4.6.0"
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-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:
version "0.5.1"
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:
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:
version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@ -5295,7 +5591,7 @@ rimraf@2.6.3:
dependencies:
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"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
dependencies:
@ -5314,9 +5610,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
run-async@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
run-async@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
dependencies:
is-promise "^2.1.0"
@ -5326,9 +5622,9 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs@^6.4.0:
version "6.5.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
rxjs@^6.5.3:
version "6.5.5"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
dependencies:
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"
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"
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"
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"
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:
version "0.2.2"
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"
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:
version "2.1.0"
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"
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:
version "2.1.0"
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"
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:
version "1.3.0"
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"
strip-json-comments@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
version "3.1.0"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180"
strip-json-comments@~2.0.1:
version "2.0.1"
@ -5998,19 +6328,13 @@ ts-loader@^6.2.1:
micromatch "^4.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"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
tslint-loader@^3.5.4:
version "3.5.4"
resolved "https://registry.yarnpkg.com/tslint-loader/-/tslint-loader-3.5.4.tgz#052af7f0772434451ea1b247bb55407f878a4c40"
dependencies:
loader-utils "^1.0.2"
mkdirp "^0.5.1"
object-assign "^4.1.1"
rimraf "^2.4.4"
semver "^5.3.0"
tslib@^1.9.0:
version "1.11.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
tslint@^5.14.0:
version "5.20.1"
@ -6036,6 +6360,12 @@ tsutils@^2.29.0:
dependencies:
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:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@ -6046,6 +6376,10 @@ type-check@~0.3.2:
dependencies:
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:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"