diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index e0d316174..ff5172442 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -179,7 +179,7 @@ const Component = Backbone.Model.extend(Styleable).extend( ...(this.defaults.attributes || {}), ...(this.get('attributes') || {}) }); - this.ccid = Component.createId(this); + this.ccid = Component.createId(this, opt); this.initClasses(); this.initTraits(); this.initComponents(); @@ -1290,13 +1290,13 @@ const Component = Backbone.Model.extend(Styleable).extend( * @return {string} * @private */ - createId(model) { + createId(model, opts = {}) { const list = Component.getList(model); let { id } = model.get('attributes'); let nextId; if (id) { - nextId = Component.getIncrementId(id, list); + nextId = Component.getIncrementId(id, list, opts); model.setId(nextId); } else { nextId = Component.getNewId(list); @@ -1320,13 +1320,16 @@ const Component = Backbone.Model.extend(Styleable).extend( return newId; }, - getIncrementId(id, list) { + getIncrementId(id, list, opts = {}) { + const { keepIds = [] } = opts; let counter = 1; let newId = id; - while (list[newId]) { - counter++; - newId = `${id}-${counter}`; + if (keepIds.indexOf(id) < 0) { + while (list[newId]) { + counter++; + newId = `${id}-${counter}`; + } } return newId; @@ -1347,14 +1350,15 @@ const Component = Backbone.Model.extend(Styleable).extend( * (are not Components/CSSRules yet), for duplicated id and fixes them * This method is used in Components.js just after the parsing */ - checkId(components, styles = [], list = {}) { + checkId(components, styles = [], list = {}, opts = {}) { const comps = isArray(components) ? components : [components]; + const { keepIds = [] } = opts; comps.forEach(comp => { const { attributes = {}, components } = comp; const { id } = attributes; // Check if we have collisions with current components - if (id && list[id]) { + if (id && list[id] && keepIds.indexOf(id) < 0) { const newId = Component.getIncrementId(id, list); attributes.id = newId; // Update passed styles @@ -1367,7 +1371,7 @@ const Component = Backbone.Model.extend(Styleable).extend( }); } - components && Component.checkId(components, styles, list); + components && Component.checkId(components, styles, list, opts); }); } } diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index 5b3b580f8..e29aa7f19 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -11,6 +11,11 @@ import { let Component; +const getIdsToKeep = prev => { + const pr = prev || []; + return pr.map(comp => comp.getId()); +}; + export default Backbone.Collection.extend({ initialize(models, opt = {}) { this.opt = opt; @@ -24,8 +29,10 @@ export default Backbone.Collection.extend({ resetChildren(models, opts = {}) { const coll = this; - const { previousModels = [] } = opts; - previousModels.forEach(md => this.removeChildren(md, coll, opts)); + const prev = opts.previousModels || []; + const toRemove = prev.filter(prev => !models.get(prev.cid)); + opts.keepIds = getIdsToKeep(prev); + toRemove.forEach(md => this.removeChildren(md, coll, opts)); models.each(model => this.onAdd(model)); }, @@ -44,12 +51,15 @@ export default Backbone.Collection.extend({ const id = removed.getId(); const sels = em.get('SelectorManager').getAll(); const rules = em.get('CssComposer').getAll(); + const canRemoveStyle = (opts.keepIds || []).indexOf(id) < 0; delete allByID[id]; // Remove all component related styles - const rulesRemoved = rules.remove( - rules.filter(r => r.getSelectors().getFullString() === `#${id}`) - ); + const rulesRemoved = canRemoveStyle + ? rules.remove( + rules.filter(r => r.getSelectors().getFullString() === `#${id}`) + ) + : []; // Clean selectors sels.remove(rulesRemoved.map(rule => rule.getSelectors().at(0))); @@ -117,7 +127,7 @@ export default Backbone.Collection.extend({ const parsed = em.get('Parser').parseHtml(value); // We need this to avoid duplicate IDs if (!Component) Component = require('./Component').default; - Component.checkId(parsed.html, parsed.css, domc.componentsById); + Component.checkId(parsed.html, parsed.css, domc.componentsById, opt); if (parsed.css && cssc && !opt.temporary) { cssc.addCollection(parsed.css, { @@ -130,6 +140,8 @@ export default Backbone.Collection.extend({ }, add(models, opt = {}) { + opt.keepIds = getIdsToKeep(opt.previousModels); + if (isString(models)) { models = this.parseString(models, opt); } else if (isArray(models)) { diff --git a/src/dom_components/view/ComponentTextView.js b/src/dom_components/view/ComponentTextView.js index 3be077a56..f331f6d1a 100644 --- a/src/dom_components/view/ComponentTextView.js +++ b/src/dom_components/view/ComponentTextView.js @@ -102,12 +102,12 @@ export default ComponentView.extend({ const content = this.getContent(); const comps = model.components(); const contentOpt = { fromDisable: 1, ...opts }; - comps.length && comps.reset(null, opts); model.set('content', '', contentOpt); // If there is a custom RTE the content is just baked staticly // inside 'content' if (rte.customRte) { + comps.length && comps.reset(null, opts); model.set('content', content, contentOpt); } else { const clean = model => { @@ -133,34 +133,12 @@ export default ComponentView.extend({ // Avoid re-render on reset with silent option !opts.silent && model.trigger('change:content', model, '', contentOpt); - comps.add(content, opts); + comps.reset(content, opts); comps.each(model => clean(model)); comps.trigger('resetNavigator'); } }, - /* - getModelsFromEl(el) { - const result = []; - const children = (el || this.el).childNodes; - - for (let index = 0; index < children.length; index++) { - const child = children[index]; - const model = child.__cashData && child.__cashData.model; - - if (model) { - model.attributes.components = this.getModelsFromEl(child); - if (model.get('content')) { - model.attributes.content = child.textContent; - } - result.push(model); - } - } - - return result; - }, - */ - /** * Callback on input event * @param {Event} e diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 61f82d238..bbe19fcbd 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -241,13 +241,14 @@ export default Backbone.View.extend({ const { model, em, el } = this; if (em && em.getConfig('avoidInlineStyle')) { - if (model.get('_innertext')) { + const style = model.getStyle(); + const empty = isEmpty(style); + !empty && model.setStyle(style); + if (model.get('_innertext') && empty) { el.removeAttribute('id'); } else { el.id = model.getId(); } - const style = model.getStyle(); - !isEmpty(style) && model.setStyle(style); } else { this.setAttribute('style', model.styleToString()); } diff --git a/src/parser/model/ParserHtml.js b/src/parser/model/ParserHtml.js index b2b4397aa..9d8848d40 100644 --- a/src/parser/model/ParserHtml.js +++ b/src/parser/model/ParserHtml.js @@ -299,7 +299,10 @@ export default config => { } const result = this.parseNode(el); - res.html = result; + // I have to keep it otherwise it breaks the DomComponents.addComponent (returns always array) + const resHtml = + result.length === 1 && !c.returnArray ? result[0] : result; + res.html = resHtml; em && em.trigger('parse:html', { input: str, output: res }); return res; diff --git a/test/specs/parser/model/ParserHtml.js b/test/specs/parser/model/ParserHtml.js index 44a5ebef0..ee06e8979 100644 --- a/test/specs/parser/model/ParserHtml.js +++ b/test/specs/parser/model/ParserHtml.js @@ -9,7 +9,8 @@ describe('ParserHtml', () => { var dom = new DomComponents(); obj = new ParserHtml({ textTags: ['br', 'b', 'i', 'u'], - pStylePrefix: 'gjs-' + pStylePrefix: 'gjs-', + returnArray: 1 }); obj.compTypes = dom.componentTypes; });