Browse Source

Merge branch 'dev' of github.com:artf/grapesjs into dev

pull/76/head
tangkikodo 9 years ago
parent
commit
bf3ee681b9
  1. 2
      bower.json
  2. 26
      dist/grapes.min.js
  3. 2
      package.json
  4. 33
      src/canvas/view/CanvasView.js
  5. 290
      src/code_manager/model/CssGenerator.js
  6. 34
      src/css_composer/main.js
  7. 7
      src/css_composer/model/CssRule.js
  8. 9
      src/css_composer/view/CssRuleView.js
  9. 31
      src/demo.js
  10. 4
      src/editor/config/config.js
  11. 18
      src/editor/main.js
  12. 4
      src/editor/model/Editor.js
  13. 50
      src/parser/model/ParserCss.js
  14. 23
      test/specs/code_manager/model/CodeModels.js
  15. 4
      test/specs/css_composer/e2e/CssComposer.js
  16. 31
      test/specs/parser/model/ParserCss.js

2
bower.json

@ -1,7 +1,7 @@
{ {
"name": "grapesjs", "name": "grapesjs",
"description": "Open source Web Template Editor", "description": "Open source Web Template Editor",
"version": "0.4.38", "version": "0.5.4",
"author": "Artur Arseniev", "author": "Artur Arseniev",
"homepage": "http://grapesjs.com", "homepage": "http://grapesjs.com",
"main": [ "main": [

26
dist/grapes.min.js

File diff suppressed because one or more lines are too long

2
package.json

@ -1,7 +1,7 @@
{ {
"name": "grapesjs", "name": "grapesjs",
"description": "Open source Web Template Editor", "description": "Open source Web Template Editor",
"version": "0.4.38", "version": "0.5.4",
"author": "Artur Arseniev", "author": "Artur Arseniev",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"homepage": "http://grapesjs.com", "homepage": "http://grapesjs.com",

33
src/canvas/view/CanvasView.js

@ -41,7 +41,7 @@ function(Backbone, FrameView) {
frame.el.onload = function () { frame.el.onload = function () {
var scripts = that.config.scripts.slice(0), // clone var scripts = that.config.scripts.slice(0), // clone
counter = 0; counter = 0;
function appendScript(scripts) { function appendScript(scripts) {
if (scripts.length > 0) { if (scripts.length > 0) {
var script = document.createElement('script'); var script = document.createElement('script');
@ -61,7 +61,7 @@ function(Backbone, FrameView) {
* Render inside frame's body * Render inside frame's body
* @private * @private
*/ */
renderBody: function(){ renderBody: function() {
var wrap = this.model.get('frame').get('wrapper'); var wrap = this.model.get('frame').get('wrapper');
var em = this.config.em; var em = this.config.em;
if(wrap) { if(wrap) {
@ -71,19 +71,22 @@ function(Backbone, FrameView) {
var conf = em.get('Config'); var conf = em.get('Config');
body.append(wrap.render()).append(cssc.render()); body.append(wrap.render()).append(cssc.render());
var protCss = conf.protectedCss; var protCss = conf.protectedCss;
var frameCss = '.' + ppfx + 'dashed :not([contenteditable]) > *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' +
'.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + // I need all this styles to make the editor work properly
'.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ var frameCss = '* {box-sizing: border-box;} body{margin:0;height:auto;background-color:#fff} #wrapper{min-height:100%; overflow:auto}' +
'.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + '.' + ppfx + 'dashed :not([contenteditable]) > *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' +
'.' + ppfx + 'no-pointer{pointer-events: none}' + '.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' +
'.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}' + '.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+
'.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' + '.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' +
'* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' + '.' + ppfx + 'no-pointer{pointer-events: none}' +
'* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' + '.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}' +
'* ::-webkit-scrollbar {width: 10px}' + '.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' +
(conf.canvasCss || ''); '* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' +
if(protCss) '* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' +
body.append('<style>' + frameCss + protCss + '</style>'); '* ::-webkit-scrollbar {width: 10px}' +
(conf.canvasCss || '');
frameCss += protCss || '';
body.append('<style>' + frameCss + '</style>');
body.append(this.getJsContainer()); body.append(this.getJsContainer());
em.trigger('loaded'); em.trigger('loaded');
this.frame.el.contentWindow.onscroll = this.onFrameScroll; this.frame.el.contentWindow.onscroll = this.onFrameScroll;

290
src/code_manager/model/CssGenerator.js

@ -1,145 +1,149 @@
define(['backbone'], define(['backbone'],
function (Backbone) { function (Backbone) {
/** /**
* @class CssGenerator * @class CssGenerator
* */ * */
return Backbone.Model.extend({ return Backbone.Model.extend({
initialize: function() { initialize: function() {
this.compCls = []; this.compCls = [];
}, },
/** /**
* Get CSS from component * Get CSS from component
* @param {Model} model * @param {Model} model
* @return {String} * @return {String}
*/ */
buildFromModel: function (model) { buildFromModel: function (model) {
var code = ''; var code = '';
var style = model.get('style'); var style = model.get('style');
var classes = model.get('classes'); var classes = model.get('classes');
// Let's know what classes I've found // Let's know what classes I've found
if(classes) { if(classes) {
classes.each(function(model){ classes.each(function(model){
this.compCls.push(model.get('name')); this.compCls.push(model.get('name'));
}, this); }, this);
} }
if(style && Object.keys(style).length !== 0) { if(style && Object.keys(style).length !== 0) {
code += '#' + model.cid + '{'; code += '#' + model.cid + '{';
for(var prop in style){ for(var prop in style){
if(style.hasOwnProperty(prop)) if(style.hasOwnProperty(prop))
code += prop + ':' + style[prop] + ';'; code += prop + ':' + style[prop] + ';';
} }
code += '}'; code += '}';
} }
return code; return code;
}, },
/** /**
* Get CSS from components * Get CSS from components
* @param {Model} model * @param {Model} model
* @return {String} * @return {String}
*/ */
buildFromComp: function(model) { buildFromComp: function(model) {
var coll = model.get('components') || model, var coll = model.get('components') || model,
code = ''; code = '';
coll.each(function(m) { coll.each(function(m) {
var cln = m.get('components'); var cln = m.get('components');
code += this.buildFromModel(m); code += this.buildFromModel(m);
if(cln.length){ if(cln.length){
code += this.buildFromComp(cln); code += this.buildFromComp(cln);
} }
}, this); }, this);
return code; return code;
}, },
/** @inheritdoc */ /** @inheritdoc */
build: function(model, cssc) { build: function(model, cssc) {
this.compCls = []; this.compCls = [];
var code = this.buildFromModel(model); var code = this.buildFromModel(model);
code += this.buildFromComp(model); code += this.buildFromComp(model);
var compCls = this.compCls; var compCls = this.compCls;
if(cssc){ if(cssc){
var rules = cssc.getAll(); var rules = cssc.getAll();
var mediaRules = {}; var mediaRules = {};
rules.each(function(rule){ rules.each(function(rule){
var width = rule.get('maxWidth'); var width = rule.get('maxWidth');
// If width setted will render it later // If width setted will render it later
if(width){ if(width){
var mRule = mediaRules[width]; var mRule = mediaRules[width];
if(mRule) if(mRule)
mRule.push(rule); mRule.push(rule);
else else
mediaRules[width] = [rule]; mediaRules[width] = [rule];
return; return;
} }
code += this.buildFromRule(rule); code += this.buildFromRule(rule);
}, this); }, this);
// Get media rules // Get media rules
for (var ruleW in mediaRules) { for (var ruleW in mediaRules) {
var meRules = mediaRules[ruleW]; var meRules = mediaRules[ruleW];
var ruleC = ''; var ruleC = '';
for(var i = 0, len = meRules.length; i < len; i++){ for(var i = 0, len = meRules.length; i < len; i++){
ruleC += this.buildFromRule(meRules[i]); ruleC += this.buildFromRule(meRules[i]);
} }
if(ruleC) if(ruleC)
code += '@media (max-width: ' + ruleW + '){' + ruleC + '}'; code += '@media (max-width: ' + ruleW + '){' + ruleC + '}';
} }
} }
return code; return code;
}, },
/** /**
* Get CSS from the rule model * Get CSS from the rule model
* @param {Model} rule * @param {Model} rule
* @return {string} CSS string * @return {string} CSS string
*/ */
buildFromRule: function(rule) { buildFromRule: function(rule) {
var result = ''; var result = '';
var selectors = rule.get('selectors'); var selectorsAdd = rule.get('selectorsAdd');
var ruleStyle = rule.get('style'); var selectors = rule.get('selectors');
var state = rule.get('state'); var ruleStyle = rule.get('style');
var strSel = ''; var state = rule.get('state');
var found = 0; var strSel = '';
var compCls = this.compCls; var found = 0;
var compCls = this.compCls;
// Get string of selectors
selectors.each(function(selector){ // Get string of selectors
strSel += '.' + selector.get('name'); selectors.each(function(selector){
if(compCls.indexOf(selector.get('name')) > -1) strSel += '.' + selector.get('name');
found = 1; if(compCls.indexOf(selector.get('name')) > -1)
}); found = 1;
});
if(strSel && found){
strSel += state ? ':' + state : ''; // With 'found' will skip rules which selectors are not found in
var strStyle = ''; // canvas components.
if ((strSel && found) || selectorsAdd) {
// Get string of style properties strSel += state ? ':' + state : '';
if(ruleStyle && Object.keys(ruleStyle).length !== 0){ strSel += selectorsAdd ? (strSel ? ', ' : '') + selectorsAdd : '';
for(var prop2 in ruleStyle){ var strStyle = '';
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';'; // Get string of style properties
} if(ruleStyle && Object.keys(ruleStyle).length !== 0){
} for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
if(strStyle) strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
result += strSel + '{' + strStyle + '}'; }
} }
return result; if(strStyle)
}, result += strSel + '{' + strStyle + '}';
}
});
return result;
},
});
}); });

34
src/css_composer/main.js

@ -107,17 +107,18 @@ define(function(require) {
* @param {Object} data Object of data to load * @param {Object} data Object of data to load
* @return {Object} Loaded rules * @return {Object} Loaded rules
*/ */
load: function(data){ load: function(data) {
var d = data || ''; var d = data || '';
if(!d && c.stm) if(!d && c.stm)
d = c.em.getCacheLoad(); d = c.em.getCacheLoad();
var obj = ''; var obj = '';
if(d.style){ if(d.styles) {
try{ try{
obj = JSON.parse(d.style); obj = JSON.parse(d.styles);
}catch(err){} }catch(err){}
}else if(d.css) } else if (d.css) {
obj = c.em.get('Parser').parseCss(d.css); obj = c.em.get('Parser').parseCss(d.css);
}
if(obj) if(obj)
rules.reset(obj); rules.reset(obj);
@ -148,6 +149,7 @@ define(function(require) {
* @param {Array<Selector>} selectors Array of selectors * @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state * @param {String} state Css rule state
* @param {String} width For which device this style is oriented * @param {String} width For which device this style is oriented
* @param {Object} opts Other options for the rule
* @return {Model} * @return {Model}
* @example * @example
* var sm = editor.SelectorManager; * var sm = editor.SelectorManager;
@ -159,17 +161,18 @@ define(function(require) {
* color: '#fff', * color: '#fff',
* }); * });
* */ * */
add: function(selectors, state, width) { add: function(selectors, state, width, opts) {
var s = state || ''; var s = state || '';
var w = width || ''; var w = width || '';
var opt = opts || {};
var rule = this.get(selectors, s, w); var rule = this.get(selectors, s, w);
if(rule) if(rule)
return rule; return rule;
else{ else {
rule = new CssRule({ opt.state = s;
state: s, opt.maxWidth = w;
maxWidth: w, opt.selectors = '';
}); rule = new CssRule(opt);
rule.get('selectors').add(selectors); rule.get('selectors').add(selectors);
rules.add(rule); rules.add(rule);
return rule; return rule;
@ -224,7 +227,8 @@ define(function(require) {
var opt = opts || {}; var opt = opts || {};
var result = []; var result = [];
var d = data instanceof Array ? data : [data]; var d = data instanceof Array ? data : [data];
for(var i = 0, l = d.length; i < l; i++){
for (var i = 0, l = d.length; i < l; i++) {
var rule = d[i] || {}; var rule = d[i] || {};
if(!rule.selectors) if(!rule.selectors)
continue; continue;
@ -234,19 +238,23 @@ define(function(require) {
var sl = rule.selectors; var sl = rule.selectors;
var sels = sl instanceof Array ? sl : [sl]; var sels = sl instanceof Array ? sl : [sl];
var newSels = []; var newSels = [];
for(var j = 0, le = sels.length; j < le; j++){
for (var j = 0, le = sels.length; j < le; j++) {
var selec = sm.add(sels[j]); var selec = sm.add(sels[j]);
newSels.push(selec); newSels.push(selec);
} }
var model = this.add(newSels, rule.state, rule.maxWidth);
var model = this.add(newSels, rule.state, rule.maxWidth, rule);
if (opt.extend) { if (opt.extend) {
var newStyle = _.extend({}, model.get('style'), rule.style || {}); var newStyle = _.extend({}, model.get('style'), rule.style || {});
model.set('style', newStyle); model.set('style', newStyle);
} else { } else {
model.set('style', rule.style || {}); model.set('style', rule.style || {});
} }
result.push(model); result.push(model);
} }
return result; return result;
}, },

7
src/css_composer/model/CssRule.js

@ -5,12 +5,19 @@ define(['backbone', './Selectors'],
defaults: { defaults: {
// Css selectors // Css selectors
selectors: {}, selectors: {},
// Additional string css selectors
selectorsAdd: '',
// Css properties style // Css properties style
style: {}, style: {},
// On which device width this rule should be rendered, eg. @media (max-width: 1000px) // On which device width this rule should be rendered, eg. @media (max-width: 1000px)
maxWidth: '', maxWidth: '',
// State of the rule, eg: hover | pressed | focused // State of the rule, eg: hover | pressed | focused
state: '', state: '',
// Indicates if the rule is stylable // Indicates if the rule is stylable
stylable: true, stylable: true,
}, },

9
src/css_composer/view/CssRuleView.js

@ -27,12 +27,15 @@ define(['backbone'],
* @return {String} * @return {String}
* @private * @private
*/ */
renderSelectors: function(){ renderSelectors: function() {
var sel = []; var sel = [];
this.model.get('selectors').each(function(m){ var model = this.model;
var add = model.get('selectorsAdd');
model.get('selectors').each(function(m){
sel.push('.' + m.get('name')); sel.push('.' + m.get('name'));
}); });
return sel.join(''); var sels = sel.join('');
return sels + (sels && add ? ', ' : '') + add;
}, },
/** /**

31
src/demo.js

@ -15,6 +15,12 @@ require(['config/require-config'], function() {
fromElement: true, fromElement: true,
clearOnRender: 0, clearOnRender: 0,
storageManager:{
autoload: 0,
storeComponents: 1,
storeStyles: 1,
},
/* /*
components: [{ components: [{
//script: 'var el = this; setInterval(function(){el.style.marginLeft = Math.random() * 50 +"px";}, 1000)', //script: 'var el = this; setInterval(function(){el.style.marginLeft = Math.random() * 50 +"px";}, 1000)',
@ -71,11 +77,6 @@ require(['config/require-config'], function() {
}], }],
}], }],
*/ */
storageManager:{
autoload: 0,
storeComponents: 1,
storeStyles: 1,
},
commands: { commands: {
defaults : [{ defaults : [{
@ -177,19 +178,7 @@ require(['config/require-config'], function() {
name: 'Left', name: 'Left',
property: 'margin-left', property: 'margin-left',
},], },],
}/*{ }],
name : 'Center blocksss',
property : 'margins',
type : 'select',
defaults : '0',
list : [{
value : '0',
name : 'Normal',
},{
value : '0 auto',
name : 'Center',
}],
}*/],
},{ },{
name: 'Flex', name: 'Flex',
open: false, open: false,
@ -361,10 +350,9 @@ require(['config/require-config'], function() {
], ],
}, },
}
); });
window.editor = editor; window.editor = editor;
@ -392,8 +380,7 @@ require(['config/require-config'], function() {
if(sender) sender.set('active', false); if(sender) sender.set('active', false);
if(confirm('Are you sure to clean the canvas?')) { if(confirm('Are you sure to clean the canvas?')) {
var comps = editor.DomComponents.clear(); var comps = editor.DomComponents.clear();
localStorage.setItem('gjs-css', ''); localStorage.clear();
localStorage.setItem('gjs-html', '');
} }
}, },
attributes: { title: 'Empty canvas' } attributes: { title: 'Empty canvas' }

4
src/editor/config/config.js

@ -24,7 +24,7 @@ define(function () {
showOffsetsSelected: false, showOffsetsSelected: false,
// Clear the canvas when editor.render() is called // Clear the canvas when editor.render() is called
clearOnRender: true, clearOnRender: false,
// Return JS of components inside HTML from 'editor.getHtml()' // Return JS of components inside HTML from 'editor.getHtml()'
jsInHtml: true, jsInHtml: true,
@ -36,7 +36,7 @@ define(function () {
width: '100%', width: '100%',
// CSS that could only be seen (for instance, inside the code viewer) // CSS that could only be seen (for instance, inside the code viewer)
protectedCss: '*{box-sizing: border-box;}body{margin:0;height:auto;background:#fff}#wrapper{min-height:100%; overflow:auto}', protectedCss: '*{box-sizing: border-box;}body{margin:0;height:auto;background-color:#fff}#wrapper{min-height:100%; overflow:auto}',
// CSS for the iframe which containing the canvas, useful if you need to custom something inside // CSS for the iframe which containing the canvas, useful if you need to custom something inside
// (eg. the style of the selected component) // (eg. the style of the selected component)

18
src/editor/main.js

@ -6,6 +6,7 @@
* * [getJs](#getjs) * * [getJs](#getjs)
* * [getComponents](#getcomponents) * * [getComponents](#getcomponents)
* * [setComponents](#setcomponents) * * [setComponents](#setcomponents)
* * [addComponents](#addcomponents)
* * [getStyle](#getstyle) * * [getStyle](#getstyle)
* * [setStyle](#setstyle) * * [setStyle](#setstyle)
* * [getSelected](#getselected) * * [getSelected](#getselected)
@ -253,6 +254,23 @@ define(function (require){
return this; return this;
}, },
/**
* Add components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {Model|Array<Model>}
* @example
* editor.addComponents('<div class="cls">New component</div>');
* // or
* editor.addComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
addComponents: function(components) {
return this.getComponents().add(components);
},
/** /**
* Returns style in JSON format object * Returns style in JSON format object
* @return {Object} * @return {Object}

4
src/editor/model/Editor.js

@ -413,6 +413,7 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
sm.store(store, clb); sm.store(store, clb);
this.set('changesCount', 0); this.set('changesCount', 0);
this.trigger('storage:store', store);
return store; return store;
}, },
@ -437,7 +438,7 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
* @return {Object} * @return {Object}
* @private * @private
*/ */
getCacheLoad: function(force){ getCacheLoad: function(force) {
var f = force ? 1 : 0; var f = force ? 1 : 0;
if(this.cacheLoad && !f) if(this.cacheLoad && !f)
return this.cacheLoad; return this.cacheLoad;
@ -457,6 +458,7 @@ define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'Dev
}); });
this.cacheLoad = sm.load(load); this.cacheLoad = sm.load(load);
this.trigger('storage:load', this.cacheLoad);
return this.cacheLoad; return this.cacheLoad;
}, },

50
src/parser/model/ParserCss.js

@ -6,16 +6,21 @@ define(function(require) {
/** /**
* Parse selector string to array. * Parse selector string to array.
* Only concatenated classes are valid as CSS rules inside editor. * Only classe based are valid as CSS rules inside editor, not valid
* selectors will be dropped as additional
* It's ok with the last part of the string as state (:hover, :active) * It's ok with the last part of the string as state (:hover, :active)
* @param {string} str Selectors string * @param {string} str Selectors string
* @return {Array<Array>} * @return {Object}
* @example * @example
* var res = ParserCss.parseSelector('.test1, .test1.test2, .test2.test3'); * var res = ParserCss.parseSelector('.test1, .test1.test2, .test2 .test3');
* console.log(res); * console.log(res);
* // [['test1'], ['test1', 'test2'], ['test2', 'test3']] * // {
* //result: [['test1'], ['test1', 'test2']],
* //add: ['.test2 .test3']
* //}
*/ */
parseSelector: function(str){ parseSelector: function(str) {
var add = [];
var result = []; var result = [];
var sels = str.split(','); var sels = str.split(',');
for (var i = 0, len = sels.length; i < len; i++) { for (var i = 0, len = sels.length; i < len; i++) {
@ -25,9 +30,14 @@ define(function(require) {
if (/^(\.{1}[\w\-]+)+(:{1,2}[\w\-()]+)?$/ig.test(sel)) { if (/^(\.{1}[\w\-]+)+(:{1,2}[\w\-()]+)?$/ig.test(sel)) {
var cls = sel.split('.').filter(Boolean); var cls = sel.split('.').filter(Boolean);
result.push(cls); result.push(cls);
} else {
add.push(sel);
} }
} }
return result; return {
result: result,
add: add,
};
}, },
/** /**
@ -35,13 +45,14 @@ define(function(require) {
* @param {StyleSheet|CSSMediaRule} el * @param {StyleSheet|CSSMediaRule} el
* @return {Array<Object>} * @return {Array<Object>}
*/ */
parseNode: function(el){ parseNode: function(el) {
var result = []; var result = [];
var nodes = el.cssRules; var nodes = el.cssRules;
for (var i = 0, len = nodes.length; i < len; i++) { for (var i = 0, len = nodes.length; i < len; i++) {
var node = nodes[i]; var node = nodes[i];
var sels = node.selectorText; var sels = node.selectorText;
var selsAdd = [];
// It's a CSSMediaRule // It's a CSSMediaRule
if(node.cssRules) { if(node.cssRules) {
@ -61,15 +72,18 @@ define(function(require) {
if(!sels) if(!sels)
continue; continue;
sels = this.parseSelector(sels); var selsParsed = this.parseSelector(sels);
sels = selsParsed.result;
selsAdd = selsParsed.add;
// Create style object from the big one // Create style object from the big one
var stl = node.style; var stl = node.style;
var style = {}; var style = {};
for(var j = 0, len2 = stl.length; j < len2; j++){ for (var j = 0, len2 = stl.length; j < len2; j++) {
style[stl[j]] = stl[stl[j]]; style[stl[j]] = stl[stl[j]];
} }
var lastRule = '';
// For each group of selectors // For each group of selectors
for (var k = 0, len3 = sels.length; k < len3; k++) { for (var k = 0, len3 = sels.length; k < len3; k++) {
var selArr = sels[k]; var selArr = sels[k];
@ -85,9 +99,25 @@ define(function(require) {
model.selectors = selArr; model.selectors = selArr;
model.style = style; model.style = style;
lastRule = model;
result.push(model); result.push(model);
} }
// Need to push somewhere not class-based selectors, if some rule was
// created will push them there, otherwise will create a new rule
if (selsAdd.length) {
var selsAddStr = selsAdd.join(', ');
if (lastRule) {
lastRule.selectorsAdd = selsAddStr;
} else {
result.push({
selectors: [],
selectorsAdd: selsAddStr,
style: style,
});
}
}
} }
return result; return result;
@ -98,7 +128,7 @@ define(function(require) {
* @param {string} str HTML string * @param {string} str HTML string
* @return {Object|Array<Object>} * @return {Object|Array<Object>}
*/ */
parse: function(str){ parse: function(str) {
var el = document.createElement('style'); var el = document.createElement('style');
/* /*
el.innerHTML = ".cssClass {border: 2px solid black; background-color: blue;} " + el.innerHTML = ".cssClass {border: 2px solid black; background-color: blue;} " +

23
test/specs/code_manager/model/CodeModels.js

@ -128,7 +128,28 @@ define([path + 'HtmlGenerator',
rule.set('style',{'prop1':'value1', 'prop2':'value2'}); rule.set('style',{'prop1':'value1', 'prop2':'value2'});
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;prop2:value2;}'); this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;prop2:value2;}');
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;prop2:value2;}'); });
it('Build rules with mixed classes', function() {
var m1 = comp.get('components').add({tagName: 'article'});
var cls1 = m1.get('classes').add({name: 'class1'});
var cls2 = m1.get('classes').add({name: 'class2'});
var cssc = newCssComp();
var rule = cssc.add([cls1, cls2]);
rule.set('style',{'prop1':'value1', 'prop2':'value2'});
rule.set('selectorsAdd', '.class1 .class2, div > .class4');
this.obj.build(comp, cssc).should.equal('.class1.class2, .class1 .class2, div > .class4{prop1:value1;prop2:value2;}');
});
it('Build rules with only not class based selectors', function() {
var cssc = newCssComp();
var rule = cssc.add([]);
rule.set('style',{'prop1':'value1', 'prop2':'value2'});
rule.set('selectorsAdd', '.class1 .class2, div > .class4');
this.obj.build(comp, cssc).should.equal('.class1 .class2, div > .class4{prop1:value1;prop2:value2;}');
}); });
it('Build correctly with class styled out', function() { it('Build correctly with class styled out', function() {

4
test/specs/css_composer/e2e/CssComposer.js

@ -102,8 +102,9 @@ define(['GrapesJS'],function(GrapesJS) {
}); });
it('Add raw rule objects twice with addCollection do not duplucate rules', function() { it('Add raw rule objects twice with addCollection do not duplucate rules', function() {
var rulesSet2Copy = JSON.parse(JSON.stringify(rulesSet2));
var coll1 = cssc.addCollection(rulesSet2); var coll1 = cssc.addCollection(rulesSet2);
var coll2 = cssc.addCollection(rulesSet2); var coll2 = cssc.addCollection(rulesSet2Copy);
cssc.getAll().length.should.equal(3); cssc.getAll().length.should.equal(3);
clsm.getAll().length.should.equal(3); clsm.getAll().length.should.equal(3);
coll1.should.deep.equal(coll2); coll1.should.deep.equal(coll2);
@ -131,6 +132,7 @@ define(['GrapesJS'],function(GrapesJS) {
name: 'test1', name: 'test1',
type: 'class', type: 'class',
}], }],
selectorsAdd: '',
state: '', state: '',
stylable: true, stylable: true,
style: { style: {

31
test/specs/parser/model/ParserCss.js

@ -19,25 +19,25 @@ define([path + 'model/ParserCss',],
it('Parse selector', function() { it('Parse selector', function() {
var str = '.test'; var str = '.test';
var result = [['test']]; var result = [['test']];
obj.parseSelector(str).should.deep.equal(result); obj.parseSelector(str).result.should.deep.equal(result);
}); });
it('Parse selectors', function() { it('Parse selectors', function() {
var str = '.test1, .test1.test2, .test2.test3'; var str = '.test1, .test1.test2, .test2.test3';
var result = [['test1'], ['test1', 'test2'], ['test2', 'test3']]; var result = [['test1'], ['test1', 'test2'], ['test2', 'test3']];
obj.parseSelector(str).should.deep.equal(result); obj.parseSelector(str).result.should.deep.equal(result);
}); });
it('Ignore not valid selectors', function() { it('Ignore not valid selectors', function() {
var str = '.test1.test2, .test2 .test3, div > .test4, #test.test5, .test6'; var str = '.test1.test2, .test2 .test3, div > .test4, #test.test5, .test6';
var result = [['test1', 'test2'], ['test6']]; var result = [['test1', 'test2'], ['test6']];
obj.parseSelector(str).should.deep.equal(result); obj.parseSelector(str).result.should.deep.equal(result);
}); });
it('Parse selectors with state', function() { it('Parse selectors with state', function() {
var str = '.test1. test2, .test2>test3, .test4.test5:hover'; var str = '.test1. test2, .test2>test3, .test4.test5:hover';
var result = [['test4', 'test5:hover']]; var result = [['test4', 'test5:hover']];
obj.parseSelector(str).should.deep.equal(result); obj.parseSelector(str).result.should.deep.equal(result);
}); });
it('Parse simple rule', function() { it('Parse simple rule', function() {
@ -153,6 +153,29 @@ define([path + 'model/ParserCss',],
obj.parse(str).should.deep.equal(result); obj.parse(str).should.deep.equal(result);
}); });
it('Parse rules with not class-based selectors', function() {
var str = ' .class1 .class2, div > .class3 { color:red }';
var result = {
selectors: [],
selectorsAdd: '.class1 .class2, div > .class3',
style: { color: 'red'}
};
obj.parse(str).should.deep.equal(result);
});
it('Parse rule with mixed selectors', function() {
var str = ' .class1 .class2, .class3, div > .class4, .class5.class6 { color:red }';
var result = [{
selectors: ['class3'],
style: { color: 'red'}
},{
selectors: ['class5', 'class6'],
selectorsAdd: '.class1 .class2, div > .class4',
style: { color: 'red'}
}];
obj.parse(str).should.deep.equal(result);
});
}); });
} }

Loading…
Cancel
Save