diff --git a/src/class_manager/config/config.js b/src/class_manager/config/config.js index f866a62e0..c3ee471fb 100644 --- a/src/class_manager/config/config.js +++ b/src/class_manager/config/config.js @@ -11,7 +11,7 @@ define(function () { label: 'Classes', // Label for states - statesLabel: 'State', + statesLabel: '', }; }); \ No newline at end of file diff --git a/src/class_manager/view/ClassTagView.js b/src/class_manager/view/ClassTagView.js index 5d2c33db3..5fed012cc 100644 --- a/src/class_manager/view/ClassTagView.js +++ b/src/class_manager/view/ClassTagView.js @@ -13,6 +13,7 @@ define(['backbone', 'text!./../template/classTag.html'], this.config = o.config || {}; this.coll = o.coll || null; this.pfx = this.config.stylePrefix || ''; + this.target = this.config.target; this.className = this.pfx + 'tag'; this.closeId = this.pfx + 'close'; this.chkId = this.pfx + 'checkbox'; @@ -28,6 +29,7 @@ define(['backbone', 'text!./../template/classTag.html'], changeStatus: function(){ this.model.set('active', !this.model.get('active')); + this.target.trigger('targetClassUpdated'); }, /** @@ -35,13 +37,15 @@ define(['backbone', 'text!./../template/classTag.html'], * @param {Object} e */ removeTag: function(e){ - var comp = this.config.target.get('selectedComponent'); + var comp = this.target.get('selectedComponent'); if(comp) comp.get('classes').remove(this.model); - if(this.coll) + if(this.coll){ this.coll.remove(this.model); + this.target.trigger('targetClassRemoved'); + } this.remove(); }, @@ -53,10 +57,13 @@ define(['backbone', 'text!./../template/classTag.html'], if(!this.$chk) this.$chk = this.$el.find('#' + this.pfx + 'checkbox'); - if(this.model.get('active')) + if(this.model.get('active')){ this.$chk.removeClass('fa-circle-o').addClass('fa-dot-circle-o'); - else + this.$el.removeClass('opac50'); + }else{ this.$chk.removeClass('fa-dot-circle-o').addClass('fa-circle-o'); + this.$el.addClass('opac50'); + } }, /** @inheritdoc */ diff --git a/src/code_manager/main.js b/src/code_manager/main.js index e88de7755..a7a9c4d4f 100644 --- a/src/code_manager/main.js +++ b/src/code_manager/main.js @@ -2,7 +2,7 @@ define(function(require) { /** * @class CodeManager * @param {Object} Configurations - * + * * @return {Object} * */ function CodeManager(config) @@ -21,38 +21,38 @@ define(function(require) { if (!(name in c)) c[name] = defaults[name]; } - + this.gi = new gInterface(); this.generators = {}; this.defaultGenerators = {}; this.currentGenerator = null; - + this.ei = new eInterface(); this.editors = {}; this.defaultEditors = {}; this.currentEditor = null; - + var geHtml = new gHtml(), geCss = new gCss(), geJson = new gJson(), edCM = new eCM(); - + this.defaultGenerators[geHtml.getId()] = geHtml; this.defaultGenerators[geCss.getId()] = geCss; this.defaultGenerators[geJson.getId()] = geJson; - + this.defaultEditors[edCM.getId()] = edCM; - + this.EditorView = editorView; this.config = c; } - + CodeManager.prototype = { - + /** * Add new code generator * @param {GeneratorInterface} generator - * + * * @return this * */ addGenerator : function(generator) @@ -63,43 +63,43 @@ define(function(require) { console.warn("addGenerator: method '"+ method +"' was not found"); return; } - + var id = generator.getId(); this.generators[id] = generator; - + if(!this.currentGenerator) this.currentGenerator = id; - + return this; }, - + /** * Returns generator * @param {String}|{Integer} id Generator ID - * + * * @return {GeneratorInterface}|null * */ getGenerator : function(id) { if(id && this.generators[id]) generator = this.generators[id]; - + return generator ? generator : null; }, - + /** * Returns generators - * + * * @return {Array} * */ getGenerators : function() { return this.generators; }, - + /** * Get current generator - * + * * @return {GeneratorInterface} * */ getCurrentGenerator : function() @@ -108,11 +108,11 @@ define(function(require) { this.loadDefaultGenerators(); return this.getGenerator(this.currentGenerator); }, - + /** * Set current generator * @param {Integer} id Generator ID - * + * * @return this * */ setCurrentGenerator : function(id) @@ -120,10 +120,10 @@ define(function(require) { this.currentGenerator = id; return this; }, - + /** * Load default generators - * + * * @return this * */ loadDefaultGenerators : function() @@ -131,14 +131,14 @@ define(function(require) { for (var id in this.defaultGenerators) { this.addGenerator(this.defaultGenerators[id]); } - + return this; }, - + /** * Add new editor * @param {EditorInterface} editor - * + * * @return this * */ addEditor : function(editor) @@ -149,43 +149,43 @@ define(function(require) { console.warn("addEditor: method '"+ method +"' was not found"); return; } - + var id = editor.getId(); this.editors[id] = editor; - + if(!this.currentEditor) this.currentEditor = id; - + return this; }, - + /** * Returns editor * @param {String}|{Integer} id Editor ID - * + * * @return {EditorInterface}|null * */ getEditor : function(id) { if(id && this.editors[id]) editor = this.editors[id]; - + return editor ? editor : null; }, - + /** * Returns editors - * + * * @return {Array} * */ getEditors : function() { return this.editors; }, - + /** * Get current editor - * + * * @return {EditorInterface} * */ getCurrentEditor : function() @@ -194,11 +194,11 @@ define(function(require) { this.loadDefaultEditors(); return this.getEditor(this.currentEditor); }, - + /** * Set current editor * @param {Integer} id Editor ID - * + * * @return this * */ setCurrentEditor : function(id) @@ -206,10 +206,10 @@ define(function(require) { this.currentEditor = id; return this; }, - + /** * Load default editors - * + * * @return this * */ loadDefaultEditors : function() @@ -217,38 +217,38 @@ define(function(require) { for (var id in this.defaultEditors) { this.addEditor(this.defaultEditors[id]); } - + return this; }, - + /** * Get code by name * @param {Backbone.Model} model Model * @param {String}|{Integer} v Id of code generator - * + * * @return {String}|null * */ - getCode : function(model, v) + getCode : function(model, v, em) { var id = v || this.currentGenerator, generator = this.generators[id]; - return generator ? generator.build(model) : null; + return generator ? generator.build(model, em) : null; }, - + /** * Update editor content * @param {EditorInteface} editor Editor * @param {String} code Code value - * + * * @return void * */ updateEditor : function(editor, code) { editor.setContent(code); }, - - + + }; - + return CodeManager; }); \ No newline at end of file diff --git a/src/code_manager/model/CssGenerator.js b/src/code_manager/model/CssGenerator.js index 60162cffb..d095239d8 100644 --- a/src/code_manager/model/CssGenerator.js +++ b/src/code_manager/model/CssGenerator.js @@ -1,44 +1,71 @@ -define(['backbone'], +define(['backbone'], function (Backbone) { /** * @class CssGenerator * */ return Backbone.Model.extend({ - + + initialize: function(){ + this.buff = []; + }, + /** @inheritdoc */ getId : function() { - return 'css'; + return 'css'; }, - + /** @inheritdoc */ - build: function(model) + build: function(model, cssc) { - var coll = model.get('components') || model, code = ''; - + coll.each(function(m){ - var css = m.get('style'), - cln = m.get('components'); // Children - + var css = m.get('style'), + cls = m.get('classes'), + cln = m.get('components'); + + // Get id-relative style if(css && Object.keys(css).length !== 0){ code += '#' + m.cid + '{'; - + for(var prop in css) if(css.hasOwnProperty(prop)) - code += prop + ': ' + css[prop] + ';'; - + code += prop + ':' + css[prop] + ';'; + code += '}'; } - + + if(cssc && cls.length){ + var rule = cssc.getRule(cls.models); + if(rule){ + var selectors = rule.get('selectors'); + var ruleStyle = rule.get('style'); + var strSel = ''; + selectors.each(function(selector){ + strSel += '.' + selector.get('name'); + }); + if(this.buff.indexOf(strSel) < 0){ + this.buff.push(strSel); + code += strSel + '{'; + if(ruleStyle && Object.keys(ruleStyle).length !== 0){ + for(var prop2 in ruleStyle) + if(ruleStyle.hasOwnProperty(prop2)) + code += prop2 + ':' + ruleStyle[prop2] + ';'; + } + code += '}'; + } + } + } + if(cln.length) - code += this.build(cln); - + code += this.build(cln, cssc); + }, this); - + return code; }, - + }); }); diff --git a/src/code_manager/model/HtmlGenerator.js b/src/code_manager/model/HtmlGenerator.js index a1ea8450a..acd1c89b2 100644 --- a/src/code_manager/model/HtmlGenerator.js +++ b/src/code_manager/model/HtmlGenerator.js @@ -16,15 +16,18 @@ define(['backbone'], code = ''; coll.each(function(m){ - var tag = m.get('tagName'), // Tag name - sTag = 0, // Single tag - attr = '', // Attributes string - cln = m.get('components'); // Children - - _.each(m.get('attributes'),function(value, prop){ + var tag = m.get('tagName'), // Tag name + sTag = 0, // Single tag + attr = '', // Attributes string + attrId = '', + strCls = '', + cln = m.get('components'), // Children + attrs = m.get('attributes'), + classes = m.get('classes'); + _.each(attrs,function(value, prop){ if(prop == 'onmousedown') return; - attr += value && prop!='style' ? ' ' + prop + '="' + value + '" ' : ''; + attr += value && prop!='style' ? ' ' + prop + '="' + value + '"' : ''; }); if(m.get('type') == 'image'){ @@ -33,7 +36,15 @@ define(['backbone'], attr += 'src="' + m.get('src') + '"'; } - code += '<'+tag+' id="'+m.cid+'"' + attr + (sTag ? '/' : '') + '>' + m.get('content'); + if(!_.isEmpty(m.get('style'))) + attrId = ' id="' + m.cid + '" '; + + classes.each(function(m){ + strCls += ' ' + m.get('name'); + }); + + strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : ''; + code += '<' + tag + strCls + attrId + attr + (sTag ? '/' : '') + '>' + m.get('content'); if(cln.length) code += this.build(cln); diff --git a/src/commands/view/ExportTemplate.js b/src/commands/view/ExportTemplate.js index 1da11c0cf..a9fecc9b1 100644 --- a/src/commands/view/ExportTemplate.js +++ b/src/commands/view/ExportTemplate.js @@ -9,6 +9,7 @@ define(function() { this.components = em.get('Canvas').getWrapper().get('components'); this.modal = em.get('Modal') || null; this.cm = em.get('CodeManager') || null; + this.cssc = em.get('CssComposer') || null; this.enable(); }, @@ -62,7 +63,8 @@ define(function() { } this.htmlEditor.setContent( this.cm.getCode(this.components, 'html') ); - this.cssEditor.setContent( this.cm.getCode(this.components, 'css') ); + this.cm.getGenerator('css').buff = []; + this.cssEditor.setContent( this.cm.getCode(this.components, 'css', this.cssc)); if(this.sender) this.sender.set('active',false); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index e77a5a1f2..bb249c6f5 100644 --- a/src/style_manager/view/SectorsView.js +++ b/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 targetClassAdded', this.targetUpdated); + this.listenTo( this.target, 'change:selectedComponent targetClassAdded targetClassRemoved targetClassUpdated', this.targetUpdated); }, diff --git a/test/runner/main.js b/test/runner/main.js index 45ac312fe..b7ed5ace2 100644 --- a/test/runner/main.js +++ b/test/runner/main.js @@ -14,6 +14,7 @@ require(['../src/config/require-config.js', 'config/config.js'], function() { 'specs/dom_components/main.js', 'specs/class_manager/main.js', 'specs/css_composer/main.js', + 'specs/code_manager/main.js', ], function(chai) { var should = chai.should(), diff --git a/test/specs/class_manager/view/ClassTagView.js b/test/specs/class_manager/view/ClassTagView.js index d6eeca2c0..e95bb63cd 100644 --- a/test/specs/class_manager/view/ClassTagView.js +++ b/test/specs/class_manager/view/ClassTagView.js @@ -12,16 +12,19 @@ define([path + 'ClassTagView', 'ClassManager/model/ClassTags'], }); beforeEach(function () { - var coll = new ClassTags(); + this.coll = new ClassTags(); this.testLabel = 'TestLabel'; - var model = coll.add({ + var model = this.coll.add({ name: 'test', label: this.testLabel, }); this.view = new ClassTagView({ config : {}, - model: model + model: model, + coll: this.coll }); + this.view.target = { get:function(){} }; + _.extend(this.view.target, Backbone.Events); this.$fixture.empty().appendTo(this.$fixtures); this.$fixture.html(this.view.render().el); }); @@ -73,6 +76,14 @@ define([path + 'ClassTagView', 'ClassManager/model/ClassTags'], this.$fixture.html().should.be.empty; }); + it('On remove triggers event', function() { + var spy = sinon.spy(); + sinon.stub(this.view.target, 'get').returns(0); + this.view.target.on("targetClassRemoved", spy); + this.view.$el.find('#close').trigger('click'); + spy.called.should.equal(true); + }); + it('Checkbox toggles status', function() { var spy = sinon.spy(); this.view.model.on("change:active", spy); @@ -82,6 +93,14 @@ define([path + 'ClassTagView', 'ClassManager/model/ClassTags'], spy.called.should.equal(true); }); + it('On toggle triggers event', function() { + var spy = sinon.spy(); + sinon.stub(this.view.target, 'get').returns(0); + this.view.target.on("targetClassUpdated", spy); + this.view.$el.find('#checkbox').trigger('click'); + spy.called.should.equal(true); + }); + it('Label toggles status', function() { var spy = sinon.spy(); this.view.model.on("change:active", spy); diff --git a/test/specs/code_manager/main.js b/test/specs/code_manager/main.js new file mode 100644 index 000000000..d8af9d133 --- /dev/null +++ b/test/specs/code_manager/main.js @@ -0,0 +1,39 @@ +var modulePath = './../../../test/specs/code_manager'; + +define([ + 'CodeManager', + modulePath + '/model/CodeModels', + //modulePath + '/view/ClassTagView', + //modulePath + '/view/ClassTagsView', + //modulePath + '/e2e/ClassManager' + ], + function( + CodeManager, + Models + ) { + + describe('Code Manager', function() { + + describe('Main', function() { + + beforeEach(function () { + this.obj = new CodeManager(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Object exists', function() { + CodeManager.should.be.exist; + }); + + }); + + Models.run(); + //ClassTagView.run(); + //ClassTagsView.run(); + //e2e.run(); + + }); +}); \ No newline at end of file diff --git a/test/specs/code_manager/model/CodeModels.js b/test/specs/code_manager/model/CodeModels.js new file mode 100644 index 000000000..95370ac23 --- /dev/null +++ b/test/specs/code_manager/model/CodeModels.js @@ -0,0 +1,106 @@ +var path = 'CodeManager/model/'; +define([path + 'HtmlGenerator', + path + 'CssGenerator', + 'DomComponents/model/Component', + 'CssComposer'], + function(HtmlGenerator, CssGenerator, Component, CssComposer) { + + return { + run : function(){ + describe('HtmlGenerator', function() { + beforeEach(function () { + this.obj = new HtmlGenerator(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Build correctly one component', function() { + var comp = new Component(); + this.obj.build(comp).should.equal(''); + }); + + it('Build correctly empty component inside', function() { + var comp = new Component(); + var m1 = comp.get('components').add({}); + this.obj.build(comp).should.equal('
'); + }); + + it('Build correctly not empty component inside', function() { + var comp = new Component(); + var m1 = comp.get('components').add({ + tagName: 'article', + attributes: { + 'data-test1': 'value1', + 'data-test2': 'value2' + } + }); + this.obj.build(comp).should.equal('
'); + }); + + it('Build correctly component with classes', function() { + var comp = new Component(); + var m1 = comp.get('components').add({ + tagName: 'article', + attributes: { + 'data-test1': 'value1', + 'data-test2': 'value2' + } + }); + ['class1', 'class2'].forEach(function(item){ + m1.get('classes').add({name: item}); + }); + this.obj.build(comp).should.equal('
'); + }); + }); + + describe('CssGenerator', function() { + beforeEach(function () { + this.obj = new CssGenerator(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Build correctly one component', function() { + var comp = new Component(); + this.obj.build(comp).should.equal(''); + }); + + it('Build correctly empty component inside', function() { + var comp = new Component(); + var m1 = comp.get('components').add({tagName: 'article'}); + this.obj.build(comp).should.equal(''); + }); + + it('Build correctly component with style inside', function() { + var comp = new Component(); + var m1 = comp.get('components').add({ + tagName: 'article', + style: { + 'prop1': 'value1', + 'prop2': 'value2' + } + }); + this.obj.build(comp).should.equal('#' + m1.cid +'{prop1:value1;prop2:value2;}'); + }); + + it('Build correctly component with class styled', function() { + var comp = new Component(); + var m1 = comp.get('components').add({tagName: 'article'}); + var cls1 = m1.get('classes').add({name: 'class1'}); + + var cssc = new CssComposer(); + var rule = cssc.newRule(cls1); + rule.set('style',{'prop1':'value1', 'prop2':'value2'}); + cssc.addRule(rule); + + this.obj.build(comp, cssc).should.equal('.class1{prop1:value1;prop2:value2;}'); + }); + }) + } + }; + +}); \ No newline at end of file