Browse Source

Merge branch 'css-composer' into dev

pull/14/head
Artur Arseniev 10 years ago
parent
commit
39f1804457
  1. 2
      src/class_manager/config/config.js
  2. 2
      src/class_manager/main.js
  3. 8
      src/class_manager/view/ClassTagsView.js
  4. 5
      src/css_composer/config/config.js
  5. 2
      src/css_composer/main.js
  6. 9
      src/css_composer/model/CssRule.js
  7. 18
      src/css_composer/model/CssRules.js
  8. 8
      src/css_composer/view/CssRuleView.js
  9. 7
      src/css_composer/view/CssRulesView.js
  10. 103
      src/editor/model/Editor.js
  11. 6
      src/style_manager/view/SectorsView.js
  12. 121
      test/specs/class_manager/e2e/ClassManager.js
  13. 2
      test/specs/class_manager/main.js
  14. 20
      test/specs/class_manager/view/ClassTagsView.js
  15. 82
      test/specs/css_composer/e2e/CssComposer.js
  16. 12
      test/specs/css_composer/main.js
  17. 101
      test/specs/css_composer/view/CssRuleView.js
  18. 54
      test/specs/css_composer/view/CssRulesView.js
  19. 25
      test/specs/panel/model/panelModel.js
  20. 49
      test/specs/panel/view/panelView.js

2
src/class_manager/config/config.js

@ -5,7 +5,7 @@ define(function () {
stylePrefix : 'clm-',
// Default classes
classes : [],
defaults : [],
// Label for classes
label: 'Classes',

2
src/class_manager/main.js

@ -16,7 +16,7 @@ define(function(require) {
c[name] = def[name];
}
this.classes = new this.ClassTags(c.classes);
this.classes = new this.ClassTags(c.defaults);
this.config = c;
};

8
src/class_manager/view/ClassTagsView.js

@ -91,8 +91,14 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
var model = cm.addClass(name);
if(this.compTarget){
this.compTarget.get('classes').add(model);
var targetCls = this.compTarget.get('classes');
var lenB = targetCls.length;
targetCls.add(model);
var lenA = targetCls.length;
this.collection.add(model);
if(lenA > lenB)
this.target.trigger('targetClassAdded');
}
}
this.endNewTag();

5
src/css_composer/config/config.js

@ -4,8 +4,11 @@ define(function () {
// Style prefix
stylePrefix: 'css-',
// Custom CSS string to render on top
'staticRules': '',
// Default CSS style
'default': '',
'defaults': [],
};
});

2
src/css_composer/main.js

