Skip to content

Commit

Permalink
Merge pull request #34 from editor-js/feature/read-only-mode
Browse files Browse the repository at this point in the history
Add read-only mode support
  • Loading branch information
neSpecc committed Sep 29, 2020
2 parents 034240d + a21232c commit 9d3d4b5
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 260 deletions.
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@editorjs/embed",
"version": "2.3.1",
"version": "2.4.0",
"keywords": [
"codex editor",
"embed",
Expand Down
95 changes: 57 additions & 38 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
import SERVICES from './services';
import './index.css';
import {debounce} from 'debounce';
import { debounce } from 'debounce';

/**
* @typedef {Object} EmbedData
* @typedef {object} EmbedData
* @description Embed Tool data
* @property {string} service - service name
* @property {string} url - source URL of embedded content
* @property {string} embed - URL to source embed page
* @property {number} [width] - embedded content width
* @property {number} [height] - embedded content height
* @property {string} [caption] - content caption
*
* @typedef {Object} Service
*/
/**
* @typedef {object} Service
* @description Service configuration object
* @property {RegExp} regex - pattern of source URLs
* @property {string} embedUrl - URL scheme to embedded page. Use '<%= remote_id %>' to define a place to insert resource id
* @property {string} html - iframe which contains embedded content
* @property {number} [height] - iframe height
* @property {number} [width] - iframe width
* @property {Function} [id] - function to get resource id from RegExp groups
*
* @typedef {Object} EmbedConfig
*/
/**
* @typedef {object} EmbedConfig
* @description Embed tool configuration object
* @property {Object} [services] - additional services provided by user. Each property should contain Service object
* @property {object} [services] - additional services provided by user. Each property should contain Service object
*/

/**
* @class Embed
* @classdesc Embed Tool for Editor.js 2.0
*
* @property {Object} api - Editor.js API
* @property {object} api - Editor.js API
* @property {EmbedData} _data - private property with Embed data
* @property {HTMLElement} element - embedded content container
*
* @property {Object} services - static property with available services
* @property {Object} patterns - static property with patterns for paste handling configuration
* @property {object} services - static property with available services
* @property {object} patterns - static property with patterns for paste handling configuration
*/
export default class Embed {
/**
* @param {{data: EmbedData, config: EmbedConfig, api: object}}
* data — previously saved data
* config - user config for Tool
* api - Editor.js API
* readOnly - read-only mode flag
*/
constructor({data, api}) {
constructor({ data, api, readOnly }) {
this.api = api;
this._data = {};
this.element = null;
this.readOnly = readOnly;

this.data = data;
}
Expand All @@ -66,7 +68,7 @@ export default class Embed {
throw Error('Embed Tool data should be object');
}

const {service, source, embed, width, height, caption = ''} = data;
const { service, source, embed, width, height, caption = '' } = data;

this._data = {
service: service || this.data.service,
Expand All @@ -85,7 +87,7 @@ export default class Embed {
}

/**
* @return {EmbedData}
* @returns {EmbedData}
*/
get data() {
if (this.element) {
Expand All @@ -99,7 +101,8 @@ export default class Embed {

/**
* Get plugin styles
* @return {Object}
*
* @returns {object}
*/
get CSS() {
return {
Expand All @@ -110,14 +113,14 @@ export default class Embed {
preloader: 'embed-tool__preloader',
caption: 'embed-tool__caption',
url: 'embed-tool__url',
content: 'embed-tool__content'
content: 'embed-tool__content',
};
}

/**
* Render Embed tool content
*
* @return {HTMLElement}
* @returns {HTMLElement}
*/
render() {
if (!this.data.service) {
Expand All @@ -128,7 +131,7 @@ export default class Embed {
return container;
}

const {html} = Embed.services[this.data.service];
const { html } = Embed.services[this.data.service];
const container = document.createElement('div');
const caption = document.createElement('div');
const template = document.createElement('template');
Expand All @@ -139,7 +142,7 @@ export default class Embed {

container.appendChild(preloader);

caption.contentEditable = true;
caption.contentEditable = !this.readOnly;
caption.dataset.placeholder = 'Enter a caption';
caption.innerHTML = this.data.caption || '';

Expand All @@ -164,7 +167,8 @@ export default class Embed {

/**
* Creates preloader to append to container while data is loading
* @return {HTMLElement} preloader
*
* @returns {HTMLElement}
*/
createPreloader() {
const preloader = document.createElement('preloader');
Expand All @@ -183,7 +187,7 @@ export default class Embed {
/**
* Save current content and return EmbedData object
*
* @return {EmbedData}
* @returns {EmbedData}
*/
save() {
return this.data;
Expand All @@ -193,12 +197,12 @@ export default class Embed {
* Handle pasted url and return Service object
*
* @param {PasteEvent} event- event with pasted data
* @return {Service}
* @returns {Service}
*/
onPaste(event) {
const {key: service, data: url} = event.detail;
const { key: service, data: url } = event.detail;

const {regex, embedUrl, width, height, id = (ids) => ids.shift()} = Embed.services[service];
const { regex, embedUrl, width, height, id = (ids) => ids.shift() } = Embed.services[service];
const result = regex.exec(url).slice(1);
const embed = embedUrl.replace(/<\%\= remote\_id \%\>/g, id(result));

Expand All @@ -207,7 +211,7 @@ export default class Embed {
source: url,
embed,
width,
height
height,
};
}

Expand All @@ -216,8 +220,8 @@ export default class Embed {
*
* @param {EmbedConfig} config
*/
static prepare({config = {}}) {
let {services = {}} = config;
static prepare({ config = {} }) {
const { services = {} } = config;

let entries = Object.entries(SERVICES);

Expand All @@ -235,15 +239,15 @@ export default class Embed {
})
.filter(([key, service]) => Embed.checkServiceConfig(service))
.map(([key, service]) => {
const {regex, embedUrl, html, height, width, id} = service;
const { regex, embedUrl, html, height, width, id } = service;

return [key, {
regex,
embedUrl,
html,
height,
width,
id
id,
} ];
});

Expand All @@ -256,10 +260,12 @@ export default class Embed {
Embed.services = entries.reduce((result, [key, service]) => {
if (!(key in result)) {
result[key] = service;

return result;
}

result[key] = Object.assign({}, result[key], service);

return result;
}, {});

Expand All @@ -275,14 +281,14 @@ export default class Embed {
* Check if Service config is valid
*
* @param {Service} config
* @return {boolean}
* @returns {boolean}
*/
static checkServiceConfig(config) {
const {regex, embedUrl, html, height, width, id} = config;
const { regex, embedUrl, html, height, width, id } = config;

let isValid = regex && regex instanceof RegExp
&& embedUrl && typeof embedUrl === 'string'
&& html && typeof html === 'string';
let isValid = regex && regex instanceof RegExp &&
embedUrl && typeof embedUrl === 'string' &&
html && typeof html === 'string';

isValid = isValid && (id !== undefined ? id instanceof Function : true);
isValid = isValid && (height !== undefined ? Number.isFinite(height) : true);
Expand All @@ -296,14 +302,24 @@ export default class Embed {
*/
static get pasteConfig() {
return {
patterns: Embed.patterns
patterns: Embed.patterns,
};
}

/**
* Notify core that read-only mode is supported
*
* @returns {boolean}
*/
static get isReadOnlySupported() {
return true;
}

/**
* Checks that mutations in DOM have finished after appending iframe content
*
* @param {HTMLElement} targetNode - HTML-element mutations of which to listen
* @return {Promise<any>} - result that all mutations have finished
* @returns {Promise<any>} - result that all mutations have finished
*/
embedIsReady(targetNode) {
const PRELOADER_DELAY = 450;
Expand All @@ -312,7 +328,10 @@ export default class Embed {

return new Promise((resolve, reject) => {
observer = new MutationObserver(debounce(resolve, PRELOADER_DELAY));
observer.observe(targetNode, {childList: true, subtree: true});
observer.observe(targetNode, {
childList: true,
subtree: true,
});
}).then(() => {
observer.disconnect();
});
Expand Down
Loading

0 comments on commit 9d3d4b5

Please sign in to comment.