diff --git a/src/css_composer/main.js b/src/css_composer/main.js index 3e97ad010..2bdfbb07d 100644 --- a/src/css_composer/main.js +++ b/src/css_composer/main.js @@ -215,11 +215,13 @@ define(function(require) { /** * Add a raw collection of rule objects * This method overrides styles, in case, of already defined rule - * @param {Array} data Array of rule objects + * @param {Array} data Array of rule objects, eg . [{selectors: ['class1'], style: {....}}, ..] + * @param {Object} opts Options * @return {Array} * @private */ - addCollection: function(data){ + addCollection: function(data, opts) { + var opt = opts || {}; var result = []; var d = data instanceof Array ? data : [data]; for(var i = 0, l = d.length; i < l; i++){ @@ -237,7 +239,12 @@ define(function(require) { newSels.push(selec); } var model = this.add(newSels, rule.state, rule.maxWidth); - model.set('style', rule.style || {}); + if (opt.extend) { + var newStyle = _.extend({}, model.get('style'), rule.style || {}); + model.set('style', newStyle); + } else { + model.set('style', rule.style || {}); + } result.push(model); } return result; diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index 2e011d281..fb4806b83 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -1,79 +1,79 @@ define([ 'backbone', 'require'], - function (Backbone, require) { + function (Backbone, require) { - return Backbone.Collection.extend({ + return Backbone.Collection.extend({ - initialize: function(models, opt){ + initialize: function(models, opt){ - this.on('add', this.onAdd); + this.on('add', this.onAdd); - this.config = opt && opt.config ? opt.config : null; + this.config = opt && opt.config ? opt.config : null; - // Inject editor - if(opt && opt.sm) - this.editor = opt.sm; + // Inject editor + if(opt && opt.sm) + this.editor = opt.sm; - this.model = function(attrs, options) { - var model; + this.model = function(attrs, options) { + var model; - if(!options.sm && opt && opt.sm) - options.sm = opt.sm; + if(!options.sm && opt && opt.sm) + options.sm = opt.sm; - if(opt && opt.config) - options.config = opt.config; + if(opt && opt.config) + options.config = opt.config; - if(opt && opt.defaultTypes) - options.defaultTypes = opt.defaultTypes; + if(opt && opt.defaultTypes) + options.defaultTypes = opt.defaultTypes; - if(opt && opt.componentTypes) - options.componentTypes = opt.componentTypes; + if(opt && opt.componentTypes) + options.componentTypes = opt.componentTypes; - var df = opt.defaultTypes; + var df = opt.defaultTypes; - for (var it = 0; it < df.length; it++) { - var dfId = df[it].id; - if(dfId == attrs.type) { - model = df[it].model; - break; - } - } + for (var it = 0; it < df.length; it++) { + var dfId = df[it].id; + if(dfId == attrs.type) { + model = df[it].model; + break; + } + } - if(!model) { - // get the last one - model = df[df.length - 1].model; - } + if(!model) { + // get the last one + model = df[df.length - 1].model; + } - return new model(attrs, options); - }; + return new model(attrs, options); + }; - }, - - add: function(models, opt){ - if(typeof models === 'string'){ - var parsed = this.editor.get('Parser').parseHtml(models); - models = parsed.html; + }, - var cssc = this.editor.get('CssComposer'); - if(parsed.css && cssc){ - var added = cssc.addCollection(parsed.css); - } - } + add: function(models, opt){ + if(typeof models === 'string'){ + var parsed = this.editor.get('Parser').parseHtml(models); + models = parsed.html; - return Backbone.Collection.prototype.add.apply(this, [models, opt]); - }, + var cssc = this.editor.get('CssComposer'); + if(parsed.css && cssc){ + var added = cssc.addCollection(parsed.css, {extend: 1}); + } + } - onAdd: function(model, c, opts){ - var style = model.get('style'); + return Backbone.Collection.prototype.add.apply(this, [models, opt]); + }, - if(!_.isEmpty(style) && this.editor){ - 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); - } + onAdd: function(model, c, opts){ + var style = model.get('style'); + + if(!_.isEmpty(style) && this.editor){ + 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/parser/model/ParserCss.js b/src/parser/model/ParserCss.js index d48f28d7e..85aebe455 100644 --- a/src/parser/model/ParserCss.js +++ b/src/parser/model/ParserCss.js @@ -44,9 +44,13 @@ define(function(require) { var sels = node.selectorText; // It's a CSSMediaRule - if(node.cssRules){ + if(node.cssRules) { var subRules = this.parseNode(node); - var width = node.media.mediaText.match(/-width:(.*)\)/i)[1]; + var widthA = node.media.mediaText.match(/-width:(.*)\)/i); + if(!widthA) { + continue; + } + var width = widthA[1]; for(var s = 0, lens = subRules.length; s < lens; s++){ var subRule = subRules[s]; subRule.maxWidth = width ? width.trim() : ''; diff --git a/test/specs/css_composer/e2e/CssComposer.js b/test/specs/css_composer/e2e/CssComposer.js index 5a48f9f38..2b32d11ee 100644 --- a/test/specs/css_composer/e2e/CssComposer.js +++ b/test/specs/css_composer/e2e/CssComposer.js @@ -109,6 +109,47 @@ define(['GrapesJS'],function(GrapesJS) { coll1.should.deep.equal(coll2); }); + it("Extend css rule style, if requested", function() { + var style1 = {color: 'red', width: '10px'}; + var style2 = {height: '20px', width: '20px'}; + var rule1 = { + selectors: ['test1'], + style: style1, + }; + var rule2 = { + selectors: ['test1'], + style: style2, + }; + var ruleOut = cssc.addCollection(rule1)[0]; + // ruleOut is a Model + ruleOut = JSON.parse(JSON.stringify(ruleOut)); + var ruleResult = { + maxWidth: '', + selectors: [{ + active: true, + label: 'test1', + name: 'test1', + type: 'class', + }], + state: '', + stylable: true, + style: { + color: 'red', + width: '10px' + } + }; + ruleOut.should.deep.equal(ruleResult); + var ruleOut = cssc.addCollection(rule2, {extend: 1})[0]; + ruleOut = JSON.parse(JSON.stringify(ruleOut)); + ruleResult.style = { + color: 'red', + height: '20px', + width: '20px', + } + ruleOut.should.deep.equal(ruleResult); + + }); + it('Add raw rule objects with width via addCollection', function() { var coll1 = cssc.addCollection(rulesSet2); coll1[2].get('maxWidth').should.equal(rulesSet2[2].maxWidth); diff --git a/test/specs/css_composer/main.js b/test/specs/css_composer/main.js index 5918876e3..d4f7ae236 100644 --- a/test/specs/css_composer/main.js +++ b/test/specs/css_composer/main.js @@ -1,180 +1,180 @@ var modulePath = './../../../test/specs/css_composer'; define([ - 'CssComposer', - modulePath + '/model/CssModels', - modulePath + '/view/CssRuleView', - modulePath + '/view/CssRulesView', - modulePath + '/e2e/CssComposer', - './../test_utils.js' - ], - function( - CssComposer, - Models, - CssRuleView, - CssRulesView, - e2e, - utils - ) { - - describe('Css Composer', function() { - - describe('Main', function() { - - var obj; - - var config; - var storagMock = utils.storageMock(); - var editorModel = { - getCss: function(){return 'testCss';}, - getCacheLoad: function(){ - return storagMock.load(); - } - }; - - var setSmConfig = function(){ - config.stm = storagMock; - config.stm.getConfig = function(){ - return { storeCss: 1, storeStyles: 1} - }; - }; - var setEm = function(){ - config.em = editorModel; - } - - - beforeEach(function () { - config = {}; - obj = new CssComposer().init(config); - }); - - afterEach(function () { - delete obj; - }); - - it('Object exists', function() { - CssComposer.should.be.exist; - }); - - it('storageKey returns array', function() { - obj.storageKey().should.be.instanceOf(Array); - }); - - it('storageKey returns correct composition', function() { - setSmConfig(); - obj.storageKey().should.eql(['css', 'styles']); - }); - - it('Store data', function() { - setSmConfig(); - setEm(); - var expected = { css: 'testCss', styles: '[]',}; - obj.store(1).should.deep.equal(expected); - }); - - it("Rules are empty", function() { - obj.getAll().length.should.equal(0); - }); - - it('Create new rule with correct selectors', function() { - var sel = new obj.Selectors(); - var s1 = sel.add({name: 'test1'}); - var rule = obj.add(sel.models); - rule.get('selectors').at(0).should.deep.equal(s1); - }); - - it('Create new rule correctly', function() { - var sel = new obj.Selectors(); - var s1 = sel.add({name: 'test1'}); - var rule = obj.add(sel.models, 'state1', 'width1'); - rule.get('state').should.equal('state1'); - rule.get('maxWidth').should.equal('width1'); - }); - - it("Add rule to collection", function() { - var sel = new obj.Selectors([{name: 'test1'}]); - var rule = obj.add(sel.models); - obj.getAll().length.should.equal(1); - obj.getAll().at(0).get('selectors').at(0).get('name').should.equal('test1'); - }); - - it("Returns correct rule with the same selector", function() { - var sel = new obj.Selectors([{name: 'test1'}]); - var rule1 = obj.add(sel.models); - var rule2 = obj.get(sel.models); - rule1.should.deep.equal(rule2); - }); - - it("Returns correct rule with the same selectors", function() { - var sel1 = new obj.Selectors([{name: 'test1'}]); - var rule1 = obj.add(sel1.models); - - var sel2 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]); - var rule2 = obj.add(sel2.models); - - var rule3 = obj.get(sel2.models); - rule3.should.deep.equal(rule2); - }); - - it("Do not create multiple rules with the same name selectors", function() { - var sel1 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]); - var rule1 = obj.add(sel1.models); - - var sel2 = new obj.Selectors([{name: 'test22'}, {name: 'test21'}]); - var rule2 = obj.add(sel2.models); - rule2.should.deep.equal(rule1); - }); - - it("Don't duplicate rules", function() { - var sel = new obj.Selectors([]); - var s1 = sel.add({name: 'test1'}); - var s2 = sel.add({name: 'test2'}); - var s3 = sel.add({name: 'test3'}); - - var rule1 = obj.add([s1, s3]); - var rule2 = obj.add([s3, s1]); - - rule2.should.deep.equal(rule1); - }); - - it("Returns correct rule with the same mixed selectors", function() { - var sel = new obj.Selectors([]); - var s1 = sel.add({name: 'test1'}); - var s2 = sel.add({name: 'test2'}); - var s3 = sel.add({name: 'test3'}); - var rule1 = obj.add([s1, s3]); - var rule2 = obj.get([s3, s1]); - rule2.should.deep.equal(rule1); - }); - - it("Returns correct rule with the same selectors and state", function() { - var sel = new obj.Selectors([]); - var s1 = sel.add({name: 'test1'}); - var s2 = sel.add({name: 'test2'}); - var s3 = sel.add({name: 'test3'}); - var rule1 = obj.add([s1, s3], 'hover'); - var rule2 = obj.get([s3, s1], 'hover'); - rule2.should.deep.equal(rule1); - }); - - it("Returns correct rule with the same selectors, state and width", function() { - var sel = new obj.Selectors([]); - var s1 = sel.add({name: 'test1'}); - var rule1 = obj.add([s1], 'hover','1'); - var rule2 = obj.get([s1], 'hover', '1'); - rule2.should.deep.equal(rule1); - }); - - it("Renders correctly", function() { - obj.render().should.be.ok; - }); - - }); - - Models.run(); - CssRuleView.run(); - CssRulesView.run(); - e2e.run(); - - }); + 'CssComposer', + modulePath + '/model/CssModels', + modulePath + '/view/CssRuleView', + modulePath + '/view/CssRulesView', + modulePath + '/e2e/CssComposer', + './../test_utils.js' + ], + function( + CssComposer, + Models, + CssRuleView, + CssRulesView, + e2e, + utils + ) { + + describe('Css Composer', function() { + + describe('Main', function() { + + var obj; + + var config; + var storagMock = utils.storageMock(); + var editorModel = { + getCss: function(){return 'testCss';}, + getCacheLoad: function(){ + return storagMock.load(); + } + }; + + var setSmConfig = function(){ + config.stm = storagMock; + config.stm.getConfig = function(){ + return { storeCss: 1, storeStyles: 1} + }; + }; + var setEm = function(){ + config.em = editorModel; + } + + + beforeEach(function () { + config = {}; + obj = new CssComposer().init(config); + }); + + afterEach(function () { + delete obj; + }); + + it('Object exists', function() { + CssComposer.should.be.exist; + }); + + it('storageKey returns array', function() { + obj.storageKey().should.be.instanceOf(Array); + }); + + it('storageKey returns correct composition', function() { + setSmConfig(); + obj.storageKey().should.eql(['css', 'styles']); + }); + + it('Store data', function() { + setSmConfig(); + setEm(); + var expected = { css: 'testCss', styles: '[]',}; + obj.store(1).should.deep.equal(expected); + }); + + it("Rules are empty", function() { + obj.getAll().length.should.equal(0); + }); + + it('Create new rule with correct selectors', function() { + var sel = new obj.Selectors(); + var s1 = sel.add({name: 'test1'}); + var rule = obj.add(sel.models); + rule.get('selectors').at(0).should.deep.equal(s1); + }); + + it('Create new rule correctly', function() { + var sel = new obj.Selectors(); + var s1 = sel.add({name: 'test1'}); + var rule = obj.add(sel.models, 'state1', 'width1'); + rule.get('state').should.equal('state1'); + rule.get('maxWidth').should.equal('width1'); + }); + + it("Add rule to collection", function() { + var sel = new obj.Selectors([{name: 'test1'}]); + var rule = obj.add(sel.models); + obj.getAll().length.should.equal(1); + obj.getAll().at(0).get('selectors').at(0).get('name').should.equal('test1'); + }); + + it("Returns correct rule with the same selector", function() { + var sel = new obj.Selectors([{name: 'test1'}]); + var rule1 = obj.add(sel.models); + var rule2 = obj.get(sel.models); + rule1.should.deep.equal(rule2); + }); + + it("Returns correct rule with the same selectors", function() { + var sel1 = new obj.Selectors([{name: 'test1'}]); + var rule1 = obj.add(sel1.models); + + var sel2 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]); + var rule2 = obj.add(sel2.models); + + var rule3 = obj.get(sel2.models); + rule3.should.deep.equal(rule2); + }); + + it("Do not create multiple rules with the same name selectors", function() { + var sel1 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]); + var rule1 = obj.add(sel1.models); + + var sel2 = new obj.Selectors([{name: 'test22'}, {name: 'test21'}]); + var rule2 = obj.add(sel2.models); + rule2.should.deep.equal(rule1); + }); + + it("Don't duplicate rules", function() { + var sel = new obj.Selectors([]); + var s1 = sel.add({name: 'test1'}); + var s2 = sel.add({name: 'test2'}); + var s3 = sel.add({name: 'test3'}); + + var rule1 = obj.add([s1, s3]); + var rule2 = obj.add([s3, s1]); + + rule2.should.deep.equal(rule1); + }); + + it("Returns correct rule with the same mixed selectors", function() { + var sel = new obj.Selectors([]); + var s1 = sel.add({name: 'test1'}); + var s2 = sel.add({name: 'test2'}); + var s3 = sel.add({name: 'test3'}); + var rule1 = obj.add([s1, s3]); + var rule2 = obj.get([s3, s1]); + rule2.should.deep.equal(rule1); + }); + + it("Returns correct rule with the same selectors and state", function() { + var sel = new obj.Selectors([]); + var s1 = sel.add({name: 'test1'}); + var s2 = sel.add({name: 'test2'}); + var s3 = sel.add({name: 'test3'}); + var rule1 = obj.add([s1, s3], 'hover'); + var rule2 = obj.get([s3, s1], 'hover'); + rule2.should.deep.equal(rule1); + }); + + it("Returns correct rule with the same selectors, state and width", function() { + var sel = new obj.Selectors([]); + var s1 = sel.add({name: 'test1'}); + var rule1 = obj.add([s1], 'hover','1'); + var rule2 = obj.get([s1], 'hover', '1'); + rule2.should.deep.equal(rule1); + }); + + it("Renders correctly", function() { + obj.render().should.be.ok; + }); + + }); + + Models.run(); + CssRuleView.run(); + CssRulesView.run(); + e2e.run(); + + }); }); diff --git a/test/specs/parser/model/ParserCss.js b/test/specs/parser/model/ParserCss.js index 935ba2ede..34ed7a2fc 100644 --- a/test/specs/parser/model/ParserCss.js +++ b/test/specs/parser/model/ParserCss.js @@ -133,7 +133,7 @@ define([path + 'model/ParserCss',], obj.parse(str).should.deep.equal(result); }); - // Phantom don't find 'node.conditionText' so will skip it + // Phantom doesn't find 'node.conditionText' so will skip it it.skip('Parse rules inside media queries', function() { var str = '.test1:hover{ color:white }@media (max-width: 992px){ .test1.test2:hover{ color:red } .test2{ color: blue }}'; var result = [{ @@ -158,4 +158,4 @@ define([path + 'model/ParserCss',], } }; -}); \ No newline at end of file +});