@ -18,7 +18,7 @@ define(function(require) {
c[name] = def[name];
}
var rules = new CssRules([]),
var rules = new CssRules(c.defaults, c),
rulesView = new CssRulesView({
collection: rules,
config: c,

9
src/css_composer/model/CssRule.js

@ -20,7 +20,16 @@ define(['backbone', './Selectors'],
initialize: function(c, opt) {
this.config = c || {};
this.sm = opt ? opt.sm || {} : {};
this.slct = this.config.selectors || [];
if(this.sm.get){
var slct = [];
for(var i = 0; i < this.slct.length; i++)
slct.push(this.sm.get('ClassManager').addClass(this.slct[i].name));
this.slct = slct;
}
this.set('selectors', new Selectors(this.slct));
},

18
src/css_composer/model/CssRules.js

@ -5,7 +5,23 @@ define(['backbone','./CssRule'],
* */
return Backbone.Collection.extend({
model: CssRule,
initialize: function(models, opt){
this.model = function(attrs, options) {
var model;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
switch(1){
default:
model = new CssRule(attrs, options);
}
return model;
};
},
});
});

8
src/css_composer/view/CssRuleView.js

@ -10,7 +10,6 @@ define(['backbone'],
initialize: function(o) {
this.config = o.config || {};
this.listenTo(this.model, 'change:style', this.render);
},
/**
@ -41,10 +40,15 @@ define(['backbone'],
},
render : function(){
var block = '',
o = '';
if(!this.selStr)
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
this.$el.html(this.selStr + '{' + prpStr + '}');
if(this.selStr)
block = prpStr !== '' ? '{' + prpStr + '}' : '';
o = this.selStr && block ? this.selStr + block : '';
this.$el.html(o);
return this;
},

7
src/css_composer/view/CssRulesView.js

@ -6,9 +6,9 @@ define(['backbone','./CssRuleView'],
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config;
this.preview = o.preview;
this.pfx = this.config.stylePrefix;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'rules';
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
@ -56,6 +56,7 @@ define(['backbone','./CssRuleView'],
}, this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
return this;
}
});

103
src/editor/model/Editor.js

@ -41,11 +41,13 @@ define([
initialize: function(c)
{
this.config = c;
this.compName = this.config.storagePrefix + 'components' + this.config.id;
this.pfx = this.config.storagePrefix;
this.compName = this.pfx + 'components' + this.config.id;
this.rulesName = this.pfx + 'rules' + this.config.id;
this.set('Config', c);
this.initClassManager();
this.initStorage();
this.initClassManager();
this.initModal();
this.initAssetManager();
this.initCodeManager();
@ -63,19 +65,68 @@ define([
/**
* Initialize Css Composer
* */
initCssComposer: function()
{
initCssComposer: function() {
var cfg = this.config.cssComposer,
df = this.loadRules();
pfx = cfg.stylePrefix || 'css-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.set('CssComposer', new CssComposer(cfg));
if(df)
cfg.defaults = df;
cfg.sm = this;
this.cssc = new CssComposer(cfg);
this.set('CssComposer', this.cssc);
if(this.stm.isAutosave())
this.listenRules(this.cssc.getRules());
},
/**
* Listen for new rules
* @param {Object} collection
*/
listenRules: function(collection) {
this.stopListening(collection, 'add remove', this.listenRule);
this.listenTo(collection, 'add remove', this.listenRule);
collection.each(function(model){
this.listenRule(model);
}, this);
},
/**
* Listen for rule changes
* @param {Object} model
*/
listenRule: function(model) {
this.stopListening(model, 'change:style', this.ruleUpdated);
this.listenTo(model, 'change:style', this.ruleUpdated);
},
/**
* Triggered when rule is updated
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
* */
ruleUpdated: function(model, val, opt) {
var count = this.get('changesCount') + 1,
avSt = opt ? opt.avoidStore : 0;
this.set('changesCount', count);
if(this.stm.isAutosave() && count < this.stm.getChangesBeforeSave())
return;
if(!avSt){
this.storeRules();
this.set('changesCount', 0);
}
},
/**
* Initialize Class manager
* */
initClassManager: function()
{
initClassManager: function() {
var cfg = this.config.classManager,
pfx = cfg.stylePrefix || 'clm-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
@ -324,7 +375,7 @@ define([
*
* @return {Object}
* */
loadComponents: function(){
loadComponents: function() {
var result = null;
try{
var r = this.stm.load(this.compName);
@ -341,7 +392,7 @@ define([
*
* @return void
* */
storeComponents: function(){
storeComponents: function() {
var wrp = this.cmp.getComponent();
if(wrp && this.cm){
var res = this.cm.getCode(wrp, 'json');
@ -349,6 +400,33 @@ define([
}
},
/**
* Load rules from storage
*
* @return {Array}
* */
loadRules: function(){
var result;
try{
var r = this.stm.load(this.rulesName);
if(r)
result = JSON.parse(r);
}catch(err){
console.warn("Load '" + this.rulesName + "':Error encountered while parsing JSON response");
}
return result;
},
/**
* Save rules to storage
* */
storeRules: function() {
var rules = this.cssc.getRules();
if(rules)
this.stm.store(this.rulesName, JSON.stringify(rules));
},
/**
* Triggered when components are updated
* @param {Object} model
@ -356,7 +434,7 @@ define([
* @param {Object} opt Options
*
* */
updateComponents: function(model, val, opt){
updateComponents: function(model, val, opt) {
var comps = model.get('components'),
classes = model.get('classes'),
avSt = opt ? opt.avoidStore : 0;
@ -365,7 +443,7 @@ define([
if(this.um)
this.um.register(comps);
// Call stopListening for not creating nested listenings
// Call stopListening for not creating nested listeners
this.stopListening(comps, 'add', this.updateComponents);
this.stopListening(comps, 'remove', this.rmComponents);
this.listenTo(comps, 'add', this.updateComponents);
@ -386,8 +464,7 @@ define([
* Init stuff like storage for already existing elements
* @param {Object} model
*/
initChildrenComp: function(model)
{
initChildrenComp: function(model) {
var comps = model.get('components');
if(comps.length){
comps.each(function(md){

6
src/style_manager/view/SectorsView.js

@ -13,7 +13,7 @@ define(['backbone','./SectorView'],
// The taget that will emit events for properties
this.propTarget = {};
_.extend(this.propTarget, Backbone.Events);
this.listenTo( this.target, 'change:selectedComponent', this.targetUpdated);
this.listenTo( this.target, 'change:selectedComponent targetClassAdded', this.targetUpdated);
},
@ -34,9 +34,9 @@ define(['backbone','./SectorView'],
var valid = _.filter(classes.models, function(item){
return item.get('active');
});
var iContainer = cssC.getRule(valid, 'status', 'mediaq');
var iContainer = cssC.getRule(valid, '', '');
if(!iContainer){
iContainer = cssC.newRule(valid, 'status', 'mediaq');
iContainer = cssC.newRule(valid, '', '');
// Hydrate styles from component element
iContainer.set('style', el.get('style'));
cssC.addRule(iContainer);

121
test/specs/class_manager/e2e/ClassManager.js

@ -54,79 +54,72 @@ define(
this.$fixture.remove();
});
describe('Interaction with Components', function() {
it('Assign correctly new class to component', function() {
var wrp = this.gjs.editor.get('Components').getWrapper().get('components');
var model = wrp.add({});
model.get('classes').length.should.equal(0);
// Init Class Manager and set editor as a target
var $clm = instClassTagViewer(this);
// Select element
this.gjs.editor.set('selectedComponent', model);
$clm.addNewTag('test');
model.get('classes').length.should.equal(1);
model.get('classes').at(0).get('name').should.equal('test');
});
it('Classes from components are correctly imported inside main container', function() {
var wrp = this.gjs.editor.get('Components').getWrapper().get('components');
var model = wrp.add([
{ classes: ['test11', 'test12', 'test13'] },
{ classes: ['test11', 'test22', 'test22'] },
]);
this.gjs.editor.get('ClassManager').getClasses().length.should.equal(4);
});
it('Class imported into component is the same model from main container', function() {
var wrp = this.gjs.editor.get('Components').getWrapper().get('components');
var model = wrp.add({
classes: ['test1']
beforeEach(function () {
this.wrapper = this.gjs.editor.get('Components').getWrapper().get('components');
this.$clm = instClassTagViewer(this);
});
var clModel = model.get('classes').at(0);
var clModel2 = this.gjs.editor.get('ClassManager').getClasses().at(0);
clModel.should.deep.equal(clModel2);
});
it('Can assign only one time the same class on selected component and the class viewer', function() {
var wrp = this.gjs.editor.get('Components').getWrapper().get('components');
var model = wrp.add({});
var $clm = instClassTagViewer(this);
// Select element
this.gjs.editor.set('selectedComponent', model);
afterEach(function () {
delete this.wrapper;
delete this.$clm;
});
$clm.addNewTag('test');
$clm.addNewTag('test');
it('Assign correctly new class to component', function() {
var model = this.wrapper.add({});
model.get('classes').length.should.equal(0);
this.gjs.editor.set('selectedComponent', model);
this.$clm.addNewTag('test');
model.get('classes').length.should.equal(1);
model.get('classes').at(0).get('name').should.equal('test');
});
model.get('classes').length.should.equal(1);
model.get('classes').at(0).get('name').should.equal('test');
it('Classes from components are correctly imported inside main container', function() {
var model = this.wrapper.add([
{ classes: ['test11', 'test12', 'test13'] },
{ classes: ['test11', 'test22', 'test22'] },
]);
this.gjs.editor.get('ClassManager').getClasses().length.should.equal(4);
});
$clm.collection.length.should.equal(1);
$clm.collection.at(0).get('name').should.equal('test');
});
it('Class imported into component is the same model from main container', function() {
var model = this.wrapper.add({ classes: ['test1'] });
var clModel = model.get('classes').at(0);
var clModel2 = this.gjs.editor.get('ClassManager').getClasses().at(0);
clModel.should.deep.equal(clModel2);
});
it('Removing from container removes also from selected component', function() {
it('Can assign only one time the same class on selected component and the class viewer', function() {
var model = this.wrapper.add({});
this.gjs.editor.set('selectedComponent', model);
this.$clm.addNewTag('test');
this.$clm.addNewTag('test');
model.get('classes').length.should.equal(1);
model.get('classes').at(0).get('name').should.equal('test');
this.$clm.collection.length.should.equal(1);
this.$clm.collection.at(0).get('name').should.equal('test');
});
var wrp = this.gjs.editor.get('Components').getWrapper().get('components');
var model = wrp.add({});
var $clm = instClassTagViewer(this);
this.gjs.editor.set('selectedComponent', model);
$clm.addNewTag('test');
$clm.collection.at(0).destroy();
it('Removing from container removes also from selected component', function() {
var model = this.wrapper.add({});
this.gjs.editor.set('selectedComponent', model);
this.$clm.addNewTag('test');
this.$clm.collection.at(0).destroy();
model.get('classes').length.should.equal(0);
});
model.get('classes').length.should.equal(0);
it("Trigger correctly event on target with new class add", function() {
var spy = sinon.spy();
var model = this.wrapper.add({});
this.gjs.editor.set('selectedComponent', model);
this.$clm.addNewTag('test');
this.gjs.editor.on("targetClassAdded", spy);
this.$clm.addNewTag('test');
spy.called.should.equal(false);
this.$clm.addNewTag('test2');
spy.called.should.equal(true);
});
});

2
test/specs/class_manager/main.js

@ -37,7 +37,7 @@ define([
it('Able to add default classes', function() {
var cm = new ClassManager({
classes: ['test1', 'test2', 'test3'],
defaults: ['test1', 'test2', 'test3'],
});
cm.getClasses().length.should.equal(3);
});

20
test/specs/class_manager/view/ClassTagsView.js

@ -21,6 +21,14 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'],
collection: this.coll
});
this.targetStub = {
addClass: function(v){ return {name: v}; }
};
this.compTargetStub = {
get: function(){ return { add: function(){} }}
};
this.$fixture.empty().appendTo(this.$fixtures);
this.$fixture.html(this.view.render().el);
this.btnAdd = this.view.$el.find('#' + this.view.addBtnId);
@ -104,16 +112,8 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'],
});
it("Accept new tags", function() {
sinon.stub(this.target, "get").returns({
addClass: function(v){
return {name: v};
}
});
this.view.compTarget = {
get: function(){
return { add: function(){} };
}
};
sinon.stub(this.target, "get").returns(this.targetStub);
this.view.compTarget = this.compTargetStub;
this.view.addNewTag('test');
this.view.addNewTag('test2');
this.$tags.children().length.should.equal(2);

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

@ -0,0 +1,82 @@
define(function(require) {
return {
run : function(){
describe('E2E tests', function() {
before(function () {
this.$fixtures = $("#fixtures");
this.$fixture = $('<div id="csscomposer-fixture"></div>');
});
beforeEach(function () {
this.Grapes = require('editor/main');
this.gjs = new this.Grapes({
stylePrefix: '',
storageType: 'none',
storageManager: { storageType: 'none', },
assetManager: { storageType: 'none', },
container: 'csscomposer-fixture',
});
this.cssc = this.gjs.editor.get('CssComposer');
this.clsm = this.gjs.editor.get('ClassManager');
this.$fixture.empty().appendTo(this.$fixtures);
this.gjs.render();
this.rulesSet = [
{ selectors: [{name: 'test1'}, {name: 'test2'}] },
{ selectors: [{name: 'test2'}, {name: 'test3'}] },
{ selectors: [{name: 'test3'}] }
];
});
afterEach(function () {
delete this.Grapes;
delete this.gjs;
delete this.cssc;
delete this.clsm;
});
after(function () {
this.$fixture.remove();
});
it('Rules are correctly imported from default property', function() {
var gj = new this.Grapes({
stylePrefix: '',
storageType: 'none',
storageManager: { storageType: 'none', },
assetManager: { storageType: 'none', },
cssComposer: { defaults: this.rulesSet},
container: 'csscomposer-fixture',
});
var cssc = gj.editor.get('CssComposer');
cssc.getRules().length.should.equal(this.rulesSet.length);
var cls = gj.editor.get('ClassManager').getClasses();
cls.length.should.equal(3);
});
it('New rule adds correctly the class inside classe manager', function() {
var rules = this.cssc.getRules();
rules.add({ selectors: [{name: 'test1'}] });
this.clsm.getClasses().at(0).get('name').should.equal('test1');
});
it('New rules are correctly imported inside classe manager', function() {
var rules = this.cssc.getRules();
this.rulesSet.forEach(function(item){
rules.add(item);
});
var cls = this.clsm.getClasses();
cls.length.should.equal(3);
cls.at(0).get('name').should.equal('test1');
cls.at(1).get('name').should.equal('test2');
cls.at(2).get('name').should.equal('test3');
});
});
}
};
});

12
test/specs/css_composer/main.js

@ -2,12 +2,17 @@ var modulePath = './../../../test/specs/css_composer';
define([
'CssComposer',
modulePath + '/model/CssModels'
modulePath + '/model/CssModels',
modulePath + '/view/CssRuleView',
modulePath + '/view/CssRulesView',
modulePath + '/e2e/CssComposer'
],
function(
CssComposer,
Models,
Selectors
CssRuleView,
CssRulesView,
e2e
) {
describe('Css Composer', function() {
@ -130,6 +135,9 @@ define([
});
Models.run();
CssRuleView.run();
CssRulesView.run();
e2e.run();
});
});

101
test/specs/css_composer/view/CssRuleView.js

@ -0,0 +1,101 @@
var path = 'CssComposer/view/';
define([path + 'CssRuleView', 'CssComposer/model/CssRule'],
function(CssRuleView, CssRule) {
return {
run : function(){
describe('CssRuleView', function() {
before(function () {
this.$fixtures = $("#fixtures");
this.$fixture = $('<div class="cssrule-fixture"></div>');
});
beforeEach(function () {
var m = new CssRule();
this.view = new CssRuleView({
model: m
});
this.$fixture.empty().appendTo(this.$fixtures);
this.$fixture.html(this.view.render().el);
});
afterEach(function () {
this.view.model.destroy();
});
after(function () {
this.$fixture.remove();
});
it('Object exists', function() {
CssRuleView.should.be.exist;
});
it('Correct behaviour of renderSelectors with single selector', function() {
this.view.model.get('selectors').add({name: 'test'});
this.view.renderSelectors().should.equal('.test');
});
it('Correct behaviour of renderSelectors with multiple selectors', function() {
this.view.model.get('selectors').add([{name: 'test2'}, {name: 'test1'}]);
this.view.renderSelectors().should.equal('.test2.test1');
});
it('Correct behaviour of renderProperties with single property', function() {
this.view.model.set('style', {'prop': 'value'});
this.view.renderProperties().should.equal('prop:value;');
});
it('Correct behaviour of renderProperties with multiple properties', function() {
this.view.model.set('style', {'prop2': 'value2', 'prop3': 'value3'});
this.view.renderProperties().should.equal('prop2:value2;prop3:value3;');
});
it('Empty style inside', function() {
this.$fixture.html().should.equal('<style></style>');
});
it('On update of style always empty as there is no selectors', function() {
this.view.model.set('style', {'prop':'value'});
this.$fixture.html().should.equal('<style></style>');
});
describe('CssRuleView with selectors', function() {
beforeEach(function () {
var m = new CssRule({
selectors: [{name:'test1'}, {name:'test2'}]
});
this.regView = new CssRuleView({
model: m
});
this.regView.render();
});
afterEach(function () {
this.regView.model.destroy();
});
it('Empty with no style', function() {
this.regView.$el.html().should.equal('');
});
it('Not empty on update of style', function() {
this.regView.model.set('style', {'prop':'value'});
this.regView.$el.html().should.equal('.test1.test2{prop:value;}');
});
it('Empty on clear', function() {
this.regView.model.set('style', {'prop':'value'});
this.regView.model.set('style', {});
this.regView.$el.html().should.equal('');
});
});
});
}
};
});

54
test/specs/css_composer/view/CssRulesView.js

@ -0,0 +1,54 @@
var path = 'CssComposer/view/';
define([path + 'CssRulesView', 'CssComposer/model/CssRules'],
function(CssRulesView, CssRules) {
return {
run : function(){
describe('CssRulesView', function() {
before(function () {
this.$fixtures = $("#fixtures");
this.$fixture = $('<div class="cssrules-fixture"></div>');
});
beforeEach(function () {
var col = new CssRules([]);
this.view = new CssRulesView({
collection: col
});
this.$fixture.empty().appendTo(this.$fixtures);
this.$fixture.html(this.view.render().el);
});
afterEach(function () {
this.view.collection.reset();
});
after(function () {
this.$fixture.remove();
});
it('Object exists', function() {
CssRulesView.should.be.exist;
});
it("Collection is empty", function (){
this.view.$el.html().should.be.empty;
});
it("Add new rule", function (){
sinon.stub(this.view, "addToCollection");
this.view.collection.add({});
this.view.addToCollection.calledOnce.should.equal(true);
});
it("Render new rule", function (){
this.view.collection.add({});
this.view.$el.html().should.not.be.empty;
});
});
}
};
});

25
test/specs/panel/model/panelModel.js

@ -1,25 +0,0 @@
define(['panelModel','appDir/panel_commands/buttons/main'],
function(panelModel, commandsButtonsProvider) {
describe('Panel', function() {
it('Contiene valori di default', function() { //Has default values
var model = new panelModel({});
model.should.be.ok;
model.get('name').should.equal("");
model.get('visible').should.equal(true);
model.get('buttons').should.equal.undefined;
});
it('Imposta valori passati', function() { //Sets passed attributes
var model = new panelModel({
name:'command',
visible: false,
buttons: commandsButtonsProvider.createButtons(),
});
model.should.be.ok;
model.get('name').should.equal("command");
model.get('visible').should.equal(false);
model.get('buttons').should.have.length(5);
});
});
});

49
test/specs/panel/view/panelView.js

@ -1,49 +0,0 @@
define(['panelModel', 'panelView', 'Buttons', 'appDir/panel_commands/buttons/main'],
function(panelModel,panelView, Buttons, commandsButtonsProvider) {
describe('PanelView', function() {
before(function () {
this.testPanelName = 'commands';
this.$fixture = $("<div id='panel-fixture'></div>");
});
beforeEach(function () {
this.view = new panelView({
model: new panelModel({
name : this.testPanelName,
buttons: commandsButtonsProvider.createButtons(),
}),
eventsQ : {
commands: {createComponent : {}}
},
});
this.$fixture.empty().appendTo($("#fixtures"));
this.$fixture.html(this.view.render().el);
});
afterEach(function () {
//this.view.model.destroy();
});
after(function () {
//this.$fixture.remove();
});
describe("Inizializzazione", function () {
it('Render pannello', function() { //Has default values
this.view.should.be.ok;
this.view.$el.attr('id').should.equal(this.testPanelName);
});
it('Non deve essere vuoto', function() {
this.view.$el.find('.c a').should.have.length.above(0);
});
it('Nessun elemento deve essere attivo', function() {
this.view.$el.find('.c a.active').should.have.length(0);
});
});
describe("Interazione", function(){
it('Elemento attivo alla richiesta', function(){
this.view.model.buttons.models[0].toggle();
this.view.$el.find('.c a.active').should.have.length(1);
});
});
});
});
Loading…
Cancel
Save