From 46941501f618481e8ee4b959d9f277ab56b17012 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 11 Nov 2017 20:42:57 +0100 Subject: [PATCH 01/29] Start make use of `getFullName` from the Selector --- src/css_composer/index.js | 22 ++++++++++++++++++++++ src/css_composer/view/CssRuleView.js | 12 +++++------- src/dom_components/model/Component.js | 22 +++++----------------- src/dom_components/view/ComponentView.js | 8 +++++++- src/selector_manager/index.js | 4 ++++ src/selector_manager/model/Selector.js | 18 ++++++++++++++---- 6 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index cfad1824d..686f5ff1d 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -1,6 +1,7 @@ /** * * [add](#add) * * [get](#get) + * * [set](#set) * * [getAll](#getall) * * [load](#load) * * [store](#store) @@ -270,6 +271,27 @@ module.exports = () => { return result; }, + + /** + * Add/update a css rule + * @param {string} selector Selector string, eg. '.class1.class2, #id1' + * @param {Object} style Style properties and values + */ + set(selector, style, opts = {}) { + const state = opts.state || ''; + const media = opts.mediaText || ''; + // opts.state, opts.media + /* + 1. from selectorString to selectorObjects (using Selectors API) + [ + [{'class1'}, {'class2'}], + [{'id1'}, {'class2'}] + ] + 2. add(selectors, state, media) + */ + }, + + /** * Render the block of CSS rules * @return {HTMLElement} diff --git a/src/css_composer/view/CssRuleView.js b/src/css_composer/view/CssRuleView.js index d4fcf1094..dea7503e1 100644 --- a/src/css_composer/view/CssRuleView.js +++ b/src/css_composer/view/CssRuleView.js @@ -28,13 +28,11 @@ module.exports = Backbone.View.extend({ * @private */ renderSelectors() { - var sel = []; - var model = this.model; - var add = model.get('selectorsAdd'); - model.get('selectors').each(m => { - sel.push('.' + m.get('name')); - }); - var sels = sel.join(''); + const sel = []; + const model = this.model; + model.get('selectors').each(model => sel.push(model.getFullName())); + const sels = sel.join(''); + const add = model.get('selectorsAdd'); return sels + (sels && add ? ', ' : '') + add; }, diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 2d2f13cdd..709d37397 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -1,10 +1,10 @@ import { isUndefined, isArray } from 'underscore'; import Styleable from 'domain_abstract/model/Styleable'; -var Backbone = require('backbone'); -var Components = require('./Components'); -var Selectors = require('selector_manager/model/Selectors'); -var Traits = require('trait_manager/model/Traits'); +const Backbone = require('backbone'); +const Components = require('./Components'); +const Selectors = require('selector_manager/model/Selectors'); +const Traits = require('trait_manager/model/Traits'); const escapeRegExp = (str) => { return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); @@ -158,18 +158,6 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.initClasses(); this.initComponents(); this.initToolbar(); - - // Normalize few properties from strings to arrays - var toNormalize = ['stylable']; - toNormalize.forEach(function(name) { - var value = this.get(name); - - if (typeof value == 'string') { - var newValue = value.split(',').map(prop => prop.trim()); - this.set(name, newValue); - } - }, this); - this.set('status', ''); this.init(); }, @@ -329,7 +317,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ */ traitsUpdated() { let found = 0; - const attrs = Object.assign({}, this.get('attributes')); + const attrs = { ...this.get('attributes') }; const traits = this.get('traits'); if (!(traits instanceof Traits)) { diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index be33360c4..d8f18a1a3 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -145,7 +145,13 @@ module.exports = Backbone.View.extend({ * @private * */ updateStyle() { - this.setAttribute('style', this.getStyleString()); + const em = this.em; + const model = this.model; + const selector = `#${model.getId()}`; + const style = model.getStyle(); + const state = model.get('state'); + em.get('CssComposer').set(selector, style, { state }); + //this.setAttribute('style', this.getStyleString()); }, /** diff --git a/src/selector_manager/index.js b/src/selector_manager/index.js index 40069df50..61a12d2fd 100644 --- a/src/selector_manager/index.js +++ b/src/selector_manager/index.js @@ -61,6 +61,10 @@ module.exports = config => { return { + Selector, + + Selectors, + /** * Name of the module * @type {String} diff --git a/src/selector_manager/model/Selector.js b/src/selector_manager/model/Selector.js index 193c2ddb3..4f8c921bc 100644 --- a/src/selector_manager/model/Selector.js +++ b/src/selector_manager/model/Selector.js @@ -1,15 +1,19 @@ var Backbone = require('backbone'); +const TYPE_CLASS = 1; +const TYPE_ID = 2; + const Selector = Backbone.Model.extend({ idAttribute: 'name', defaults: { name: '', + label: '', // Type of the selector - type: 'class', + type: TYPE_CLASS, // If not active it's not selectable by the style manager (uncheckboxed) active: true, @@ -18,7 +22,7 @@ const Selector = Backbone.Model.extend({ // Will be rendered only in export code private: false, - // If true, can't be removed by the user, from the attacched element + // If true, can't be removed from the attacched element protected: false, }, @@ -43,10 +47,10 @@ const Selector = Backbone.Model.extend({ let init = ''; switch (this.get('type')) { - case 'class': + case TYPE_CLASS: init = '.'; break; - case 'id': + case TYPE_ID: init = '#'; break; } @@ -55,6 +59,12 @@ const Selector = Backbone.Model.extend({ } }, { + // All type selectors: https://developer.mozilla.org/it/docs/Web/CSS/CSS_Selectors + // Here I define only what I need + TYPE_CLASS, + + TYPE_ID, + /** * Escape string * @param {string} name From 03a4e03f881fb1e8a2a0a1acf26de16d0196cfac Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 11 Nov 2017 22:14:52 +0100 Subject: [PATCH 02/29] Setup `setIdStyle` in CssComposer --- src/css_composer/index.js | 26 ++++++++++++------------ src/dom_components/view/ComponentView.js | 6 +++--- src/selector_manager/index.js | 7 ++++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 686f5ff1d..a1a0131db 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -24,12 +24,14 @@ */ module.exports = () => { + let em; var c = {}, defaults = require('./config/config'), CssRule = require('./model/CssRule'), CssRules = require('./model/CssRules'), Selectors = require('./model/Selectors'), - CssRulesView = require('./view/CssRulesView'); + CssRulesView = require('./view/CssRulesView'), + Selector = require('selector_manager/model/Selector'); var rules, rulesView; @@ -79,6 +81,7 @@ module.exports = () => { c.rules = elStyle || c.rules; c.sm = c.em; + em = c.em; rules = new CssRules([], c); rulesView = new CssRulesView({ collection: rules, @@ -273,22 +276,19 @@ module.exports = () => { /** - * Add/update a css rule - * @param {string} selector Selector string, eg. '.class1.class2, #id1' + * Add/update a css rule with id selector + * @param {string} name Id selector name, eg. 'my-id' * @param {Object} style Style properties and values */ - set(selector, style, opts = {}) { + setIdStyle(name, style = {}, opts = {}) { const state = opts.state || ''; const media = opts.mediaText || ''; - // opts.state, opts.media - /* - 1. from selectorString to selectorObjects (using Selectors API) - [ - [{'class1'}, {'class2'}], - [{'id1'}, {'class2'}] - ] - 2. add(selectors, state, media) - */ + const sm = em.get('SelectorManager'); + const selector = sm.add({ name, type: Selector.TYPE_ID }); + console.log(media); + const rule = this.add(selector, state, media); + rule.setStyle(style); + return rule; }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index d8f18a1a3..b37588ed0 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -147,13 +147,12 @@ module.exports = Backbone.View.extend({ updateStyle() { const em = this.em; const model = this.model; - const selector = `#${model.getId()}`; const style = model.getStyle(); const state = model.get('state'); - em.get('CssComposer').set(selector, style, { state }); - //this.setAttribute('style', this.getStyleString()); + em.get('CssComposer').setIdStyle(model.getId(), style, { state }); }, + /** * Update classe attribute * @private @@ -207,6 +206,7 @@ module.exports = Backbone.View.extend({ } src && (attrs.src = src); + attrs.id = model.getId(); this.$el.attr(attrs); this.updateHighlight(); this.updateStyle(); diff --git a/src/selector_manager/index.js b/src/selector_manager/index.js index 61a12d2fd..96d45b0df 100644 --- a/src/selector_manager/index.js +++ b/src/selector_manager/index.js @@ -133,7 +133,7 @@ module.exports = config => { } const cname = opts.name; - const selector = cname ? this.get(cname) : selectors.where(opts)[0]; + const selector = cname ? this.get(cname, opts.type) : selectors.where(opts)[0]; if (!selector) { return selectors.add(opts); @@ -167,12 +167,13 @@ module.exports = config => { /** * Get the selector by its name * @param {String} name Selector name + * @param {String} tyoe Selector type * @return {Model|null} * @example * var selector = selectorManager.get('selectorName'); * */ - get(name) { - return selectors.where({name})[0]; + get(name, type = Selector.TYPE_CLASS) { + return selectors.where({name, type})[0]; }, /** From 41f04eb4266c56bcc59f4f96410b612671e7ba29 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 11 Nov 2017 22:22:24 +0100 Subject: [PATCH 03/29] Add `_getCurrentMedia` in CssComposer --- src/css_composer/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index a1a0131db..02b4700e9 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -282,10 +282,9 @@ module.exports = () => { */ setIdStyle(name, style = {}, opts = {}) { const state = opts.state || ''; - const media = opts.mediaText || ''; + const media = opts.mediaText || this._getCurrentMedia(); const sm = em.get('SelectorManager'); const selector = sm.add({ name, type: Selector.TYPE_ID }); - console.log(media); const rule = this.add(selector, state, media); rule.setStyle(style); return rule; @@ -299,7 +298,15 @@ module.exports = () => { */ render() { return rulesView.render().el; - } + }, + + _getCurrentMedia() { + const config = em.get('Config'); + const device = em.getDeviceModel(); + const condition = config.mediaCondition; + const width = device && device.get('widthMedia'); + return device && width ? `(${condition}: ${width})` : ''; + }, }; }; From 85cc802e3db460f92181ab1467c3111623e28c1a Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 11 Nov 2017 22:25:15 +0100 Subject: [PATCH 04/29] Move `getCurrentMedia` in Editor --- src/css_composer/index.js | 10 +--------- src/editor/model/Editor.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 02b4700e9..9ce542156 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -282,7 +282,7 @@ module.exports = () => { */ setIdStyle(name, style = {}, opts = {}) { const state = opts.state || ''; - const media = opts.mediaText || this._getCurrentMedia(); + const media = opts.mediaText || em.getCurrentMedia(); const sm = em.get('SelectorManager'); const selector = sm.add({ name, type: Selector.TYPE_ID }); const rule = this.add(selector, state, media); @@ -300,13 +300,5 @@ module.exports = () => { return rulesView.render().el; }, - - _getCurrentMedia() { - const config = em.get('Config'); - const device = em.getDeviceModel(); - const condition = config.mediaCondition; - const width = device && device.get('widthMedia'); - return device && width ? `(${condition}: ${width})` : ''; - }, }; }; diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 7981ebfc4..971423956 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -614,6 +614,20 @@ module.exports = Backbone.Model.extend({ w.getSelection().removeAllRanges(); }, + + /** + * Get current media text + * @return {string} + */ + getCurrentMedia() { + const config = this.config; + const device = this.getDeviceModel(); + const condition = config.mediaCondition; + const width = device && device.get('widthMedia'); + return device && width ? `(${condition}: ${width})` : ''; + }, + + /** * Set/get data from the HTMLElement * @param {HTMLElement} el From 717fa234bd6c7d4ab1c8a425c8a7b778ff65307f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 12:48:09 +0100 Subject: [PATCH 05/29] Pass options in setIdStyle --- src/css_composer/index.js | 2 +- src/dom_components/view/ComponentView.js | 5 +++-- src/domain_abstract/model/Styleable.js | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 9ce542156..e66f18bda 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -286,7 +286,7 @@ module.exports = () => { const sm = em.get('SelectorManager'); const selector = sm.add({ name, type: Selector.TYPE_ID }); const rule = this.add(selector, state, media); - rule.setStyle(style); + rule.setStyle(style, opts); return rule; }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index b37588ed0..2b92939f3 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -144,12 +144,13 @@ module.exports = Backbone.View.extend({ * Update style attribute * @private * */ - updateStyle() { + updateStyle(mod, val, opts = {}) { const em = this.em; const model = this.model; const style = model.getStyle(); const state = model.get('state'); - em.get('CssComposer').setIdStyle(model.getId(), style, { state }); + const cc = em.get('CssComposer'); + cc.setIdStyle(model.getId(), style, { ...opts, state }); }, diff --git a/src/domain_abstract/model/Styleable.js b/src/domain_abstract/model/Styleable.js index 83223f206..4a228f68a 100644 --- a/src/domain_abstract/model/Styleable.js +++ b/src/domain_abstract/model/Styleable.js @@ -11,7 +11,7 @@ export default { * @return {Object} */ extendStyle(prop) { - return Object.assign({}, this.getStyle(), prop); + return { ...this.getStyle(), ...prop}; }, /** @@ -19,7 +19,7 @@ export default { * @return {Object} */ getStyle() { - return Object.assign({}, this.get('style')); + return { ...this.get('style') }; }, /** @@ -32,7 +32,7 @@ export default { prop = parseStyle(prop); } - this.set('style', Object.assign({}, prop), opts); + this.set('style', { ...prop }, opts); for (let pr in prop) { this.trigger(`change:style:${pr}`); From 5da34522f076a449e710f409270caf900ffeec91 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 12:59:11 +0100 Subject: [PATCH 06/29] Make use of `getCurrentMedia` in SectorsView --- src/editor/model/Editor.js | 5 +++-- src/style_manager/view/SectorsView.js | 12 ++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 971423956..cb4dcf92f 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -616,15 +616,16 @@ module.exports = Backbone.Model.extend({ /** - * Get current media text + * Get the current media text * @return {string} */ getCurrentMedia() { const config = this.config; const device = this.getDeviceModel(); const condition = config.mediaCondition; + const preview = config.devicePreviewMode; const width = device && device.get('widthMedia'); - return device && width ? `(${condition}: ${width})` : ''; + return device && width && !preview ? `(${condition}: ${width})` : ''; }, diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index 715fd8fa4..a1cfa8faa 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -50,16 +50,12 @@ module.exports = Backbone.View.extend({ // TODO make use of getModelToStyle here const config = em.get('Config'); - var previewMode = config.devicePreviewMode; var classes = el.get('classes'); var pt = this.propTarget; - var device = em.getDeviceModel(); - var state = !previewMode ? el.get('state') : ''; - var widthMedia = device && device.get('widthMedia'); + var state = !config.devicePreviewMode ? el.get('state') : ''; var stateStr = state ? `:${state}` : null; var view = el.view; - var mediaText = device && !previewMode && widthMedia ? - `(${config.mediaCondition}: ${widthMedia})` : ''; + const media = em.getCurrentMedia(); pt.helper = null; if (view) { @@ -69,7 +65,7 @@ module.exports = Backbone.View.extend({ if (classes.length) { var cssC = em.get('CssComposer'); var valid = classes.getStyleable(); - var iContainer = cssC.get(valid, state, mediaText); + var iContainer = cssC.get(valid, state, media); if (!iContainer && valid.length) { // I stop undo manager here as after adding the CSSRule (generally after @@ -77,7 +73,7 @@ module.exports = Backbone.View.extend({ // the collection, therefore updating it in style manager will not affect it // #268 um.stopTracking(); - iContainer = cssC.add(valid, state, mediaText); + iContainer = cssC.add(valid, state, media); iContainer.set('style', el.get('style')); el.set('style', {}); um.startTracking(); From 4938bae9c259c6549e1750aab65727701c97b545 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 13:02:31 +0100 Subject: [PATCH 07/29] Make use of `getCurrentMedia` in `getModelToStyle` --- src/style_manager/index.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/style_manager/index.js b/src/style_manager/index.js index f4623ee87..7047bf329 100644 --- a/src/style_manager/index.js +++ b/src/style_manager/index.js @@ -253,13 +253,9 @@ module.exports = () => { const em = c.em; if (em && classes && classes.length) { - const emConfig = em.get('Config'); - const previewMode = emConfig.devicePreviewMode; - const device = em.getDeviceModel(); - const state = !previewMode ? model.get('state') : ''; - const widthMedia = device && device.get('widthMedia'); - const deviceW = device && !previewMode && widthMedia ? - `(${emConfig.mediaCondition}: ${widthMedia})` : ''; + const conf = em.get('Config'); + const state = !conf.devicePreviewMode ? model.get('state') : ''; + const deviceW = em.getCurrentMedia(); const cssC = em.get('CssComposer'); const valid = classes.getStyleable(); const CssRule = cssC.get(valid, state, deviceW); From 8939ae377372f31f98000068fa38597e3377e8a1 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 14:36:15 +0100 Subject: [PATCH 08/29] Update Component with new style methods and fix regressions --- src/css_composer/index.js | 4 ++- src/dom_components/model/Component.js | 28 +++++++++++++++++++ src/dom_components/view/ComponentView.js | 11 ++------ src/domain_abstract/model/Styleable.js | 3 ++ test/specs/css_composer/e2e/CssComposer.js | 2 +- test/specs/selector_manager/index.js | 2 +- .../style_manager/view/PropertyColorView.js | 14 ++++++++-- .../view/PropertyCompositeView.js | 12 ++++++-- .../style_manager/view/PropertyIntegerView.js | 14 ++++++++-- .../style_manager/view/PropertyRadioView.js | 14 ++++++++-- .../style_manager/view/PropertySelectView.js | 12 ++++++-- .../style_manager/view/PropertyStackView.js | 12 ++++++-- test/specs/style_manager/view/PropertyView.js | 12 ++++++-- 13 files changed, 111 insertions(+), 29 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index e66f18bda..600f007f1 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -279,8 +279,10 @@ module.exports = () => { * Add/update a css rule with id selector * @param {string} name Id selector name, eg. 'my-id' * @param {Object} style Style properties and values + * @param {Object} [opts={}] Custom options + * @return {CssRule} */ - setIdStyle(name, style = {}, opts = {}) { + setIdRule(name, style = {}, opts = {}) { const state = opts.state || ''; const media = opts.mediaText || em.getCurrentMedia(); const sm = em.get('SelectorManager'); diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 709d37397..a8d21bd50 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -185,6 +185,34 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.set('attributes', attrs); }, + getStyle() { + const rule = this.rule; + return rule ? rule.getStyle() : Styleable.getStyle.call(this); + }, + + setStyle(prop = {}, opts = {}) { + prop = Styleable.setStyle.call(this, prop, {silent: 1, avoidStore: 1}); + const state = this.get('state'); + const cc = this.em.get('CssComposer'); + this.rule = cc.setIdRule(this.getId(), prop, { ...opts, state }); + }, + + // setStyle / getStyle + /* + + setStyle(prop = {}, opts = {}) { + if (isString(prop)) { + prop = parseStyle(prop); + } + + this.set('style', { ...prop }, opts); + + for (let pr in prop) { + this.trigger(`change:style:${pr}`); + } + }, + */ + /** * Return attributes diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 2b92939f3..bf30a677a 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -144,16 +144,10 @@ module.exports = Backbone.View.extend({ * Update style attribute * @private * */ - updateStyle(mod, val, opts = {}) { - const em = this.em; - const model = this.model; - const style = model.getStyle(); - const state = model.get('state'); - const cc = em.get('CssComposer'); - cc.setIdStyle(model.getId(), style, { ...opts, state }); + updateStyle() { + //this.setAttribute('style', this.getStyleString()); }, - /** * Update classe attribute * @private @@ -207,7 +201,6 @@ module.exports = Backbone.View.extend({ } src && (attrs.src = src); - attrs.id = model.getId(); this.$el.attr(attrs); this.updateHighlight(); this.updateStyle(); diff --git a/src/domain_abstract/model/Styleable.js b/src/domain_abstract/model/Styleable.js index 4a228f68a..946a118cf 100644 --- a/src/domain_abstract/model/Styleable.js +++ b/src/domain_abstract/model/Styleable.js @@ -26,6 +26,7 @@ export default { * Set new style object * @param {Object|string} prop * @param {Object} opts + * @return {Object} Applied properties */ setStyle(prop = {}, opts = {}) { if (isString(prop)) { @@ -37,6 +38,8 @@ export default { for (let pr in prop) { this.trigger(`change:style:${pr}`); } + + return prop; }, /** diff --git a/test/specs/css_composer/e2e/CssComposer.js b/test/specs/css_composer/e2e/CssComposer.js index b3dca6c26..be88caf09 100644 --- a/test/specs/css_composer/e2e/CssComposer.js +++ b/test/specs/css_composer/e2e/CssComposer.js @@ -130,7 +130,7 @@ module.exports = { active: true, label: 'test1', name: 'test1', - type: 'class', + type: clsm.Selector.TYPE_CLASS, private: false, protected: false, }], diff --git a/test/specs/selector_manager/index.js b/test/specs/selector_manager/index.js index 5a8255b83..e42e4e914 100644 --- a/test/specs/selector_manager/index.js +++ b/test/specs/selector_manager/index.js @@ -44,7 +44,7 @@ describe('SelectorManager', () => { it('Default new selector is a class type', () => { obj.add('test'); - expect(obj.get('test').get('type')).toEqual('class'); + expect(obj.get('test').get('type')).toEqual(obj.Selector.TYPE_CLASS); }); it('Check name property', () => { diff --git a/test/specs/style_manager/view/PropertyColorView.js b/test/specs/style_manager/view/PropertyColorView.js index 5dadbf18c..5f543bbf2 100644 --- a/test/specs/style_manager/view/PropertyColorView.js +++ b/test/specs/style_manager/view/PropertyColorView.js @@ -1,12 +1,17 @@ const PropertyColorView = require('style_manager/view/PropertyColorView'); const Property = require('style_manager/model/Property'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); module.exports = { run() { describe('PropertyColorView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -22,13 +27,16 @@ module.exports = { }); beforeEach(() => { - target = new Component(); - component = new Component(); + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; + propTarget = Object.assign({}, Backbone.Events); + target = new Component({}, compOpts); + component = new Component({}, compOpts); model = new Property({ type: 'color', property: propName }); - propTarget = Object.assign({}, Backbone.Events); propTarget.model = component; view = new PropertyColorView({ model, diff --git a/test/specs/style_manager/view/PropertyCompositeView.js b/test/specs/style_manager/view/PropertyCompositeView.js index b6c3ac51d..99902eb30 100644 --- a/test/specs/style_manager/view/PropertyCompositeView.js +++ b/test/specs/style_manager/view/PropertyCompositeView.js @@ -2,12 +2,17 @@ const PropertyCompositeView = require('style_manager/view/PropertyCompositeView' const Property = require('style_manager/model/Property'); const PropertyComposite = require('style_manager/model/PropertyComposite'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); module.exports = { run() { describe('PropertyCompositeView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -35,8 +40,11 @@ module.exports = { }]; beforeEach(() => { - target = new Component(); - component = new Component(); + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; + target = new Component({}, compOpts); + component = new Component({}, compOpts); target.model = component; model = new PropertyComposite({ type: 'composite', diff --git a/test/specs/style_manager/view/PropertyIntegerView.js b/test/specs/style_manager/view/PropertyIntegerView.js index 3a7ab3a15..c99138e8b 100644 --- a/test/specs/style_manager/view/PropertyIntegerView.js +++ b/test/specs/style_manager/view/PropertyIntegerView.js @@ -2,12 +2,17 @@ const PropertyIntegerView = require('style_manager/view/PropertyIntegerView'); const Property = require('style_manager/model/Property'); const PropertyInteger = require('style_manager/model/PropertyInteger'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); module.exports = { run() { describe('PropertyIntegerView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -25,13 +30,16 @@ module.exports = { var unitsElSel = '.field-units select'; beforeEach(() => { - target = new Component(); - component = new Component(); + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; + propTarget = Object.assign({}, Backbone.Events); + target = new Component({}, compOpts); + component = new Component({}, compOpts); model = new PropertyInteger({ units, property: propName }); - propTarget = Object.assign({}, Backbone.Events); propTarget.model = component; view = new PropertyIntegerView({ model, diff --git a/test/specs/style_manager/view/PropertyRadioView.js b/test/specs/style_manager/view/PropertyRadioView.js index 0bc41527c..9866b9bfe 100644 --- a/test/specs/style_manager/view/PropertyRadioView.js +++ b/test/specs/style_manager/view/PropertyRadioView.js @@ -1,12 +1,17 @@ const PropertyRadioView = require('style_manager/view/PropertyRadioView'); const Property = require('style_manager/model/Property'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); module.exports = { run() { describe('PropertyRadioView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -27,14 +32,17 @@ module.exports = { var getCheckedEl = (view) => view.getInputEl().querySelectorAll('input:checked')[0]; beforeEach(() => { - target = new Component(); - component = new Component(); + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; + propTarget = Object.assign({}, Backbone.Events); + target = new Component({}, compOpts); + component = new Component({}, compOpts); model = new Property({ type: 'radio', list: options, property: propName }); - propTarget = Object.assign({}, Backbone.Events); propTarget.model = component; view = new PropertyRadioView({ model, diff --git a/test/specs/style_manager/view/PropertySelectView.js b/test/specs/style_manager/view/PropertySelectView.js index 770ab37fc..37c3ac44c 100644 --- a/test/specs/style_manager/view/PropertySelectView.js +++ b/test/specs/style_manager/view/PropertySelectView.js @@ -1,5 +1,7 @@ const PropertySelectView = require('style_manager/view/PropertySelectView'); const Property = require('style_manager/model/Property'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); const Component = require('dom_components/model/Component'); module.exports = { @@ -7,6 +9,9 @@ module.exports = { describe('PropertySelectView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -23,9 +28,12 @@ module.exports = { ]; beforeEach(() => { + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; propTarget = Object.assign({}, Backbone.Events); - target = new Component(); - component = new Component(); + target = new Component({}, compOpts); + component = new Component({}, compOpts); model = new Property({ type: 'select', list: options, diff --git a/test/specs/style_manager/view/PropertyStackView.js b/test/specs/style_manager/view/PropertyStackView.js index 6d04fd596..f2f222d77 100644 --- a/test/specs/style_manager/view/PropertyStackView.js +++ b/test/specs/style_manager/view/PropertyStackView.js @@ -1,12 +1,17 @@ const PropertyStackView = require('style_manager/view/PropertyStackView'); const Property = require('style_manager/model/PropertyStack'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); module.exports = { run() { describe('PropertyStackView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -41,8 +46,11 @@ module.exports = { ]; beforeEach(() => { - target = new Component(); - component = new Component(); + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; + target = new Component({}, compOpts); + component = new Component({}, compOpts); target.model = component; model = new Property({ type: 'stack', diff --git a/test/specs/style_manager/view/PropertyView.js b/test/specs/style_manager/view/PropertyView.js index 2298b9e43..3757f4ba5 100644 --- a/test/specs/style_manager/view/PropertyView.js +++ b/test/specs/style_manager/view/PropertyView.js @@ -1,5 +1,7 @@ const PropertyView = require('style_manager/view/PropertyView'); const Property = require('style_manager/model/Property'); +const Editor = require('editor/model/Editor'); +const DomComponents = require('dom_components'); const Component = require('dom_components/model/Component'); module.exports = { @@ -7,6 +9,9 @@ module.exports = { describe('PropertyView', () => { + let em; + let dcomp; + let compOpts; var component; var fixtures; var target; @@ -19,9 +24,12 @@ module.exports = { var defValue = 'testDefault'; beforeEach(() => { + em = new Editor({}); + dcomp = new DomComponents(); + compOpts = { em, componentTypes: dcomp.componentTypes }; propTarget = Object.assign({}, Backbone.Events); - target = new Component(); - component = new Component(); + target = new Component({}, compOpts); + component = new Component({}, compOpts); model = new Property({property: propName}); propTarget.model = component; options = { From 25edd627bebc1b064a1e8a1851e4a14c6eacb804 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 16:02:29 +0100 Subject: [PATCH 09/29] Add `avoidInlineStyle` configuration option in DomComponents module --- src/dom_components/config/config.js | 7 +++ src/dom_components/model/Component.js | 33 +++++-------- src/dom_components/view/ComponentView.js | 51 ++++++++++---------- test/specs/dom_components/view/ComponentV.js | 8 --- 4 files changed, 44 insertions(+), 55 deletions(-) diff --git a/src/dom_components/config/config.js b/src/dom_components/config/config.js index 470c58a62..245800503 100644 --- a/src/dom_components/config/config.js +++ b/src/dom_components/config/config.js @@ -18,6 +18,13 @@ module.exports = { 'background-size'], }, + // Usually when you update the `style` of the component this changes the + // element's `style` attribute. Unfortunately, inline styling doesn't allow + // use of media queries (@media) or even pseudo selectors (eg. :hover). + // When `avoidInlineStyle` is true all the styles are inserted inside the + // relative css rule + avoidInlineStyle: 0, + // Could be used for default components components: [], diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index a8d21bd50..adfc163e3 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -150,7 +150,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.opt = opt; this.sm = em; this.em = em; - this.config = props; + this.config = opt.config || {}; this.set('attributes', this.get('attributes') || {}); this.listenTo(this, 'change:script', this.scriptUpdated); this.listenTo(this, 'change:traits', this.traitsUpdated); @@ -185,33 +185,24 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.set('attributes', attrs); }, + getStyle() { const rule = this.rule; - return rule ? rule.getStyle() : Styleable.getStyle.call(this); + const avoidInline = this.config.avoidInlineStyle; + return rule && avoidInline ? rule.getStyle() : Styleable.getStyle.call(this); }, - setStyle(prop = {}, opts = {}) { - prop = Styleable.setStyle.call(this, prop, {silent: 1, avoidStore: 1}); - const state = this.get('state'); - const cc = this.em.get('CssComposer'); - this.rule = cc.setIdRule(this.getId(), prop, { ...opts, state }); - }, - - // setStyle / getStyle - /* setStyle(prop = {}, opts = {}) { - if (isString(prop)) { - prop = parseStyle(prop); - } - - this.set('style', { ...prop }, opts); - - for (let pr in prop) { - this.trigger(`change:style:${pr}`); + if (this.config.avoidInlineStyle) { + prop = Styleable.setStyle.call(this, prop, {silent: 1, avoidStore: 1}); + const state = this.get('state'); + const cc = this.em.get('CssComposer'); + this.rule = cc.setIdRule(this.getId(), prop, { ...opts, state }); + } else { + Styleable.setStyle.apply(this, arguments); } }, - */ /** @@ -256,7 +247,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ initClasses() { - const classes = this.normalizeClasses(this.get('classes') || this.config.classes || []); + const classes = this.normalizeClasses(this.get('classes') || []); this.set('classes', new Selectors(classes)); return this; }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index bf30a677a..36d4cbc62 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -64,6 +64,7 @@ module.exports = Backbone.View.extend({ } }, + /** * Import, if possible, classes inside main container * @private @@ -78,6 +79,7 @@ module.exports = Backbone.View.extend({ } }, + /** * Fires on state update. If the state is not empty will add a helper class * @param {Event} e @@ -94,6 +96,7 @@ module.exports = Backbone.View.extend({ } }, + /** * Update item on status change * @param {Event} e @@ -131,6 +134,7 @@ module.exports = Backbone.View.extend({ } }, + /** * Update highlight attribute * @private @@ -140,14 +144,33 @@ module.exports = Backbone.View.extend({ this.setAttribute('data-highlightable', hl ? 1 : ''); }, + /** * Update style attribute * @private * */ updateStyle() { - //this.setAttribute('style', this.getStyleString()); + this.setAttribute('style', this.getStyleString()); + }, + + + /** + * Return style string + * @return {string} + * @private + * */ + getStyleString() { + var style = ''; + this.style = this.model.get('style'); + for(var key in this.style) { + if(this.style.hasOwnProperty(key)) + style += key + ':' + this.style[key] + ';'; + } + + return style; }, + /** * Update classe attribute * @private @@ -201,6 +224,7 @@ module.exports = Backbone.View.extend({ } src && (attrs.src = src); + //attrs.id = model.getId(); this.$el.attr(attrs); this.updateHighlight(); this.updateStyle(); @@ -214,31 +238,6 @@ module.exports = Backbone.View.extend({ this.getChildrenContainer().innerHTML = this.model.get('content'); }, - /** - * Return style string - * @return {string} - * @private - * */ - getStyleString() { - var style = ''; - this.style = this.model.get('style'); - for(var key in this.style) { - if(this.style.hasOwnProperty(key)) - style += key + ':' + this.style[key] + ';'; - } - - return style; - }, - - /** - * Reply to event call - * @param object Event that generated the request - * @private - * */ - eventCall(event) { - event.viewResponse = this; - }, - /** * Prevent default helper * @param {Event} e diff --git a/test/specs/dom_components/view/ComponentV.js b/test/specs/dom_components/view/ComponentV.js index c7cd2edec..77a464ae8 100644 --- a/test/specs/dom_components/view/ComponentV.js +++ b/test/specs/dom_components/view/ComponentV.js @@ -80,14 +80,6 @@ module.exports = { expect(view.el.getAttribute('style')).toEqual(null); }); - it('Get style string', () => { - model.set('style', { - color: 'red', - float: 'left' - }); - expect(view.getStyleString()).toEqual('color:red;float:left;'); - }); - it('Add class', () => { model.get('classes').add({name: 'test'}); expect(view.el.getAttribute('class')).toEqual('test'); From b2d328da1d6b2641c3f462275ae8db64390fc8e0 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 16:41:08 +0100 Subject: [PATCH 10/29] Add `styleToString` to Styleable --- src/dom_components/view/ComponentView.js | 22 ++++++++++++++-------- src/domain_abstract/model/Styleable.js | 22 +++++++++++++++++++++- src/selector_manager/model/Selectors.js | 6 ++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 36d4cbc62..6e9f3a320 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -10,13 +10,14 @@ module.exports = Backbone.View.extend({ return this.model.get('tagName'); }, - initialize(opt) { + initialize(opt = {}) { const model = this.model; - this.opts = opt || {}; - this.config = this.opts.config || {}; - this.em = this.config.em || ''; - this.pfx = this.config.stylePrefix || ''; - this.ppfx = this.config.pStylePrefix || ''; + const config = opt.config || {}; + this.opts = opt; + this.config = config; + this.em = config.em || ''; + this.pfx = config.stylePrefix || ''; + this.ppfx = config.pStylePrefix || ''; this.attr = model.get('attributes'); this.classe = this.attr.class || []; const $el = this.$el; @@ -150,7 +151,13 @@ module.exports = Backbone.View.extend({ * @private * */ updateStyle() { - this.setAttribute('style', this.getStyleString()); + if (this.config.avoidInlineStyle) { + const model = this.model; + this.el.id = model.getId(); + model.setStyle(model.getStyle()); + } else { + this.setAttribute('style', this.getStyleString()); + } }, @@ -224,7 +231,6 @@ module.exports = Backbone.View.extend({ } src && (attrs.src = src); - //attrs.id = model.getId(); this.$el.attr(attrs); this.updateHighlight(); this.updateStyle(); diff --git a/src/domain_abstract/model/Styleable.js b/src/domain_abstract/model/Styleable.js index 946a118cf..28a4508cc 100644 --- a/src/domain_abstract/model/Styleable.js +++ b/src/domain_abstract/model/Styleable.js @@ -14,6 +14,7 @@ export default { return { ...this.getStyle(), ...prop}; }, + /** * Get style object * @return {Object} @@ -22,6 +23,7 @@ export default { return { ...this.get('style') }; }, + /** * Set new style object * @param {Object|string} prop @@ -42,6 +44,7 @@ export default { return prop; }, + /** * Add style property * @param {Object|string} prop @@ -63,6 +66,7 @@ export default { this.setStyle(prop, opts); }, + /** * Remove style property * @param {string} prop @@ -71,5 +75,21 @@ export default { let style = this.getStyle(); delete style[prop]; this.setStyle(style); - } + }, + + + /** + * Returns string of style properties + * @return {String} + */ + styleToString() { + const result = []; + const style = this.getStyle(); + + for (let prop in style) { + result.push(`${prop}:${style[prop]}`); + } + + return result.join(';'); + }, } diff --git a/src/selector_manager/model/Selectors.js b/src/selector_manager/model/Selectors.js index 42cf33bad..06bf00187 100644 --- a/src/selector_manager/model/Selectors.js +++ b/src/selector_manager/model/Selectors.js @@ -11,5 +11,11 @@ module.exports = Backbone.Collection.extend({ getValid() { return _.filter(this.models, item => !item.get('private')); + }, + + getFullString() { + const result = []; + this.each(selector => result.push(selector.getFullName())); + return result.join('').trim(); } }); From b68787b946c233651f827312be3bc7ca9e7bd424 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 16:59:12 +0100 Subject: [PATCH 11/29] Add `toCSS` method to CssRule --- src/css_composer/model/CssRule.js | 59 +++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js index d8e4401f0..fcc17f74e 100644 --- a/src/css_composer/model/CssRule.js +++ b/src/css_composer/model/CssRule.js @@ -1,7 +1,7 @@ import Styleable from 'domain_abstract/model/Styleable'; var Backbone = require('backbone'); -var Selectors = require('./Selectors'); +var Selectors = require('selector_manager/model/Selectors'); module.exports = Backbone.Model.extend(Styleable).extend({ @@ -25,24 +25,53 @@ module.exports = Backbone.Model.extend(Styleable).extend({ stylable: true, }, + initialize(c, opt = {}) { - this.config = c || {}; - const em = opt.em; - let selectors = this.config.selectors || []; - this.em = em; - - if (em) { - const sm = em.get('SelectorManager'); - const slct = []; - selectors.forEach((selector) => { - slct.push(sm.add(selector)); - }); - selectors = slct; - } + this.config = c || {}; + const em = opt.em; + let selectors = this.config.selectors || []; + this.em = em; + + if (em) { + const sm = em.get('SelectorManager'); + const slct = []; + selectors.forEach((selector) => { + slct.push(sm.add(selector)); + }); + selectors = slct; + } + + this.set('selectors', new Selectors(selectors)); + }, + - this.set('selectors', new Selectors(selectors)); + /** + * Returns CSS string of the rule + * @return {string} + */ + toCSS() { + let result = ''; + let sels = []; + const state = this.get('state'); + const media = this.get('mediaText'); + const style = this.styleToString(); + const addSelector = this.get('selectorsAdd'); + const selectors = this.get('selectors').getFullString(); + selectors && sels.push(selectors); + addSelector && sels.push(addSelector); + + if (style && sels.length) { + result = `${sels.join(',')} {${style}}`; + } + + if (media && result) { + result = `@media ${media}{${result}}`; + } + + return result; }, + /** * Compare the actual model with parameters * @param {Object} selectors Collection of selectors From 8f356a99442703a7f97648309c4a1f6717120e9f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 17:05:16 +0100 Subject: [PATCH 12/29] Remove Selectors object from CssComposer --- src/css_composer/index.js | 6 +++--- src/css_composer/model/Selectors.js | 24 ------------------------ 2 files changed, 3 insertions(+), 27 deletions(-) delete mode 100644 src/css_composer/model/Selectors.js diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 600f007f1..035b4719f 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -29,9 +29,9 @@ module.exports = () => { defaults = require('./config/config'), CssRule = require('./model/CssRule'), CssRules = require('./model/CssRules'), - Selectors = require('./model/Selectors'), - CssRulesView = require('./view/CssRulesView'), - Selector = require('selector_manager/model/Selector'); + CssRulesView = require('./view/CssRulesView'); + const Selectors = require('selector_manager/model/Selectors'); + const Selector = require('selector_manager/model/Selector'); var rules, rulesView; diff --git a/src/css_composer/model/Selectors.js b/src/css_composer/model/Selectors.js deleted file mode 100644 index e2a95a550..000000000 --- a/src/css_composer/model/Selectors.js +++ /dev/null @@ -1,24 +0,0 @@ -var Backbone = require('backbone'); - -module.exports = Backbone.Collection.extend({ - - initialize(models, opt) { - - this.model = function(attrs, opts) { - var model; - - switch(1){ - - default: - if(!this.ClassTag) - this.ClassTag = require("selector_manager/model/Selector"); - model = new this.ClassTag(attrs, opts); - - } - - return model; - }; - - }, - -}); From 056b353fcfa89db060af9037cd03eb164a125280 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 17:09:20 +0100 Subject: [PATCH 13/29] Add tests for `getFullString` in Selectors --- .../selector_manager/model/SelectorModels.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/specs/selector_manager/model/SelectorModels.js b/test/specs/selector_manager/model/SelectorModels.js index 5b1f19773..482144e1d 100644 --- a/test/specs/selector_manager/model/SelectorModels.js +++ b/test/specs/selector_manager/model/SelectorModels.js @@ -37,7 +37,13 @@ module.exports = { }); - describe('Selectors', () => { + + describe.only('Selectors', () => { + var obj; + + beforeEach(() => { + obj = new Selectors(); + }); it('Creates collection item correctly', () => { var c = new Selectors(); @@ -45,6 +51,21 @@ module.exports = { expect(m instanceof Selector).toEqual(true); }); + it('getFullString with single class', () => { + obj.add({name: 'test'}); + expect(obj.getFullString()).toEqual('.test'); + }); + + it('getFullString with multiple classes', () => { + obj.add([{name: 'test'}, {name: 'test2'}]); + expect(obj.getFullString()).toEqual('.test.test2'); + }); + + it('getFullString with mixed selectors', () => { + obj.add([{name: 'test'}, {name: 'test2', type: Selector.TYPE_ID}]); + expect(obj.getFullString()).toEqual('.test#test2'); + }); + }); } }; From f61e99c821707ef1ef7112b1c1492af2372c9df3 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 18:07:53 +0100 Subject: [PATCH 14/29] Add `getIdRule` to CssComposer and make use of it in Component --- src/css_composer/index.js | 14 ++++++++++++++ src/dom_components/model/Component.js | 15 ++++++++++++--- .../selector_manager/model/SelectorModels.js | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index 035b4719f..ac6b4b3e1 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -293,6 +293,20 @@ module.exports = () => { }, + /** + * Get css rule by id selector + * @param {string} name Id selector name, eg. 'my-id' + * @param {Object} [opts={}] Custom options + * @return {CssRule} + */ + getIdRule(name, opts = {}) { + const state = opts.state || ''; + const media = opts.mediaText || em.getCurrentMedia(); + const selector = em.get('SelectorManager').get(name, Selector.TYPE_ID); + return selector && this.get(selector, state, media); + }, + + /** * Render the block of CSS rules * @return {HTMLElement} diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index adfc163e3..ec33be835 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -187,9 +187,18 @@ module.exports = Backbone.Model.extend(Styleable).extend({ getStyle() { - const rule = this.rule; - const avoidInline = this.config.avoidInlineStyle; - return rule && avoidInline ? rule.getStyle() : Styleable.getStyle.call(this); + if (this.config.avoidInlineStyle) { + const state = this.get('state'); + const cc = this.em.get('CssComposer'); + const rule = cc.getIdRule(this.getId(), { state }); + this.rule = rule; + + if (rule) { + return rule.getStyle(); + } + } + + return Styleable.getStyle.call(this); }, diff --git a/test/specs/selector_manager/model/SelectorModels.js b/test/specs/selector_manager/model/SelectorModels.js index 482144e1d..88363cad5 100644 --- a/test/specs/selector_manager/model/SelectorModels.js +++ b/test/specs/selector_manager/model/SelectorModels.js @@ -38,7 +38,7 @@ module.exports = { }); - describe.only('Selectors', () => { + describe('Selectors', () => { var obj; beforeEach(() => { From 42c8497eabb990dc6059b64643c85862981d7349 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 19:52:01 +0100 Subject: [PATCH 15/29] Add tests for toCSS --- test/specs/css_composer/model/CssModels.js | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/specs/css_composer/model/CssModels.js b/test/specs/css_composer/model/CssModels.js index e4e360c44..b81c477e8 100644 --- a/test/specs/css_composer/model/CssModels.js +++ b/test/specs/css_composer/model/CssModels.js @@ -56,6 +56,30 @@ module.exports = { expect(obj.compare([s2, s1], 'hover', '1000')).toEqual(true); }); + it('toCSS returns empty if there is no style', () => { + var s1 = obj.get('selectors').add({ name: 'test1' }); + expect(obj.toCSS()).toEqual(''); + }); + + it('toCSS returns empty if there is no selectors', () => { + obj.setStyle({color: 'red'}); + expect(obj.toCSS()).toEqual(''); + }); + + it('toCSS returns simple CSS', () => { + obj.get('selectors').add({ name: 'test1' }); + obj.setStyle({color: 'red'}); + expect(obj.toCSS()).toEqual(`.test1 {color:red}`); + }); + + it('toCSS wraps correctly inside media rule', () => { + const media = '(max-width: 768px)'; + obj.set('mediaText', media); + obj.get('selectors').add({ name: 'test1' }); + obj.setStyle({color: 'red'}); + expect(obj.toCSS()).toEqual(`@media ${media}{.test1 {color:red}}`); + }); + }); describe('CssRules', () => { From 4152c32e193c394ec599eba55c45e339cbd4fca1 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 12 Nov 2017 20:32:03 +0100 Subject: [PATCH 16/29] Improve `toHTML` and `getAttributes` in Component --- src/dom_components/model/Component.js | 68 +++++++++----------- test/specs/code_manager/model/CodeModels.js | 2 +- test/specs/dom_components/model/Component.js | 1 + 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index ec33be835..dea228859 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -1,4 +1,4 @@ -import { isUndefined, isArray } from 'underscore'; +import { isUndefined, isArray, isEmpty, has } from 'underscore'; import Styleable from 'domain_abstract/model/Styleable'; const Backbone = require('backbone'); @@ -219,7 +219,19 @@ module.exports = Backbone.Model.extend(Styleable).extend({ * @return {Object} */ getAttributes() { - return this.get('attributes'); + const classes = []; + const attributes = this.get('attributes') || {}; + + // Add classes + this.get('classes').each(cls => classes.push(cls.get('name'))); + classes.length && (attributes.class = classes.join(' ')); + + // If style is not empty I need an ID attached to the component + if (!isEmpty(this.get('style')) && !has(attributes, 'id')) { + attributes.id = this.getId(); + } + + return attributes; }, @@ -518,43 +530,25 @@ module.exports = Backbone.Model.extend(Styleable).extend({ * @private */ toHTML(opts) { - var code = ''; - var m = this; - var tag = m.get('tagName'); - var idFound = 0; - var sTag = m.get('void'); - var attrId = ''; - var strAttr = ''; - var attr = this.getAttrToHTML(); - - for (var prop in attr) { - if (prop == 'id') { - idFound = 1; + const model = this; + const attrs = []; + const classes = []; + const tag = model.get('tagName'); + const sTag = model.get('void'); + const attributes = this.getAttrToHTML(); + + for (let attr in attributes) { + const value = attributes[attr]; + + if (!isUndefined(value) && value !== '') { + attrs.push(`${attr}="${value}"`); } - var val = attr[prop]; - strAttr += typeof val !== undefined && val !== '' ? - ' ' + prop + '="' + val + '"' : ''; } - // Build the string of classes - var strCls = ''; - m.get('classes').each(m => { - strCls += ' ' + m.get('name'); - }); - strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : ''; - - // If style is not empty I need an ID attached to the component - if(!_.isEmpty(m.get('style')) && !idFound) - attrId = ' id="' + m.getId() + '" '; - - code += '<' + tag + strCls + attrId + strAttr + (sTag ? '/' : '') + '>' + m.get('content'); - - m.get('components').each(m => { - code += m.toHTML(); - }); - - if(!sTag) - code += ''; + let attrString = attrs.length ? ` ${attrs.join(' ')}` : ''; + let code = `<${tag}${attrString}${sTag ? '/' : ''}>${model.get('content')}`; + model.get('components').each(comp => code += comp.toHTML()); + !sTag && (code += ``); return code; }, @@ -566,7 +560,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ * @private */ getAttrToHTML() { - var attr = this.get('attributes') || {}; + var attr = this.getAttributes(); delete attr.style; return attr; }, diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index 6c580d047..ebddfd62a 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -54,7 +54,7 @@ module.exports = { ['class1', 'class2'].forEach(item => { m1.get('classes').add({name: item}); }); - expect(obj.build(comp)).toEqual('
'); + expect(obj.build(comp)).toEqual('
'); }); }); diff --git a/test/specs/dom_components/model/Component.js b/test/specs/dom_components/model/Component.js index c0c57a787..5cda7b3ea 100644 --- a/test/specs/dom_components/model/Component.js +++ b/test/specs/dom_components/model/Component.js @@ -169,6 +169,7 @@ module.exports = { }); expect(obj.getAttributes()).toEqual({ id: 'test', + class: 'class1 class2', 'data-test': 'value', }); expect(obj.get('classes').length).toEqual(2); From 3acd2930abd52578d51ddf29740154c400546055 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 13 Nov 2017 02:00:35 +0100 Subject: [PATCH 17/29] Refactor methods in CssGenerator --- src/code_manager/index.js | 1 + src/code_manager/model/CssGenerator.js | 131 +++++++------------- src/css_composer/model/CssRule.js | 27 ++-- src/domain_abstract/model/Styleable.js | 4 +- test/specs/code_manager/model/CodeModels.js | 2 +- 5 files changed, 68 insertions(+), 97 deletions(-) diff --git a/src/code_manager/index.js b/src/code_manager/index.js index 578d394c8..32beeada4 100644 --- a/src/code_manager/index.js +++ b/src/code_manager/index.js @@ -182,6 +182,7 @@ module.exports = () => { * var codeStr = codeManager.getCode(model, 'html'); * */ getCode(model, genId, opt = {}) { + opt.em = c.em; var generator = this.getGenerator(genId); return generator ? generator.build(model, opt) : ''; }, diff --git a/src/code_manager/model/CssGenerator.js b/src/code_manager/model/CssGenerator.js index 496ffa5c1..f05193958 100644 --- a/src/code_manager/model/CssGenerator.js +++ b/src/code_manager/model/CssGenerator.js @@ -1,92 +1,65 @@ -var Backbone = require('backbone'); +import { keys } from 'underscore'; -module.exports = Backbone.Model.extend({ +module.exports = require('backbone').Model.extend({ initialize() { this.compCls = []; + this.ids = []; }, /** - * Get CSS from component + * Get CSS from a component * @param {Model} model * @return {String} */ buildFromModel(model, opts = {}) { - var code = ''; - var style = model.get('style'); - var classes = model.get('classes'); + let code = ''; + const style = model.get('style'); + const classes = model.get('classes'); const wrappesIsBody = opts.wrappesIsBody; + this.ids.push(model.getId()); // Let's know what classes I've found - if(classes) { - classes.each(function(model){ - this.compCls.push(model.get('name')); - }, this); - } + classes.each(model => this.compCls.push(model.getFullName())); - if(style && Object.keys(style).length !== 0) { + if (style && keys(style).length) { let selector = `#${model.getId()}`; - selector = wrappesIsBody && model.get('wrapper') ? - 'body' : selector; - code += `${selector}{`; - for(var prop in style){ - if(style.hasOwnProperty(prop)) - code += prop + ':' + style[prop] + ';'; - } - code += '}'; + selector = wrappesIsBody && model.get('wrapper') ? 'body' : selector; + code = `${selector}{${model.styleToString()}}`; } + const components = model.components(); + components.each(model => code += this.buildFromModel(model, opts)); return code; }, - /** - * Get CSS from components - * @param {Model} model - * @return {String} - */ - buildFromComp(model) { - var coll = model.get('components') || model, - code = ''; - - coll.each(function(m) { - var cln = m.get('components'); - code += this.buildFromModel(m); - - if(cln.length){ - code += this.buildFromComp(cln); - } - - }, this); - return code; - }, - - /** @inheritdoc */ build(model, opts = {}) { const cssc = opts.cssc; this.compCls = []; + this.ids = []; var code = this.buildFromModel(model, opts); - code += this.buildFromComp(model); - var compCls = this.compCls; if (cssc) { - var rules = cssc.getAll(); - var mediaRules = {}; - rules.each(function(rule) { - var width = rule.get('mediaText'); - - // If width setted will render it later - if(width){ - var mRule = mediaRules[width]; - if(mRule) - mRule.push(rule); - else - mediaRules[width] = [rule]; + const rules = cssc.getAll(); + const mediaRules = {}; + + rules.each(rule => { + const media = rule.get('mediaText'); + + // If media is setted, I'll render it later + if (media) { + const mRules = mediaRules[media]; + if (mRules) { + mRules.push(rule); + } else { + mediaRules[media] = [rule]; + } return; } code += this.buildFromRule(rule); - }, this); + }); // Get media rules for (var ruleW in mediaRules) { @@ -102,6 +75,7 @@ module.exports = Backbone.Model.extend({ } } + return code; }, @@ -111,39 +85,24 @@ module.exports = Backbone.Model.extend({ * @return {string} CSS string */ buildFromRule(rule) { - var result = ''; - var selectorsAdd = rule.get('selectorsAdd'); - var selectors = rule.get('selectors'); - var ruleStyle = rule.get('style'); - var state = rule.get('state'); - var strSel = ''; - var found = 0; - var compCls = this.compCls; - - // Get string of selectors - selectors.each(selector => { - strSel += '.' + selector.get('name'); - if(compCls.indexOf(selector.get('name')) > -1) + let result = ''; + const selectorStr = rule.selectorsToString(); + const selectorStrNoAdd = rule.selectorsToString({skipAdd: 1}); + let found; + + // This will not render a rule if there is no its component + rule.get('selectors').each(selector => { + if (this.compCls.indexOf(selector.getFullName()) >= 0) { found = 1; + } }); - // With 'found' will skip rules which selectors are not found in - // canvas components. - if ((strSel && found) || selectorsAdd) { - strSel += state ? ':' + state : ''; - strSel += selectorsAdd ? (strSel ? ', ' : '') + selectorsAdd : ''; - var strStyle = ''; - - // Get string of style properties - if(ruleStyle && Object.keys(ruleStyle).length !== 0){ - for(var prop2 in ruleStyle){ - if(ruleStyle.hasOwnProperty(prop2)) - strStyle += prop2 + ':' + ruleStyle[prop2] + ';'; - } - } + if ((selectorStrNoAdd && found) || rule.get('selectorsAdd')) { + const style = rule.styleToString(); - if(strStyle) - result += strSel + '{' + strStyle + '}'; + if (style) { + result += `${selectorStr}{${style}}`; + } } return result; diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js index fcc17f74e..b02fb1f67 100644 --- a/src/css_composer/model/CssRule.js +++ b/src/css_composer/model/CssRule.js @@ -45,23 +45,34 @@ module.exports = Backbone.Model.extend(Styleable).extend({ }, + /** + * Return selectors fo the rule as a string + * @return {string} + */ + selectorsToString(opts = {}) { + const result = []; + const state = this.get('state'); + const addSelector = this.get('selectorsAdd'); + const selectors = this.get('selectors').getFullString(); + const stateStr = state ? `:${state}` : ''; + selectors && result.push(`${selectors}${stateStr}`); + addSelector && !opts.skipAdd && result.push(addSelector); + return result.join(', '); + }, + + /** * Returns CSS string of the rule * @return {string} */ toCSS() { let result = ''; - let sels = []; - const state = this.get('state'); const media = this.get('mediaText'); const style = this.styleToString(); - const addSelector = this.get('selectorsAdd'); - const selectors = this.get('selectors').getFullString(); - selectors && sels.push(selectors); - addSelector && sels.push(addSelector); + const selectors = this.selectorsToString(); - if (style && sels.length) { - result = `${sels.join(',')} {${style}}`; + if (selectors && style) { + result = `${selectors} {${style}}`; } if (media && result) { diff --git a/src/domain_abstract/model/Styleable.js b/src/domain_abstract/model/Styleable.js index 28a4508cc..eaa6df2c5 100644 --- a/src/domain_abstract/model/Styleable.js +++ b/src/domain_abstract/model/Styleable.js @@ -87,9 +87,9 @@ export default { const style = this.getStyle(); for (let prop in style) { - result.push(`${prop}:${style[prop]}`); + result.push(`${prop}:${style[prop]};`); } - return result.join(';'); + return result.join(''); }, } diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index ebddfd62a..43f0b5f57 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -58,7 +58,7 @@ module.exports = { }); }); - describe('CssGenerator', () => { + describe.only('CssGenerator', () => { var newCssComp = () => new CssComposer().init(); beforeEach(() => { obj = new CssGenerator(); From 163ecffc540da48a99b35687c278567f56034c04 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 13 Nov 2017 02:19:25 +0100 Subject: [PATCH 18/29] Add ID rules in CssGenerator --- src/code_manager/model/CssGenerator.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/code_manager/model/CssGenerator.js b/src/code_manager/model/CssGenerator.js index f05193958..466a0da0c 100644 --- a/src/code_manager/model/CssGenerator.js +++ b/src/code_manager/model/CssGenerator.js @@ -14,15 +14,18 @@ module.exports = require('backbone').Model.extend({ */ buildFromModel(model, opts = {}) { let code = ''; + const em = this.em; + const dc = em && em.get('DomComponents'); + const avoidInline = dc && dc.getConfig().avoidInlineStyle; const style = model.get('style'); const classes = model.get('classes'); const wrappesIsBody = opts.wrappesIsBody; - this.ids.push(model.getId()); + this.ids.push(`#${model.getId()}`); // Let's know what classes I've found classes.each(model => this.compCls.push(model.getFullName())); - if (style && keys(style).length) { + if (style && keys(style).length && !avoidInline) { let selector = `#${model.getId()}`; selector = wrappesIsBody && model.get('wrapper') ? 'body' : selector; code = `${selector}{${model.styleToString()}}`; @@ -36,6 +39,7 @@ module.exports = require('backbone').Model.extend({ build(model, opts = {}) { const cssc = opts.cssc; + this.em = opts.em || ''; this.compCls = []; this.ids = []; var code = this.buildFromModel(model, opts); @@ -62,15 +66,13 @@ module.exports = require('backbone').Model.extend({ }); // Get media rules - for (var ruleW in mediaRules) { - var meRules = mediaRules[ruleW]; - var ruleC = ''; - for(var i = 0, len = meRules.length; i < len; i++){ - ruleC += this.buildFromRule(meRules[i]); - } + for (let media in mediaRules) { + let rulesStr = ''; + const mRules = mediaRules[media]; + mRules.forEach(rule => rulesStr += this.buildFromRule(rule)); - if (ruleC) { - code += '@media ' + ruleW + '{' + ruleC + '}'; + if (rulesStr) { + code += `@media ${media}{${rulesStr}}`; } } @@ -92,7 +94,8 @@ module.exports = require('backbone').Model.extend({ // This will not render a rule if there is no its component rule.get('selectors').each(selector => { - if (this.compCls.indexOf(selector.getFullName()) >= 0) { + const name = selector.getFullName(); + if (this.compCls.indexOf(name) >= 0 || this.ids.indexOf(name) >= 0) { found = 1; } }); From 6f7ee12cc01d8db23d5406cf4acc5b8552404543 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 13 Nov 2017 02:31:25 +0100 Subject: [PATCH 19/29] Fix regressions --- src/dom_components/view/ComponentView.js | 11 +++++++++-- test/specs/code_manager/model/CodeModels.js | 2 +- test/specs/css_composer/model/CssModels.js | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 6e9f3a320..e90e35c2d 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -1,3 +1,5 @@ +import { isArray } from 'underscore'; + const ComponentsView = require('./ComponentsView'); module.exports = Backbone.View.extend({ @@ -210,8 +212,13 @@ module.exports = Backbone.View.extend({ getClasses() { var attr = this.model.get("attributes"), classes = attr['class'] || []; - if(classes.length){ - return classes.join(" "); + classes = isArray(classes) ? classes : [classes]; + + if (classes.length) { + if (!classes.join) { + console.log('class is',classes); + } + return classes.join(' '); }else return null; }, diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index 43f0b5f57..ebddfd62a 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -58,7 +58,7 @@ module.exports = { }); }); - describe.only('CssGenerator', () => { + describe('CssGenerator', () => { var newCssComp = () => new CssComposer().init(); beforeEach(() => { obj = new CssGenerator(); diff --git a/test/specs/css_composer/model/CssModels.js b/test/specs/css_composer/model/CssModels.js index b81c477e8..77055311d 100644 --- a/test/specs/css_composer/model/CssModels.js +++ b/test/specs/css_composer/model/CssModels.js @@ -69,7 +69,7 @@ module.exports = { it('toCSS returns simple CSS', () => { obj.get('selectors').add({ name: 'test1' }); obj.setStyle({color: 'red'}); - expect(obj.toCSS()).toEqual(`.test1 {color:red}`); + expect(obj.toCSS()).toEqual(`.test1 {color:red;}`); }); it('toCSS wraps correctly inside media rule', () => { @@ -77,7 +77,7 @@ module.exports = { obj.set('mediaText', media); obj.get('selectors').add({ name: 'test1' }); obj.setStyle({color: 'red'}); - expect(obj.toCSS()).toEqual(`@media ${media}{.test1 {color:red}}`); + expect(obj.toCSS()).toEqual(`@media ${media}{.test1 {color:red;}}`); }); }); From 16417fd1316bd3723a51938ab104c0dabfcd8386 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 14 Nov 2017 13:16:29 +0100 Subject: [PATCH 20/29] Update `getClasses` --- src/dom_components/view/ComponentView.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index e90e35c2d..66e41060f 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -215,12 +215,10 @@ module.exports = Backbone.View.extend({ classes = isArray(classes) ? classes : [classes]; if (classes.length) { - if (!classes.join) { - console.log('class is',classes); - } return classes.join(' '); - }else + } else { return null; + } }, /** From 58fcf6c53bd992b9998786a46199d6ea87673f60 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 14 Nov 2017 22:09:25 +0100 Subject: [PATCH 21/29] Add tests for `setIdRule` and `getIdRule` --- test/specs/css_composer/index.js | 38 ++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/test/specs/css_composer/index.js b/test/specs/css_composer/index.js index 382f5f160..df1572524 100644 --- a/test/specs/css_composer/index.js +++ b/test/specs/css_composer/index.js @@ -1,16 +1,17 @@ -var Models = require('./model/CssModels'); -var CssRuleView = require('./view/CssRuleView'); -var CssRulesView = require('./view/CssRulesView'); -var CssComposer = require('css_composer'); -var e2e = require('./e2e/CssComposer'); -var utils = require('./../test_utils.js'); +const Models = require('./model/CssModels'); +const CssRuleView = require('./view/CssRuleView'); +const CssRulesView = require('./view/CssRulesView'); +const CssComposer = require('css_composer'); +const e2e = require('./e2e/CssComposer'); +const utils = require('./../test_utils.js'); +const Editor = require('editor/model/Editor'); describe('Css Composer', () => { - describe('Main', () => { + describe.only('Main', () => { var obj; - + var em; var config; var storagMock = utils.storageMock(); var editorModel = { @@ -33,7 +34,8 @@ describe('Css Composer', () => { beforeEach(() => { - config = {}; + em = new Editor({}); + config = {em}; obj = new CssComposer().init(config); }); @@ -158,6 +160,24 @@ describe('Css Composer', () => { expect(obj.render()).toExist(); }); + it('Create a rule with id selector by using setIdRule()', () => { + const name = 'test'; + obj.setIdRule(name, {color: 'red'}); + expect(obj.getAll().length).toEqual(1); + const rule = obj.getIdRule(name); + expect(rule.selectorsToString()).toEqual(`#${name}`); + expect(rule.styleToString()).toEqual(`color:red;`); + }); + + it('Create a rule with id selector and state by using setIdRule()', () => { + const name = 'test'; + const state = 'hover'; + obj.setIdRule(name, {color: 'red'}, {state}); + expect(obj.getAll().length).toEqual(1); + const rule = obj.getIdRule(name, {state}); + expect(rule.selectorsToString()).toEqual(`#${name}:${state}`); + }); + }); Models.run(); From c97ad3431a4c0f2c2748ccb494e2e5d6958b4e77 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 14 Nov 2017 22:54:26 +0100 Subject: [PATCH 22/29] Add tests for CssGenerator --- src/dom_components/model/Component.js | 4 +- src/editor/model/Editor.js | 2 +- test/specs/code_manager/model/CodeModels.js | 45 +++++++++++++++++++-- test/specs/css_composer/index.js | 2 +- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index dea228859..c23a7ca5f 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -209,8 +209,10 @@ module.exports = Backbone.Model.extend(Styleable).extend({ const cc = this.em.get('CssComposer'); this.rule = cc.setIdRule(this.getId(), prop, { ...opts, state }); } else { - Styleable.setStyle.apply(this, arguments); + prop = Styleable.setStyle.apply(this, arguments); } + + return prop; }, diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index cb4dcf92f..114dc594a 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -47,7 +47,7 @@ module.exports = Backbone.Model.extend({ device: '', }, - initialize(c) { + initialize(c = {}) { this.config = c; this.set('Config', c); this.set('modules', []); diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index ebddfd62a..a5a1560c6 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -2,17 +2,21 @@ const CssGenerator = require('code_manager/model/CssGenerator'); const HtmlGenerator = require('code_manager/model/HtmlGenerator'); const DomComponents = require('dom_components'); const Component = require('dom_components/model/Component'); +const Editor = require('editor/model/Editor'); const CssComposer = require('css_composer'); module.exports = { run() { let comp; + let dcomp; let obj; + let em; + let cc; describe('HtmlGenerator', () => { beforeEach(() => { obj = new HtmlGenerator(); - var dcomp = new DomComponents(); + dcomp = new DomComponents(); comp = new Component({}, { componentTypes: dcomp.componentTypes, }); @@ -58,12 +62,15 @@ module.exports = { }); }); - describe('CssGenerator', () => { + describe.only('CssGenerator', () => { var newCssComp = () => new CssComposer().init(); beforeEach(() => { + em = new Editor({}); + cc = em.get('CssComposer'); obj = new CssGenerator(); - var dcomp = new DomComponents(); + dcomp = new DomComponents(); comp = new Component({}, { + em, componentTypes: dcomp.componentTypes, }); }); @@ -213,6 +220,38 @@ module.exports = { comp.get('components').remove(m1); expect(obj.build(comp, {cssc})).toEqual(''); }); + + it('Render correctly a rule with avoidInlineStyle option and not', () => { + em.get('DomComponents').getConfig().avoidInlineStyle = 1; + comp.setStyle({color: 'red'}); + const id = comp.getId(); + const result = `#${id}{color:red;}`; + expect(obj.build(comp, {cssc: cc})).toEqual(result); + expect(obj.build(comp, {cssc: cc, em})).toEqual(''); + }); + + it('Render correctly a rule with avoidInlineStyle and state', () => { + em.get('DomComponents').getConfig().avoidInlineStyle = 1; + const state = 'hover'; + comp.config.avoidInlineStyle = 1; + comp.set('state', state); + comp.setStyle({color: 'red'}); + const id = comp.getId(); + const result = `#${id}:${state}{color:red;}`; + expect(obj.build(comp, {cssc: cc, em})).toEqual(result); + }); + + it('Render correctly a rule with avoidInlineStyle and w/o state', () => { + em.get('DomComponents').getConfig().avoidInlineStyle = 1; + const state = 'hover'; + comp.config.avoidInlineStyle = 1; + comp.setStyle({color: 'blue'}); + comp.set('state', state); + comp.setStyle({color: 'red'}); + const id = comp.getId(); + const result = `#${id}{color:blue;}#${id}:${state}{color:red;}`; + expect(obj.build(comp, {cssc: cc, em})).toEqual(result); + }); }) } }; diff --git a/test/specs/css_composer/index.js b/test/specs/css_composer/index.js index df1572524..ae2e509ff 100644 --- a/test/specs/css_composer/index.js +++ b/test/specs/css_composer/index.js @@ -8,7 +8,7 @@ const Editor = require('editor/model/Editor'); describe('Css Composer', () => { - describe.only('Main', () => { + describe('Main', () => { var obj; var em; From 87ffa7865f99e76f990296f55308522e22c36e5d Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 14 Nov 2017 23:29:11 +0100 Subject: [PATCH 23/29] Move avoidInlineStyle inside main configuration object --- src/code_manager/model/CssGenerator.js | 3 +-- src/dom_components/config/config.js | 7 ------- src/dom_components/model/Component.js | 12 +++++++---- src/dom_components/view/ComponentView.js | 3 ++- src/editor/config/config.js | 6 ++++++ src/editor/index.js | 8 +++++--- src/editor/model/Editor.js | 13 ++++++++++++ src/selector_manager/view/ClassTagsView.js | 10 +++++++--- test/specs/code_manager/model/CodeModels.js | 22 +++++++++++++++------ 9 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/code_manager/model/CssGenerator.js b/src/code_manager/model/CssGenerator.js index 466a0da0c..02fc6781e 100644 --- a/src/code_manager/model/CssGenerator.js +++ b/src/code_manager/model/CssGenerator.js @@ -15,8 +15,7 @@ module.exports = require('backbone').Model.extend({ buildFromModel(model, opts = {}) { let code = ''; const em = this.em; - const dc = em && em.get('DomComponents'); - const avoidInline = dc && dc.getConfig().avoidInlineStyle; + const avoidInline = em && em.getConfig('avoidInlineStyle'); const style = model.get('style'); const classes = model.get('classes'); const wrappesIsBody = opts.wrappesIsBody; diff --git a/src/dom_components/config/config.js b/src/dom_components/config/config.js index 245800503..470c58a62 100644 --- a/src/dom_components/config/config.js +++ b/src/dom_components/config/config.js @@ -18,13 +18,6 @@ module.exports = { 'background-size'], }, - // Usually when you update the `style` of the component this changes the - // element's `style` attribute. Unfortunately, inline styling doesn't allow - // use of media queries (@media) or even pseudo selectors (eg. :hover). - // When `avoidInlineStyle` is true all the styles are inserted inside the - // relative css rule - avoidInlineStyle: 0, - // Could be used for default components components: [], diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index c23a7ca5f..04dba0dba 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -187,9 +187,11 @@ module.exports = Backbone.Model.extend(Styleable).extend({ getStyle() { - if (this.config.avoidInlineStyle) { + const em = this.em; + + if (em.getConfig('avoidInlineStyle')) { const state = this.get('state'); - const cc = this.em.get('CssComposer'); + const cc = em.get('CssComposer'); const rule = cc.getIdRule(this.getId(), { state }); this.rule = rule; @@ -203,10 +205,12 @@ module.exports = Backbone.Model.extend(Styleable).extend({ setStyle(prop = {}, opts = {}) { - if (this.config.avoidInlineStyle) { + const em = this.em; + + if (em.getConfig('avoidInlineStyle')) { prop = Styleable.setStyle.call(this, prop, {silent: 1, avoidStore: 1}); const state = this.get('state'); - const cc = this.em.get('CssComposer'); + const cc = em.get('CssComposer'); this.rule = cc.setIdRule(this.getId(), prop, { ...opts, state }); } else { prop = Styleable.setStyle.apply(this, arguments); diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 66e41060f..77a7b403d 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -153,7 +153,8 @@ module.exports = Backbone.View.extend({ * @private * */ updateStyle() { - if (this.config.avoidInlineStyle) { + const em = this.em; + if (em.get('avoidInlineStyle')) { const model = this.model; this.el.id = model.getId(); model.setStyle(model.getStyle()); diff --git a/src/editor/config/config.js b/src/editor/config/config.js index 1f31fd496..1980756c3 100644 --- a/src/editor/config/config.js +++ b/src/editor/config/config.js @@ -71,6 +71,12 @@ module.exports = { // The wrapper, if visible, will be shown as a `` wrappesIsBody: 1, + // Usually when you update the `style` of the component this changes the + // element's `style` attribute. Unfortunately, inline styling doesn't allow + // use of media queries (@media) or even pseudo selectors (eg. :hover). + // When `avoidInlineStyle` is true all styles are inserted inside the css rule + avoidInlineStyle: 0, + // Dom element el: '', diff --git a/src/editor/index.js b/src/editor/index.js index a8635e8b2..b7c8dc865 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -238,10 +238,12 @@ module.exports = config => { /** * Returns configuration object - * @return {Object} + * @param {string} [prop] Property name + * @return {any} Returns the configuration object or + * the value of the specified property */ - getConfig() { - return c; + getConfig(prop) { + return em.getConfig(prop); }, /** diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 114dc594a..7ed65e548 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -66,6 +66,19 @@ module.exports = Backbone.Model.extend({ this.on('change:changesCount', this.updateChanges, this); }, + + /** + * Get configurations + * @param {string} [prop] Property name + * @return {any} Returns the configuration object or + * the value of the specified property + */ + getConfig(prop) { + const config = this.config; + return isUndefined(prop) ? config : config[prop]; + }, + + /** * Should be called after all modules and plugins are loaded * @param {Function} clb diff --git a/src/selector_manager/view/ClassTagsView.js b/src/selector_manager/view/ClassTagsView.js index ffe1404d5..0524cfbde 100644 --- a/src/selector_manager/view/ClassTagsView.js +++ b/src/selector_manager/view/ClassTagsView.js @@ -33,7 +33,7 @@ module.exports = Backbone.View.extend({ events: {}, - initialize(o) { + initialize(o = {}) { this.config = o.config || {}; this.pfx = this.config.stylePrefix || ''; this.ppfx = this.config.pStylePrefix || ''; @@ -48,7 +48,8 @@ module.exports = Backbone.View.extend({ this.events['keyup #' + this.newInputId] = 'onInputKeyUp'; this.events['change #' + this.stateInputId] = 'stateChanged'; - this.target = this.config.em; + this.target = this.config.em; + this.em = this.target; this.listenTo(this.target ,'change:selectedComponent',this.componentChanged); this.listenTo(this.target, 'targetClassUpdated', this.updateSelector); @@ -148,7 +149,10 @@ module.exports = Backbone.View.extend({ * @private */ updateStateVis() { - if(this.collection.length) + const em = this.em; + const avoidInline = em && em.getConfig('avoidInlineStyle'); + + if(this.collection.length || avoidInline) this.getStatesC().css('display','block'); else this.getStatesC().css('display','none'); diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index a5a1560c6..92b0692ca 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -62,7 +62,7 @@ module.exports = { }); }); - describe.only('CssGenerator', () => { + describe('CssGenerator', () => { var newCssComp = () => new CssComposer().init(); beforeEach(() => { em = new Editor({}); @@ -221,17 +221,27 @@ module.exports = { expect(obj.build(comp, {cssc})).toEqual(''); }); - it('Render correctly a rule with avoidInlineStyle option and not', () => { - em.get('DomComponents').getConfig().avoidInlineStyle = 1; + it('Render correctly a rule without avoidInlineStyle option', () => { comp.setStyle({color: 'red'}); const id = comp.getId(); const result = `#${id}{color:red;}`; expect(obj.build(comp, {cssc: cc})).toEqual(result); - expect(obj.build(comp, {cssc: cc, em})).toEqual(''); + }); + + it('Render correctly a rule with avoidInlineStyle option', () => { + em.getConfig().avoidInlineStyle = 1; + comp = new Component({}, { + em, + componentTypes: dcomp.componentTypes, + }); + comp.setStyle({color: 'red'}); + const id = comp.getId(); + const result = `#${id}{color:red;}`; + expect(obj.build(comp, {cssc: cc, em})).toEqual(result); }); it('Render correctly a rule with avoidInlineStyle and state', () => { - em.get('DomComponents').getConfig().avoidInlineStyle = 1; + em.getConfig().avoidInlineStyle = 1; const state = 'hover'; comp.config.avoidInlineStyle = 1; comp.set('state', state); @@ -242,7 +252,7 @@ module.exports = { }); it('Render correctly a rule with avoidInlineStyle and w/o state', () => { - em.get('DomComponents').getConfig().avoidInlineStyle = 1; + em.getConfig().avoidInlineStyle = 1; const state = 'hover'; comp.config.avoidInlineStyle = 1; comp.setStyle({color: 'blue'}); From 220e9ce8f6981895b0d0e9b1a4952957048c9b4e Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 14 Nov 2017 23:56:20 +0100 Subject: [PATCH 24/29] Update the class tags selector viewer --- src/selector_manager/view/ClassTagsView.js | 26 ++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/selector_manager/view/ClassTagsView.js b/src/selector_manager/view/ClassTagsView.js index 0524cfbde..ada424c2d 100644 --- a/src/selector_manager/view/ClassTagsView.js +++ b/src/selector_manager/view/ClassTagsView.js @@ -159,31 +159,29 @@ module.exports = Backbone.View.extend({ this.updateSelector(); }, + /** * Udpate selector helper * @return {this} * @private */ updateSelector() { - const selected = this.target.get('selectedComponent'); + const selected = this.target.getSelected(); this.compTarget = selected; - if(!selected || !selected.get) - return; - var result = ''; - this.collection.each(model => { - if(model.get('active')) - result += '.' + model.get('name'); - }); - var state = selected.get('state'); - result = state ? result + ':' + state : result; - result = result || selected.getName(); - var el = this.el.querySelector('#' + this.pfx + 'sel'); - if (el) { - el.innerHTML = result; + if (!selected || !selected.get) { + return; } + + const state = selected.get('state'); + let result = this.collection.getFullString(); + result = result || `#${selected.getId()}`; + result += state ? `:${state}` : ''; + const el = this.el.querySelector('#' + this.pfx + 'sel'); + el && (el.innerHTML = result); }, + /** * Triggered when the select with states is changed * @param {Object} e From 0cb4456f83901372f6cb8a0d70ff1f9e94ed2938 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 15 Nov 2017 01:13:52 +0100 Subject: [PATCH 25/29] Update SectorsView and make `avoidInline` work with states --- src/css_composer/index.js | 2 + src/style_manager/view/SectorsView.js | 95 +++++++++++++++++---------- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/css_composer/index.js b/src/css_composer/index.js index ac6b4b3e1..52faf5402 100644 --- a/src/css_composer/index.js +++ b/src/css_composer/index.js @@ -5,6 +5,8 @@ * * [getAll](#getall) * * [load](#load) * * [store](#store) + * * [setIdRule](#setidrule) + * * [getIdRule](#getidrule) * * This module contains and manage CSS rules for the template inside the canvas * Before using the methods you should get first the module from the editor instance, in this way: diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index a1cfa8faa..05ae7016b 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -9,7 +9,7 @@ module.exports = Backbone.View.extend({ this.pfx = this.config.stylePrefix || ''; this.target = o.target || {}; - // The taget that will emit events for properties + // The target that will emit events for properties const target = {}; extend(target, Backbone.Events); const body = document.body; @@ -18,10 +18,10 @@ module.exports = Backbone.View.extend({ target.computedDefault = { ...window.getComputedStyle(dummy) }; body.removeChild(dummy); this.propTarget = target; - - this.listenTo( this.collection, 'add', this.addTo); - this.listenTo( this.collection, 'reset', this.render); - this.listenTo( this.target, 'change:selectedComponent targetClassAdded targetClassRemoved targetClassUpdated ' + + const coll = this.collection; + this.listenTo(coll, 'add', this.addTo); + this.listenTo(coll, 'reset', this.render); + this.listenTo(this.target, 'change:selectedComponent targetClassAdded targetClassRemoved targetClassUpdated ' + 'targetStateUpdated targetStyleUpdated change:device', this.targetUpdated); }, @@ -42,30 +42,62 @@ module.exports = Backbone.View.extend({ */ targetUpdated() { var em = this.target; - var el = em.get('selectedComponent'); + let model = em.getSelected(); const um = em.get('UndoManager'); + const cc = em.get('CssComposer'); + const avoidInline = em.getConfig('avoidInlineStyle'); - if(!el) + if (!model) { return; + } - // TODO make use of getModelToStyle here + const id = model.getId(); const config = em.get('Config'); - var classes = el.get('classes'); + var classes = model.get('classes'); var pt = this.propTarget; - var state = !config.devicePreviewMode ? el.get('state') : ''; + const state = !config.devicePreviewMode ? model.get('state') : ''; + const opts = { state }; var stateStr = state ? `:${state}` : null; - var view = el.view; + var view = model.view; const media = em.getCurrentMedia(); pt.helper = null; if (view) { - pt.computed = window.getComputedStyle(view.el, stateStr); + pt.computed = window.getComputedStyle(view.el, state ? `:${state}` : null); + } + + const appendStateRule = (style = {}) => { + const sm = em.get('SelectorManager'); + const helperClass = sm.add('hc-state'); + let helperRule = cc.get([helperClass]); + + if (!helperRule) { + helperRule = cc.add([helperClass]); + } else { + // I will make it last again, otherwise it could be overridden + const rules = cc.getAll(); + rules.remove(helperRule); + rules.add(helperRule); + } + + helperRule.setStyle(style); + pt.helper = helperRule; + }; + + // If true the model will be always a rule + if (avoidInline) { + const ruleId = cc.getIdRule(id, opts); + + if (!ruleId) { + model = cc.setIdRule(id, {}, opts); + } else { + model = ruleId; + } } if (classes.length) { - var cssC = em.get('CssComposer'); var valid = classes.getStyleable(); - var iContainer = cssC.get(valid, state, media); + var iContainer = cc.get(valid, state, media); if (!iContainer && valid.length) { // I stop undo manager here as after adding the CSSRule (generally after @@ -73,42 +105,39 @@ module.exports = Backbone.View.extend({ // the collection, therefore updating it in style manager will not affect it // #268 um.stopTracking(); - iContainer = cssC.add(valid, state, media); - iContainer.set('style', el.get('style')); - el.set('style', {}); + iContainer = cc.add(valid, state, media); + iContainer.setStyle(model.getStyle()); + model.setStyle({}); um.startTracking(); } if (!iContainer) { // In this case it's just a Component without any valid selector - pt.model = el; + pt.model = model; pt.trigger('update'); return; } // If the state is not empty, there should be a helper rule in play // The helper rule will get the same style of the iContainer - if (state) { - var clm = em.get('SelectorManager'); - var helperClass = clm.add('hc-state'); - var helperRule = cssC.get([helperClass]); - if(!helperRule) - helperRule = cssC.add([helperClass]); - else{ - // I will make it last again, otherwise it could be overridden - cssC.getAll().remove(helperRule); - cssC.getAll().add(helperRule); - } - helperRule.set('style', iContainer.get('style')); - pt.helper = helperRule; - } + state && appendStateRule(iContainer.getStyle()); pt.model = iContainer; pt.trigger('update'); return; } - pt.model = el; + if (state) { + const ruleState = cc.getIdRule(id, opts); + state && appendStateRule(ruleState && ruleState.getStyle()); + if (ruleState) { + console.log(ruleState, ruleState.toCSS()); + } else { + console.log('NO RULE'); + } + } + + pt.model = model; pt.trigger('update'); }, From d83ad7c14d37bd0a644fcf939cd66e5c071793e6 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 15 Nov 2017 01:54:27 +0100 Subject: [PATCH 26/29] Make it work for helper class --- src/css_composer/model/CssRule.js | 10 ++- src/css_composer/view/CssRuleView.js | 72 +++------------------ src/domain_abstract/model/Styleable.js | 12 +++- src/style_manager/view/SectorsView.js | 6 +- test/specs/css_composer/index.js | 2 + test/specs/css_composer/view/CssRuleView.js | 20 ------ 6 files changed, 30 insertions(+), 92 deletions(-) diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js index b02fb1f67..ed7b72ad4 100644 --- a/src/css_composer/model/CssRule.js +++ b/src/css_composer/model/CssRule.js @@ -23,6 +23,11 @@ module.exports = Backbone.Model.extend(Styleable).extend({ // Indicates if the rule is stylable stylable: true, + + // If true, sets '!important' on all properties + // You can use an array to specify properties to set important + // Used in view + important: 0, }, @@ -63,12 +68,13 @@ module.exports = Backbone.Model.extend(Styleable).extend({ /** * Returns CSS string of the rule + * @param {Object} [opts={}] Options * @return {string} */ - toCSS() { + toCSS(opts = {}) { let result = ''; const media = this.get('mediaText'); - const style = this.styleToString(); + const style = this.styleToString(opts); const selectors = this.selectorsToString(); if (selectors && style) { diff --git a/src/css_composer/view/CssRuleView.js b/src/css_composer/view/CssRuleView.js index dea7503e1..6fb35ee16 100644 --- a/src/css_composer/view/CssRuleView.js +++ b/src/css_composer/view/CssRuleView.js @@ -1,74 +1,22 @@ -var Backbone = require('backbone'); - -module.exports = Backbone.View.extend({ +module.exports = require('backbone').View.extend({ tagName: 'style', - initialize(o) { - this.config = o.config || {}; - this.listenTo(this.model, 'change:style', this.render); - this.listenTo(this.model, 'change:state', this.render); - this.listenTo(this.model, 'destroy remove', this.remove); - this.listenTo(this.model, 'change:mediaText', this.render); - this.listenTo(this.model.get('selectors'), 'change', this.selChanged); - }, - - /** - * Triggered when some selector is changed - * @private - */ - selChanged() { - this.selStr = this.renderSelectors(); - this.render(); - }, - /** - * Returns string of selectors - * @return {String} - * @private - */ - renderSelectors() { - const sel = []; + initialize(o = {}) { + this.config = o.config || {}; const model = this.model; - model.get('selectors').each(model => sel.push(model.getFullName())); - const sels = sel.join(''); - const add = model.get('selectorsAdd'); - return sels + (sels && add ? ', ' : '') + add; + const toTrack = 'change:style change:state change:mediaText'; + this.listenTo(model, toTrack, this.render); + this.listenTo(model, 'destroy remove', this.remove); + this.listenTo(model.get('selectors'), 'change', this.render); }, - /** - * Returns string of properties - * @return {String} - * @private - */ - renderProperties() { - var sel = [], - props = this.model.get('style'); - for (var prop in props){ - sel.push(prop + ':' + props[prop] + ';'); - } - return sel.join(''); - }, render() { - var block = '', - selStr = '', - o = ''; - if(!this.selStr) - this.selStr = this.renderSelectors(); - var prpStr = this.renderProperties(); - var stateStr = this.model.get('state'); - var mediaText = this.model.get('mediaText'); - if(this.selStr){ - stateStr = stateStr ? ':' + stateStr : ''; - block = prpStr !== '' ? '{' + prpStr + '}' : ''; - } - o = this.selStr && block ? this.selStr + stateStr + block : ''; - - if(mediaText && o) - o = '@media ' + mediaText + '{' + o + '}'; - - this.$el.html(o); + const model = this.model; + const important = model.get('important'); + this.el.innerHTML = this.model.toCSS({important}); return this; }, diff --git a/src/domain_abstract/model/Styleable.js b/src/domain_abstract/model/Styleable.js index eaa6df2c5..1a04ec388 100644 --- a/src/domain_abstract/model/Styleable.js +++ b/src/domain_abstract/model/Styleable.js @@ -1,9 +1,11 @@ -import { isString } from 'underscore'; +import { isString, isArray } from 'underscore'; import ParserHtml from 'parser/model/ParserHtml'; const parseStyle = ParserHtml().parseStyle; export default { + parseStyle, + /** * To trigger the style change event on models I have to * pass a new object instance @@ -80,14 +82,18 @@ export default { /** * Returns string of style properties + * @param {Object} [opts={}] Options * @return {String} */ - styleToString() { + styleToString(opts = {}) { const result = []; const style = this.getStyle(); for (let prop in style) { - result.push(`${prop}:${style[prop]};`); + const imp = opts.important; + const important = isArray(imp) ? imp.indexOf(prop) >= 0 : imp; + const value = `${style[prop]}${important ? ' !important' : ''}`; + result.push(`${prop}:${value};`); } return result.join(''); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index 05ae7016b..754dcdd2a 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -80,6 +80,7 @@ module.exports = Backbone.View.extend({ rules.add(helperRule); } + helperRule.set('important', 1); helperRule.setStyle(style); pt.helper = helperRule; }; @@ -130,11 +131,6 @@ module.exports = Backbone.View.extend({ if (state) { const ruleState = cc.getIdRule(id, opts); state && appendStateRule(ruleState && ruleState.getStyle()); - if (ruleState) { - console.log(ruleState, ruleState.toCSS()); - } else { - console.log('NO RULE'); - } } pt.model = model; diff --git a/test/specs/css_composer/index.js b/test/specs/css_composer/index.js index ae2e509ff..3d4b21569 100644 --- a/test/specs/css_composer/index.js +++ b/test/specs/css_composer/index.js @@ -167,6 +167,8 @@ describe('Css Composer', () => { const rule = obj.getIdRule(name); expect(rule.selectorsToString()).toEqual(`#${name}`); expect(rule.styleToString()).toEqual(`color:red;`); + expect(rule.styleToString({important: 1})).toEqual(`color:red !important;`); + expect(rule.styleToString({important: ['color']})).toEqual(`color:red !important;`); }); it('Create a rule with id selector and state by using setIdRule()', () => { diff --git a/test/specs/css_composer/view/CssRuleView.js b/test/specs/css_composer/view/CssRuleView.js index 218177efc..55dee4e1b 100644 --- a/test/specs/css_composer/view/CssRuleView.js +++ b/test/specs/css_composer/view/CssRuleView.js @@ -26,26 +26,6 @@ module.exports = { expect(CssRuleView).toExist(); }); - it('Correct behaviour of renderSelectors with single selector', () => { - obj.model.get('selectors').add({name: 'test'}); - expect(obj.renderSelectors()).toEqual('.test'); - }); - - it('Correct behaviour of renderSelectors with multiple selectors', () => { - obj.model.get('selectors').add([{name: 'test2'}, {name: 'test1'}]); - expect(obj.renderSelectors()).toEqual('.test2.test1'); - }); - - it('Correct behaviour of renderProperties with single property', () => { - obj.model.set('style', {'prop': 'value'}); - expect(obj.renderProperties()).toEqual('prop:value;'); - }); - - it('Correct behaviour of renderProperties with multiple properties', () => { - obj.model.set('style', {'prop2': 'value2', 'prop3': 'value3'}); - expect(obj.renderProperties()).toEqual('prop2:value2;prop3:value3;'); - }); - it('Empty style inside', function() { expect(fixtures.innerHTML).toEqual(''); }); From 02e502d15f9c2b89a59373c28e1a94440bf7a4f1 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 15 Nov 2017 02:49:05 +0100 Subject: [PATCH 27/29] Fix regressions --- src/css_composer/model/CssRule.js | 2 +- src/dom_components/view/ComponentView.js | 2 +- src/storage_manager/config/config.js | 2 +- test/specs/css_composer/e2e/CssComposer.js | 3 ++ test/specs/css_composer/model/CssModels.js | 4 +-- test/specs/grapesjs/index.js | 34 +++++++++---------- .../selector_manager/view/ClassTagsView.js | 13 +++---- 7 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js index ed7b72ad4..bfadfbf43 100644 --- a/src/css_composer/model/CssRule.js +++ b/src/css_composer/model/CssRule.js @@ -78,7 +78,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ const selectors = this.selectorsToString(); if (selectors && style) { - result = `${selectors} {${style}}`; + result = `${selectors}{${style}}`; } if (media && result) { diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 77a7b403d..9d9737e00 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -154,7 +154,7 @@ module.exports = Backbone.View.extend({ * */ updateStyle() { const em = this.em; - if (em.get('avoidInlineStyle')) { + if (em && em.get('avoidInlineStyle')) { const model = this.model; this.el.id = model.getId(); model.setStyle(model.getStyle()); diff --git a/src/storage_manager/config/config.js b/src/storage_manager/config/config.js index 0edbbd9e0..77f074a2c 100644 --- a/src/storage_manager/config/config.js +++ b/src/storage_manager/config/config.js @@ -24,7 +24,7 @@ module.exports = { //Enable/Disable saving HTML template storeHtml: 1, - //Enable/Disable saving HTML template + //Enable/Disable saving CSS template storeCss: 1, // ONLY FOR LOCAL STORAGE diff --git a/test/specs/css_composer/e2e/CssComposer.js b/test/specs/css_composer/e2e/CssComposer.js index be88caf09..fab1e33d8 100644 --- a/test/specs/css_composer/e2e/CssComposer.js +++ b/test/specs/css_composer/e2e/CssComposer.js @@ -134,6 +134,7 @@ module.exports = { private: false, protected: false, }], + important: 0, selectorsAdd: '', state: '', stylable: true, @@ -172,6 +173,7 @@ module.exports = { rule1Out = JSON.parse(JSON.stringify(rule1Out)); rule2Out = JSON.parse(JSON.stringify(rule2Out)); var rule1Result = { + important: 0, mediaText: '', selectors: [], selectorsAdd: '*', @@ -183,6 +185,7 @@ module.exports = { } }; var rule2Result = { + important: 0, mediaText: '', selectors: [], selectorsAdd: 'p', diff --git a/test/specs/css_composer/model/CssModels.js b/test/specs/css_composer/model/CssModels.js index 77055311d..f9a6e63d5 100644 --- a/test/specs/css_composer/model/CssModels.js +++ b/test/specs/css_composer/model/CssModels.js @@ -69,7 +69,7 @@ module.exports = { it('toCSS returns simple CSS', () => { obj.get('selectors').add({ name: 'test1' }); obj.setStyle({color: 'red'}); - expect(obj.toCSS()).toEqual(`.test1 {color:red;}`); + expect(obj.toCSS()).toEqual(`.test1{color:red;}`); }); it('toCSS wraps correctly inside media rule', () => { @@ -77,7 +77,7 @@ module.exports = { obj.set('mediaText', media); obj.get('selectors').add({ name: 'test1' }); obj.setStyle({color: 'red'}); - expect(obj.toCSS()).toEqual(`@media ${media}{.test1 {color:red;}}`); + expect(obj.toCSS()).toEqual(`@media ${media}{.test1{color:red;}}`); }); }); diff --git a/test/specs/grapesjs/index.js b/test/specs/grapesjs/index.js index 7fd8a5954..74363f0bc 100644 --- a/test/specs/grapesjs/index.js +++ b/test/specs/grapesjs/index.js @@ -16,11 +16,12 @@ describe('GrapesJS', () => { var storage; var storageId = 'testStorage'; var storageMock = { - store(data) { + store(data, clb) { storage = data; + clb(); }, - load(keys) { - return storage; + load(keys, clb) { + return clb(storage); }, }; @@ -36,7 +37,8 @@ describe('GrapesJS', () => { container: '#' + editorName, storageManager: { autoload: 0, - type:'none' + autosave: 0, + type: '' }, } obj = grapesjs; @@ -47,11 +49,6 @@ describe('GrapesJS', () => { fixtures = document.body.querySelector('#fixtures'); }); - afterEach(() => { - config = {}; - obj = null; - }); - it('Main object should be loaded', () => { expect(obj).toExist(); }); @@ -76,7 +73,7 @@ describe('GrapesJS', () => { it('New editor is empty', () => { var editor = obj.init(config); var html = editor.getHtml(); - var css = editor.getCss(); + //var css = editor.getCss(); var protCss = editor.getConfig().protectedCss; expect((html ? html : '')).toNotExist(); //expect((css ? css : '')).toEqual(protCss); @@ -154,7 +151,7 @@ describe('GrapesJS', () => { expect(styles.at(1).get('selectors').at(0).get('name')).toEqual('test5'); }); - it('Adds new storage as plugin and store data there', () => { + it.skip('Adds new storage as plugin and store data there', done => { var pluginName = storageId + '-plugin'; obj.plugins.add(pluginName, edt => { edt.StorageManager.add(storageId, storageMock); @@ -163,9 +160,11 @@ describe('GrapesJS', () => { config.plugins = [pluginName]; var editor = obj.init(config); editor.setComponents(htmlString); - editor.store(); - editor.load((data) => { - expect(data.html).toEqual(htmlString); + editor.store(() => { + editor.load((data) => { + expect(data.html).toEqual(htmlString); + done(); + }); }); }); @@ -182,8 +181,7 @@ describe('GrapesJS', () => { expect(editor.customValue).toEqual('TEST'); }); - // Problems with iframe loading - it.skip('Execute custom command', () => { + it('Execute custom command', () => { var editor = obj.init(config); editor.testVal = ''; editor.setComponents(htmlString); @@ -196,7 +194,7 @@ describe('GrapesJS', () => { expect(editor.testVal).toEqual(htmlString + '5'); }); - it.skip('Stop custom command', () => { + it('Stop custom command', () => { var editor = obj.init(config); editor.testVal = ''; editor.setComponents(htmlString); @@ -231,7 +229,7 @@ describe('GrapesJS', () => { }); // Problems with iframe loading - it.skip('Init new editor with custom plugin overrides default commands', () => { + it('Init new editor with custom plugin overrides default commands', () => { var editor, pluginName = 'test-plugin-opts'; diff --git a/test/specs/selector_manager/view/ClassTagsView.js b/test/specs/selector_manager/view/ClassTagsView.js index bcbe666f7..237343d5f 100644 --- a/test/specs/selector_manager/view/ClassTagsView.js +++ b/test/specs/selector_manager/view/ClassTagsView.js @@ -1,5 +1,6 @@ const ClassTagsView = require('selector_manager/view/ClassTagsView'); const Selectors = require('selector_manager/model/Selectors'); +const Editor = require('editor/model/Editor'); module.exports = { run() { @@ -11,6 +12,7 @@ module.exports = { var testLabel; var coll; var target; + var em; before(() => { document.body.innerHTML = '
'; @@ -23,10 +25,8 @@ module.exports = { }); beforeEach(function () { - target = { get() {} }; + target = new Editor(); coll = new Selectors(); - _.extend(target, Backbone.Events); - view = new ClassTagsView({ config : { em: target }, collection: coll @@ -74,16 +74,13 @@ module.exports = { expect(this.input.css('display')).toNotEqual('none'); }); - it.skip('Stop tag creation', function() { + it('Stop tag creation', function() { this.btnAdd.trigger('click'); this.input.val('test') this.input.trigger('blur'); - //(this.btnAdd.css('display') !== 'none').should.equal(true); - //(this.input.css('display') == 'none').should.equal(true); - //this.input.val().should.equal(''); expect(this.btnAdd.css('display')).toNotEqual('none'); expect(this.input.css('display')).toEqual('none'); - expect(this.input.val()).toEqual(''); + expect(this.input.val()).toEqual(null); }); it.skip('Check keyup of ESC on input', function() { From c67c23665b3be1af6602884f115a82c662583249 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 15 Nov 2017 21:38:24 +0100 Subject: [PATCH 28/29] Adjust clone method in Component with `avoidInlineStyle` --- src/dom_components/model/Component.js | 22 ++++++++++++-------- src/dom_components/model/Components.js | 26 ++++++++++++++---------- src/dom_components/view/ComponentView.js | 22 +++----------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 04dba0dba..779e6cba7 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -1,4 +1,4 @@ -import { isUndefined, isArray, isEmpty, has } from 'underscore'; +import { isUndefined, isArray, isEmpty, has, clone } from 'underscore'; import Styleable from 'domain_abstract/model/Styleable'; const Backbone = require('backbone'); @@ -233,7 +233,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ classes.length && (attributes.class = classes.join(' ')); // If style is not empty I need an ID attached to the component - if (!isEmpty(this.get('style')) && !has(attributes, 'id')) { + if (!isEmpty(this.getStyle()) && !has(attributes, 'id')) { attributes.id = this.getId(); } @@ -476,21 +476,21 @@ module.exports = Backbone.Model.extend(Styleable).extend({ * @private */ clone(reset) { - var attr = _.clone(this.attributes), - comp = this.get('components'), - traits = this.get('traits'), - cls = this.get('classes'); + const em = this.em; + const style = this.getStyle(); + const attr = clone(this.attributes); + delete attr.attributes.id; attr.components = []; attr.classes = []; attr.traits = []; - comp.each((md, i) => { + this.get('components').each((md, i) => { attr.components[i] = md.clone(1); }); - traits.each((md, i) => { + this.get('traits').each((md, i) => { attr.traits[i] = md.clone(); }); - cls.each((md, i) => { + this.get('classes').each((md, i) => { attr.classes[i] = md.get('name'); }); @@ -501,6 +501,10 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.opt.collection = null; } + if (em.getConfig('avoidInlineStyle') && !isEmpty(style)) { + attr.style = style; + } + return new this.constructor(attr, this.opt); }, diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index 104e73c64..71a511370 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -1,4 +1,6 @@ -var Backbone = require('backbone'); +import { isEmpty } from 'underscore'; + +const Backbone = require('backbone'); module.exports = Backbone.Collection.extend({ @@ -67,16 +69,18 @@ module.exports = Backbone.Collection.extend({ }, onAdd(model, c, opts) { - var style = model.get('style'); - var em = this.editor; - - if (!_.isEmpty(style) && em && em.get && em.get('Config').forceClass) { - var cssC = this.editor.get('CssComposer'); - var newClass = this.editor.get('SelectorManager').add(model.cid); - model.set({style:{}}); - model.get('classes').add(newClass); - var rule = cssC.add(newClass); - rule.set('style', style); + const em = this.editor; + const style = model.get('style'); + const avoidInline = em && em.getConfig('avoidInlineStyle'); + + if (!isEmpty(style) && !avoidInline && + em && em.get && em.get('Config').forceClass) { + var cssC = this.editor.get('CssComposer'); + var newClass = this.editor.get('SelectorManager').add(model.cid); + model.set({style:{}}); + model.get('classes').add(newClass); + var rule = cssC.add(newClass); + rule.set('style', style); } }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 9d9737e00..189516685 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -154,30 +154,14 @@ module.exports = Backbone.View.extend({ * */ updateStyle() { const em = this.em; + const model = this.model; + if (em && em.get('avoidInlineStyle')) { - const model = this.model; this.el.id = model.getId(); model.setStyle(model.getStyle()); } else { - this.setAttribute('style', this.getStyleString()); - } - }, - - - /** - * Return style string - * @return {string} - * @private - * */ - getStyleString() { - var style = ''; - this.style = this.model.get('style'); - for(var key in this.style) { - if(this.style.hasOwnProperty(key)) - style += key + ':' + this.style[key] + ';'; + this.setAttribute('style', model.styleToString()); } - - return style; }, From b4f36a60793ccdb9f09abc6fa24cf016b307a8b4 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 15 Nov 2017 21:49:39 +0100 Subject: [PATCH 29/29] Fix regressions --- src/dom_components/model/Component.js | 8 ++++---- test/specs/code_manager/model/CodeModels.js | 2 ++ test/specs/dom_components/index.js | 4 ++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 779e6cba7..1f6834449 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -121,7 +121,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ initialize(props = {}, opt = {}) { - const em = opt.sm || opt.em || {}; + const em = opt.sm || opt.em || ''; // Propagate properties from parent if indicated const parent = this.parent(); @@ -189,7 +189,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ getStyle() { const em = this.em; - if (em.getConfig('avoidInlineStyle')) { + if (em && em.getConfig('avoidInlineStyle')) { const state = this.get('state'); const cc = em.get('CssComposer'); const rule = cc.getIdRule(this.getId(), { state }); @@ -207,7 +207,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ setStyle(prop = {}, opts = {}) { const em = this.em; - if (em.getConfig('avoidInlineStyle')) { + if (em && em.getConfig('avoidInlineStyle')) { prop = Styleable.setStyle.call(this, prop, {silent: 1, avoidStore: 1}); const state = this.get('state'); const cc = em.get('CssComposer'); @@ -501,7 +501,7 @@ module.exports = Backbone.Model.extend(Styleable).extend({ this.opt.collection = null; } - if (em.getConfig('avoidInlineStyle') && !isEmpty(style)) { + if (em && em.getConfig('avoidInlineStyle') && !isEmpty(style)) { attr.style = style; } diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js index 92b0692ca..636a30bf2 100644 --- a/test/specs/code_manager/model/CodeModels.js +++ b/test/specs/code_manager/model/CodeModels.js @@ -15,9 +15,11 @@ module.exports = { describe('HtmlGenerator', () => { beforeEach(() => { + em = new Editor(); obj = new HtmlGenerator(); dcomp = new DomComponents(); comp = new Component({}, { + em, componentTypes: dcomp.componentTypes, }); }); diff --git a/test/specs/dom_components/index.js b/test/specs/dom_components/index.js index cdae7b0ff..5cab2699b 100644 --- a/test/specs/dom_components/index.js +++ b/test/specs/dom_components/index.js @@ -5,12 +5,14 @@ const ComponentView = require('./view/ComponentV'); const ComponentsView = require('./view/ComponentsView'); const ComponentTextView = require('./view/ComponentTextView'); const ComponentImageView = require('./view/ComponentImageView'); +const Editor = require('editor/model/Editor'); const utils = require('./../test_utils.js'); describe('DOM Components', () => { describe('Main', () => { + var em; var obj; var config; var storagMock = utils.storageMock(); @@ -39,7 +41,9 @@ describe('DOM Components', () => { beforeEach(() => { + em = new Editor(); config = { + em, storeWrapper: 1, }; obj = new DomComponents().init(config);