From 0a12930eb37b55c8749322b20efbd4c1b535f5df Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 4 Aug 2021 08:20:36 +0200 Subject: [PATCH] Refactor for headless feature --- src/asset_manager/index.js | 41 ++++++++++++---------- src/canvas/index.js | 9 ++--- src/commands/view/OpenAssets.js | 7 ++-- src/commands/view/OpenTraitManager.js | 3 +- src/css_composer/index.js | 11 +++--- src/device_manager/index.js | 12 ++++--- src/editor/index.js | 17 ++++----- src/i18n/index.js | 3 +- src/index.js | 9 +++-- src/modal_dialog/index.js | 11 +++--- src/navigator/index.js | 19 +++++----- src/panels/index.js | 13 +++---- src/rich_text_editor/index.js | 3 +- src/selector_manager/index.js | 26 +++++++------- src/selector_manager/view/ClassTagsView.js | 10 +----- src/style_manager/index.js | 13 +++---- src/style_manager/model/PropertyInteger.js | 3 +- src/style_manager/view/SectorsView.js | 1 + src/trait_manager/index.js | 40 +++++++++++++++------ src/trait_manager/view/TraitsView.js | 14 -------- test/specs/grapesjs/headless.js | 3 +- 21 files changed, 142 insertions(+), 126 deletions(-) diff --git a/src/asset_manager/index.js b/src/asset_manager/index.js index de0e6ed06..1bbd8f309 100644 --- a/src/asset_manager/index.js +++ b/src/asset_manager/index.js @@ -37,7 +37,7 @@ import FileUpload from './view/FileUploader'; export default () => { let c = {}; - let assets, am, fu; + let assets, assetsVis, am, fu; return { /** @@ -79,15 +79,7 @@ export default () => { // Global assets collection assets = new Assets([]); - const obj = { - // Collection visible in asset manager - collection: new Assets([]), - globalCollection: assets, - config: c - }; - fu = new FileUpload(obj); - obj.fu = fu; - am = new AssetsView(obj); + assetsVis = new Assets([]); // Setup the sync between the global and public collections assets.listenTo(assets, 'add', model => { @@ -159,7 +151,7 @@ export default () => { * @return {Collection} */ getAllVisible() { - return am.collection; + return assetsVis; }, /** @@ -251,12 +243,25 @@ export default () => { */ render(assets) { const toRender = assets || this.getAll().models; + fu && fu.remove(); + am && am.remove(); + + if (!am) { + const obj = { + collection: assetsVis, // Collection visible in asset manager + globalCollection: assets, + config: c + }; + fu = new FileUpload(obj); + obj.fu = fu; + am = new AssetsView(obj); + } if (!am.rendered) { am.render(); } - am.collection.reset(toRender); + assetsVis.reset(toRender); return this.getContainer(); }, @@ -310,7 +315,7 @@ export default () => { }, postRender(editorView) { - c.dropzone && fu.initDropzone(editorView); + c.dropzone && fu && fu.initDropzone(editorView); }, /** @@ -319,7 +324,7 @@ export default () => { * @private * */ setTarget(m) { - am.collection.target = m; + assetsVis.target = m; }, /** @@ -328,7 +333,7 @@ export default () => { * @private * */ onSelect(f) { - am.collection.onSelect = f; + assetsVis.onSelect = f; }, /** @@ -351,9 +356,9 @@ export default () => { destroy() { assets.reset(); - fu.collection.reset(); - fu.remove(); - am.remove(); + assetsVis.reset(); + fu && fu.remove(); + am && am.remove(); [assets, am, fu].forEach(i => (i = null)); c = {}; } diff --git a/src/canvas/index.js b/src/canvas/index.js index a114efbd0..0e6edc1f5 100644 --- a/src/canvas/index.js +++ b/src/canvas/index.js @@ -80,10 +80,6 @@ export default () => { onLoad() { this.model.init(); - CanvasView = new canvasView({ - model: canvas, - config: c - }); }, getModel() { @@ -260,6 +256,11 @@ export default () => { * @private * */ render() { + CanvasView && CanvasView.remove(); + CanvasView = new canvasView({ + model: canvas, + config: c + }); return CanvasView.render().el; }, diff --git a/src/commands/view/OpenAssets.js b/src/commands/view/OpenAssets.js index 68552016e..d1d3559b8 100644 --- a/src/commands/view/OpenAssets.js +++ b/src/commands/view/OpenAssets.js @@ -3,7 +3,6 @@ export default { const modal = editor.Modal; const am = editor.AssetManager; const config = am.getConfig(); - const amContainer = am.getContainer(); const title = opts.modalTitle || editor.t('assetManager.modalTitle') || ''; const types = opts.types; const accept = opts.accept; @@ -21,11 +20,11 @@ export default { } am.render(assets); - this.rendered = 1; + this.rendered = am.getContainer(); } if (accept) { - const uploadEl = amContainer.querySelector( + const uploadEl = this.rendered.querySelector( `input#${config.stylePrefix}uploadFile` ); uploadEl && uploadEl.setAttribute('accept', accept); @@ -34,7 +33,7 @@ export default { modal .open({ title, - content: amContainer + content: this.rendered }) .getModel() .once('change:open', () => editor.stopCommand(this.id)); diff --git a/src/commands/view/OpenTraitManager.js b/src/commands/view/OpenTraitManager.js index f90ba2964..2f6173c9d 100644 --- a/src/commands/view/OpenTraitManager.js +++ b/src/commands/view/OpenTraitManager.js @@ -13,7 +13,6 @@ export default { var panelC; if (!this.$cn) { - var tmView = tm.getTraitsViewer(); var confTm = tm.getConfig(); this.$cn = $('
'); this.$cn2 = $('
'); @@ -27,7 +26,7 @@ export default { this.$cn2.append( `
${em.t('traitManager.label')}
` ); - this.$cn2.append(tmView.render().el); + this.$cn2.append(tm.render()); var panels = editor.Panels; if (!panels.getPanel('views-container')) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 0165016e5..90ee36bc1 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -86,10 +86,6 @@ export default () => { em = c.em; rules = new CssRules([], c); - rulesView = new CssRulesView({ - collection: rules, - config: c - }); return this; }, @@ -479,13 +475,18 @@ export default () => { * @private */ render() { + rulesView && rulesView.remove(); + rulesView = new CssRulesView({ + collection: rules, + config: c + }); return rulesView.render().el; }, destroy() { rules.reset(); rules.stopListening(); - rulesView.remove(); + rulesView && rulesView.remove(); [em, rules, rulesView].forEach(i => (i = null)); c = {}; } diff --git a/src/device_manager/index.js b/src/device_manager/index.js index d2374a7a1..9617856db 100644 --- a/src/device_manager/index.js +++ b/src/device_manager/index.js @@ -60,10 +60,7 @@ export default () => { devices = new Devices(); (c.devices || []).forEach(dv => this.add(dv.id || dv.name, dv.width, dv)); - view = new DevicesView({ - collection: devices, - config: c - }); + return this; }, @@ -123,13 +120,18 @@ export default () => { * @private */ render() { + view && view.remove(); + view = new DevicesView({ + collection: devices, + config: c + }); return view.render().el; }, destroy() { devices.reset(); devices.stopListening(); - view.remove(); + view && view.remove(); [devices, view].forEach(i => (i = null)); c = {}; } diff --git a/src/editor/index.js b/src/editor/index.js index 8adefc03f..5a42c953b 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -123,11 +123,8 @@ export default (config = {}) => { }; c.pStylePrefix = c.stylePrefix; - var em = new EditorModel(c); - var editorView = new EditorView({ - model: em, - config: c - }); + let em = new EditorModel(c); + let editorView; return { $, @@ -691,7 +688,7 @@ export default (config = {}) => { * @private */ getEl() { - return editorView.el; + return editorView && editorView.el; }, /** @@ -708,8 +705,12 @@ export default (config = {}) => { * @return {HTMLElement} */ render() { - editorView.render(); - return editorView.el; + editorView && editorView.remove(); + editorView = new EditorView({ + model: em, + config: c + }); + return editorView.render().el; }, /** diff --git a/src/i18n/index.js b/src/i18n/index.js index 66a10ada2..26e327901 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -27,6 +27,7 @@ * @module I18n */ import { isUndefined, isString } from 'underscore'; +import { hasWin } from 'utils/mixins'; import config from './config'; const isObj = el => !Array.isArray(el) && el !== null && typeof el === 'object'; @@ -210,7 +211,7 @@ export default () => { }, _localLang() { - const nav = window.navigator || {}; + const nav = (hasWin() && window.navigator) || {}; const lang = nav.language || nav.userLanguage; return lang ? lang.split('-')[0] : 'en'; }, diff --git a/src/index.js b/src/index.js index 3a87ba697..e1ce3cf5d 100644 --- a/src/index.js +++ b/src/index.js @@ -36,6 +36,7 @@ export default { * @param {Boolean} [config.autorender=true] If true, auto-render the content * @param {Array} [config.plugins=[]] Array of plugins to execute on start * @param {Object} [config.pluginsOpts={}] Custom options for plugins + * @param {Boolean} [config.headless=false] Init headless editor * @return {Editor} Editor instance * @example * var editor = grapesjs.init({ @@ -45,10 +46,12 @@ export default { * }) */ init(config = {}) { + const { headless } = config; const els = config.container; - if (!els) throw new Error("'container' is required"); + if (!els && !headless) throw new Error("'container' is required"); config = { ...defaultConfig, ...config, grapesjs: this }; - config.el = isElement(els) ? els : document.querySelector(els); + config.el = + !headless && (isElement(els) ? els : document.querySelector(els)); const editor = new Editor(config).init(); const em = editor.getModel(); @@ -79,7 +82,7 @@ export default { // A plugin might have extended/added some custom type so this // is a good point to load stuff like components, css rules, etc. em.loadOnStart(); - config.autorender && editor.render(); + config.autorender && !headless && editor.render(); editors.push(editor); return editor; diff --git a/src/modal_dialog/index.js b/src/modal_dialog/index.js index 043a9a5c7..27609d16b 100644 --- a/src/modal_dialog/index.js +++ b/src/modal_dialog/index.js @@ -69,10 +69,6 @@ export default () => { model = new ModalM(c); model.on('change:open', (m, enb) => triggerEvent(enb, em)); - modal = new ModalView({ - model, - config: c - }); return this; }, @@ -201,11 +197,16 @@ export default () => { * @private */ render() { + modal && modal.remove(); + modal = new ModalView({ + model, + config: c + }); return modal.render().$el; }, destroy() { - modal.remove(); + modal && modal.remove(); [c, model, modal].forEach(i => (i = {})); this.em = {}; } diff --git a/src/navigator/index.js b/src/navigator/index.js index db89fb2a8..ecaff5ba0 100644 --- a/src/navigator/index.js +++ b/src/navigator/index.js @@ -22,14 +22,6 @@ export default () => { }, onLoad() { - const ItemView = View.extend(config.extend); - layers = new ItemView({ - ItemView, - level: 0, - config, - opened: config.opened || {}, - model: em.get('DomComponents').getWrapper() - }); em && em.on('component:selected', this.componentChanged); this.componentChanged(); }, @@ -60,7 +52,7 @@ export default () => { * @return {Component} */ getRoot() { - return layers.model; + return layers && layers.model; }, /** @@ -96,6 +88,15 @@ export default () => { }, render() { + const ItemView = View.extend(config.extend); + layers && layers.remove(); + layers = new ItemView({ + ItemView, + level: 0, + config, + opened: config.opened || {}, + model: em.get('DomComponents').getWrapper() + }); return layers.render().el; }, diff --git a/src/panels/index.js b/src/panels/index.js index e3fe1b3ad..7946d3942 100644 --- a/src/panels/index.js +++ b/src/panels/index.js @@ -57,10 +57,6 @@ export default () => { if (ppfx) c.stylePrefix = ppfx + c.stylePrefix; panels = new Panels(c.defaults); - PanelsViewObj = new PanelsView({ - collection: panels, - config: c - }); return this; }, @@ -77,7 +73,7 @@ export default () => { * @return {HTMLElement} */ getPanelsEl() { - return PanelsViewObj.el; + return PanelsViewObj && PanelsViewObj.el; }, /** @@ -205,6 +201,11 @@ export default () => { * @private */ render() { + PanelsViewObj && PanelsViewObj.remove(); + PanelsViewObj = new PanelsView({ + collection: panels, + config: c + }); return PanelsViewObj.render().el; }, @@ -235,7 +236,7 @@ export default () => { destroy() { panels.reset(); panels.stopListening(); - PanelsViewObj.remove(); + PanelsViewObj && PanelsViewObj.remove(); [c, panels, PanelsViewObj].forEach(i => (i = {})); }, diff --git a/src/rich_text_editor/index.js b/src/rich_text_editor/index.js index 8d6daaccb..d523c598c 100644 --- a/src/rich_text_editor/index.js +++ b/src/rich_text_editor/index.js @@ -27,7 +27,7 @@ */ import RichTextEditor from './model/RichTextEditor'; -import { on, off } from 'utils/mixins'; +import { on, hasWin } from 'utils/mixins'; import defaults from './config/config'; export default () => { @@ -75,6 +75,7 @@ export default () => { this.pfx = config.stylePrefix; actions = config.actions || []; + if (!hasWin()) return this; toolbar = document.createElement('div'); toolbar.className = `${ppfx}rte-toolbar ${ppfx}one-bg`; globalRte = this.initRte(document.createElement('div')); diff --git a/src/selector_manager/index.js b/src/selector_manager/index.js index 7b79be4a5..7565db10f 100644 --- a/src/selector_manager/index.js +++ b/src/selector_manager/index.js @@ -99,11 +99,6 @@ export default config => { c.stylePrefix = ppfx + c.stylePrefix; } - this.selectorTags = new ClassTagsView({ - collection: new Selectors([], { em, config: c }), - config: c - }); - // Global selectors container selectors = new Selectors(c.selectors); selectors.on('add', model => em.trigger('selector:add', model)); @@ -133,6 +128,7 @@ export default config => { select(value, opts = {}) { const targets = Array.isArray(value) ? value : [value]; const toSelect = this.em.get('StyleManager').setTarget(targets, opts); + const selTags = this.selectorTags; const res = toSelect .filter(i => i) .map(sel => @@ -142,7 +138,7 @@ export default config => { ? sel : sel.getSelectorsString() ); - this.selectorTags.componentChanged({ targets: res }); + selTags && selTags.componentChanged({ targets: res }); return this; }, @@ -305,19 +301,21 @@ export default config => { * @private */ render(selectors) { - if (selectors) { - this.selectorTags = new ClassTagsView({ - collection: new Selectors(selectors), - config: c - }); - return this.selectorTags.render().el; - } else return this.selectorTags.render().el; + const { em, selectorTags } = this; + selectorTags && selectorTags.remove(); + this.selectorTags = new ClassTagsView({ + collection: new Selectors(selectors || [], { em, config: c }), + config: c + }); + + return this.selectorTags.render().el; }, destroy() { + const { selectorTags } = this; selectors.reset(); selectors.stopListening(); - this.selectorTags.remove(); + selectorTags && selectorTags.remove(); [c, selectors].forEach(i => (i = {})); this.em = {}; this.selectorTags = {}; diff --git a/src/selector_manager/view/ClassTagsView.js b/src/selector_manager/view/ClassTagsView.js index 3f981aa8d..965e4e082 100644 --- a/src/selector_manager/view/ClassTagsView.js +++ b/src/selector_manager/view/ClassTagsView.js @@ -67,11 +67,10 @@ export default Backbone.View.extend({ const coll = this.collection; this.target = this.config.em; this.em = em; - const emitter = this.getStyleEmitter(); const toList = 'component:toggled component:update:classes'; const toListCls = 'component:update:classes change:state'; this.listenTo(em, toList, this.componentChanged); - this.listenTo(emitter, 'update', this.componentChanged); + this.listenTo(em, 'styleManager:update', this.componentChanged); this.listenTo(em, toListCls, this.__handleStateChange); this.listenTo(em, 'styleable:change change:device', this.checkSync); // component:styleUpdate this.listenTo(coll, 'add', this.addNew); @@ -116,13 +115,6 @@ export default Backbone.View.extend({ }); }, - getStyleEmitter() { - const { em } = this; - const sm = em && em.get('StyleManager'); - const emitter = sm && sm.getEmitter(); - return emitter || {}; - }, - /** * Triggered when a tag is removed from collection * @param {Object} model Removed model diff --git a/src/style_manager/index.js b/src/style_manager/index.js index 35dc688fd..d7e2b099c 100644 --- a/src/style_manager/index.js +++ b/src/style_manager/index.js @@ -75,11 +75,6 @@ export default () => { if (ppfx) c.stylePrefix = ppfx + c.stylePrefix; properties = new Properties(); sectors = new Sectors([], c); - SectView = new SectorsView({ - collection: sectors, - target: c.em, - config: c - }); return this; }, @@ -427,6 +422,12 @@ export default () => { * @private * */ render() { + SectView && SectView.remove(); + SectView = new SectorsView({ + collection: sectors, + target: c.em, + config: c + }); return SectView.render().el; }, @@ -440,7 +441,7 @@ export default () => { coll.reset(); coll.stopListening(); }); - SectView.remove(); + SectView && SectView.remove(); [c, properties, sectors, SectView].forEach(i => (i = {})); this.em = {}; } diff --git a/src/style_manager/model/PropertyInteger.js b/src/style_manager/model/PropertyInteger.js index 513cf1a21..8391221e1 100644 --- a/src/style_manager/model/PropertyInteger.js +++ b/src/style_manager/model/PropertyInteger.js @@ -1,6 +1,7 @@ import { isUndefined } from 'underscore'; import Property from './Property'; import InputNumber from 'domain_abstract/ui/InputNumber'; +import { hasWin } from 'utils/mixins'; export default Property.extend({ defaults: { @@ -25,7 +26,7 @@ export default Property.extend({ Property.callParentInit(Property, this, props, opts); const unit = this.get('unit'); const units = this.get('units'); - this.input = new InputNumber({ model: this }); + this.input = hasWin() && new InputNumber({ model: this }); if (units.length && !unit) { this.set('unit', units[0]); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index 96f0c3a66..57928d93f 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -114,6 +114,7 @@ export default Backbone.View.extend({ pt.targets = targets.map(t => sm.getModelToStyle(t)).filter(Boolean); } pt.trigger('update'); + em.trigger('styleManager:update'); }, /** diff --git a/src/trait_manager/index.js b/src/trait_manager/index.js index b1e5870d8..f9837572d 100644 --- a/src/trait_manager/index.js +++ b/src/trait_manager/index.js @@ -1,14 +1,31 @@ import { defaults, isElement } from 'underscore'; import defaultOpts from './config/config'; import TraitsView from './view/TraitsView'; +import TraitView from './view/TraitView'; +import TraitSelectView from './view/TraitSelectView'; +import TraitCheckboxView from './view/TraitCheckboxView'; +import TraitNumberView from './view/TraitNumberView'; +import TraitColorView from './view/TraitColorView'; +import TraitButtonView from './view/TraitButtonView'; export default () => { let c = {}; + let types = {}; let TraitsViewer; + const typesDef = { + text: TraitView, + number: TraitNumberView, + select: TraitSelectView, + checkbox: TraitCheckboxView, + color: TraitColorView, + button: TraitButtonView + }; return { TraitsView, + types, + /** * Name of the module * @type {String} @@ -33,12 +50,8 @@ export default () => { c = config; defaults(c, defaultOpts); const ppfx = c.pStylePrefix; + types = { ...typesDef }; ppfx && (c.stylePrefix = `${ppfx}${c.stylePrefix}`); - TraitsViewer = new TraitsView({ - collection: [], - editor: c.em, - config: c - }); return this; }, @@ -66,8 +79,8 @@ export default () => { * @param {Object} methods Object representing the trait */ addType(name, trait) { - var itemView = TraitsViewer.itemView; - TraitsViewer.itemsView[name] = itemView.extend(trait); + const baseView = types.text; + types[name] = baseView.extend(trait); }, /** @@ -76,16 +89,23 @@ export default () => { * @return {Object} */ getType(name) { - return TraitsViewer.itemsView[name]; + return types[name]; }, render() { + TraitsViewer && TraitsViewer.remove(); + TraitsViewer = new TraitsView({ + collection: [], + editor: c.em, + config: c + }); + TraitsViewer.itemsView = types; return TraitsViewer.render().el; }, destroy() { - TraitsViewer.remove(); - [c, TraitsViewer].forEach(i => (i = {})); + TraitsViewer && TraitsViewer.remove(); + [c, types, TraitsViewer].forEach(i => (i = {})); } }; }; diff --git a/src/trait_manager/view/TraitsView.js b/src/trait_manager/view/TraitsView.js index 001e22437..1d1df36cf 100644 --- a/src/trait_manager/view/TraitsView.js +++ b/src/trait_manager/view/TraitsView.js @@ -1,25 +1,11 @@ import DomainViews from 'domain_abstract/view/DomainViews'; import TraitView from './TraitView'; -import TraitSelectView from './TraitSelectView'; -import TraitCheckboxView from './TraitCheckboxView'; -import TraitNumberView from './TraitNumberView'; -import TraitColorView from './TraitColorView'; -import TraitButtonView from './TraitButtonView'; export default DomainViews.extend({ ns: 'Traits', itemView: TraitView, reuseView: 1, - itemsView: { - text: TraitView, - number: TraitNumberView, - select: TraitSelectView, - checkbox: TraitCheckboxView, - color: TraitColorView, - button: TraitButtonView - }, - initialize(o = {}) { const config = o.config || {}; this.config = config; diff --git a/test/specs/grapesjs/headless.js b/test/specs/grapesjs/headless.js index 109900de6..8e4998025 100644 --- a/test/specs/grapesjs/headless.js +++ b/test/specs/grapesjs/headless.js @@ -4,6 +4,7 @@ describe('GrapesJS Headless', () => { test('Can init editor', () => { - // const editor = grapesjs.init({ headless: true }); + const editor = grapesjs.init({ headless: true }); + expect(editor).toBeTruthy(); }); });