diff --git a/src/abstract/moduleLegacy.js b/src/abstract/moduleLegacy.js
index 1594b8de9..9023d830d 100644
--- a/src/abstract/moduleLegacy.js
+++ b/src/abstract/moduleLegacy.js
@@ -1,10 +1,10 @@
import { isString, isElement } from 'underscore';
import { createId, deepMerge, isDef } from 'utils/mixins';
-export default {
+export default class ModuleLegacy {
getConfig(name) {
return this.__getConfig(name);
- },
+ }
getProjectData(data) {
const obj = {};
@@ -13,7 +13,7 @@ export default {
obj[key] = data || this.getAll();
}
return obj;
- },
+ }
loadProjectData(data = {}, { all, onResult, reset } = {}) {
const key = this.storageKey;
@@ -38,35 +38,35 @@ export default {
}
return result;
- },
+ }
clear(opts = {}) {
const { all } = this;
all && all.reset(null, opts);
return this;
- },
+ }
__getConfig(name) {
const res = this.config || {};
return name ? res[name] : res;
- },
+ }
getAll(opts = {}) {
return this.all ? (opts.array ? [...this.all.models] : this.all) : [];
- },
+ }
getAllMap() {
return this.getAll().reduce((acc, i) => {
acc[i.get(i.idAttribute)] = i;
return acc;
}, {});
- },
+ }
__initConfig(def = {}, conf = {}) {
this.config = deepMerge(def, conf);
this.em = this.config.em;
this.cls = [];
- },
+ }
__initListen(opts = {}) {
const { all, em, events } = this;
@@ -87,7 +87,7 @@ export default {
[em, all].map(md => md.trigger(event, model, opt));
});
});
- },
+ }
__remove(model, opts = {}) {
const { em } = this;
@@ -98,14 +98,14 @@ export default {
};
!opts.silent && em && em.trigger(this.events.removeBefore, md, rm, opts);
return !opts.abort && rm();
- },
+ }
__catchAllEvent(event, model, coll, opts) {
const { em, events } = this;
const options = opts || coll;
em && events.all && em.trigger(events.all, { event, model, options });
this.__onAllEvent();
- },
+ }
__appendTo() {
const elTo = this.getConfig().appendTo;
@@ -115,13 +115,13 @@ export default {
if (!el) return this.__logWarn('"appendTo" element not found');
el.appendChild(this.render());
}
- },
+ }
- __onAllEvent() {},
+ __onAllEvent() {}
__logWarn(str, opts) {
this.em.logWarning(`[${this.name}]: ${str}`, opts);
- },
+ }
_createId(len = 16) {
const all = this.getAll();
@@ -134,19 +134,19 @@ export default {
} while (allMap[id]);
return id;
- },
+ }
__listenAdd(model, event) {
model.on('add', (m, c, o) => this.em.trigger(event, m, o));
- },
+ }
__listenRemove(model, event) {
model.on('remove', (m, c, o) => this.em.trigger(event, m, o));
- },
+ }
__listenUpdate(model, event) {
model.on('change', (p, c) => this.em.trigger(event, p, p.changedAttributes(), c));
- },
+ }
__destroy() {
this.cls.forEach(coll => {
@@ -157,5 +157,5 @@ export default {
this.config = 0;
this.view?.remove();
this.view = 0;
- },
-};
+ }
+}
diff --git a/src/block_manager/index.js b/src/block_manager/index.js
index e4e418919..9d6dd8bf0 100644
--- a/src/block_manager/index.js
+++ b/src/block_manager/index.js
@@ -64,312 +64,306 @@ export const evDragStart = `${evDrag}:start`;
export const evDragStop = `${evDrag}:stop`;
export const evCustom = `${evPfx}custom`;
-export default () => {
- var c = {};
- var blocks, blocksVisible, blocksView;
- var categories = [];
-
- return {
- ...Module,
-
- name: 'BlockManager',
-
- Block,
-
- Blocks,
-
- Category,
-
- Categories,
-
- events: {
- all: evAll,
- update: evUpdate,
- add: evAdd,
- remove: evRemove,
- removeBefore: evRemoveBefore,
- drag: evDrag,
- dragStart: evDragStart,
- dragEnd: evDragStop,
- custom: evCustom,
- },
-
- init(config = {}) {
- c = { ...defaults, ...config };
- const { em } = c;
- this.em = em;
-
- // Global blocks collection
- blocks = new Blocks(c.blocks);
- blocksVisible = new Blocks(blocks.models);
- categories = new Categories();
- this.all = blocks;
- this.__initListen();
-
- // Setup the sync between the global and public collections
- blocks.on('add', model => blocksVisible.add(model));
- blocks.on('remove', model => blocksVisible.remove(model));
- blocks.on('reset', coll => blocksVisible.reset(coll.models));
-
- return this;
- },
-
- __trgCustom() {
- this.em.trigger(this.events.custom, this.__customData());
- },
-
- __customData() {
- const bhv = this.__getBehaviour();
- return {
- bm: this,
- blocks: this.getAll().models,
- container: bhv.container,
- dragStart: (block, ev) => this.startDrag(block, ev),
- drag: ev => this.__drag(ev),
- dragStop: cancel => this.endDrag(cancel),
- };
- },
-
- __startDrag(block, ev) {
- const { em, events } = this;
- const content = block.getContent ? block.getContent() : block;
- this._dragBlock = block;
- em.set({ dragResult: null, dragContent: content });
- [em, blocks].map(i => i.trigger(events.dragStart, block, ev));
- },
-
- __drag(ev) {
- const { em, events } = this;
- const block = this._dragBlock;
- [em, blocks].map(i => i.trigger(events.drag, block, ev));
- },
-
- __endDrag() {
- const { em, events } = this;
- const block = this._dragBlock;
- const cmp = em.get('dragResult');
- this._dragBlock = null;
-
- if (cmp) {
- const oldKey = 'activeOnRender';
- const oldActive = cmp.get && cmp.get(oldKey);
- const toActive = block.get('activate') || oldActive;
- const toSelect = block.get('select');
- const first = isArray(cmp) ? cmp[0] : cmp;
-
- if (toSelect || (toActive && toSelect !== false)) {
- em.setSelected(first);
- }
-
- if (toActive) {
- first.trigger('active');
- oldActive && first.unset(oldKey);
- }
-
- if (block.get('resetId')) {
- first.onAll(block => block.resetId());
- }
- }
+export default class BlockManager extends Module {
+ name = 'BlockManager';
- em.set({ dragResult: null, dragContent: null });
- [em, blocks].map(i => i.trigger(events.dragEnd, cmp, block));
- },
-
- __getFrameViews() {
- return this.em
- .get('Canvas')
- .getFrames()
- .map(frame => frame.view);
- },
-
- __behaviour(opts = {}) {
- return (this._bhv = {
- ...(this._bhv || {}),
- ...opts,
- });
- },
-
- __getBehaviour() {
- return this._bhv || {};
- },
-
- startDrag(block, ev) {
- this.__startDrag(block, ev);
- this.__getFrameViews().forEach(fv => fv.droppable.startCustom());
- },
-
- endDrag(cancel) {
- this.__getFrameViews().forEach(fv => fv.droppable.endCustom(cancel));
- this.__endDrag();
- },
-
- /**
- * Get configuration object
- * @return {Object}
- */
- getConfig() {
- return c;
- },
-
- postRender() {
- const collection = blocksVisible;
- blocksView = new BlocksView({ collection, categories }, c);
- const elTo = this.getConfig().appendTo;
-
- if (elTo) {
- const el = isElement(elTo) ? elTo : document.querySelector(elTo);
- if (!el) return this.__logWarn('"appendTo" element not found');
- el.appendChild(this.render(blocksVisible.models));
- }
+ Block = Block;
- this.__trgCustom();
- },
-
- /**
- * Add new block.
- * @param {String} id Block ID
- * @param {[Block]} props Block properties
- * @returns {[Block]} Added block
- * @example
- * blockManager.add('h1-block', {
- * label: 'Heading',
- * content: '
Put your title here
',
- * category: 'Basic',
- * attributes: {
- * title: 'Insert h1 block'
- * }
- * });
- */
- add(id, props, opts = {}) {
- const prp = props || {};
- prp.id = id;
- return blocks.add(prp, opts);
- },
-
- /**
- * Get the block by id.
- * @param {String} id Block id
- * @returns {[Block]}
- * @example
- * const block = blockManager.get('h1-block');
- * console.log(JSON.stringify(block));
- * // {label: 'Heading', content: 'Put your ...', ...}
- */
- get(id) {
- return blocks.get(id);
- },
-
- /**
- * Return all blocks.
- * @returns {Collection<[Block]>}
- * @example
- * const blocks = blockManager.getAll();
- * console.log(JSON.stringify(blocks));
- * // [{label: 'Heading', content: 'Put your ...'}, ...]
- */
- getAll() {
- return blocks;
- },
-
- /**
- * Return the visible collection, which containes blocks actually rendered
- * @returns {Collection<[Block]>}
- */
- getAllVisible() {
- return blocksVisible;
- },
-
- /**
- * Remove block.
- * @param {String|[Block]} block Block or block ID
- * @returns {[Block]} Removed block
- * @example
- * const removed = blockManager.remove('BLOCK_ID');
- * // or by passing the Block
- * const block = blockManager.get('BLOCK_ID');
- * blockManager.remove(block);
- */
- remove(block, opts = {}) {
- return this.__remove(block, opts);
- },
-
- /**
- * Get all available categories.
- * It's possible to add categories only within blocks via 'add()' method
- * @return {Array|Collection}
- */
- getCategories() {
- return categories;
- },
-
- /**
- * Return the Blocks container element
- * @return {HTMLElement}
- */
- getContainer() {
- return blocksView.el;
- },
-
- /**
- * Render blocks
- * @param {Array} blocks Blocks to render, without the argument will render all global blocks
- * @param {Object} [opts={}] Options
- * @param {Boolean} [opts.external] Render blocks in a new container (HTMLElement will be returned)
- * @param {Boolean} [opts.ignoreCategories] Render blocks without categories
- * @return {HTMLElement} Rendered element
- * @example
- * // Render all blocks (inside the global collection)
- * blockManager.render();
- *
- * // Render new set of blocks
- * const blocks = blockManager.getAll();
- * const filtered = blocks.filter(block => block.get('category') == 'sections')
- *
- * blockManager.render(filtered);
- * // Or a new set from an array
- * blockManager.render([
- * {label: 'Label text', content: '
Content
'}
- * ]);
- *
- * // Back to blocks from the global collection
- * blockManager.render();
- *
- * // You can also render your blocks outside of the main block container
- * const newBlocksEl = blockManager.render(filtered, { external: true });
- * document.getElementById('some-id').appendChild(newBlocksEl);
- */
- render(blocks, opts = {}) {
- const toRender = blocks || this.getAll().models;
-
- if (opts.external) {
- const collection = new Blocks(toRender);
- return new BlocksView({ collection, categories }, { ...c, ...opts }).render().el;
- }
+ Blocks = Blocks;
- if (blocksView) {
- blocksView.updateConfig(opts);
- blocksView.collection.reset(toRender);
+ Category = Category;
- if (!blocksView.rendered) {
- blocksView.render();
- blocksView.rendered = 1;
- }
- }
+ Categories = Categories;
- return this.getContainer();
- },
-
- destroy() {
- const colls = [blocks, blocksVisible, categories];
- colls.map(c => c.stopListening());
- colls.map(c => c.reset());
- blocksView && blocksView.remove();
- c = {};
- blocks = {};
- blocksVisible = {};
- blocksView = {};
- categories = [];
- this.all = {};
- },
+ events = {
+ all: evAll,
+ update: evUpdate,
+ add: evAdd,
+ remove: evRemove,
+ removeBefore: evRemoveBefore,
+ drag: evDrag,
+ dragStart: evDragStart,
+ dragEnd: evDragStop,
+ custom: evCustom,
};
-};
+
+ init(config = {}) {
+ this.c = { ...defaults, ...config };
+ const { em } = this.c;
+ this.em = em;
+
+ // Global blocks collection
+ this.blocks = new Blocks(this.c.blocks);
+ this.blocksVisible = new Blocks(this.blocks.models);
+ this.categories = new Categories();
+ this.all = this.blocks;
+ this.__initListen();
+
+ // Setup the sync between the global and public collections
+ this.blocks.on('add', model => this.blocksVisible.add(model));
+ this.blocks.on('remove', model => this.blocksVisible.remove(model));
+ this.blocks.on('reset', coll => this.blocksVisible.reset(coll.models));
+
+ return this;
+ }
+
+ __trgCustom() {
+ this.em.trigger(this.events.custom, this.__customData());
+ }
+
+ __customData() {
+ const bhv = this.__getBehaviour();
+ return {
+ bm: this,
+ blocks: this.getAll().models,
+ container: bhv.container,
+ dragStart: (block, ev) => this.startDrag(block, ev),
+ drag: ev => this.__drag(ev),
+ dragStop: cancel => this.endDrag(cancel),
+ };
+ }
+
+ __startDrag(block, ev) {
+ const { em, events, blocks } = this;
+ const content = block.getContent ? block.getContent() : block;
+ this._dragBlock = block;
+ em.set({ dragResult: null, dragContent: content });
+ [em, blocks].map(i => i.trigger(events.dragStart, block, ev));
+ }
+
+ __drag(ev) {
+ const { em, events, blocks } = this;
+ const block = this._dragBlock;
+ [em, blocks].map(i => i.trigger(events.drag, block, ev));
+ }
+
+ __endDrag() {
+ const { em, events, blocks } = this;
+ const block = this._dragBlock;
+ const cmp = em.get('dragResult');
+ this._dragBlock = null;
+
+ if (cmp) {
+ const oldKey = 'activeOnRender';
+ const oldActive = cmp.get && cmp.get(oldKey);
+ const toActive = block.get('activate') || oldActive;
+ const toSelect = block.get('select');
+ const first = isArray(cmp) ? cmp[0] : cmp;
+
+ if (toSelect || (toActive && toSelect !== false)) {
+ em.setSelected(first);
+ }
+
+ if (toActive) {
+ first.trigger('active');
+ oldActive && first.unset(oldKey);
+ }
+
+ if (block.get('resetId')) {
+ first.onAll(block => block.resetId());
+ }
+ }
+
+ em.set({ dragResult: null, dragContent: null });
+ [em, blocks].map(i => i.trigger(events.dragEnd, cmp, block));
+ }
+
+ __getFrameViews() {
+ return this.em
+ .get('Canvas')
+ .getFrames()
+ .map(frame => frame.view);
+ }
+
+ __behaviour(opts = {}) {
+ return (this._bhv = {
+ ...(this._bhv || {}),
+ ...opts,
+ });
+ }
+
+ __getBehaviour() {
+ return this._bhv || {};
+ }
+
+ startDrag(block, ev) {
+ this.__startDrag(block, ev);
+ this.__getFrameViews().forEach(fv => fv.droppable.startCustom());
+ }
+
+ endDrag(cancel) {
+ this.__getFrameViews().forEach(fv => fv.droppable.endCustom(cancel));
+ this.__endDrag();
+ }
+
+ /**
+ * Get configuration object
+ * @return {Object}
+ */
+ getConfig() {
+ return this.c;
+ }
+
+ postRender() {
+ const { categories } = this;
+ const collection = this.blocksVisible;
+ this.blocksView = new BlocksView({ collection, categories }, this.c);
+ const elTo = this.getConfig().appendTo;
+
+ if (elTo) {
+ const el = isElement(elTo) ? elTo : document.querySelector(elTo);
+ if (!el) return this.__logWarn('"appendTo" element not found');
+ el.appendChild(this.render(this.blocksVisible.models));
+ }
+
+ this.__trgCustom();
+ }
+
+ /**
+ * Add new block.
+ * @param {String} id Block ID
+ * @param {[Block]} props Block properties
+ * @returns {[Block]} Added block
+ * @example
+ * blockManager.add('h1-block', {
+ * label: 'Heading',
+ * content: 'Put your title here
',
+ * category: 'Basic',
+ * attributes: {
+ * title: 'Insert h1 block'
+ * }
+ * });
+ */
+ add(id, props, opts = {}) {
+ const prp = props || {};
+ prp.id = id;
+ return this.blocks.add(prp, opts);
+ }
+
+ /**
+ * Get the block by id.
+ * @param {String} id Block id
+ * @returns {[Block]}
+ * @example
+ * const block = blockManager.get('h1-block');
+ * console.log(JSON.stringify(block));
+ * // {label: 'Heading', content: 'Put your ...', ...}
+ */
+ get(id) {
+ return this.blocks.get(id);
+ }
+
+ /**
+ * Return all blocks.
+ * @returns {Collection<[Block]>}
+ * @example
+ * const blocks = blockManager.getAll();
+ * console.log(JSON.stringify(blocks));
+ * // [{label: 'Heading', content: 'Put your ...'}, ...]
+ */
+ getAll() {
+ return this.blocks;
+ }
+
+ /**
+ * Return the visible collection, which containes blocks actually rendered
+ * @returns {Collection<[Block]>}
+ */
+ getAllVisible() {
+ return this.blocksVisible;
+ }
+
+ /**
+ * Remove block.
+ * @param {String|[Block]} block Block or block ID
+ * @returns {[Block]} Removed block
+ * @example
+ * const removed = blockManager.remove('BLOCK_ID');
+ * // or by passing the Block
+ * const block = blockManager.get('BLOCK_ID');
+ * blockManager.remove(block);
+ */
+ remove(block, opts = {}) {
+ return this.__remove(block, opts);
+ }
+
+ /**
+ * Get all available categories.
+ * It's possible to add categories only within blocks via 'add()' method
+ * @return {Array|Collection}
+ */
+ getCategories() {
+ return this.categories;
+ }
+
+ /**
+ * Return the Blocks container element
+ * @return {HTMLElement}
+ */
+ getContainer() {
+ return this.blocksView.el;
+ }
+
+ /**
+ * Render blocks
+ * @param {Array} blocks Blocks to render, without the argument will render all global blocks
+ * @param {Object} [opts={}] Options
+ * @param {Boolean} [opts.external] Render blocks in a new container (HTMLElement will be returned)
+ * @param {Boolean} [opts.ignoreCategories] Render blocks without categories
+ * @return {HTMLElement} Rendered element
+ * @example
+ * // Render all blocks (inside the global collection)
+ * blockManager.render();
+ *
+ * // Render new set of blocks
+ * const blocks = blockManager.getAll();
+ * const filtered = blocks.filter(block => block.get('category') == 'sections')
+ *
+ * blockManager.render(filtered);
+ * // Or a new set from an array
+ * blockManager.render([
+ * {label: 'Label text', content: '
Content
'}
+ * ]);
+ *
+ * // Back to blocks from the global collection
+ * blockManager.render();
+ *
+ * // You can also render your blocks outside of the main block container
+ * const newBlocksEl = blockManager.render(filtered, { external: true });
+ * document.getElementById('some-id').appendChild(newBlocksEl);
+ */
+ render(blocks, opts = {}) {
+ const { categories } = this.categories;
+ const toRender = blocks || this.getAll().models;
+
+ if (opts.external) {
+ const collection = new Blocks(toRender);
+ return new BlocksView({ collection, categories }, { ...this.c, ...opts }).render().el;
+ }
+
+ if (this.blocksView) {
+ this.blocksView.updateConfig(opts);
+ this.blocksView.collection.reset(toRender);
+
+ if (!this.blocksView.rendered) {
+ this.blocksView.render();
+ this.blocksView.rendered = 1;
+ }
+ }
+
+ return this.getContainer();
+ }
+
+ destroy() {
+ const colls = [this.blocks, this.blocksVisible, this.categories];
+ colls.map(c => c.stopListening());
+ colls.map(c => c.reset());
+ this.blocksView?.remove();
+ this.c = {};
+ this.blocks = {};
+ this.blocksVisible = {};
+ this.blocksView = {};
+ this.categories = [];
+ this.all = {};
+ }
+}
diff --git a/src/css_composer/index.js b/src/css_composer/index.js
index 4ab97490f..c627d3be4 100644
--- a/src/css_composer/index.js
+++ b/src/css_composer/index.js
@@ -37,450 +37,442 @@ import CssRule from './model/CssRule';
import CssRules from './model/CssRules';
import CssRulesView from './view/CssRulesView';
-export default () => {
- let em;
- var c = {};
- var rules, rulesView;
-
- return {
- ...Module,
-
- Selectors,
-
- /**
- * Name of the module
- * @type {String}
- * @private
- */
- name: 'CssComposer',
-
- storageKey: 'styles',
-
- getConfig() {
- return c;
- },
-
- /**
- * Initializes module. Automatically called with a new instance of the editor
- * @param {Object} config Configurations
- * @private
- */
- init(config) {
- c = config || {};
- for (var name in defaults) {
- if (!(name in c)) c[name] = defaults[name];
- }
-
- var ppfx = c.pStylePrefix;
- if (ppfx) c.stylePrefix = ppfx + c.stylePrefix;
-
- var elStyle = (c.em && c.em.config.style) || '';
- c.rules = elStyle || c.rules;
-
- em = c.em;
- rules = new CssRules([], c);
- return this;
- },
-
- /**
- * On load callback
- * @private
- */
- onLoad() {
- rules.add(c.rules, { silent: 1 });
- },
-
- /**
- * Do stuff after load
- * @param {Editor} em
- * @private
- */
- postLoad() {
- const um = em && em.get('UndoManager');
- um && um.add(this.getAll());
- },
-
- store() {
- return this.getProjectData();
- },
-
- load(data) {
- return this.loadProjectData(data);
- },
-
- /**
- * Add new rule to the collection, if not yet exists with the same selectors
- * @param {Array} selectors Array of selectors
- * @param {String} state Css rule state
- * @param {String} width For which device this style is oriented
- * @param {Object} props Other props for the rule
- * @param {Object} opts Options for the add of new rule
- * @return {Model}
- * @private
- * @example
- * var sm = editor.SelectorManager;
- * var sel1 = sm.add('myClass1');
- * var sel2 = sm.add('myClass2');
- * var rule = cssComposer.add([sel1, sel2], 'hover');
- * rule.set('style', {
- * width: '100px',
- * color: '#fff',
- * });
- * */
- add(selectors, state, width, opts = {}, addOpts = {}) {
- var s = state || '';
- var w = width || '';
- var opt = { ...opts };
- var rule = this.get(selectors, s, w, opt);
-
- // do not create rules that were found before
- // unless this is a single at-rule, for which multiple declarations
- // make sense (e.g. multiple `@font-type`s)
- if (rule && rule.config && !rule.config.singleAtRule) {
- return rule;
- } else {
- opt.state = s;
- opt.mediaText = w;
- opt.selectors = [];
- w && (opt.atRuleType = 'media');
- rule = new CssRule(opt, c);
- rule.get('selectors').add(selectors, addOpts);
- rules.add(rule, addOpts);
- return rule;
- }
- },
-
- /**
- * Get the rule
- * @param {String|Array} selectors Array of selectors or selector string, eg `.myClass1.myClass2`
- * @param {String} state Css rule state, eg. 'hover'
- * @param {String} width Media rule value, eg. '(max-width: 992px)'
- * @param {Object} ruleProps Other rule props
- * @return {Model|null}
- * @private
- * @example
- * const sm = editor.SelectorManager;
- * const sel1 = sm.add('myClass1');
- * const sel2 = sm.add('myClass2');
- * const rule = cssComposer.get([sel1, sel2], 'hover', '(max-width: 992px)');
- * // Update the style
- * rule.set('style', {
- * width: '300px',
- * color: '#000',
- * });
- * */
- get(selectors, state, width, ruleProps) {
- let slc = selectors;
- if (isString(selectors)) {
- const sm = em.get('SelectorManager');
- const singleSel = selectors.split(',')[0].trim();
- const node = em.get('Parser').parserCss.checkNode({ selectors: singleSel })[0];
- slc = sm.get(node.selectors);
- }
- return rules.find(rule => rule.compare(slc, state, width, ruleProps)) || null;
- },
-
- getAll() {
- return rules;
- },
-
- /**
- * Add a raw collection of rule objects
- * This method overrides styles, in case, of already defined rule
- * @param {String|Array