[Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
George Berezhnoy 2020-04-18 21:55:19 +03:00 committed by GitHub
parent f5e9a6648e
commit 4c0d806a12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 1062 additions and 878 deletions

View file

@ -6,13 +6,40 @@
/**
* 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"]
},
"settings": {
"jsdoc": {
"mode": "typescript"
}
},
"globals": {
"Node": true,
"Range": true,
"HTMLElement": true,
"HTMLDivElement": true,
"Element": true,
"Selection": true,
"SVGElement": true,
"Text": true,
"InsertPosition": true,
"PropertyKey": true,
"MouseEvent": true,
"TouchEvent": true,
"KeyboardEvent": true,
"ClipboardEvent": true,
"DragEvent": true,
"Event": true,
"EventTarget": true,
"Document": true,
"NodeList": true,
"File": true,
"FileList": true,
"MutationRecord": true,
"AddEventListenerOptions": true,
"DataTransfer": true,
"DOMRect": true,
"ClientRect": true,
"ArrayLike": true
}
}

2
dist/editor.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
/*!
* Editor.js
*
* @version 2.17.0
* @version 2.18.0
*
* @licence Apache-2.0
* @author CodeX <https://codex.so>
@ -15,8 +15,9 @@
* Or, sanitizing config can be defined globally in editors initialization. That config will be used everywhere
* At least, if there is no config overrides, that API uses Default configuration
*
* @uses https://www.npmjs.com/package/html-janitor
* @license https://github.com/guardian/html-janitor/blob/master/LICENSE
* @see {@link https://www.npmjs.com/package/html-janitor}
* @license Apache-2.0
* @see {@link https://github.com/guardian/html-janitor/blob/master/LICENSE}
*
* @param {SanitizerConfig} config - sanitizer extension
*/
@ -26,9 +27,9 @@
*
* Short Description (눈_눈;)
*
* @version 2.0
* @version 2.18.0
*
* @licence Apache-2.0
* @license Apache-2.0
* @author CodeX-Team <https://ifmo.su>
*/

View file

@ -2,7 +2,8 @@
### 2.18
- `Improvements` - Deprecated TSLint replaced with ESLint, old config changed to [CodeX ESLint Config](https://github.com/codex-team/eslint-config).
- `Improvements` - TSLint (deprecated) replaced with ESLint, old config changed to [CodeX ESLint Config](https://github.com/codex-team/eslint-config).
- `Improvements` - Fix many code-style issues, add missed annotations.
- `Improvements` - Adjusted GitHub action for ESLint.
### 2.17

View file

@ -93,7 +93,8 @@ body {
max-width: 180px;
background: #4A9DF8;
padding: 17px 30px;
box-shadow: 0 6px 4px -4px rgba(137, 207, 255, 0.77);
box-shadow: 0 22px 18px -4px rgba(137, 207, 255, 0.77);
transition: all 150ms ease;
cursor: pointer;
border-radius: 31px;
color: #fff;
@ -103,6 +104,8 @@ body {
.ce-example__button:hover {
background: #3D8DE5;
transform: translateY(2px);
box-shadow: 0 20px 15px -4px rgba(137, 207, 255, 0.77);
}
.ce-example__output-footer {

View file

@ -1,6 +1,6 @@
{
"name": "@editorjs/editorjs",
"version": "2.17.0",
"version": "2.18.0",
"description": "Editor.js — Native JS, based on API and Open Source",
"main": "dist/editor.js",
"types": "./types/index.d.ts",

View file

@ -17,9 +17,9 @@ declare const VERSION: string;
*
* Short Description (_눈;)
*
* @version 2.0
* @version 2.18.0
*
* @licence Apache-2.0
* @license Apache-2.0
* @author CodeX-Team <https://ifmo.su>
*/
export default class EditorJS {
@ -35,7 +35,7 @@ export default class EditorJS {
public destroy: () => void;
/** Editor version */
static get version(): string {
public static get version(): string {
return VERSION;
}
@ -46,7 +46,8 @@ export default class EditorJS {
/**
* Set default onReady function
*/
let onReady = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
let onReady = (): void => {};
/**
* If `onReady` was passed in `configuration` then redefine onReady function
@ -75,18 +76,18 @@ export default class EditorJS {
/**
* Export external API methods
*
* @param editor
* @param {Core} editor Editor's instance
*/
public exportAPI(editor: Core): void {
const fieldsToExport = [ 'configuration' ];
const destroy = () => {
const destroy = (): void => {
editor.moduleInstances.Listeners.removeAll();
editor.moduleInstances.UI.destroy();
editor.moduleInstances.ModificationsObserver.destroy();
editor = null;
for (const field in this) {
if (this.hasOwnProperty(field)) {
if (Object.prototype.hasOwnProperty.call(this, field)) {
delete this[field];
}
}

View file

@ -28,7 +28,7 @@ export default class Module {
/**
* @class
* @param {EditorConfig}
* @param {EditorConfig} config - Editor's config
*/
constructor({ config }: ModuleConfig) {
if (new.target === Module) {
@ -41,9 +41,9 @@ export default class Module {
/**
* Editor modules setter
*
* @param {EditorModules} Editor
* @param {EditorModules} Editor - Editor's Modules
*/
set state(Editor: EditorModules) {
public set state(Editor: EditorModules) {
this.Editor = Editor;
}
}

View file

@ -14,7 +14,7 @@ export default class DeleteTune implements BlockTune {
/**
* Property that contains Editor.js API methods
*
* @see {docs/api.md}
* @see {@link docs/api.md}
*/
private readonly api: API;
@ -35,7 +35,7 @@ export default class DeleteTune implements BlockTune {
/**
* set false confirmation state
*/
private resetConfirmation: () => void;
private readonly resetConfirmation: () => void;
/**
* Tune nodes
@ -47,12 +47,12 @@ export default class DeleteTune implements BlockTune {
/**
* DeleteTune constructor
*
* @param {{api: API}} api
* @param {API} api - Editor's API
*/
constructor({ api }) {
this.api = api;
this.resetConfirmation = () => {
this.resetConfirmation = (): void => {
this.setConfirmation(false);
};
}
@ -60,9 +60,9 @@ export default class DeleteTune implements BlockTune {
/**
* Create "Delete" button and add click event listener
*
* @returns [Element}
* @returns {HTMLElement}
*/
public render() {
public render(): HTMLElement {
this.nodes.button = $.make('div', [this.CSS.button, this.CSS.buttonDelete], {});
this.nodes.button.appendChild($.svg('cross', 12, 12));
this.api.listeners.on(this.nodes.button, 'click', (event: MouseEvent) => this.handleClick(event), false);
@ -78,7 +78,7 @@ export default class DeleteTune implements BlockTune {
/**
* Delete block conditions passed
*
* @param {MouseEvent} event
* @param {MouseEvent} event - click event
*/
public handleClick(event: MouseEvent): void {
/**
@ -114,9 +114,9 @@ export default class DeleteTune implements BlockTune {
/**
* change tune state
*
* @param state
* @param {boolean} state - delete confirmation state
*/
private setConfirmation(state): void {
private setConfirmation(state: boolean): void {
this.needConfirmation = state;
this.nodes.button.classList.add(this.CSS.buttonConfirm);
}

View file

@ -15,7 +15,7 @@ export default class MoveDownTune implements BlockTune {
/**
* Property that contains Editor.js API methods
*
* @see {api.md}
* @see {@link docs/api.md}
*/
private readonly api: API;
@ -33,7 +33,7 @@ export default class MoveDownTune implements BlockTune {
/**
* MoveDownTune constructor
*
* @param {{api: API}} api
* @param {API} api Editor's API
*/
constructor({ api }) {
this.api = api;
@ -41,8 +41,10 @@ export default class MoveDownTune implements BlockTune {
/**
* Return 'move down' button
*
* @returns {HTMLElement}
*/
public render() {
public render(): HTMLElement {
const moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
moveDownButton.appendChild($.svg('arrow-down', 14, 14));
@ -64,10 +66,10 @@ export default class MoveDownTune implements BlockTune {
/**
* Handle clicks on 'move down' button
*
* @param {MouseEvent} event
* @param {HTMLElement} button
* @param {MouseEvent} event - click event
* @param {HTMLElement} button - clicked button
*/
public handleClick(event: MouseEvent, button: HTMLElement) {
public handleClick(event: MouseEvent, button: HTMLElement): void {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
// If Block is last do nothing

View file

@ -14,7 +14,7 @@ export default class MoveUpTune implements BlockTune {
/**
* Property that contains Editor.js API methods
*
* @see {api.md}
* @see {@link docs/api.md}
*/
private readonly api: API;
@ -32,7 +32,7 @@ export default class MoveUpTune implements BlockTune {
/**
* MoveUpTune constructor
*
* @param {{api: API}} api
* @param {API} api - Editor's API
*/
constructor({ api }) {
this.api = api;
@ -41,7 +41,7 @@ export default class MoveUpTune implements BlockTune {
/**
* Create "MoveUp" button and add click event listener
*
* @returns [HTMLElement}
* @returns {HTMLElement}
*/
public render(): HTMLElement {
const moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
@ -65,8 +65,8 @@ export default class MoveUpTune implements BlockTune {
/**
* Move current block up
*
* @param {MouseEvent} event
* @param {HTMLElement} button
* @param {MouseEvent} event - click event
* @param {HTMLElement} button - clicked button
*/
public handleClick(event: MouseEvent, button: HTMLElement): void {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();

View file

@ -47,10 +47,9 @@ export enum BlockToolAPI {
/**
* @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance
*
* @property tool - Tool instance
* @property html - Returns HTML content of plugin
* @property holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class
* @property pluginsContent - HTML content that returns by Tool's render function
* @property {BlockTool} tool - Tool instance
* @property {HTMLElement} holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class
* @property {HTMLElement} pluginsContent - HTML content that returns by Tool's render function
*/
export default class Block {
/**
@ -58,7 +57,7 @@ export default class Block {
*
* @returns {{wrapper: string, content: string}}
*/
static get CSS() {
public static get CSS(): {[name: string]: string} {
return {
wrapper: 'ce-block',
wrapperStretched: 'ce-block--stretched',
@ -69,259 +68,6 @@ export default class Block {
};
}
/**
* Find and return all editable elements (contenteditables and native inputs) in the Tool HTML
*
* @returns {HTMLElement[]}
*/
get inputs(): HTMLElement[] {
/**
* Return from cache if existed
*/
if (this.cachedInputs.length !== 0) {
return this.cachedInputs;
}
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(', ');
let inputs = _.array(content.querySelectorAll(selector));
/**
* If contenteditable element contains block elements, treat them as inputs.
*/
inputs = inputs.reduce((result, input) => {
if ($.isNativeInput(input) || $.containsOnlyInlineElements(input)) {
return [...result, input];
}
return [...result, ...$.getDeepestBlockElements(input)];
}, []);
/**
* If inputs amount was changed we need to check if input index is bigger then inputs array length
*/
if (this.inputIndex > inputs.length - 1) {
this.inputIndex = inputs.length - 1;
}
/**
* Cache inputs
*/
this.cachedInputs = inputs;
return inputs;
}
/**
* Return current Tool`s input
*
* @returns {HTMLElement}
*/
get currentInput(): HTMLElement | Node {
return this.inputs[this.inputIndex];
}
/**
* Set input index to the passed element
*
* @param {HTMLElement} element
*/
set currentInput(element: HTMLElement | Node) {
const index = this.inputs.findIndex((input) => input === element || input.contains(element));
if (index !== -1) {
this.inputIndex = index;
}
}
/**
* Return first Tool`s input
*
* @returns {HTMLElement}
*/
get firstInput(): HTMLElement {
return this.inputs[0];
}
/**
* Return first Tool`s input
*
* @returns {HTMLElement}
*/
get lastInput(): HTMLElement {
const inputs = this.inputs;
return inputs[inputs.length - 1];
}
/**
* Return next Tool`s input or undefined if it doesn't exist
*
* @returns {HTMLElement}
*/
get nextInput(): HTMLElement {
return this.inputs[this.inputIndex + 1];
}
/**
* Return previous Tool`s input or undefined if it doesn't exist
*
* @returns {HTMLElement}
*/
get previousInput(): HTMLElement {
return this.inputs[this.inputIndex - 1];
}
/**
* Returns Plugins content
*
* @returns {HTMLElement}
*/
get pluginsContent(): HTMLElement {
const blockContentNodes = this.holder.querySelector(`.${Block.CSS.content}`);
if (blockContentNodes && blockContentNodes.childNodes.length) {
/**
* Editors Block content can contain different Nodes from extensions
* We use DOM isExtensionNode to ignore such Nodes and return first Block that does not match filtering list
*/
for (let child = blockContentNodes.childNodes.length - 1; child >= 0; child--) {
const contentNode = blockContentNodes.childNodes[child];
if (!$.isExtensionNode(contentNode)) {
return contentNode as HTMLElement;
}
}
}
return null;
}
/**
* Get Block's JSON data
*
* @returns {object}
*/
get data(): BlockToolData {
return this.save().then((savedObject) => {
if (savedObject && !_.isEmpty(savedObject.data)) {
return savedObject.data;
} else {
return {};
}
});
}
/**
* Returns tool's sanitizer config
*
* @returns {object}
*/
get sanitize(): SanitizerConfig {
return this.tool.sanitize;
}
/**
* is block mergeable
* We plugin have merge function then we call it mergable
*
* @returns {boolean}
*/
get mergeable(): boolean {
return typeof this.tool.merge === 'function';
}
/**
* Check block for emptiness
*
* @returns {boolean}
*/
get isEmpty(): boolean {
const emptyText = $.isEmpty(this.pluginsContent);
const emptyMedia = !this.hasMedia;
return emptyText && emptyMedia;
}
/**
* Check if block has a media content such as images, iframes and other
*
* @returns {boolean}
*/
get hasMedia(): boolean {
/**
* This tags represents media-content
*
* @type {string[]}
*/
const mediaTags = [
'img',
'iframe',
'video',
'audio',
'source',
'input',
'textarea',
'twitterwidget',
];
return !!this.holder.querySelector(mediaTags.join(','));
}
/**
* Set focused state
*
* @param {boolean} state - 'true' to select, 'false' to remove selection
*/
set focused(state: boolean) {
this.holder.classList.toggle(Block.CSS.focused, state);
}
/**
* 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
*/
set selected(state: boolean) {
if (state) {
this.holder.classList.add(Block.CSS.selected);
} else {
this.holder.classList.remove(Block.CSS.selected);
}
}
/**
* Returns True if it is Selected
*
* @returns {boolean}
*/
get selected(): boolean {
return this.holder.classList.contains(Block.CSS.selected);
}
/**
* Set stretched state
*
* @param {boolean} state - 'true' to enable, 'false' to disable stretched statte
*/
set stretched(state: boolean) {
this.holder.classList.toggle(Block.CSS.wrapperStretched, state);
}
/**
* Toggle drop target state
*
* @param {boolean} state
*/
public set dropTarget(state) {
this.holder.classList.toggle(Block.CSS.dropTarget, state);
}
/**
* Block Tool`s name
*/
@ -432,15 +178,268 @@ export default class Block {
this.tunes = this.makeTunes();
}
/**
* Find and return all editable elements (contenteditables and native inputs) in the Tool HTML
*
* @returns {HTMLElement[]}
*/
public get inputs(): HTMLElement[] {
/**
* Return from cache if existed
*/
if (this.cachedInputs.length !== 0) {
return this.cachedInputs;
}
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(', ');
let inputs = _.array(content.querySelectorAll(selector));
/**
* If contenteditable element contains block elements, treat them as inputs.
*/
inputs = inputs.reduce((result, input) => {
if ($.isNativeInput(input) || $.containsOnlyInlineElements(input)) {
return [...result, input];
}
return [...result, ...$.getDeepestBlockElements(input)];
}, []);
/**
* If inputs amount was changed we need to check if input index is bigger then inputs array length
*/
if (this.inputIndex > inputs.length - 1) {
this.inputIndex = inputs.length - 1;
}
/**
* Cache inputs
*/
this.cachedInputs = inputs;
return inputs;
}
/**
* Return current Tool`s input
*
* @returns {HTMLElement}
*/
public get currentInput(): HTMLElement | Node {
return this.inputs[this.inputIndex];
}
/**
* Set input index to the passed element
*
* @param {HTMLElement | Node} element - HTML Element to set as current input
*/
public set currentInput(element: HTMLElement | Node) {
const index = this.inputs.findIndex((input) => input === element || input.contains(element));
if (index !== -1) {
this.inputIndex = index;
}
}
/**
* Return first Tool`s input
*
* @returns {HTMLElement}
*/
public get firstInput(): HTMLElement {
return this.inputs[0];
}
/**
* Return first Tool`s input
*
* @returns {HTMLElement}
*/
public get lastInput(): HTMLElement {
const inputs = this.inputs;
return inputs[inputs.length - 1];
}
/**
* Return next Tool`s input or undefined if it doesn't exist
*
* @returns {HTMLElement}
*/
public get nextInput(): HTMLElement {
return this.inputs[this.inputIndex + 1];
}
/**
* Return previous Tool`s input or undefined if it doesn't exist
*
* @returns {HTMLElement}
*/
public get previousInput(): HTMLElement {
return this.inputs[this.inputIndex - 1];
}
/**
* Returns Plugins content
*
* @returns {HTMLElement}
*/
public get pluginsContent(): HTMLElement {
const blockContentNodes = this.holder.querySelector(`.${Block.CSS.content}`);
if (blockContentNodes && blockContentNodes.childNodes.length) {
/**
* Editors Block content can contain different Nodes from extensions
* We use DOM isExtensionNode to ignore such Nodes and return first Block that does not match filtering list
*/
for (let child = blockContentNodes.childNodes.length - 1; child >= 0; child--) {
const contentNode = blockContentNodes.childNodes[child];
if (!$.isExtensionNode(contentNode)) {
return contentNode as HTMLElement;
}
}
}
return null;
}
/**
* Get Block's JSON data
*
* @returns {object}
*/
public get data(): BlockToolData {
return this.save().then((savedObject) => {
if (savedObject && !_.isEmpty(savedObject.data)) {
return savedObject.data;
} else {
return {};
}
});
}
/**
* Returns tool's sanitizer config
*
* @returns {object}
*/
public get sanitize(): SanitizerConfig {
return this.tool.sanitize;
}
/**
* is block mergeable
* We plugin have merge function then we call it mergable
*
* @returns {boolean}
*/
public mergeable(): boolean {
return typeof this.tool.merge === 'function';
}
/**
* Check block for emptiness
*
* @returns {boolean}
*/
public get isEmpty(): boolean {
const emptyText = $.isEmpty(this.pluginsContent);
const emptyMedia = !this.hasMedia;
return emptyText && emptyMedia;
}
/**
* Check if block has a media content such as images, iframes and other
*
* @returns {boolean}
*/
public get hasMedia(): boolean {
/**
* This tags represents media-content
*
* @type {string[]}
*/
const mediaTags = [
'img',
'iframe',
'video',
'audio',
'source',
'input',
'textarea',
'twitterwidget',
];
return !!this.holder.querySelector(mediaTags.join(','));
}
/**
* Set focused state
*
* @param {boolean} state - 'true' to select, 'false' to remove selection
*/
public set focused(state: boolean) {
this.holder.classList.toggle(Block.CSS.focused, state);
}
/**
* 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
*/
public set selected(state: boolean) {
if (state) {
this.holder.classList.add(Block.CSS.selected);
} else {
this.holder.classList.remove(Block.CSS.selected);
}
}
/**
* Returns True if it is Selected
*
* @returns {boolean}
*/
public get selected(): boolean {
return this.holder.classList.contains(Block.CSS.selected);
}
/**
* Set stretched state
*
* @param {boolean} state - 'true' to enable, 'false' to disable stretched statte
*/
public set stretched(state: boolean) {
this.holder.classList.toggle(Block.CSS.wrapperStretched, state);
}
/**
* Toggle drop target state
*
* @param {boolean} state - 'true' if block is drop target, false otherwise
*/
public set dropTarget(state) {
this.holder.classList.toggle(Block.CSS.dropTarget, state);
}
/**
* Calls Tool's method
*
* 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 - method to call
* @param {object} params - method argument
*/
public call(methodName: string, params?: object) {
public call(methodName: string, params?: object): void {
/**
* call Tool's method with the instance context
*/
@ -457,7 +456,7 @@ export default class Block {
/**
* Call plugins merge method
*
* @param {object} data
* @param {BlockToolData} data - data to merge
*/
public async mergeWith(data: BlockToolData): Promise<void> {
await this.tool.merge(data);
@ -500,7 +499,7 @@ export default class Block {
*
* @description Method returns true|false whether data passed the validation or not
*
* @param {BlockToolData} data
* @param {BlockToolData} data - data to validate
* @returns {Promise<boolean>} valid
*/
public async validate(data: BlockToolData): Promise<boolean> {
@ -574,7 +573,7 @@ export default class Block {
/**
* Is fired when Block will be unselected
*/
public willUnselect() {
public willUnselect(): void {
this.mutationObserver.disconnect();
}

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 } from '../../types/tools';
/**
* @class Blocks
@ -13,6 +13,26 @@ import { MoveEvent, MoveEventDetail } from '../../types/tools';
*
*/
export default class Blocks {
/**
* Array of Block instances in order of addition
*/
public blocks: Block[];
/**
* Editor`s area where to add Block`s HTML
*/
public workingArea: HTMLElement;
/**
* @class
*
* @param {HTMLElement} workingArea editor`s working node
*/
constructor(workingArea: HTMLElement) {
this.blocks = [];
this.workingArea = workingArea;
}
/**
* Get length of Block instances array
*
@ -47,11 +67,11 @@ 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 {PropertyKey} property block index or any Blocks class property key to set
* @param {Block} value value to set
* @returns {boolean}
*/
public static set(instance: Blocks, property: number | string, value: Block | any) {
public static set(instance: Blocks, property: PropertyKey, value: Block | unknown): boolean {
/**
* If property name is not a number (method or other property, access it via reflect
*/
@ -67,7 +87,7 @@ export default class Blocks {
* @example
* blocks[0] = new Block();
*/
instance.insert(+property, value);
instance.insert(+(property as number), value as Block);
return true;
}
@ -76,10 +96,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 {PropertyKey} property Blocks class property key
* @returns {Block|*}
*/
public static get(instance: Blocks, property: any | number) {
public static get(instance: Blocks, property: PropertyKey): Block | unknown {
/**
* If property is not a number, get it via Reflect object
*/
@ -90,33 +110,13 @@ export default class Blocks {
/**
* If property is a number (Block index) return Block by passed index
*/
return instance.get(+property);
}
/**
* Array of Block instances in order of addition
*/
public blocks: Block[];
/**
* Editor`s area where to add Block`s HTML
*/
public workingArea: HTMLElement;
/**
* @class
*
* @param {HTMLElement} workingArea editor`s working node
*/
constructor(workingArea: HTMLElement) {
this.blocks = [];
this.workingArea = workingArea;
return instance.get(+(property as number));
}
/**
* Push new Block to the blocks array and append it to working area
*
* @param {Block} block
* @param {Block} block - Block to add
*/
public push(block: Block): void {
this.blocks.push(block);
@ -228,7 +228,7 @@ export default class Blocks {
/**
* Remove block
*
* @param {number|null} index
* @param {number} index - index of Block to remove
*/
public remove(index: number): void {
if (isNaN(index)) {
@ -258,7 +258,7 @@ export default class Blocks {
*
* @todo decide if this method is necessary
*
* @param {Block} targetBlock target after wich Block should be inserted
* @param {Block} targetBlock target after which Block should be inserted
* @param {Block} newBlock Block to insert
*/
public insertAfter(targetBlock: Block, newBlock: Block): void {
@ -280,7 +280,7 @@ export default class Blocks {
/**
* Return index of passed Block
*
* @param {Block} block
* @param {Block} block - Block to find
* @returns {number}
*/
public indexOf(block: Block): number {
@ -307,13 +307,12 @@ export default class Blocks {
/**
* Composes Block event with passed type and details
*
* @param {string} type
* @param {MoveEventDetail} detail
* @param {string} type - event type
* @param {object} detail - event detail
*/
private composeBlockEvent(type: string, detail: MoveEventDetail): MoveEvent {
private composeBlockEvent(type: string, detail: object): MoveEvent {
return new CustomEvent(type, {
detail,
}
) as MoveEvent;
}) as MoveEvent;
}
}

View file

@ -1,8 +1,10 @@
import $ from './dom';
// eslint-disable-next-line import/no-duplicates
import * as _ from './utils';
// eslint-disable-next-line import/no-duplicates
import { LogLevels } from './utils';
import { EditorConfig, OutputData, SanitizerConfig } from '../../types';
import { EditorModules } from '../types-internal/editor-modules';
import { LogLevels } from './utils';
/**
* @typedef {Core} Core - editor core class
@ -31,8 +33,8 @@ contextRequire.keys().forEach((filename) => {
*
* @classdesc Editor.js core class
*
* @property this.config - all settings
* @property this.moduleInstances - constructed editor components
* @property {EditorConfig} config - all settings
* @property {EditorModules} moduleInstances - constructed editor components
*
* @type {Core}
*/
@ -110,9 +112,9 @@ export default class Core {
/**
* Setting for configuration
*
* @param {EditorConfig|string|undefined} config
* @param {EditorConfig|string} config - Editor's config to set
*/
set configuration(config: EditorConfig|string) {
public set configuration(config: EditorConfig|string) {
/**
* Process zero-configuration or with only holderId
* Make config object
@ -185,8 +187,10 @@ export default class Core {
this.config.hideToolbar = this.config.hideToolbar ? this.config.hideToolbar : false;
this.config.tools = this.config.tools || {};
this.config.data = this.config.data || {} as OutputData;
this.config.onReady = this.config.onReady || (() => {});
this.config.onChange = this.config.onChange || (() => {});
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.config.onReady = this.config.onReady || ((): void => {});
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.config.onChange = this.config.onChange || ((): void => {});
/**
* Initialize Blocks to pass data to the Renderer
@ -206,7 +210,7 @@ export default class Core {
*
* @returns {EditorConfig}
*/
get configuration(): EditorConfig|string {
public get configuration(): EditorConfig|string {
return this.config;
}
@ -239,7 +243,7 @@ export default class Core {
* - make and save instances
* - configure
*/
public init() {
public init(): void {
/**
* Make modules instances and save it to the @property this.moduleInstances
*/
@ -256,9 +260,9 @@ export default class Core {
*
* Get list of modules that needs to be prepared and return a sequence (Promise)
*
* @returns {Promise}
* @returns {Promise<void>}
*/
public async start() {
public async start(): Promise<void> {
const modulesToPrepare = [
'Tools',
'UI',
@ -327,7 +331,7 @@ export default class Core {
*/
private configureModules(): void {
for (const name in this.moduleInstances) {
if (this.moduleInstances.hasOwnProperty(name)) {
if (Object.prototype.hasOwnProperty.call(this.moduleInstances, name)) {
/**
* Module does not need self-instance
*/

View file

@ -5,7 +5,7 @@ export default class Dom {
/**
* Check if passed tag has no closed tag
*
* @param {HTMLElement} tag
* @param {HTMLElement} tag - element to check
* @returns {boolean}
*/
public static isSingleTag(tag: HTMLElement): boolean {
@ -32,10 +32,10 @@ export default class Dom {
/**
* Check if element is BR or WBR
*
* @param {HTMLElement} element
* @param {HTMLElement} element - element to check
* @returns {boolean}
*/
public static isLineBreakTag(element: HTMLElement) {
public static isLineBreakTag(element: HTMLElement): element is HTMLBRElement {
return element && element.tagName && [
'BR',
'WBR',
@ -45,9 +45,10 @@ 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
* @param {string} tagName - new Element tag name
* @param {string[]|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 {
@ -60,7 +61,7 @@ export default class Dom {
}
for (const attrName in attributes) {
if (attributes.hasOwnProperty(attrName)) {
if (Object.prototype.hasOwnProperty.call(attributes, attrName)) {
el[attrName] = attributes[attrName];
}
}
@ -72,6 +73,7 @@ export default class Dom {
* Creates Text Node with the passed content
*
* @param {string} content - text content
*
* @returns {Text}
*/
public static text(content: string): Text {
@ -82,8 +84,9 @@ 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
* @param {number} [width] - icon width
* @param {number} [height] - icon height
*
* @returns {SVGElement}
*/
public static svg(name: string, width = 14, height = 14): SVGElement {
@ -100,8 +103,8 @@ export default class Dom {
/**
* Append one or several elements to the parent
*
* @param {Element|DocumentFragment} parent - where to append
* @param {Element|Element[]|Text|Text[]} elements - element or elements list
* @param {Element|DocumentFragment} parent - where to append
* @param {Element|Element[]|DocumentFragment|Text|Text[]} elements - element or elements list
*/
public static append(
parent: Element|DocumentFragment,
@ -170,10 +173,10 @@ export default class Dom {
/**
* Get Element by Id
*
* @param {string} id
* @param {string} id - id to find
* @returns {HTMLElement | null}
*/
public static get(id: string): HTMLElement {
public static get(id: string): HTMLElement | null {
return document.getElementById(id);
}
@ -182,8 +185,9 @@ export default class Dom {
*
* Returns all matches
*
* @param {Element} el - element we searching inside. Default - DOM Document
* @param {Element|Document} el - element we searching inside. Default - DOM Document
* @param {string} selector - searching string
*
* @returns {NodeList}
*/
public static findAll(el: Element|Document = document, selector: string): NodeList {
@ -198,7 +202,8 @@ 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
* @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 = false): Node {
@ -250,9 +255,11 @@ export default class Dom {
/**
* Check if object is DOM node
*
* @param {object} node
* @param {*} node - object to check
*
* @returns {boolean}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static isElement(node: any): node is Element {
return node && typeof node === 'object' && node.nodeType && node.nodeType === Node.ELEMENT_NODE;
}
@ -260,17 +267,19 @@ export default class Dom {
/**
* Check if object is DocumentFragmemt node
*
* @param {object} node
* @param {object} node - object to check
* @returns {boolean}
*/
public static isFragment(node: any): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static isFragment(node: any): node is DocumentFragment {
return node && typeof node === 'object' && node.nodeType && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
}
/**
* Check if passed element is contenteditable
*
* @param {HTMLElement} element
* @param {HTMLElement} element - html element to check
*
* @returns {boolean}
*/
public static isContentEditable(element: HTMLElement): boolean {
@ -280,10 +289,12 @@ export default class Dom {
/**
* Checks target if it is native input
*
* @param {Element|string|Node} target - HTML element or string
* @param {*} target - HTML element or string
*
* @returns {boolean}
*/
public static isNativeInput(target: any): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public static isNativeInput(target: any): target is HTMLInputElement | HTMLTextAreaElement {
const nativeInputs = [
'INPUT',
'TEXTAREA',
@ -295,16 +306,15 @@ export default class Dom {
/**
* Checks if we can set caret
*
* @param {HTMLElement} target
* @param {HTMLElement} target - target to check
*
* @returns {boolean}
*/
public static canSetCaret(target: HTMLElement): boolean {
let result = true;
if (Dom.isNativeInput(target)) {
const inputElement = target as HTMLInputElement;
switch (inputElement.type) {
switch (target.type) {
case 'file':
case 'checkbox':
case 'radio':
@ -329,7 +339,8 @@ export default class Dom {
* @description Method checks simple Node without any childs for emptiness
* If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method
*
* @param {Node} node
* @param {Node} node - node to check
*
* @returns {boolean} true if it is empty
*/
public static isNodeEmpty(node: Node): boolean {
@ -351,7 +362,8 @@ export default class Dom {
/**
* checks node if it is doesn't have any child nodes
*
* @param {Node} node
* @param {Node} node - node to check
*
* @returns {boolean}
*/
public static isLeaf(node: Node): boolean {
@ -368,7 +380,7 @@ export default class Dom {
*
* @description Pushes to stack all DOM leafs and checks for emptiness
*
* @param {Node} node
* @param {Node} node - node to check
* @returns {boolean}
*/
public static isEmpty(node: Node): boolean {
@ -414,7 +426,7 @@ export default class Dom {
}
/**
* If one of childs is not empty, checked Node is not empty too
* If one of child is not empty, checked Node is not empty too
*/
if (node && !this.isNodeEmpty(node)) {
return false;
@ -427,8 +439,9 @@ export default class Dom {
/**
* Check if string contains html elements
*
* @param {string} str - string to check
*
* @returns {boolean}
* @param {string} str
*/
public static isHTMLString(str: string): boolean {
const wrapper = Dom.make('div');
@ -441,7 +454,8 @@ export default class Dom {
/**
* Return length of node`s text content
*
* @param {Node} node
* @param {Node} node - node with content
*
* @returns {number}
*/
public static getContentLength(node: Node): number {
@ -461,7 +475,7 @@ export default class Dom {
*
* @returns {string[]}
*/
static get blockElements(): string[] {
public static get blockElements(): string[] {
return [
'address',
'article',
@ -507,6 +521,7 @@ export default class Dom {
* Check if passed content includes only inline elements
*
* @param {string|HTMLElement} data - element or html string
*
* @returns {boolean}
*/
public static containsOnlyInlineElements(data: string | HTMLElement): boolean {
@ -519,7 +534,7 @@ export default class Dom {
wrapper = data;
}
const check = (element: HTMLElement) => {
const check = (element: HTMLElement): boolean => {
return !Dom.blockElements.includes(element.tagName.toLowerCase()) &&
Array.from(element.children).every(check);
};
@ -530,7 +545,7 @@ export default class Dom {
/**
* Find and return all block elements in the passed parent (including subtree)
*
* @param {HTMLElement} parent
* @param {HTMLElement} parent - root element
*
* @returns {HTMLElement[]}
*/
@ -547,7 +562,9 @@ export default class Dom {
/**
* Helper for get holder from {string} or return HTMLElement
*
* @param element
* @param {string | HTMLElement} element - holder's id or holder's HTML Element
*
* @returns {HTMLElement}
*/
public static getHolder(element: string | HTMLElement): HTMLElement {
if (typeof element === 'string') {
@ -561,6 +578,8 @@ export default class Dom {
* Method checks passed Node if it is some extension Node
*
* @param {Node} node - any node
*
* @returns {boolean}
*/
public static isExtensionNode(node: Node): boolean {
const extensions = [
@ -573,9 +592,11 @@ export default class Dom {
/**
* Returns true if element is anchor (is A tag)
*
* @param element
* @param {Element} element - element to check
*
* @returns {boolean}
*/
public static isAnchor(element: Element): boolean {
public static isAnchor(element: Element): element is HTMLAnchorElement {
return element.tagName.toLowerCase() === 'a';
}
}

View file

@ -61,7 +61,7 @@ export default class DomIterator {
/**
* Sets items. Can be used when iterable items changed dynamically
*
* @param {HTMLElement[]} nodeList
* @param {HTMLElement[]} nodeList - nodes to iterate
*/
public setItems(nodeList: HTMLElement[]): void {
this.items = nodeList;

View file

@ -3,6 +3,8 @@ import * as _ from './utils';
/**
* Flipper construction options
*
* @interface FlipperOptions
*/
export interface FlipperOptions {
/**
@ -66,7 +68,6 @@ export default class Flipper {
* @class
*
* @param {FlipperOptions} options - different constructing settings
* @
*/
constructor(options: FlipperOptions) {
this.allowArrows = typeof options.allowArrows === 'boolean' ? options.allowArrows : true;
@ -115,7 +116,7 @@ export default class Flipper {
/**
* Array of keys (codes) that is handled by Flipper
* Used to:
* - preventDefault only for this keys, not all keywdowns (@see constructor)
* - preventDefault only for this keys, not all keydowns (@see constructor)
* - to skip external behaviours only for these keys, when filler is activated (@see BlockEvents@arrowRightAndDown)
*/
public static get usedKeys(): number[] {
@ -180,7 +181,7 @@ 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
* @param {KeyboardEvent} event - keydown keyboard event
* @returns {boolean}
*/
private isEventReadyForHandling(event: KeyboardEvent): boolean {
@ -208,7 +209,7 @@ export default class Flipper {
/**
* When flipper is activated tab press will leaf the items
*
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - tab keydown event
*/
private handleTabPress(event: KeyboardEvent): void {
/** this property defines leaf direction */
@ -242,7 +243,7 @@ export default class Flipper {
/**
* Enter press will click current item if flipper is activated
*
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - enter keydown event
*/
private handleEnterPress(event: KeyboardEvent): void {
if (!this.activated) {

View file

@ -1,5 +1,5 @@
import $ from '../dom';
import { API, InlineTool, SanitizerConfig } from '../../../types';
import { InlineTool, SanitizerConfig } from '../../../types';
/**
* Bold Tool
@ -27,7 +27,7 @@ export default class BoldInlineTool implements InlineTool {
*
* @returns {object}
*/
static get sanitize(): SanitizerConfig {
public static get sanitize(): SanitizerConfig {
return {
b: {},
} as SanitizerConfig;
@ -69,7 +69,7 @@ export default class BoldInlineTool implements InlineTool {
/**
* Wrap range with <b> tag
*
* @param {Range} range
* @param {Range} range - range to wrap
*/
public surround(range: Range): void {
document.execCommand(this.commandName);
@ -78,7 +78,9 @@ export default class BoldInlineTool implements InlineTool {
/**
* Check selection and set activated state to button if there are <b> tag
*
* @param {Selection} selection
* @param {Selection} selection - selection to check
*
* @returns {boolean}
*/
public checkState(selection: Selection): boolean {
const isActive = document.queryCommandState(this.commandName);
@ -90,6 +92,8 @@ export default class BoldInlineTool implements InlineTool {
/**
* Set a shortcut
*
* @returns {boolean}
*/
public get shortcut(): string {
return 'CMD+B';

View file

@ -27,7 +27,7 @@ export default class ItalicInlineTool implements InlineTool {
*
* @returns {object}
*/
static get sanitize(): SanitizerConfig {
public static get sanitize(): SanitizerConfig {
return {
i: {},
} as SanitizerConfig;
@ -69,7 +69,7 @@ export default class ItalicInlineTool implements InlineTool {
/**
* Wrap range with <i> tag
*
* @param {Range} range
* @param {Range} range - range to wrap
*/
public surround(range: Range): void {
document.execCommand(this.commandName);
@ -78,7 +78,7 @@ export default class ItalicInlineTool implements InlineTool {
/**
* Check selection and set activated state to button if there are <i> tag
*
* @param {Selection} selection
* @param {Selection} selection - selection to check
*/
public checkState(selection: Selection): boolean {
const isActive = document.queryCommandState(this.commandName);

View file

@ -31,7 +31,7 @@ export default class LinkInlineTool implements InlineTool {
*
* @returns {object}
*/
static get sanitize(): SanitizerConfig {
public static get sanitize(): SanitizerConfig {
return {
a: {
href: true,
@ -101,7 +101,7 @@ export default class LinkInlineTool implements InlineTool {
private notifier: Notifier;
/**
* @param {{api: API}} - Editor.js API
* @param {API} api - Editor.js API
*/
constructor({ api }) {
this.toolbar = api.toolbar;
@ -142,7 +142,7 @@ export default class LinkInlineTool implements InlineTool {
/**
* Handle clicks on the Inline Toolbar icon
*
* @param {Range} range
* @param {Range} range - range to wrap with link
*/
public surround(range: Range): void {
/**
@ -182,7 +182,7 @@ export default class LinkInlineTool implements InlineTool {
/**
* Check selection and set activated state to button if there are <a> tag
*
* @param {Selection} selection
* @param {Selection} selection - selection to check
*/
public checkState(selection?: Selection): boolean {
const anchorTag = this.selection.findParentTag('A');
@ -275,7 +275,7 @@ export default class LinkInlineTool implements InlineTool {
/**
* Enter pressed on input
*
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - enter keydown event
*/
private enterPressed(event: KeyboardEvent): void {
let value = this.nodes.input.value || '';
@ -318,7 +318,7 @@ export default class LinkInlineTool implements InlineTool {
/**
* Detects if passed string is URL
*
* @param {string} str
* @param {string} str - string to validate
* @returns {boolean}
*/
private validateURL(str: string): boolean {
@ -345,7 +345,7 @@ export default class LinkInlineTool implements InlineTool {
/**
* Add 'http' protocol to the links like 'vc.ru', 'google.com'
*
* @param {string} link
* @param {string} link - string to process
*/
private addProtocol(link: string): string {
/**

View file

@ -14,19 +14,19 @@ export default class BlocksAPI extends Module {
*
* @returns {Blocks}
*/
get methods(): Blocks {
public get methods(): Blocks {
return {
clear: () => this.clear(),
render: (data: OutputData) => this.render(data),
renderFromHTML: (data: string) => this.renderFromHTML(data),
delete: () => this.delete(),
swap: (fromIndex: number, toIndex: number) => this.swap(fromIndex, toIndex),
move: (toIndex: number, fromIndex?: number) => this.move(toIndex, fromIndex),
getBlockByIndex: (index: number) => this.getBlockByIndex(index),
getCurrentBlockIndex: () => this.getCurrentBlockIndex(),
getBlocksCount: () => this.getBlocksCount(),
stretchBlock: (index: number, status = true) => this.stretchBlock(index, status),
insertNewBlock: () => this.insertNewBlock(),
clear: (): void => this.clear(),
render: (data: OutputData): Promise<void> => this.render(data),
renderFromHTML: (data: string): Promise<void> => this.renderFromHTML(data),
delete: (): void => this.delete(),
swap: (fromIndex: number, toIndex: number): void => this.swap(fromIndex, toIndex),
move: (toIndex: number, fromIndex?: number): void => this.move(toIndex, fromIndex),
getBlockByIndex: (index: number): HTMLElement => this.getBlockByIndex(index),
getCurrentBlockIndex: (): number => this.getCurrentBlockIndex(),
getBlocksCount: (): number => this.getBlocksCount(),
stretchBlock: (index: number, status = true): void => this.stretchBlock(index, status),
insertNewBlock: (): void => this.insertNewBlock(),
insert: this.insert,
};
}
@ -52,7 +52,7 @@ export default class BlocksAPI extends Module {
/**
* Returns Block holder by Block index
*
* @param {number} index
* @param {number} index - index to get
*
* @returns {HTMLElement}
*/
@ -82,8 +82,8 @@ export default class BlocksAPI extends Module {
/**
* Move block from one index to another
*
* @param {number} toIndex
* @param {number} fromIndex
* @param {number} toIndex - index to move to
* @param {number} fromIndex - index to move from
*/
public move(toIndex: number, fromIndex?: number): void {
this.Editor.BlockManager.move(toIndex, fromIndex);
@ -98,7 +98,7 @@ export default class BlocksAPI extends Module {
/**
* Deletes Block
*
* @param blockIndex
* @param {number} blockIndex - index of Block to delete
*/
public delete(blockIndex?: number): void {
this.Editor.BlockManager.removeBlock(blockIndex);
@ -141,7 +141,7 @@ export default class BlocksAPI extends Module {
/**
* Render passed HTML string
*
* @param {string} data
* @param {string} data - HTML string to render
* @returns {Promise<void>}
*/
public renderFromHTML(data: string): Promise<void> {
@ -153,7 +153,7 @@ export default class BlocksAPI extends Module {
/**
* Stretch Block's content
*
* @param {number} index
* @param {number} index - index of Block to stretch
* @param {boolean} status - true to enable, false to disable
*/
public stretchBlock(index: number, status = true): void {
@ -189,13 +189,13 @@ export default class BlocksAPI extends Module {
index,
needToFocus
);
}
};
/**
* Insert new Block
* After set caret to this Block
*
* @todo: remove in 3.0.0
* @todo remove in 3.0.0
*
* @deprecated with insert() method
*/

View file

@ -11,7 +11,7 @@ export default class CaretAPI extends Module {
*
* @returns {Caret}
*/
get methods(): Caret {
public get methods(): Caret {
return {
setToFirstBlock: this.setToFirstBlock,
setToLastBlock: this.setToLastBlock,
@ -127,7 +127,7 @@ export default class CaretAPI extends Module {
*
* @returns {boolean}
*/
private focus = (atEnd = false) => {
private focus = (atEnd = false): boolean => {
if (atEnd) {
return this.setToLastBlock(this.Editor.Caret.positions.END);
}

View file

@ -11,19 +11,19 @@ export default class EventsAPI extends Module {
*
* @returns {Events}
*/
get methods(): Events {
public get methods(): Events {
return {
emit: (eventName: string, data: object) => this.emit(eventName, data),
off: (eventName: string, callback: () => void) => this.off(eventName, callback),
on: (eventName: string, callback: () => void) => this.on(eventName, callback),
emit: (eventName: string, data: object): void => this.emit(eventName, data),
off: (eventName: string, callback: () => void): void => this.off(eventName, callback),
on: (eventName: string, callback: () => void): void => this.on(eventName, callback),
};
}
/**
* Subscribe on Events
*
* @param {string} eventName
* @param {Function} callback
* @param {string} eventName - event name to subscribe
* @param {Function} callback - event handler
*/
public on(eventName, callback): void {
this.Editor.Events.on(eventName, callback);
@ -32,8 +32,8 @@ export default class EventsAPI extends Module {
/**
* Emit event with data
*
* @param {string} eventName
* @param {object} data
* @param {string} eventName - event to emit
* @param {object} data - event's data
*/
public emit(eventName, data): void {
this.Editor.Events.emit(eventName, data);
@ -42,8 +42,8 @@ export default class EventsAPI extends Module {
/**
* Unsubscribe from Event
*
* @param {string} eventName
* @param {Function} callback
* @param {string} eventName - event to unsubscribe
* @param {Function} callback - event handler
*/
public off(eventName, callback): void {
this.Editor.Events.off(eventName, callback);

View file

@ -11,10 +11,10 @@ export default class InlineToolbarAPI extends Module {
*
* @returns {InlineToolbar}
*/
get methods(): InlineToolbar {
public get methods(): InlineToolbar {
return {
close: () => this.close(),
open: () => this.open(),
close: (): void => this.close(),
open: (): void => this.open(),
};
}

View file

@ -11,20 +11,20 @@ export default class ListenersAPI extends Module {
*
* @returns {Listeners}
*/
get methods(): Listeners {
public get methods(): Listeners {
return {
on: (element: HTMLElement, eventType, handler, useCapture) => this.on(element, eventType, handler, useCapture),
off: (element, eventType, handler) => this.off(element, eventType, handler),
on: (element: HTMLElement, eventType, handler, useCapture): void => this.on(element, eventType, handler, useCapture),
off: (element, eventType, handler, useCapture): void => this.off(element, eventType, handler, useCapture),
};
}
/**
* adds DOM event listener
*
* @param {HTMLElement} element
* @param {string} eventType
* @param {() => void} handler
* @param {boolean} useCapture
* @param {HTMLElement} element - Element to set handler to
* @param {string} eventType - event type
* @param {() => void} handler - event handler
* @param {boolean} useCapture - capture event or not
*/
public on(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void {
this.Editor.Listeners.on(element, eventType, handler, useCapture);
@ -33,11 +33,12 @@ export default class ListenersAPI extends Module {
/**
* Removes DOM listener from element
*
* @param element
* @param eventType
* @param handler
* @param {Element} element - Element to remove handler from
* @param eventType - event type
* @param handler - event handler
* @param {boolean} useCapture - capture event or not
*/
public off(element, eventType, handler): void {
this.Editor.Listeners.off(element, eventType, handler);
public off(element: Element, eventType: string, handler: () => void, useCapture?: boolean): void {
this.Editor.Listeners.off(element, eventType, handler, useCapture);
}
}

View file

@ -9,15 +9,16 @@ export default class NotifierAPI extends Module {
/**
* Available methods
*/
get methods(): Notifier {
public get methods(): Notifier {
return {
show: (options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions) => this.show(options),
show: (options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions): void => this.show(options),
};
}
/**
* Show notification
* @param options
*
* @param {NotifierOptions} options - message option
*/
public show(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions): void {
return this.Editor.Notifier.show(options);

View file

@ -1,5 +1,6 @@
import Module from '../../__module';
import { Sanitizer } from '../../../../types/api';
import { SanitizerConfig } from '../../../../types/configs';
/**
* @class SanitizerAPI
@ -11,18 +12,21 @@ export default class SanitizerAPI extends Module {
*
* @returns {Sanitizer}
*/
get methods(): Sanitizer {
public get methods(): Sanitizer {
return {
clean: (taintString, config) => this.clean(taintString, config),
clean: (taintString, config): string => this.clean(taintString, config),
};
}
/**
* Perform sanitizing of a string
* @param taintString - what to sanitize
* @param config - sanitizer config
*
* @param {string} taintString - what to sanitize
* @param {SanitizerConfig} config - sanitizer config
*
* @returns {string}
*/
public clean(taintString, config) {
public clean(taintString: string, config: SanitizerConfig): string {
return this.Editor.Sanitizer.clean(taintString, config);
}
}

View file

@ -12,9 +12,9 @@ export default class SaverAPI extends Module {
*
* @returns {Saver}
*/
get methods(): Saver {
public get methods(): Saver {
return {
save: () => this.save(),
save: (): Promise<OutputData> => this.save(),
};
}

View file

@ -14,8 +14,8 @@ export default class SelectionAPI extends Module {
*/
public get methods(): SelectionAPIInterface {
return {
findParentTag: (tagName: string, className?: string) => this.findParentTag(tagName, className),
expandToTag: (node: HTMLElement) => this.expandToTag(node),
findParentTag: (tagName: string, className?: string): HTMLElement | null => this.findParentTag(tagName, className),
expandToTag: (node: HTMLElement): void => this.expandToTag(node),
};
}
@ -24,9 +24,10 @@ export default class SelectionAPI extends Module {
*
* @param {string} tagName - tag to find
* @param {string} className - tag's class name
*
* @returns {HTMLElement|null}
*/
public findParentTag(tagName: string, className?: string): HTMLElement|null {
public findParentTag(tagName: string, className?: string): HTMLElement | null {
return new SelectionUtils().findParentTag(tagName, className);
}

View file

@ -11,10 +11,10 @@ export default class ToolbarAPI extends Module {
*
* @returns {Toolbar}
*/
get methods(): Toolbar {
public get methods(): Toolbar {
return {
close: () => this.close(),
open: () => this.open(),
close: (): void => this.close(),
open: (): void => this.open(),
};
}

View file

@ -10,46 +10,46 @@ export default class TooltipAPI extends Module {
/**
* Available methods
*/
get methods(): Tooltip {
public get methods(): Tooltip {
return {
show: (element: HTMLElement,
content: TooltipContent,
options?: TooltipOptions
) => this.show(element, content, options),
hide: () => this.hide(),
): void => this.show(element, content, options),
hide: (): void => this.hide(),
onHover: (element: HTMLElement,
content: TooltipContent,
options?: TooltipOptions
) => this.onHover(element, content, options),
): void => this.onHover(element, content, options),
};
}
/**
* Method show tooltip on element with passed HTML content
*
* @param {HTMLElement} element
* @param {TooltipContent} content
* @param {TooltipOptions} options
* @param {HTMLElement} element - element on which tooltip should be shown
* @param {TooltipContent} content - tooltip content
* @param {TooltipOptions} options - tooltip options
*/
public show(element: HTMLElement, content: TooltipContent, options?: TooltipOptions) {
public show(element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void {
this.Editor.Tooltip.show(element, content, options);
}
/**
* Method hides tooltip on HTML page
*/
public hide() {
public hide(): void {
this.Editor.Tooltip.hide();
}
/**
* Decorator for showing Tooltip by mouseenter/mouseleave
*
* @param {HTMLElement} element
* @param {TooltipContent} content
* @param {TooltipOptions} options
* @param {HTMLElement} element - element on which tooltip should be shown
* @param {TooltipContent} content - tooltip content
* @param {TooltipOptions} options - tooltip options
*/
public onHover(element: HTMLElement, content: TooltipContent, options?: TooltipOptions) {
public onHover(element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void {
this.Editor.Tooltip.onHover(element, content, options);
}
}

View file

@ -95,7 +95,7 @@ export default class BlockEvents extends Module {
* - shows Inline Toolbar if something selected
* - shows conversion toolbar with 85% of block selection
*
* @param event
* @param {KeyboardEvent} event - keyup event
*/
public keyup(event): void {
/**
@ -114,7 +114,7 @@ export default class BlockEvents extends Module {
/**
* Set up mouse selection handlers
*
* @param {MouseEvent} event
* @param {MouseEvent} event - mouse down event
*/
public mouseDown(event: MouseEvent): void {
/**
@ -129,7 +129,7 @@ export default class BlockEvents extends Module {
/**
* Open Toolbox to leaf Tools
*
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - tab keydown event
*/
public tabPressed(event): void {
/**
@ -162,7 +162,7 @@ export default class BlockEvents extends Module {
* Escape pressed
* If some of Toolbar components are opened, then close it otherwise close Toolbar
*
* @param {Event} event
* @param {Event} event - escape keydown event
*/
public escapePressed(event): void {
/**
@ -186,9 +186,9 @@ export default class BlockEvents extends Module {
/**
* Add drop target styles
*
* @param {DragEvent} e
* @param {DragEvent} e - drag over event
*/
public dragOver(e: DragEvent) {
public dragOver(e: DragEvent): void {
const block = this.Editor.BlockManager.getBlockByChildNode(e.target as Node);
block.dropTarget = true;
@ -197,9 +197,9 @@ export default class BlockEvents extends Module {
/**
* Remove drop target style
*
* @param {DragEvent} e
* @param {DragEvent} e - drag leave event
*/
public dragLeave(e: DragEvent) {
public dragLeave(e: DragEvent): void {
const block = this.Editor.BlockManager.getBlockByChildNode(e.target as Node);
block.dropTarget = false;
@ -209,7 +209,7 @@ export default class BlockEvents extends Module {
* Copying selected blocks
* Before putting to the clipboard we sanitize all blocks and then copy to the clipboard
*
* @param {ClipboardEvent} event
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandC(event: ClipboardEvent): void {
const { BlockSelection } = this.Editor;
@ -225,7 +225,7 @@ export default class BlockEvents extends Module {
/**
* Copy and Delete selected Blocks
*
* @param {ClipboardEvent} event
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandX(event: ClipboardEvent): void {
const { BlockSelection, BlockManager, Caret } = this.Editor;
@ -325,7 +325,7 @@ export default class BlockEvents extends Module {
/**
* Check if Block should be removed by current Backspace keydown
*/
if (currentBlock.selected || currentBlock.isEmpty && currentBlock.currentInput === currentBlock.firstInput) {
if (currentBlock.selected || (currentBlock.isEmpty && currentBlock.currentInput === currentBlock.firstInput)) {
event.preventDefault();
const index = BlockManager.currentBlockIndex;
@ -384,7 +384,7 @@ export default class BlockEvents extends Module {
/**
* Merge current and previous Blocks if they have the same type
*/
private mergeBlocks() {
private mergeBlocks(): void {
const { BlockManager, Caret, Toolbar } = this.Editor;
const targetBlock = BlockManager.previousBlock;
const blockToMerge = BlockManager.currentBlock;
@ -427,7 +427,7 @@ export default class BlockEvents extends Module {
/**
* Handle right and down keyboard keys
*
* @param event
* @param {KeyboardEvent} event - keyboard event
*/
private arrowRightAndDown(event: KeyboardEvent): void {
const isFlipperCombination = Flipper.usedKeys.includes(event.keyCode) &&
@ -481,7 +481,7 @@ export default class BlockEvents extends Module {
/**
* Handle left and up keyboard keys
*
* @param event
* @param {KeyboardEvent} event - keyboard event
*/
private arrowLeftAndUp(event: KeyboardEvent): void {
/**
@ -536,9 +536,9 @@ export default class BlockEvents extends Module {
/**
* Cases when we need to close Toolbar
*
* @param event
* @param {KeyboardEvent} event - keyboard event
*/
private needToolbarClosing(event) {
private needToolbarClosing(event: KeyboardEvent): boolean {
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),

View file

@ -31,7 +31,7 @@ export default class BlockManager extends Module {
/**
* Set current Block index and fire Block lifecycle callbacks
*
* @param newIndex
* @param {number} newIndex - index of Block to set as current
*/
public set currentBlockIndex(newIndex: number) {
if (this._blocks[this._currentBlockIndex]) {
@ -163,7 +163,7 @@ export default class BlockManager extends Module {
*
* @returns {Promise}
*/
public async prepare() {
public async prepare(): Promise<void> {
const blocks = new Blocks(this.Editor.UI.nodes.redactor);
const { BlockEvents, Listeners } = this.Editor;
@ -252,7 +252,7 @@ export default class BlockManager extends Module {
/**
* Insert pasted content. Call onPaste callback after insert.
*
* @param {string} toolName
* @param {string} toolName - name of Tool to insert
* @param {PasteEvent} pasteEvent - pasted data
* @param {boolean} replace - should replace current block
*/
@ -288,7 +288,7 @@ export default class BlockManager extends Module {
*
* @returns {Block} inserted Block
*/
public insertInitialBlockAtIndex(index: number, needToFocus = false) {
public insertInitialBlockAtIndex(index: number, needToFocus = false): Block {
const block = this.composeBlock(this.config.initialBlock, {}, {});
this._blocks[index] = block;
@ -347,7 +347,7 @@ export default class BlockManager extends Module {
/**
* Remove block with passed index or remove last
*
* @param {number|null} index
* @param {number|null} index - index of Block to remove
*/
public removeBlock(index?: number): void {
if (index === undefined) {
@ -374,7 +374,7 @@ export default class BlockManager extends Module {
* Remove only selected Blocks
* and returns first Block index where started removing...
*
* @returns number|undefined
* @returns {number|undefined}
*/
public removeSelectedBlocks(): number|undefined {
let firstSelectedBlockIndex;
@ -461,7 +461,8 @@ export default class BlockManager extends Module {
/**
* Returns Block by passed index
*
* @param {number} index
* @param {number} index - index to get
*
* @returns {Block}
*/
public getBlockByIndex(index): Block {
@ -471,7 +472,8 @@ export default class BlockManager extends Module {
/**
* Get Block instance by html element
*
* @param {Node} element
* @param {Node} element - html element to get Block by
*
* @returns {Block}
*/
public getBlock(element: HTMLElement): Block {
@ -519,7 +521,7 @@ export default class BlockManager extends Module {
* 2) Mark it as current
*
* @param {Node} childNode - look ahead from this node.
* @param {string} caretPosition - position where to set caret
*
* @throws Error - when passed Node is not included at the Block
*/
public setCurrentBlockByChildNode(childNode: Node): Block {
@ -549,7 +551,8 @@ export default class BlockManager extends Module {
/**
* Return block which contents passed node
*
* @param {Node} childNode
* @param {Node} childNode - node to get Block by
*
* @returns {Block}
*/
public getBlockByChildNode(childNode: Node): Block {
@ -568,8 +571,9 @@ export default class BlockManager extends Module {
/**
* Swap Blocks Position
*
* @param {number} fromIndex
* @param {number} toIndex
* @param {number} fromIndex - index of first block
* @param {number} toIndex - index of second block
*
* @deprecated use 'move' instead
*/
public swap(fromIndex, toIndex): void {
@ -583,8 +587,8 @@ export default class BlockManager extends Module {
/**
* Move a block to a new index
*
* @param {number} toIndex
* @param {number} fromIndex
* @param {number} toIndex - index where to move Block
* @param {number} fromIndex - index of Block to move
*/
public move(toIndex, fromIndex = this.currentBlockIndex): void {
// make sure indexes are valid and within a valid range
@ -640,7 +644,7 @@ export default class BlockManager extends Module {
/**
* Bind Events
*
* @param {object} block
* @param {Block} block - Block to which event should be bound
*/
private bindEvents(block: Block): void {
const { BlockEvents, Listeners } = this.Editor;
@ -656,12 +660,10 @@ 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
*
* @returns {boolean}
*/
private validateIndex(index: number): boolean {
if (index < 0 || index >= this._blocks.length) {
return false;
}
return true;
return !(index < 0 || index >= this._blocks.length);
}
}

View file

@ -11,6 +11,7 @@ import * as _ from '../utils';
import $ from '../dom';
import SelectionUtils from '../selection';
import { SanitizerConfig } from '../../../types/configs';
/**
*
@ -21,7 +22,7 @@ export default class BlockSelection extends Module {
*
* @returns {SanitizerConfig}
*/
private get sanitizerConfig() {
private get sanitizerConfig(): SanitizerConfig {
return {
p: {},
h1: {},
@ -62,7 +63,7 @@ export default class BlockSelection extends Module {
/**
* Set selected all blocks
*
* @param {boolean} state
* @param {boolean} state - state to set
*/
public set allBlocksSelected(state: boolean) {
const { BlockManager } = this.Editor;
@ -160,7 +161,7 @@ export default class BlockSelection extends Module {
*
* @param {number?} index - Block index according to the BlockManager's indexes
*/
public unSelectBlockByIndex(index?) {
public unSelectBlockByIndex(index?): void {
const { BlockManager } = this.Editor;
let block;
@ -180,7 +181,7 @@ export default class BlockSelection extends Module {
* @param {Event} reason - event caused clear of selection
* @param {boolean} restoreSelection - if true, restore saved selection
*/
public clearSelection(reason?: Event, restoreSelection = false) {
public clearSelection(reason?: Event, restoreSelection = false): void {
const { BlockManager, Caret, RectangleSelection } = this.Editor;
this.needToSelectAll = false;
@ -226,7 +227,7 @@ export default class BlockSelection extends Module {
*
* @param {ClipboardEvent} e - copy/cut event
*
* @returns Promise<void>
* @returns {Promise<void>}
*/
public async copySelectedBlocks(e: ClipboardEvent): Promise<void> {
/**
@ -263,7 +264,7 @@ export default class BlockSelection extends Module {
*
* @param {number?} index - Block index according to the BlockManager's indexes
*/
public selectBlockByIndex(index?) {
public selectBlockByIndex(index?): void {
const { BlockManager } = this.Editor;
/**
@ -294,7 +295,7 @@ export default class BlockSelection extends Module {
* First CMD+A selects all input content by native behaviour,
* next CMD+A keypress selects all blocks
*
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - keyboard event
*/
private handleCommandA(event: KeyboardEvent): void {
this.Editor.RectangleSelection.clearSelection();
@ -365,7 +366,7 @@ export default class BlockSelection extends Module {
* Select All Blocks
* Each Block has selected setter that makes Block copyable
*/
private selectAllBlocks() {
private selectAllBlocks(): void {
/**
* Save selection
* Will be restored when closeSelection fired

View file

@ -131,7 +131,7 @@ export default class Caret extends Module {
* We use <= comparison for case:
* "| Hello" <--- selection.anchorOffset is 0, but firstLetterPosition is 1
*/
return firstNode === null || focusNode === firstNode && focusOffset <= firstLetterPosition;
return firstNode === null || (focusNode === firstNode && focusOffset <= firstLetterPosition);
}
/**
@ -191,7 +191,7 @@ export default class Caret extends Module {
*/
const isLastBR = i === rightSiblings.length - 1 && $.isLineBreakTag(node as HTMLElement);
return (isLastBR) || $.isEmpty(node) && !$.isLineBreakTag(node);
return isLastBR || ($.isEmpty(node) && !$.isLineBreakTag(node));
});
if (nothingAtRight && focusOffset === focusNode.textContent.length) {
@ -286,9 +286,7 @@ export default class Caret extends Module {
break;
case this.positions.END:
const contentLength = $.getContentLength(nodeToSet);
this.set(nodeToSet as HTMLElement, contentLength);
this.set(nodeToSet as HTMLElement, $.getContentLength(nodeToSet));
break;
default:
@ -459,19 +457,19 @@ export default class Caret extends Module {
/**
* Inserts shadow element after passed element where caret can be placed
*
* @param {Node} element
* @param {Element} element - element after which shadow caret should be inserted
*/
public createShadow(element): void {
public createShadow(element: Element): void {
const shadowCaret = document.createElement('span');
shadowCaret.classList.add(Caret.CSS.shadowCaret);
element.insertAdjacentElement('beforeEnd', shadowCaret);
element.insertAdjacentElement('beforeend', shadowCaret);
}
/**
* Restores caret position
*
* @param {HTMLElement} element
* @param {HTMLElement} element - element where caret should be restored
*/
public restoreCaret(element: HTMLElement): void {
const shadowCaret = element.querySelector(`.${Caret.CSS.shadowCaret}`);
@ -543,11 +541,13 @@ export default class Caret extends Module {
* <p></p> | right first-level siblings
* <p></p> |
* </div>
* @returns {Element[]}
* @param from
* @param direction
*
* @param {HTMLElement} from - element from which siblings should be searched
* @param {'left' | 'right'} direction - direction of search
*
* @returns {HTMLElement[]}
*/
private getHigherLevelSiblings(from: HTMLElement, direction?: string): HTMLElement[] {
private getHigherLevelSiblings(from: HTMLElement, direction?: 'left' | 'right'): HTMLElement[] {
let current = from;
const siblings = [];

View file

@ -27,7 +27,7 @@ export default class CrossBlockSelection extends Module {
return;
}
const { BlockManager, UI, Listeners } = this.Editor;
const { BlockManager, Listeners } = this.Editor;
this.firstSelectedBlock = BlockManager.getBlock(event.target as HTMLElement);
this.lastSelectedBlock = this.firstSelectedBlock;
@ -86,7 +86,7 @@ export default class CrossBlockSelection extends Module {
*
* @param {Event} reason - event caused clear of selection
*/
public clear(reason?: Event) {
public clear(reason?: Event): void {
const { BlockManager, BlockSelection, Caret } = this.Editor;
const fIndex = BlockManager.blocks.indexOf(this.firstSelectedBlock);
const lIndex = BlockManager.blocks.indexOf(this.lastSelectedBlock);
@ -135,7 +135,7 @@ export default class CrossBlockSelection extends Module {
* Mouse over event handler
* Gets target and related blocks and change selected state for blocks in between
*
* @param {MouseEvent} event
* @param {MouseEvent} event - mouse over event
*/
private onMouseOver = (event: MouseEvent): void => {
const { BlockManager } = this.Editor;
@ -176,8 +176,8 @@ export default class CrossBlockSelection extends Module {
/**
* Change blocks selection state between passed two blocks.
*
* @param {Block} firstBlock
* @param {Block} lastBlock
* @param {Block} firstBlock - first block in range
* @param {Block} lastBlock - last block in range
*/
private toggleBlocksSelectedState(firstBlock: Block, lastBlock: Block): void {
const { BlockManager } = this.Editor;

View file

@ -8,15 +8,13 @@ export default class DragNDrop extends Module {
/**
* If drag has been started at editor, we save it
*
* @type Boolean
* @type {boolean}
* @private
*/
private isStartedAtEditor = false;
/**
* Bind events
*
* @private
*/
public prepare(): void {
this.bindEvents();
@ -45,7 +43,7 @@ export default class DragNDrop extends Module {
/**
* Handle drop event
*
* @param {DragEvent} dropEvent
* @param {DragEvent} dropEvent - drop event
*/
private processDrop = async (dropEvent: DragEvent): Promise<void> => {
const {

View file

@ -19,7 +19,7 @@ export default class Events extends Module {
*
* @type {{}}
*/
private subscribers: {[name: string]: Array<(data?: any) => any>} = {};
private subscribers: {[name: string]: Array<(data?: object) => object>} = {};
/**
* Subscribe any event on callback
@ -27,7 +27,7 @@ export default class Events extends Module {
* @param {string} eventName - event name
* @param {Function} callback - subscriber
*/
public on(eventName: string, callback: (data: any) => any) {
public on(eventName: string, callback: (data: object) => object): void {
if (!(eventName in this.subscribers)) {
this.subscribers[eventName] = [];
}
@ -42,12 +42,12 @@ export default class Events extends Module {
* @param {string} eventName - event name
* @param {Function} callback - subscriber
*/
public once(eventName: string, callback: (data: any) => any) {
public once(eventName: string, callback: (data: object) => object): void {
if (!(eventName in this.subscribers)) {
this.subscribers[eventName] = [];
}
const wrappedCallback = (data: any) => {
const wrappedCallback = (data: object): object => {
const result = callback(data);
const indexOfHandler = this.subscribers[eventName].indexOf(wrappedCallback);
@ -69,7 +69,7 @@ export default class Events extends Module {
* @param {string} eventName - event name
* @param {object} data - subscribers get this data when they were fired
*/
public emit(eventName: string, data?: any): void {
public emit(eventName: string, data?: object): void {
if (!this.subscribers[eventName]) {
return;
}
@ -82,12 +82,12 @@ export default class Events extends Module {
}
/**
* Unsubsribe callback from event
* Unsubscribe callback from event
*
* @param eventName
* @param callback
* @param {string} eventName - event name
* @param {Function} callback - event handler
*/
public off(eventName: string, callback: (data: any) => void): void {
public off(eventName: string, callback: (data: object) => object): void {
for (let i = 0; i < this.subscribers[eventName].length; i++) {
if (this.subscribers[eventName][i] === callback) {
delete this.subscribers[eventName][i];

View file

@ -2,6 +2,8 @@ import Module from '../__module';
/**
* Event listener information
*
* @interface ListenerData
*/
export interface ListenerData {
/**
@ -17,7 +19,7 @@ export interface ListenerData {
/**
* Event handler
*
* @param {Event} event
* @param {Event} event - event object
*/
handler: (event: Event) => void;
@ -40,7 +42,7 @@ export interface ListenerData {
/**
* @typedef {Listeners} Listeners
* @property {Array} allListeners
* @property {ListenerData[]} allListeners - listeners store
*/
export default class Listeners extends Module {
/**
@ -109,9 +111,12 @@ export default class Listeners extends Module {
}
/**
* @param {EventTarget} element
* @param {string} eventType
* @param {Function} handler
* Finds and returns first listener by passed params
*
* @param {EventTarget} element - event target
* @param {string} [eventType] - event type
* @param {Function} [handler] - event handler
*
* @returns {ListenerData|null}
*/
public findOne(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData {
@ -121,9 +126,12 @@ export default class Listeners extends Module {
}
/**
* @param {EventTarget} element
* @param {string} eventType
* @param {Function} handler
* Return all stored listeners by passed params
*
* @param {EventTarget} element - event target
* @param {string} eventType - event type
* @param {Function} handler - event handler
*
* @returns {ListenerData[]}
*/
public findAll(element: EventTarget, eventType?: string, handler?: (event: Event) => void): ListenerData[] {
@ -156,6 +164,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
*/
private findByEventTarget(element: EventTarget): ListenerData[] {
@ -169,8 +178,9 @@ export default class Listeners extends Module {
/**
* Search method: looks for listener by passed event type
*
* @param {string} eventType
* @returns {Array} listeners that found on element
* @param {string} eventType - event type
*
* @returns {ListenerData[]} listeners that found on element
*/
private findByType(eventType: string): ListenerData[] {
return this.allListeners.filter((listener) => {
@ -183,8 +193,9 @@ export default class Listeners extends Module {
/**
* Search method: looks for listener by passed handler
*
* @param {Function} handler
* @returns {Array} listeners that found on element
* @param {Function} handler - event handler
*
* @returns {ListenerData[]} listeners that found on element
*/
private findByHandler(handler: (event: Event) => void): ListenerData[] {
return this.allListeners.filter((listener) => {

View file

@ -49,7 +49,7 @@ export default class ModificationsObserver extends Module {
/**
* Clear timeout and set null to mutationDebouncer property
*/
public destroy() {
public destroy(): void {
this.mutationDebouncer = null;
if (this.observer) {
this.observer.disconnect();
@ -76,7 +76,7 @@ export default class ModificationsObserver extends Module {
* Allows to disable observer,
* for example when Editor wants to stealthy mutate DOM
*/
public disable() {
public disable(): void {
this.disabled = true;
}
@ -84,7 +84,7 @@ export default class ModificationsObserver extends Module {
* Enables mutation handling
* Should be called after .disable()
*/
public enable() {
public enable(): void {
this.disabled = false;
}
@ -113,10 +113,10 @@ export default class ModificationsObserver extends Module {
/**
* MutationObserver events handler
*
* @param mutationList
* @param observer
* @param {MutationRecord[]} mutationList - list of mutations
* @param {MutationObserver} observer - observer instance
*/
private mutationHandler(mutationList, observer) {
private mutationHandler(mutationList: MutationRecord[], observer): void {
/**
* Skip mutations in stealth mode
*/
@ -134,18 +134,14 @@ export default class ModificationsObserver extends Module {
mutationList.forEach((mutation) => {
switch (mutation.type) {
case 'childList':
case 'subtree':
case 'characterData':
case 'characterDataOldValue':
contentMutated = true;
break;
case 'attributes':
const mutatedTarget = mutation.target as Element;
/**
* Changes on Element.ce-block usually is functional
*/
if (!mutatedTarget.classList.contains(Block.CSS.wrapper)) {
if (!(mutation.target as Element).classList.contains(Block.CSS.wrapper)) {
contentMutated = true;
}
break;

View file

@ -14,9 +14,9 @@ export default class Notifier extends Module {
/**
* Show web notification
*
* @param {NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions} options
* @param {NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions} options - notification options
*/
public show(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions) {
public show(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions): void {
notifier.show(options);
}
}

View file

@ -70,6 +70,8 @@ interface FilesSubstitution {
/**
* Processed paste data object.
*
* @interface PasteData
*/
interface PasteData {
/**
@ -151,7 +153,7 @@ export default class Paste extends Module {
* Handle pasted or dropped data transfer object
*
* @param {DataTransfer} dataTransfer - pasted or dropped data transfer object
* @param {boolean} isDragNDrop
* @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events
*/
public async processDataTransfer(dataTransfer: DataTransfer, isDragNDrop = false): Promise<void> {
const { Sanitizer } = this.Editor;
@ -161,6 +163,7 @@ export default class Paste extends Module {
/**
* In Microsoft Edge types is DOMStringList. So 'contains' is used to check if 'Files' type included
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const includesFiles = types.includes ? types.includes('Files') : (types as any).contains('Files');
if (includesFiles) {
@ -216,7 +219,7 @@ 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 = false) {
public async processText(data: string, isHTML = false): Promise<void> {
const { Caret, BlockManager, Tools } = this.Editor;
const dataToInsert = isHTML ? this.processHTML(data) : this.processPlain(data);
@ -266,9 +269,6 @@ export default class Paste extends Module {
/**
* Process paste config for each tool
*
* @param {string} name
* @param {Tool} tool
*/
private processTool = ([name, tool]: [string, BlockToolConstructable]): void => {
try {
@ -312,7 +312,7 @@ export default class Paste extends Module {
const tags = toolPasteConfig.tags || [];
tags.forEach((tag) => {
if (this.toolsTags.hasOwnProperty(tag)) {
if (Object.prototype.hasOwnProperty.call(this.toolsTags, tag)) {
_.log(
`Paste handler for «${name}» Tool on «${tag}» tag is skipped ` +
`because it is already used by «${this.toolsTags[tag].tool}» Tool.`,
@ -404,6 +404,7 @@ export default class Paste extends Module {
* Check if browser behavior suits better
*
* @param {EventTarget} element - element where content has been pasted
*
* @returns {boolean}
*/
private isNativeBehaviour(element: EventTarget): boolean {
@ -413,14 +414,14 @@ export default class Paste extends Module {
/**
* Check if Editor should process pasted data and pass data transfer object to handler
*
* @param {ClipboardEvent} event
* @param {ClipboardEvent} event - clipboard event
*/
private handlePasteEvent = async (event: ClipboardEvent): Promise<void> => {
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;
}
@ -444,7 +445,7 @@ export default class Paste extends Module {
*
* @param {FileList} items - pasted or dropped items
*/
private async processFiles(items: FileList) {
private async processFiles(items: FileList): Promise<void> {
const { BlockManager, Tools } = this.Editor;
let dataToInsert: Array<{type: string; event: PasteEvent}>;
@ -469,9 +470,9 @@ export default class Paste extends Module {
/**
* Get information about file and find Tool to handle it
*
* @param {File} file
* @param {File} file - file to process
*/
private async processFile(file: File) {
private async processFile(file: File): Promise<{event: PasteEvent; type: string}> {
const extension = _.getFileExtension(file);
const foundConfig = Object
@ -507,7 +508,8 @@ export default class Paste extends Module {
/**
* Split HTML string to blocks and return it as array of Block data
*
* @param {string} innerHTML
* @param {string} innerHTML - html string to process
*
* @returns {PasteData[]}
*/
private processHTML(innerHTML: string): PasteData[] {
@ -569,12 +571,12 @@ export default class Paste extends Module {
/**
* Split plain text by new line symbols and return it as array of Block data
*
* @param {string} plain
* @param {string} plain - string to process
*
* @returns {PasteData[]}
*/
private processPlain(plain: string): PasteData[] {
const { initialBlock } = this.config as {initialBlock: string},
{ Tools } = this.Editor;
const { initialBlock } = this.config as {initialBlock: string};
if (!plain) {
return [];
@ -606,7 +608,7 @@ export default class Paste extends Module {
/**
* Process paste of single Block tool content
*
* @param {PasteData} dataToInsert
* @param {PasteData} dataToInsert - data of Block to inseret
*/
private async processSingleBlock(dataToInsert: PasteData): Promise<void> {
const { Caret, BlockManager, Tools } = this.Editor;
@ -634,11 +636,11 @@ export default class Paste extends Module {
* 2. Insert new block if it is not the same type as current one
* 3. Just insert text if there is no substitutions
*
* @param {PasteData} dataToInsert
* @param {PasteData} dataToInsert - data of Block to insert
*/
private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
const { BlockManager, Caret, Sanitizer, Tools } = this.Editor;
const { content, tool } = dataToInsert;
const { content } = dataToInsert;
const currentBlockIsInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
@ -675,8 +677,9 @@ export default class Paste extends Module {
/**
* Get patterns` matches
*
* @param {string} text
* @returns Promise<{data: BlockToolData, tool: string}>
* @param {string} text - text to process
*
* @returns {Promise<{event: PasteEvent, tool: string}>}
*/
private async processPattern(text: string): Promise<{event: PasteEvent; tool: string}> {
const pattern = this.toolsPatterns.find((substitute) => {
@ -707,8 +710,9 @@ export default class Paste extends Module {
/**
* Insert pasted Block content to Editor
*
* @param {PasteData} data
* @param {PasteData} data - data to insert
* @param {boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block
*
* @returns {void}
*/
private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void {
@ -757,17 +761,62 @@ export default class Paste extends Module {
});
}
/**
* Fetch nodes from Element node
*
* @param {Node} node - current node
* @param {Node[]} nodes - processed nodes
* @param {Node} destNode - destination node
*
* @returns {Node[]}
*/
private processElementNode(node: Node, nodes: Node[], destNode: Node): Node[] | void {
const tags = Object.keys(this.toolsTags);
const element = node as HTMLElement;
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)
);
const containsBlockElements = Array.from(element.children).some(
({ 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)
) {
return [...nodes, destNode, element];
}
}
/**
* Recursively divide HTML string to two types of nodes:
* 1. Block element
* 2. Document Fragments contained text and markup tags like a, b, i etc.
*
* @param {Node} wrapper
* @param {Node} wrapper - wrapper of paster HTML content
*
* @returns {Node[]}
*/
private getNodes(wrapper: Node): Node[] {
const children = Array.from(wrapper.childNodes),
tags = Object.keys(this.toolsTags);
const children = Array.from(wrapper.childNodes);
let elementNodeProcessingResult: Node[] | void;
const reducer = (nodes: Node[], node: Node): Node[] => {
if ($.isEmpty(node) && !$.isSingleTag(node as HTMLElement)) {
@ -789,35 +838,10 @@ export default class Paste extends Module {
* 2. Check if it contains another block or substitutable elements
*/
case Node.ELEMENT_NODE:
const element = node as HTMLElement;
elementNodeProcessingResult = this.processElementNode(node, nodes, destNode);
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)
);
const containsBlockElements = Array.from(element.children).some(
({ 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)
) {
return [...nodes, destNode, element];
if (elementNodeProcessingResult) {
return elementNodeProcessingResult;
}
break;
@ -842,8 +866,8 @@ export default class Paste extends Module {
/**
* Compose paste event with passed type and detail
*
* @param {string} type
* @param {PasteEventDetail} detail
* @param {string} type - event type
* @param {PasteEventDetail} detail - event detail
*/
private composePasteEvent(type: string, detail: PasteEventDetail): PasteEvent {
return new CustomEvent(type, {

View file

@ -20,7 +20,7 @@ export default class RectangleSelection extends Module {
*
* @returns {{wrapper: string, content: string}}
*/
static get CSS() {
public static get CSS(): {[name: string]: string} {
return {
overlay: 'codex-editor-overlay',
overlayContainer: 'codex-editor-overlay__container',
@ -136,7 +136,7 @@ export default class RectangleSelection extends Module {
* @param {number} pageX - X coord of mouse
* @param {number} pageY - Y coord of mouse
*/
public startSelection(pageX, pageY) {
public startSelection(pageX, pageY): void {
const elemWhereSelectionStart = document.elementFromPoint(pageX - window.pageXOffset, pageY - window.pageYOffset);
/**
@ -175,7 +175,7 @@ export default class RectangleSelection extends Module {
/**
* Clear all params to end selection
*/
public endSelection() {
public endSelection(): void {
this.mousedown = false;
this.startX = 0;
this.startY = 0;
@ -185,14 +185,14 @@ export default class RectangleSelection extends Module {
/**
* is RectSelection Activated
*/
public isRectActivated() {
public isRectActivated(): boolean {
return this.isRectSelectionActivated;
}
/**
* Mark that selection is end
*/
public clearSelection() {
public clearSelection(): void {
this.isRectSelectionActivated = false;
}
@ -201,7 +201,7 @@ export default class RectangleSelection extends Module {
*
* @param {number} clientY - Y coord of mouse
*/
private scrollByZones(clientY) {
private scrollByZones(clientY): void {
this.inScrollZone = null;
if (clientY <= this.HEIGHT_OF_SCROLL_ZONE) {
this.inScrollZone = this.TOP_SCROLL_ZONE;
@ -223,9 +223,11 @@ export default class RectangleSelection extends Module {
}
/**
* Generates required HTML elements
*
* @returns {object<string, Element>}
*/
private genHTML() {
private genHTML(): {container: Element; overlay: Element} {
const { UI } = this.Editor;
const container = UI.nodes.holder.querySelector('.' + UI.CSS.editorWrapper);
@ -250,7 +252,7 @@ export default class RectangleSelection extends Module {
*
* @param {number} speed - speed of scrolling
*/
private scrollVertical(speed) {
private scrollVertical(speed): void {
if (!(this.inScrollZone && this.mousedown)) {
return;
}
@ -266,9 +268,9 @@ export default class RectangleSelection extends Module {
/**
* Handles the change in the rectangle and its effect
*
* @param {MouseEvent} event
* @param {MouseEvent} event - mouse event
*/
private changingRectangle(event) {
private changingRectangle(event): void {
if (!this.mousedown) {
return;
}
@ -310,7 +312,7 @@ export default class RectangleSelection extends Module {
/**
* Shrink rect to singular point
*/
private shrinkRectangleToPoint() {
private shrinkRectangleToPoint(): void {
this.overlayRectangle.style.left = `${this.startX - window.pageXOffset}px`;
this.overlayRectangle.style.top = `${this.startY - window.pageYOffset}px`;
this.overlayRectangle.style.bottom = `calc(100% - ${this.startY - window.pageYOffset}px`;
@ -320,17 +322,17 @@ export default class RectangleSelection extends Module {
/**
* Select or unselect all of blocks in array if rect is out or in selectable area
*/
private inverseSelection() {
private inverseSelection(): void {
const firstBlockInStack = this.Editor.BlockManager.getBlockByIndex(this.stackOfSelected[0]);
const isSelecteMode = firstBlockInStack.selected;
const isSelectedMode = firstBlockInStack.selected;
if (this.rectCrossesBlocks && !isSelecteMode) {
if (this.rectCrossesBlocks && !isSelectedMode) {
for (const it of this.stackOfSelected) {
this.Editor.BlockSelection.selectBlockByIndex(it);
}
}
if (!this.rectCrossesBlocks && isSelecteMode) {
if (!this.rectCrossesBlocks && isSelectedMode) {
for (const it of this.stackOfSelected) {
this.Editor.BlockSelection.unSelectBlockByIndex(it);
}
@ -340,7 +342,7 @@ export default class RectangleSelection extends Module {
/**
* Updates size of rectangle
*/
private updateRectangleSize() {
private updateRectangleSize(): void {
// Depending on the position of the mouse relative to the starting point,
// change this.e distance from the desired edge of the screen*/
if (this.mouseY >= this.startY) {
@ -363,9 +365,9 @@ export default class RectangleSelection extends Module {
/**
* Collects information needed to determine the behavior of the rectangle
*
* @returns {number} index - index next Block, leftPos - start of left border of Block, rightPos - right border
* @returns {object} index - index next Block, leftPos - start of left border of Block, rightPos - right border
*/
private genInfoForMouseSelection() {
private genInfoForMouseSelection(): {index: number; leftPos: number; rightPos: number} {
const widthOfRedactor = document.body.offsetWidth;
const centerOfRedactor = widthOfRedactor / 2;
const Y = this.mouseY - window.pageYOffset;
@ -393,7 +395,7 @@ export default class RectangleSelection extends Module {
*
* @param index - index of block in redactor
*/
private addBlockInSelection(index) {
private addBlockInSelection(index): void {
if (this.rectCrossesBlocks) {
this.Editor.BlockSelection.selectBlockByIndex(index);
}
@ -405,7 +407,7 @@ export default class RectangleSelection extends Module {
*
* @param {object} index - index of new block in the reactor
*/
private trySelectNextBlock(index) {
private trySelectNextBlock(index): void {
const sameBlock = this.stackOfSelected[this.stackOfSelected.length - 1] === index;
const sizeStack = this.stackOfSelected.length;
const down = 1, up = -1, undef = 0;
@ -415,10 +417,16 @@ export default class RectangleSelection extends Module {
}
const blockNumbersIncrease = this.stackOfSelected[sizeStack - 1] - this.stackOfSelected[sizeStack - 2] > 0;
const direction = sizeStack <= 1 ? undef : blockNumbersIncrease ? down : up;
const selectionInDownDurection = index > this.stackOfSelected[sizeStack - 1] && direction === down;
let direction = undef;
if (sizeStack > 1) {
direction = blockNumbersIncrease ? down : up;
}
const selectionInDownDirection = index > this.stackOfSelected[sizeStack - 1] && direction === down;
const selectionInUpDirection = index < this.stackOfSelected[sizeStack - 1] && direction === up;
const generalSelection = selectionInDownDurection || selectionInUpDirection || direction === undef;
const generalSelection = selectionInDownDirection || selectionInUpDirection || direction === undef;
const reduction = !generalSelection;
// When the selection is too fast, some blocks do not have time to be noticed. Fix it.
@ -451,9 +459,9 @@ export default class RectangleSelection extends Module {
// cmp for different directions
if (index > this.stackOfSelected[sizeStack - 1]) {
cmp = () => index > this.stackOfSelected[i];
cmp = (): boolean => index > this.stackOfSelected[i];
} else {
cmp = () => index < this.stackOfSelected[i];
cmp = (): boolean => index < this.stackOfSelected[i];
}
// Remove blocks missed due to speed.

View file

@ -43,10 +43,10 @@ export default class Renderer extends Module {
/**
* Make plugin blocks from array of plugin`s data
*
* @param {RendererBlocks[]} blocks
* @param {BlockToolData[]} blocks - blocks to render
*/
public async render(blocks: BlockToolData[]): Promise<void> {
const chainData = blocks.map((block) => ({ function: () => this.insertBlock(block) }));
const chainData = blocks.map((block) => ({ function: (): Promise<void> => this.insertBlock(block) }));
const sequence = await _.sequence(chainData as ChainData[]);
@ -60,9 +60,8 @@ export default class Renderer extends Module {
* Add plugin instance to BlockManager
* Insert block to working zone
*
* @param {object} item
* @param {object} item - Block data to insert
* @returns {Promise<void>}
* @private
*/
public async insertBlock(item): Promise<void> {
const { Tools, BlockManager } = this.Editor;

View file

@ -57,8 +57,7 @@ export default class Sanitizer extends Module {
*
* Enumerate blocks and clean data
*
* @param blocksData
* @param {{tool, data: BlockToolData}[]} blocksData[]
* @param {Array<{tool, data: BlockToolData}>} blocksData - blocks' data to sanitize
*/
public sanitizeBlocks(
blocksData: Array<{tool: string; data: BlockToolData}>
@ -70,7 +69,7 @@ export default class Sanitizer extends Module {
return block;
}
block.data = this.deepSanitize(block.data, toolConfig);
block.data = this.deepSanitize(block.data, toolConfig) as BlockToolData;
return block;
});
@ -82,7 +81,7 @@ export default class Sanitizer extends Module {
* @param {BlockToolData|object|*} dataToSanitize - taint string or object/array that contains taint string
* @param {SanitizerConfig} rules - object with sanitizer rules
*/
public deepSanitize(dataToSanitize: any, rules: SanitizerConfig): any {
public deepSanitize(dataToSanitize: object | string, rules: SanitizerConfig): object | string {
/**
* BlockData It may contain 3 types:
* - Array
@ -138,8 +137,8 @@ export default class Sanitizer extends Module {
/**
* Merge with inline tool config
*
* @param {string} toolName
* @param {SanitizerConfig} toolRules
* @param {string} toolName - tool name
*
* @returns {SanitizerConfig}
*/
public composeToolConfig(toolName: string): SanitizerConfig {
@ -166,7 +165,7 @@ export default class Sanitizer extends Module {
const toolConfig = {} as SanitizerConfig;
for (const fieldName in toolRules) {
if (toolRules.hasOwnProperty(fieldName)) {
if (Object.prototype.hasOwnProperty.call(toolRules, fieldName)) {
const rule = toolRules[fieldName];
if (typeof rule === 'object') {
@ -186,7 +185,7 @@ export default class Sanitizer extends Module {
* When Tool's "inlineToolbar" value is True, get all sanitizer rules from all tools,
* otherwise get only enabled
*
* @param name
* @param {string} name - Inline Tool name
*/
public getInlineToolsConfig(name: string): SanitizerConfig {
const { Tools } = this.Editor;
@ -234,7 +233,7 @@ export default class Sanitizer extends Module {
const config: SanitizerConfig = {} as SanitizerConfig;
Object.entries(Tools.inline)
.forEach(([name, inlineTool]: [string, InlineToolConstructable]) => {
.forEach(([, inlineTool]: [string, InlineToolConstructable]) => {
Object.assign(config, inlineTool[Tools.INTERNAL_SETTINGS.SANITIZE_CONFIG]);
});
@ -247,9 +246,9 @@ export default class Sanitizer extends Module {
* Clean array
*
* @param {Array} array - [1, 2, {}, []]
* @param {object} ruleForItem
* @param {SanitizerConfig} ruleForItem - sanitizer config for array
*/
private cleanArray(array: any[], ruleForItem: SanitizerConfig): any[] {
private cleanArray(array: Array<object | string>, ruleForItem: SanitizerConfig): Array<object | string> {
return array.map((arrayItem) => this.deepSanitize(arrayItem, ruleForItem));
}
@ -260,11 +259,11 @@ export default class Sanitizer extends Module {
* @param {object} rules - { b: true } or true|false
* @returns {object}
*/
private cleanObject(object: any, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): any {
private cleanObject(object: object, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): object {
const cleanData = {};
for (const fieldName in object) {
if (!object.hasOwnProperty(fieldName)) {
if (!Object.prototype.hasOwnProperty.call(object, fieldName)) {
continue;
}
@ -284,8 +283,11 @@ export default class Sanitizer extends Module {
}
/**
* @param {string} taintString
* @param {SanitizerConfig|boolean} rule
* Clean primitive value
*
* @param {string} taintString - string to clean
* @param {SanitizerConfig|boolean} rule - sanitizer rule
*
* @returns {string}
*/
private cleanOneItem(taintString: string, rule: SanitizerConfig|boolean): string {
@ -303,7 +305,7 @@ export default class Sanitizer extends Module {
* { a : true }, {}, false, true, function(){} correct rules
* undefined, null, 0, 1, 2 not a rules
*
* @param config
* @param {SanitizerConfig} config - config to check
*/
private isRule(config: SanitizerConfig): boolean {
return typeof config === 'object' || typeof config === 'boolean' || typeof config === 'function';
@ -314,8 +316,9 @@ export default class Sanitizer extends Module {
* Or, sanitizing config can be defined globally in editors initialization. That config will be used everywhere
* At least, if there is no config overrides, that API uses Default configuration
*
* @uses https://www.npmjs.com/package/html-janitor
* @license https://github.com/guardian/html-janitor/blob/master/LICENSE
* @see {@link https://www.npmjs.com/package/html-janitor}
* @license Apache-2.0
* @see {@link https://github.com/guardian/html-janitor/blob/master/LICENSE}
*
* @param {SanitizerConfig} config - sanitizer extension
*/

View file

@ -67,7 +67,7 @@ export default class Saver extends Module {
/**
* Creates output object with saved data, time and version of editor
*
* @param {ValidatedData} allExtractedData
* @param {ValidatedData} allExtractedData - data extracted from Blocks
* @returns {OutputData}
*/
private makeOutput(allExtractedData): OutputData {

View file

@ -10,6 +10,8 @@ import Module from '../__module';
* Each shortcut must have name and handler
* `name` is a shortcut, like 'CMD+K', 'CMD+B' etc
* `handler` is a callback
*
* @interface ShortcutData
*/
export interface ShortcutData {
@ -42,7 +44,7 @@ export default class Shortcuts extends Module {
/**
* Register shortcut
*
* @param {ShortcutData} shortcut
* @param {ShortcutData} shortcut - shortcut options
*/
public add(shortcut: ShortcutData): void {
const newShortcut = new Shortcut({
@ -57,7 +59,7 @@ export default class Shortcuts extends Module {
/**
* Remove shortcut
*
* @param {ShortcutData} shortcut
* @param {string} shortcut - shortcut name
*/
public remove(shortcut: string): void {
const index = this.registeredShortcuts.findIndex((shc) => shc.name === shortcut);

View file

@ -32,7 +32,7 @@ export default class BlockSettings extends Module {
*
* @returns {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
*/
public get CSS() {
public get CSS(): {[name: string]: string} {
return {
// Settings Panel
wrapper: 'ce-settings',
@ -81,8 +81,6 @@ export default class BlockSettings extends Module {
* Panel with block settings with 2 sections:
* - Tool's Settings
* - Default Settings [Move, Remove, etc]
*
* @returns {Element}
*/
public make(): void {
this.nodes.wrapper = $.make('div', this.CSS.wrapper);

View file

@ -3,7 +3,6 @@ import $ from '../../dom';
import { BlockToolConstructable } from '../../../../types';
import * as _ from '../../utils';
import { SavedData } from '../../../types-internal/block-data';
import Block from '../../block';
import Flipper from '../../flipper';
/**
@ -155,7 +154,7 @@ export default class ConversionToolbar extends Module {
* Replaces one Block with another
* For that Tools must provide import/export methods
*
* @param {string} replacingToolName
* @param {string} replacingToolName - name of Tool which replaces current
*/
public async replaceWithBlock(replacingToolName: string): Promise <void> {
/**
@ -251,7 +250,7 @@ export default class ConversionToolbar extends Module {
const tools = this.Editor.Tools.blockTools;
for (const toolName in tools) {
if (!tools.hasOwnProperty(toolName)) {
if (!Object.prototype.hasOwnProperty.call(tools, toolName)) {
continue;
}
@ -281,9 +280,9 @@ export default class ConversionToolbar extends Module {
/**
* Add tool to the Conversion Toolbar
*
* @param toolName
* @param toolIcon
* @param title
* @param {string} toolName - name of Tool to add
* @param {string} toolIcon - Tool icon
* @param {string} title - button 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 - Toolbar 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
@ -76,7 +76,7 @@ export default class Toolbar extends Module {
*
* @returns {object}
*/
public get CSS() {
public get CSS(): {[name: string]: string} {
return {
toolbar: 'ce-toolbar',
content: 'ce-toolbar__content',
@ -273,8 +273,8 @@ export default class Toolbar extends Module {
*/
public get plusButton(): {hide: () => void; show: () => void} {
return {
hide: () => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
show: () => {
hide: (): void => this.nodes.plusButton.classList.add(this.CSS.plusButtonHidden),
show: (): void => {
if (this.Editor.Toolbox.isEmpty) {
return;
}
@ -290,10 +290,10 @@ export default class Toolbar extends Module {
*/
private get blockActions(): {hide: () => void; show: () => void} {
return {
hide: () => {
hide: (): void => {
this.nodes.actions.classList.remove(this.CSS.actionsOpened);
},
show: () => {
show: (): void => {
this.nodes.actions.classList.add(this.CSS.actionsOpened);
},
};
@ -301,8 +301,6 @@ export default class Toolbar extends Module {
/**
* Handler for Plus Button
*
* @param {MouseEvent} event
*/
private plusButtonClicked(): void {
this.Editor.Toolbox.toggle();

View file

@ -94,15 +94,15 @@ export default class InlineToolbar extends Module {
/**
* Inline Toolbar Tools
*
* @returns Map<string, InlineTool>
* @returns {Map<string, InlineTool>}
*/
get tools(): Map<string, InlineTool> {
public get tools(): Map<string, InlineTool> {
if (!this.toolsInstances || this.toolsInstances.size === 0) {
const allTools = this.inlineTools;
this.toolsInstances = new Map();
for (const tool in allTools) {
if (allTools.hasOwnProperty(tool)) {
if (Object.prototype.hasOwnProperty.call(allTools, tool)) {
this.toolsInstances.set(tool, allTools[tool]);
}
}
@ -114,7 +114,7 @@ export default class InlineToolbar extends Module {
/**
* Making DOM
*/
public make() {
public make(): void {
this.nodes.wrapper = $.make('div', this.CSS.inlineToolbar);
this.nodes.buttons = $.make('div', this.CSS.buttonsWrapper);
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
@ -516,8 +516,8 @@ export default class InlineToolbar extends Module {
/**
* Add tool button and activate clicks
*
* @param toolName
* @param tool
* @param {string} toolName - name of Tool to add
* @param {InlineTool} tool - Tool class instance
*/
private addTool(toolName: string, tool: InlineTool): void {
const {
@ -561,7 +561,7 @@ export default class InlineToolbar extends Module {
*/
const internalTools: string[] = Object
.entries(Tools.internalTools)
.filter(([name, toolClass]: [string, ToolConstructable | ToolSettings]) => {
.filter(([, toolClass]: [string, ToolConstructable | ToolSettings]) => {
if (_.isFunction(toolClass)) {
return toolClass[Tools.INTERNAL_SETTINGS.IS_INLINE];
}
@ -671,7 +671,7 @@ export default class InlineToolbar extends Module {
const result = {};
for (const tool in this.Editor.Tools.inline) {
if (this.Editor.Tools.inline.hasOwnProperty(tool)) {
if (Object.prototype.hasOwnProperty.call(this.Editor.Tools.inline, tool)) {
const toolSettings = this.Editor.Tools.getToolSettings(tool);
result[tool] = this.Editor.Tools.constructInline(this.Editor.Tools.inline[tool], toolSettings);

View file

@ -19,10 +19,9 @@ export default class Toolbox extends Module {
/**
* CSS styles
*
* @returns {{toolbox: string, toolboxButton string, toolboxButtonActive: string,
* toolboxOpened: string, tooltip: string, tooltipShown: string, tooltipShortcut: string}}
* @returns {object.<string, string>}
*/
get CSS() {
public get CSS(): {[name: string]: string} {
return {
toolbox: 'ce-toolbox',
toolboxButton: 'ce-toolbox__button',
@ -90,8 +89,8 @@ export default class Toolbox extends Module {
/**
* Toolbox Tool's button click handler
*
* @param {MouseEvent|KeyboardEvent} event
* @param {string} toolName
* @param {MouseEvent|KeyboardEvent} event - event that activates toolbox button
* @param {string} toolName - button to activate
*/
public toolButtonActivate(event: MouseEvent|KeyboardEvent, toolName: string): void {
const tool = this.Editor.Tools.toolsClasses[toolName] as BlockToolConstructable;
@ -143,7 +142,7 @@ export default class Toolbox extends Module {
const tools = this.Editor.Tools.available;
for (const toolName in tools) {
if (tools.hasOwnProperty(toolName)) {
if (Object.prototype.hasOwnProperty.call(tools, toolName)) {
this.addTool(toolName, tools[toolName] as BlockToolConstructable);
}
}
@ -228,7 +227,7 @@ export default class Toolbox extends Module {
* Draw tooltip for toolbox tools
*
* @param {string} toolName - toolbox tool name
* @returns { HTMLElement }
* @returns {HTMLElement}
*/
private drawTooltip(toolName: string): HTMLElement {
const toolSettings = this.Editor.Tools.getToolSettings(toolName);
@ -261,7 +260,7 @@ export default class Toolbox extends Module {
* @param {string} toolName - Tool name
* @param {string} shortcut - shortcut according to the ShortcutData Module format
*/
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string) {
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string): void {
this.Editor.Shortcuts.add({
name: shortcut,
handler: (event: KeyboardEvent) => {
@ -290,11 +289,8 @@ export default class Toolbox extends Module {
* @param {BlockToolConstructable} tool - Tool Class
* @param {string} toolName - Tool name
*/
private insertNewBlock(tool: BlockToolConstructable, toolName: string) {
private insertNewBlock(tool: BlockToolConstructable, toolName: string): void {
const { BlockManager, Caret } = this.Editor;
/**
* @type {Block}
*/
const { currentBlock } = BlockManager;
let newBlock;

View file

@ -2,7 +2,8 @@ import Paragraph from '../tools/paragraph/dist/bundle';
import Module from '../__module';
import * as _ from '../utils';
import {
BlockToolConstructable,
BlockTool,
BlockToolConstructable, BlockToolData, EditorConfig,
InlineTool,
InlineToolConstructable, Tool,
ToolConfig,
@ -67,7 +68,7 @@ export default class Tools extends Module {
return this._inlineTools;
}
const tools = Object.entries(this.available).filter(([name, tool]) => {
const tools = Object.entries(this.available).filter(([, tool]) => {
if (!tool[this.INTERNAL_SETTINGS.IS_INLINE]) {
return false;
}
@ -112,8 +113,7 @@ export default class Tools extends Module {
* Return editor block tools
*/
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(([, tool]) => {
return !tool[this.INTERNAL_SETTINGS.IS_INLINE];
});
@ -134,7 +134,7 @@ export default class Tools extends Module {
*
* @returns {object}
*/
public get INTERNAL_SETTINGS() {
public get INTERNAL_SETTINGS(): {[name: string]: string} {
return {
IS_ENABLED_LINE_BREAKS: 'enableLineBreaks',
IS_INLINE: 'isInline',
@ -151,7 +151,7 @@ export default class Tools extends Module {
*
* return {object}
*/
public get USER_SETTINGS() {
public get USER_SETTINGS(): {[name: string]: string} {
return {
SHORTCUT: 'shortcut',
TOOLBOX: 'toolbox',
@ -196,7 +196,7 @@ export default class Tools extends Module {
/**
* @class
*
* @param {EditorConfig} config
* @param {EditorConfig} config - Editor's configuration
*/
constructor({ config }) {
super({ config });
@ -227,9 +227,9 @@ export default class Tools extends Module {
/**
* Creates instances via passed or default configuration
*
* @returns {Promise}
* @returns {Promise<void>}
*/
public prepare() {
public prepare(): Promise<void> {
this.validateTools();
/**
@ -237,7 +237,7 @@ export default class Tools extends Module {
*/
this.config.tools = _.deepMerge({}, this.internalTools, this.config.tools);
if (!this.config.hasOwnProperty('tools') || Object.keys(this.config.tools).length === 0) {
if (!Object.prototype.hasOwnProperty.call(this.config, 'tools') || Object.keys(this.config.tools).length === 0) {
throw Error('Can\'t start without tools');
}
@ -298,26 +298,30 @@ export default class Tools extends Module {
}
/**
* to see how it works {@link Util#sequence}
* to see how it works {@link '../utils.ts#sequence'}
*/
return _.sequence(sequenceData, (data: any) => {
return _.sequence(sequenceData, (data: {toolName: string}) => {
this.success(data);
}, (data) => {
}, (data: {toolName: string}) => {
this.fallback(data);
});
}
/**
* @param {ChainData.data} data - append tool to available list
* Success callback
*
* @param {object} data - append tool to available list
*/
public success(data) {
public success(data: {toolName: string}): void {
this.toolsAvailable[data.toolName] = this.toolsClasses[data.toolName];
}
/**
* @param {ChainData.data} data - append tool to unavailable list
* Fail callback
*
* @param {object} data - append tool to unavailable list
*/
public fallback(data) {
public fallback(data: {toolName: string}): void {
this.toolsUnavailable[data.toolName] = this.toolsClasses[data.toolName];
}
@ -325,11 +329,12 @@ export default class Tools extends Module {
* Return Tool`s instance
*
* @param {string} tool tool name
* @param {BlockToolData} data initial data
* @param {object} data initial data
*
* @returns {BlockTool}
*/
public construct(tool, data) {
const Plugin = this.toolsClasses[tool];
public construct(tool: string, data: BlockToolData): BlockTool {
const Plugin = this.toolsClasses[tool] as BlockToolConstructable;
/**
* Configuration to be passed to the Tool's constructor
@ -341,9 +346,6 @@ export default class Tools extends Module {
config.placeholder = this.config.placeholder;
}
/**
* @type {{api: API, config: ({}), data: BlockToolData}}
*/
const constructorOptions = {
api: this.Editor.API.methods,
config,
@ -356,14 +358,12 @@ export default class Tools extends Module {
/**
* Return Inline Tool's instance
*
* @param {InlineTool} tool
* @param {ToolSettings} toolSettings
* @param {InlineTool} tool - Inline Tool instance
* @param {ToolSettings} toolSettings - tool settings
*
* @returns {InlineTool} instance
*/
public constructInline(tool: InlineToolConstructable, toolSettings: ToolSettings = {} as ToolSettings): InlineTool {
/**
* @type {{api: API}}
*/
const constructorOptions = {
api: this.Editor.API.methods,
config: (toolSettings[this.USER_SETTINGS.CONFIG] || {}) as ToolSettings,
@ -377,22 +377,41 @@ export default class Tools extends Module {
* Check if passed Tool is an instance of Initial Block Tool
*
* @param {Tool} tool - Tool to check
*
* @returns {boolean}
*/
public isInitial(tool) {
public isInitial(tool): boolean {
return tool instanceof this.available[this.config.initialBlock];
}
/**
* Return Tool's config by name
*
* @param {string} toolName
* @param {string} toolName - name of tool
*
* @returns {ToolSettings}
*/
public getToolSettings(toolName): ToolSettings {
return this.toolsSettings[toolName];
}
/**
* Returns internal tools
* Includes Bold, Italic, Link and Paragraph
*/
public get internalTools(): {[toolName: string]: ToolConstructable|ToolSettings} {
return {
bold: { class: BoldInlineTool },
italic: { class: ItalicInlineTool },
link: { class: LinkInlineTool },
paragraph: {
class: Paragraph,
inlineToolbar: true,
},
stub: { class: Stub },
};
}
/**
* Binds prepare function of plugins with user or default config
*
@ -408,7 +427,7 @@ export default class Tools extends Module {
> = [];
for (const toolName in this.toolsClasses) {
if (this.toolsClasses.hasOwnProperty(toolName)) {
if (Object.prototype.hasOwnProperty.call(this.toolsClasses, toolName)) {
const toolClass = this.toolsClasses[toolName];
if (typeof toolClass.prepare === 'function') {
@ -434,12 +453,12 @@ export default class Tools extends Module {
/**
* Validate Tools configuration objects and throw Error for user if it is invalid
*/
private validateTools() {
private validateTools(): void {
/**
* Check Tools for a class containing
*/
for (const toolName in this.config.tools) {
if (this.config.tools.hasOwnProperty(toolName)) {
if (Object.prototype.hasOwnProperty.call(this.config.tools, toolName)) {
if (toolName in this.internalTools) {
return;
}
@ -454,21 +473,4 @@ export default class Tools extends Module {
}
}
}
/**
* Returns internal tools
* Includes Bold, Italic, Link and Paragraph
*/
get internalTools() {
return {
bold: { class: BoldInlineTool },
italic: { class: ItalicInlineTool },
link: { class: LinkInlineTool },
paragraph: {
class: Paragraph,
inlineToolbar: true,
},
stub: { class: Stub },
};
}
}

View file

@ -1,3 +1,4 @@
/* eslint-disable jsdoc/no-undefined-types */
import Module from '../__module';
/**
@ -21,7 +22,7 @@ export default class Tooltip extends Module {
/**
* @class
* @param {EditorConfig}
* @param {EditorConfig} - Editor's config
*/
constructor({ config }: ModuleConfig) {
super({ config });

View file

@ -1,3 +1,4 @@
/* eslint-disable jsdoc/no-undefined-types */
/**
* Prebuilded sprite of SVG icons
*/
@ -28,8 +29,8 @@ import Flipper from '../flipper';
*
* @typedef {UI} UI
* @property {EditorConfig} config - editor configuration {@link EditorJS#configuration}
* @property {Object} Editor - available editor modules {@link EditorJS#moduleInstances}
* @property {Object} nodes -
* @property {object} Editor - available editor modules {@link EditorJS#moduleInstances}
* @property {object} nodes -
* @property {Element} nodes.holder - element where we need to append redactor
* @property {Element} nodes.wrapper - <codex-editor>
* @property {Element} nodes.redactor - <ce-redactor>
@ -37,7 +38,8 @@ import Flipper from '../flipper';
export default class UI extends Module {
/**
* Editor.js UI CSS class names
* @return {{editorWrapper: string, editorZone: string}}
*
* @returns {{editorWrapper: string, editorZone: string}}
*/
public get CSS(): {
editorWrapper: string; editorWrapperNarrow: string; editorZone: string; editorZoneHidden: string;
@ -55,7 +57,8 @@ export default class UI extends Module {
/**
* Return Width of center column of Editor
* @return {DOMRect}
*
* @returns {DOMRect}
*/
public get contentRect(): DOMRect {
if (this.contentRectCache) {
@ -82,6 +85,7 @@ export default class UI extends Module {
/**
* Flag that became true on mobile viewport
*
* @type {boolean}
*/
public isMobile = false;
@ -98,12 +102,14 @@ export default class UI extends Module {
/**
* Cache for center column rectangle info
* Invalidates on window resize
*
* @type {DOMRect}
*/
private contentRectCache: DOMRect = undefined;
/**
* Handle window resize only when it finished
*
* @type {() => void}
*/
private resizeDebouncer: () => void = _.debounce(() => {
@ -184,7 +190,8 @@ export default class UI extends Module {
/**
* Check if one of Toolbar is opened
* Used to prevent global keydowns (for example, Enter) conflicts with Enter-on-toolbar
* @return {boolean}
*
* @returns {boolean}
*/
public get someToolbarOpened(): boolean {
const { Toolbox, BlockSettings, InlineToolbar, ConversionToolbar } = this.Editor;
@ -226,17 +233,19 @@ export default class UI extends Module {
/**
* Check for mobile mode and cache a result
*/
private checkIsMobile() {
private checkIsMobile(): void {
this.isMobile = window.innerWidth < 650;
}
/**
* Makes Editor.js interface
* @return {Promise<void>}
*
* @returns {Promise<void>}
*/
private async make(): Promise<void> {
/**
* Element where we need to append Editor.js
*
* @type {Element}
*/
this.nodes.holder = $.getHolder(this.config.holder);
@ -270,6 +279,7 @@ export default class UI extends Module {
/**
* Load CSS
*/
// eslint-disable-next-line @typescript-eslint/no-var-requires
const styles = require('../../styles/main.css');
/**
@ -340,7 +350,8 @@ export default class UI extends Module {
/**
* All keydowns on document
* @param {Event} event
*
* @param {KeyboardEvent} event - keyboard event
*/
private documentKeydown(event: KeyboardEvent): void {
switch (event.keyCode) {
@ -358,7 +369,8 @@ export default class UI extends Module {
/**
* Ignore all other document's keydown events
* @param {KeyboardEvent} event
*
* @param {KeyboardEvent} event - keyboard event
*/
private defaultBehaviour(event: KeyboardEvent): void {
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`);
@ -384,7 +396,7 @@ export default class UI extends Module {
}
/**
* @param {KeyboardEvent} event
* @param {KeyboardEvent} event - keyboard event
*/
private backspacePressed(event: KeyboardEvent): void {
const { BlockManager, BlockSelection, Caret } = this.Editor;
@ -410,7 +422,8 @@ export default class UI extends Module {
/**
* Enter pressed on document
* @param event
*
* @param {KeyboardEvent} event - keyboard event
*/
private enterPressed(event: KeyboardEvent): void {
const { BlockManager, BlockSelection, Caret } = this.Editor;
@ -468,7 +481,8 @@ export default class UI extends Module {
/**
* All clicks on document
* @param {MouseEvent} event - Click
*
* @param {MouseEvent} event - Click event
*/
private documentClicked(event: MouseEvent): void {
/**
@ -521,6 +535,8 @@ export default class UI extends Module {
* Also:
* - Move and show the Toolbar
* - Set a Caret
*
* @param {MouseEvent | TouchEvent} event - touch or mouse event
*/
private documentTouched(event: MouseEvent | TouchEvent): void {
let clickedNode = event.target as HTMLElement;
@ -571,7 +587,7 @@ export default class UI extends Module {
/**
* All clicks on the redactor zone
*
* @param {MouseEvent} event
* @param {MouseEvent} event - click event
*
* @description
* - By clicks on the Editor's bottom zone:
@ -628,7 +644,8 @@ export default class UI extends Module {
/**
* Handle selection changes on mobile devices
* Uses for showing the Inline Toolbar
* @param {Event} event
*
* @param {Event} event - selection event
*/
private selectionChanged(event: Event): void {
const focusedElement = Selection.anchorElement as Element;

View file

@ -18,7 +18,9 @@ interface Element {
* would be selected by the specified selector string;
* otherwise, returns false.
*
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill}
*
* @param {string} s - selector
*/
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.matchesSelector ||
@ -26,7 +28,7 @@ if (!Element.prototype.matches) {
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function (s) {
function (s): boolean {
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
let i = matches.length;
@ -43,10 +45,12 @@ if (!Element.prototype.matches) {
* matches the selectors given in parameter.
* If there isn't such an ancestor, it returns null.
*
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill}
*
* @param {string} s - selector
*/
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
Element.prototype.closest = function (s): Element | null {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let el = this;
@ -71,20 +75,22 @@ if (!Element.prototype.closest) {
* or DOMString objects before the first child of the ParentNode.
* DOMString objects are inserted as equivalent Text nodes.
*
* {@link https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend#Polyfill}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend#Polyfill}
*
* @param {Node | Node[] | string | string[]} nodes - nodes to prepend
*/
if (!Element.prototype.prepend) {
Element.prototype.prepend = function prepend(nodes: Node|Node[]|any) {
Element.prototype.prepend = function prepend(nodes: Array<Node | string> | Node | string): void {
const docFrag = document.createDocumentFragment();
if (!Array.isArray(nodes)) {
nodes = [ nodes ];
}
nodes.forEach((node: Node|any) => {
nodes.forEach((node: Node | string) => {
const isNode = node instanceof Node;
docFrag.appendChild(isNode ? node : document.createTextNode(String(node)));
docFrag.appendChild(isNode ? node as Node : document.createTextNode(node as string));
});
this.insertBefore(docFrag, this.firstChild);

View file

@ -39,7 +39,7 @@ export default class SelectionUtils {
*
* @returns {{editorWrapper: string, editorZone: string}}
*/
static get CSS(): { editorWrapper: string; editorZone: string } {
public static get CSS(): { editorWrapper: string; editorZone: string } {
return {
editorWrapper: 'codex-editor',
editorZone: 'codex-editor__redactor',
@ -52,7 +52,7 @@ export default class SelectionUtils {
*
* @returns {Node|null}
*/
static get anchorNode(): Node | null {
public static get anchorNode(): Node | null {
const selection = window.getSelection();
return selection ? selection.anchorNode : null;
@ -63,7 +63,7 @@ export default class SelectionUtils {
*
* @returns {Element|null}
*/
static get anchorElement(): Element | null {
public static get anchorElement(): Element | null {
const selection = window.getSelection();
if (!selection) {
@ -89,7 +89,7 @@ export default class SelectionUtils {
*
* @returns {number|null}
*/
static get anchorOffset(): number | null {
public static get anchorOffset(): number | null {
const selection = window.getSelection();
return selection ? selection.anchorOffset : null;
@ -100,7 +100,7 @@ export default class SelectionUtils {
*
* @returns {boolean|null}
*/
static get isCollapsed(): boolean | null {
public static get isCollapsed(): boolean | null {
const selection = window.getSelection();
return selection ? selection.isCollapsed : null;
@ -111,7 +111,7 @@ export default class SelectionUtils {
*
* @returns {boolean}
*/
static get isAtEditor(): boolean {
public static get isAtEditor(): boolean {
const selection = SelectionUtils.get();
/**
@ -140,7 +140,7 @@ export default class SelectionUtils {
*
* @returns {Range|null}
*/
static get range(): Range {
public static get range(): Range | null {
const selection = window.getSelection();
return selection && selection.rangeCount ? selection.getRangeAt(0) : null;
@ -149,9 +149,9 @@ export default class SelectionUtils {
/**
* Calculates position and size of selected text
*
* @returns {{x, y, width, height, top?, left?, bottom?, right?}}
* @returns {DOMRect | ClientRect}
*/
static get rect(): DOMRect | ClientRect {
public static get rect(): DOMRect | ClientRect {
let sel: Selection | MSSelection = (document as Document).selection,
range: TextRange | Range;
@ -224,20 +224,15 @@ export default class SelectionUtils {
*
* @returns {string}
*/
static get text(): string {
public static get text(): string {
return window.getSelection ? window.getSelection().toString() : '';
}
/**
* Returns window SelectionUtils
* {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection}
* Selection instances
*
* @returns {Selection}
* @todo Check if this is still relevant
*/
public static get(): Selection {
return window.getSelection();
}
public instance: Selection = null;
public selection: Selection = null;
@ -261,10 +256,20 @@ export default class SelectionUtils {
private readonly commandBackground: string = 'backColor';
private readonly commandRemoveFormat: string = 'removeFormat';
/**
* Returns window SelectionUtils
* {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection}
*
* @returns {Selection}
*/
public static get(): Selection {
return window.getSelection();
}
/**
* Removes fake background
*/
public removeFakeBackground() {
public removeFakeBackground(): void {
if (!this.isFakeBackgroundEnabled) {
return;
}
@ -276,7 +281,7 @@ export default class SelectionUtils {
/**
* Sets fake background
*/
public setFakeBackground() {
public setFakeBackground(): void {
document.execCommand(this.commandBackground, false, '#a8d6ff');
this.isFakeBackgroundEnabled = true;
@ -329,6 +334,7 @@ export default class SelectionUtils {
* @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 {
@ -402,7 +408,7 @@ export default class SelectionUtils {
/**
* Expands selection range to the passed parent node
*
* @param {HTMLElement} element
* @param {HTMLElement} element - element which contents should be selcted
*/
public expandToTag(element: HTMLElement): void {
const selection = window.getSelection();

View file

@ -1,6 +1,11 @@
import $ from '../../dom';
import { BlockTool, BlockToolData } from '../../../../types';
export interface StubData extends BlockToolData{
title: string;
savedData: BlockToolData;
}
/**
*
*/
@ -38,11 +43,9 @@ export default class Stub implements BlockTool {
private readonly savedData: BlockToolData;
/**
* @param data
* @param config
* @param api
* @param {BlockToolData} data - stub tool data
*/
constructor({ data, config, api }) {
constructor({ data }: {data: StubData}) {
this.title = data.title || 'Error';
this.subtitle = 'The block can not be displayed correctly.';
this.savedData = data.savedData;

View file

@ -22,10 +22,13 @@ declare const VERSION: string;
/**
* @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
* @property {Function} function - function's that must be called asynchronously
*
* @interface ChainData
*/
export interface ChainData {
data?: any;
data?: object;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function: (...args: any[]) => any;
}
@ -79,6 +82,7 @@ function _log(
labeled: boolean,
msg: string,
type = 'log',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args?: any,
style = 'color: inherit'
): void {
@ -172,7 +176,8 @@ export const logLabeled = _log.bind(window, true);
/**
* Returns true if passed key code is printable (a-Z, 0-9, etc) character.
*
* @param {number} keyCode
* @param {number} keyCode - key code
*
* @returns {boolean}
*/
export function isPrintableKey(keyCode: number): boolean {
@ -196,24 +201,24 @@ export function isPrintableKey(keyCode: number): boolean {
export async function sequence(
chains: ChainData[],
// eslint-disable-next-line @typescript-eslint/no-empty-function
success: (data: any) => void = (): void => {},
success: (data: object) => void = (): void => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
fallback: (data: any) => void = (): void => {},
fallback: (data: object) => void = (): void => {}
): Promise<void> {
/**
* Decorator
*
* @param {ChainData} chainData
* @param {ChainData} chainData - Chain data
*
* @param {Function} successCallback
* @param {Function} fallbackCallback
* @param {Function} successCallback - success callback
* @param {Function} fallbackCallback - fail callback
*
* @returns {Promise}
*/
async function waitNextBlock(
chainData: ChainData,
successCallback: (data: any) => void,
fallbackCallback: (data: any) => void
successCallback: (data: object) => void,
fallbackCallback: (data: object) => void
): Promise<void> {
try {
await chainData.function(chainData.data);
@ -240,10 +245,11 @@ export async function sequence(
/**
* Make array from array-like collection
*
* @param {ArrayLike} collection
* @param {ArrayLike} collection - collection to convert to array
*
* @returns {Array}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function array(collection: ArrayLike<any>): any[] {
return Array.prototype.slice.call(collection);
}
@ -251,19 +257,23 @@ export function array(collection: ArrayLike<any>): any[] {
/**
* Check if passed variable is a function
*
* @param {*} fn
* @param {*} fn - function to check
*
* @returns {boolean}
*/
export function isFunction(fn: any): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isFunction(fn: any): fn is Function {
return typeof fn === 'function';
}
/**
* Check if passed function is a class
*
* @param {Function} fn
* @param {Function} fn - function to check
*
* @returns {boolean}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isClass(fn: any): boolean {
return typeof fn === 'function' && /^\s*class\s+/.test(fn.toString());
}
@ -271,7 +281,8 @@ export function isClass(fn: any): boolean {
/**
* Checks if object is empty
*
* @param {object} object
* @param {object} object - object to check
*
* @returns {boolean}
*/
export function isEmpty(object: object): boolean {
@ -288,18 +299,20 @@ export function isEmpty(object: object): boolean {
* @param {*} object - object to check
* @returns {boolean}
*/
export function isPromise(object: any): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isPromise(object: any): object is Promise<any> {
return Promise.resolve(object) === object;
}
/**
* Delays method execution
*
* @param {Function} method
* @param {number} timeout
* @param {Function} method - method to execute
* @param {number} timeout - timeout in ms
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function delay(method: (...args: any[]) => any, timeout: number) {
return function() {
return function (): void {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this,
// eslint-disable-next-line prefer-rest-params
@ -312,8 +325,9 @@ export function delay(method: (...args: any[]) => any, timeout: number) {
/**
* Get file extension
*
* @param {File} file
* @returns string
* @param {File} file - file
*
* @returns {string}
*/
export function getFileExtension(file: File): string {
return file.name.split('.').pop();
@ -322,8 +336,9 @@ export function getFileExtension(file: File): string {
/**
* Check if string is MIME type
*
* @param {string} type
* @returns boolean
* @param {string} type - string to check
*
* @returns {boolean}
*/
export function isValidMimeType(type: string): boolean {
return /^[-\w]+\/([-+\w]+|\*)$/.test(type);
@ -416,22 +431,35 @@ export function getUserOS(): {[key: string]: boolean} {
/**
* Capitalizes first letter of the string
*
* @param {string} text
* @param {string} text - text to capitalize
*
* @returns {string}
*/
export function capitalize(text: string): string {
return text[0].toUpperCase() + text.slice(1);
}
/**
* Return string representation of the object type
*
* @param {*} object - object to get type
*
* @returns {string}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function typeOf(object: any): string {
return Object.prototype.toString.call(object).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
/**
* Merge to objects recursively
*
* @param {object} target
* @param {object[]} sources
* @param {object} target - merge target
* @param {object[]} sources - merge sources
* @returns {object}
*/
export function deepMerge(target, ...sources): {[key: string]: any} {
const isObject = (item) => item && typeOf(item) === 'object';
export function deepMerge<T extends object>(target, ...sources): T {
const isObject = (item): item is object => item && typeOf(item) === 'object';
if (!sources.length) {
return target;
@ -467,15 +495,6 @@ export function deepMerge(target, ...sources): {[key: string]: any} {
*/
export const isTouchSupported: boolean = 'ontouchstart' in document.documentElement;
/**
* Return string representation of the object type
*
* @param {any} object
*/
export function typeOf(object: any): string {
return Object.prototype.toString.call(object).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
/**
* Make shortcut command more human-readable
*
@ -511,7 +530,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 - url to prettify
*/
export function getValidUrl(url: string): string {
try {

View file

@ -2,4 +2,4 @@
* Object returned by Tool's {@link BlockTool#save} method
* Specified by Tool developer, so leave it as object
*/
export type BlockToolData = object;
export type BlockToolData<T extends object = any> = T;

View file

@ -1,10 +1,11 @@
import { ConversionConfig, PasteConfig, SanitizerConfig } from '../configs';
import { BlockToolData } from './block-tool-data';
import { BaseTool, BaseToolConstructable } from './tool';
import {BaseTool, BaseToolConstructable, BaseToolConstructorOptions} from './tool';
import { ToolConfig } from './tool-config';
import { API } from '../index';
import { PasteEvent } from './paste-events';
import { MoveEvent } from './hook-events';
/**
* Describe Block Tool object
* @see {@link docs/tools.md}
@ -73,6 +74,15 @@ export interface BlockTool extends BaseTool {
moved?(event: MoveEvent): void;
}
/**
* Describe constructor parameters
*/
export interface BlockToolConstructorOptions extends BaseToolConstructorOptions {
api: API;
data: BlockToolData;
config?: ToolConfig;
}
export interface BlockToolConstructable extends BaseToolConstructable {
/**
* Tool's Toolbox settings
@ -101,7 +111,10 @@ export interface BlockToolConstructable extends BaseToolConstructable {
/**
* @constructor
*
* @param {BlockToolConstructorOptions} config - constructor parameters
*
* @return {BlockTool}
*/
new(config: { api: API, config: ToolConfig, data: BlockToolData }): BlockTool;
new(config: BlockToolConstructorOptions): BlockTool;
}

View file

@ -12,4 +12,4 @@ export * from './paste-events';
export * from './hook-events';
export type Tool = BaseTool | BlockTool | InlineTool;
export type ToolConstructable = BaseToolConstructable | BlockToolConstructable | InlineToolConstructable;
export type ToolConstructable = BlockToolConstructable | InlineToolConstructable;

View file

@ -1,4 +1,5 @@
import {BaseTool, BaseToolConstructable} from './tool';
import {API, ToolConfig} from "../index";
/**
* Base structure for the Inline Toolbar Tool
*/
@ -34,4 +35,20 @@ export interface InlineTool extends BaseTool {
clear?(): void;
}
export interface InlineToolConstructable extends BaseToolConstructable {}
/**
* Describe constructor parameters
*/
export interface InlineToolConstructorOptions {
api: API;
config?: ToolConfig;
}
export interface InlineToolConstructable extends BaseToolConstructable {
/**
* Constructor
*
* @param {InlineToolConstructorOptions} config - constructor parameters
*/
new(config: InlineToolConstructorOptions): BaseTool;
}

View file

@ -1,4 +1,4 @@
import {API, ToolSettings} from '../index';
import {API, BlockToolData, ToolSettings} from '../index';
import {ToolConfig} from './tool-config';
import {SanitizerConfig} from '../configs';
@ -30,11 +30,6 @@ export interface BaseToolConstructable {
*/
title?: string;
/**
* Describe constructor parameters
*/
new (config: {api: API, config?: ToolSettings}): BaseTool;
/**
* Tool`s prepare method. Can be async
* @param data