From 0de9f3f450792e30d24b0a6d993fb2f38f864b42 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 1 Mar 2016 02:49:40 +0100 Subject: [PATCH 1/8] Go on with Class Manager feature --- src/commands/view/OpenStyleManager.js | 2 -- src/style_manager/view/PropertyView.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/commands/view/OpenStyleManager.js b/src/commands/view/OpenStyleManager.js index da42937b4..4cab47f1c 100644 --- a/src/commands/view/OpenStyleManager.js +++ b/src/commands/view/OpenStyleManager.js @@ -24,13 +24,11 @@ define(['StyleManager'], function(StyleManager) { // Class Manager container this.clm = em.get('ClassManager'); if(this.clm){ - /* this.$clm = new this.clm.ClassTagsView({ collection: new this.clm.ClassTags([]), config: this.clm.config, }).render().el; this.$cn2.append(this.$clm); - */ } // Style Manager manager container diff --git a/src/style_manager/view/PropertyView.js b/src/style_manager/view/PropertyView.js index ab6670898..40e6fc25a 100644 --- a/src/style_manager/view/PropertyView.js +++ b/src/style_manager/view/PropertyView.js @@ -47,14 +47,12 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], componentSelected: function(e){ this.selectedComponent = this.target.get('selectedComponent'); if(this.selectedComponent){ - /* var classes = this.selectedComponent.get('classes'); if(classes.length){ var valid = _.filter(classes.models, function(item){ return item.get('active'); }); var ids = _.pluck(valid, 'cid'); var cssBlock = '';//this.sm.get('CssManager').getRule(ids, 'status', 'mediaq'); } - */ //I will rerender it only if the assigned one is different from the actuale value //console.log('property '+this.property+" view: "+this.componentValue+" model: "+ this.model.get('value')); if( !this.sameValue() ){ From c64eb505d28b50c1c5670ba148157f37f77f0b1a Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Thu, 3 Mar 2016 01:10:00 +0100 Subject: [PATCH 2/8] Add Css Composer service --- src/config/require-config.js | 1 + .../config/config.js | 0 src/css_composer/main.js | 115 ++++++++++++++++++ src/css_composer/model/CssRule.js | 22 ++++ .../model/CssRules.js | 0 src/css_composer/model/Selectors.js | 29 +++++ src/css_manager/main.js | 64 ---------- src/css_manager/model/CssRule.js | 16 --- src/editor/config/config.js | 3 + src/editor/model/Editor.js | 14 +++ src/style_manager/view/PropertiesView.js | 46 +++---- src/style_manager/view/PropertyView.js | 16 ++- src/style_manager/view/SectorView.js | 2 + src/style_manager/view/SectorsView.js | 28 +++++ 14 files changed, 248 insertions(+), 108 deletions(-) rename src/{css_manager => css_composer}/config/config.js (100%) create mode 100644 src/css_composer/main.js create mode 100644 src/css_composer/model/CssRule.js rename src/{css_manager => css_composer}/model/CssRules.js (100%) create mode 100644 src/css_composer/model/Selectors.js delete mode 100644 src/css_manager/main.js delete mode 100644 src/css_manager/model/CssRule.js diff --git a/src/config/require-config.js b/src/config/require-config.js index 84e6cc784..3d2836551 100644 --- a/src/config/require-config.js +++ b/src/config/require-config.js @@ -42,6 +42,7 @@ require.config({ { name: 'RichTextEditor', location: 'rich_text_editor', }, { name: 'ModalDialog', location: 'modal_dialog', }, { name: 'CodeManager', location: 'code_manager', }, + { name: 'CssComposer', location: 'css_composer', }, { name: 'Commands', location: 'commands', }, { name: 'Canvas', location: 'canvas', }, { name: 'Panels', location: 'panels', } diff --git a/src/css_manager/config/config.js b/src/css_composer/config/config.js similarity index 100% rename from src/css_manager/config/config.js rename to src/css_composer/config/config.js diff --git a/src/css_composer/main.js b/src/css_composer/main.js new file mode 100644 index 000000000..b79c15826 --- /dev/null +++ b/src/css_composer/main.js @@ -0,0 +1,115 @@ +define(function(require) { + /** + * @class CssManager + * @param {Object} config Configurations + * + * */ + var CssManager = function(config) + { + var c = config || {}, + def = require('./config/config'), + CssRule = require('./model/CssRule'), + CssRules = require('./model/CssRules'); + + for (var name in def) { + if (!(name in c)) + c[name] = def[name]; + } + + //this.qset = { '' : CssRules, '340px': CssRules }; + var rules = new CssRules([]); + + return { + + /** + * Add new class to collection only if it's not already exists + * @param {String} name Class name + * + * @return {Object} Model class + * */ + addRule: function(name){ + /* + var label = name; + var c = this.getRule(name); + if(!c) + return this.classes.add({name: name, label: label}); + return c; + */ + }, + + /** + * Get class by its name + * @param {Array} selectors Array of selectors + * @param {String} state Rule status + * @param {String} set Query set + * + * @return {Object|null} + * */ + getRule : function(selectors, state, set) { + var req = _.pluck(selectors, 'cid'); + fRule = null; + rules.each(function(rule){ + if(fRule) + return; + + var sel = _.pluck(rule.get('selectors'), 'cid'); + + if(this.same(req, sel)) + fRule = rule; + }, this); + return fRule; + }, + + /** + * Create new rule + * @param {Array} selectors Array of selectors + * @param {String} state Rule status + * @param {String} set Query set + * + * @return {Object} + * */ + newRule: function(selectors, state, set) { + var rule = new CssRule({ state: state }); + rule.get('selectors').add(selectors); + return rule; + }, + + /** + * Compare 2 arrays to check if are the same + * @param {Array} arr1 + * @param {Array} arr2 + * @return {Boolean} + */ + same: function(a1, a2){ + if(a1.length !== a2.length) + return; + + for (var i = 0; i < a1.length; i++) { + var f = 0; + + for (var j = 0; j < a2.length; j++) { + if (a1[i] === a2[j]) + f = 1; + } + + if(f === 0) + return; + } + return true; + }, + + /** + * Get collection of css rules + * + * @return {Object} + * */ + getRules : function() { + return rules; + }, + + }; + }; + + return CssManager; + +}); \ No newline at end of file diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js new file mode 100644 index 000000000..54586aa5f --- /dev/null +++ b/src/css_composer/model/CssRule.js @@ -0,0 +1,22 @@ +define(['backbone', './Selectors'], + function (Backbone, Selectors) { + /** + * @class CssRule + * */ + return Backbone.Model.extend({ + + defaults: { + selectors: {}, + style: {}, + stylable: true, + state: '', + }, + + initialize: function(c, opt) { + this.config = c || {}; + this.slct = this.config.selectors || []; + this.set('selectors', new Selectors(this.slct)); + }, + + }); +}); diff --git a/src/css_manager/model/CssRules.js b/src/css_composer/model/CssRules.js similarity index 100% rename from src/css_manager/model/CssRules.js rename to src/css_composer/model/CssRules.js diff --git a/src/css_composer/model/Selectors.js b/src/css_composer/model/Selectors.js new file mode 100644 index 000000000..62f8511fc --- /dev/null +++ b/src/css_composer/model/Selectors.js @@ -0,0 +1,29 @@ +define([ 'backbone', 'require'], + function (Backbone, require) { + /** + * @class Selectors + * */ + + return Backbone.Collection.extend({ + + initialize: function(models, opt){ + + this.model = function(attrs, opts) { + var model; + + switch(1){ + + default: + if(!this.ClassTag) + this.ClassTag = require("ClassManager/model/ClassTag"); + model = new this.ClassTag(attrs, opts); + + } + + return model; + }; + + }, + + }); +}); diff --git a/src/css_manager/main.js b/src/css_manager/main.js deleted file mode 100644 index 20e63a356..000000000 --- a/src/css_manager/main.js +++ /dev/null @@ -1,64 +0,0 @@ -define(function(require) { - /** - * @class CssManager - * @param {Object} config Configurations - * - * */ - var CssManager = function(config) - { - var c = config || {}, - def = require('./config/config'); - this.CssRules = require('./model/CssRules'); - - for (var name in def) { - if (!(name in c)) - c[name] = def[name]; - } - - this.rules = new this.CssRules([]); - this.config = c; - }; - - CssManager.prototype = { - - /** - * Add new class to collection only if it's not already exists - * @param {String} name Class name - * - * @return {Object} Model class - * */ - addRule: function(name){ - var label = name; - var c = this.getClass(name); - if(!c) - return this.classes.add({name: name, label: label}); - return c; - }, - - /** - * Get class by its name - * @param {Array[String]} ids Array of ids - * @param {String} status Rule status - * @param {String} set Query set - * - * @return {Object|null} - * */ - getRule : function(ids, status, set) { - var res = this.classes.where({name: id}); - return res.length ? res[0] : null; - }, - - /** - * Get collection of css rules - * - * @return {Object} - * */ - getRules : function() { - return this.rules; - }, - - }; - - return ClassManager; - -}); \ No newline at end of file diff --git a/src/css_manager/model/CssRule.js b/src/css_manager/model/CssRule.js deleted file mode 100644 index f53aba979..000000000 --- a/src/css_manager/model/CssRule.js +++ /dev/null @@ -1,16 +0,0 @@ -define(['backbone'], - function (Backbone) { - /** - * @class CssRule - * */ - return Backbone.Model.extend({ - - defaults: { - classes: {}, - style: {}, - stylable: true, - state: '', - }, - - }); -}); diff --git a/src/editor/config/config.js b/src/editor/config/config.js index 9b06d527b..d5094f37f 100644 --- a/src/editor/config/config.js +++ b/src/editor/config/config.js @@ -64,6 +64,9 @@ define(function () { //Configurations for Class Manager classManager : {}, + //Configurations for Css Composer + cssComposer : {}, + }; return config; }); \ No newline at end of file diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 6bddebf0b..b33125eee 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -6,6 +6,7 @@ define([ 'StorageManager', 'ModalDialog', 'CodeManager', + 'CssComposer', 'Commands', 'Canvas', 'RichTextEditor', @@ -20,6 +21,7 @@ define([ StorageManager, ModalDialog, CodeManager, + CssComposer, Commands, Canvas, RichTextEditor, @@ -53,10 +55,22 @@ define([ this.initComponents(); this.initCanvas(); this.initUndoManager(); + this.initCssComposer(); this.on('change:selectedComponent', this.componentSelected, this); }, + /** + * Initialize Css Composer + * */ + initCssComposer: function() + { + var cfg = this.config.cssComposer, + pfx = cfg.stylePrefix || 'css-'; + cfg.stylePrefix = this.config.stylePrefix + pfx; + this.set('CssComposer', new CssComposer(cfg)); + }, + /** * Initialize Class manager * */ diff --git a/src/style_manager/view/PropertiesView.js b/src/style_manager/view/PropertiesView.js index 846458fbe..492a56d91 100644 --- a/src/style_manager/view/PropertiesView.js +++ b/src/style_manager/view/PropertiesView.js @@ -1,58 +1,60 @@ -define(['backbone','./PropertyView', './PropertyIntegerView', './PropertyRadioView', './PropertySelectView', - './PropertyColorView', './PropertyFileView', './PropertyCompositeView', './PropertyStackView'], - function (Backbone, PropertyView, PropertyIntegerView, PropertyRadioView, PropertySelectView, +define(['backbone','./PropertyView', './PropertyIntegerView', './PropertyRadioView', './PropertySelectView', + './PropertyColorView', './PropertyFileView', './PropertyCompositeView', './PropertyStackView'], + function (Backbone, PropertyView, PropertyIntegerView, PropertyRadioView, PropertySelectView, PropertyColorView, PropertyFileView, PropertyCompositeView, PropertyStackView) { - /** + /** * @class PropertiesView * */ return Backbone.View.extend({ - + initialize: function(o) { this.config = o.config; this.pfx = this.config.stylePrefix; - this.target = o.target || {}; - this.onChange = o.onChange || {}; - this.onInputRender = o.onInputRender || {}; - this.customValue = o.customValue || {}; + this.target = o.target || {}; + this.propTarget = o.propTarget || {}; + this.onChange = o.onChange || {}; + this.onInputRender = o.onInputRender || {}; + this.customValue = o.customValue || {}; }, - + render: function() { var fragment = document.createDocumentFragment(); - + this.collection.each(function(model){ var objView = PropertyView; - + switch(model.get('type')){ - case 'integer': + case 'integer': objView = PropertyIntegerView; break; - case 'radio': + case 'radio': objView = PropertyRadioView; break; - case 'select': + case 'select': objView = PropertySelectView; break; - case 'color': + case 'color': objView = PropertyColorView; break; - case 'file': + case 'file': objView = PropertyFileView; break; - case 'composite': + case 'composite': objView = PropertyCompositeView;break; - case 'stack': + case 'stack': objView = PropertyStackView; break; } - + var view = new objView({ model : model, name : model.get('name'), id : this.pfx + model.get('property'), target : this.target, + propTarget : this.propTarget, onChange : this.onChange, onInputRender : this.onInputRender, config : this.config, }); - + if(model.get('type') != 'composite'){ view.customValue = this.customValue; } - + fragment.appendChild(view.render().el); },this); diff --git a/src/style_manager/view/PropertyView.js b/src/style_manager/view/PropertyView.js index 40e6fc25a..2768aaa75 100644 --- a/src/style_manager/view/PropertyView.js +++ b/src/style_manager/view/PropertyView.js @@ -16,6 +16,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], this.config = o.config; this.pfx = this.config.stylePrefix || ''; this.target = o.target || {}; + this.propTarget = o.propTarget || {}; this.onChange = o.onChange || {}; this.onInputRender = o.onInputRender || {}; this.customValue = o.customValue || {}; @@ -35,8 +36,17 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], this.componentValue = this.selectedComponent.get('style')[this.property]; } + this.listenTo( this.propTarget, 'update', this.targetUpdated); this.listenTo( this.target ,'change:selectedComponent',this.componentSelected); this.listenTo( this.model ,'change:value', this.valueChanged); + + }, + + /** + * Fired when target is updated + */ + targetUpdated: function() { + console.log('Property: target updated'); }, /** @@ -47,12 +57,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], componentSelected: function(e){ this.selectedComponent = this.target.get('selectedComponent'); if(this.selectedComponent){ - var classes = this.selectedComponent.get('classes'); - if(classes.length){ - var valid = _.filter(classes.models, function(item){ return item.get('active'); }); - var ids = _.pluck(valid, 'cid'); - var cssBlock = '';//this.sm.get('CssManager').getRule(ids, 'status', 'mediaq'); - } //I will rerender it only if the assigned one is different from the actuale value //console.log('property '+this.property+" view: "+this.componentValue+" model: "+ this.model.get('value')); if( !this.sameValue() ){ diff --git a/src/style_manager/view/SectorView.js b/src/style_manager/view/SectorView.js index dc583cd48..60fc4e608 100644 --- a/src/style_manager/view/SectorView.js +++ b/src/style_manager/view/SectorView.js @@ -13,6 +13,7 @@ define(['backbone', './PropertiesView', 'text!./../templates/sector.html'], this.config = o.config; this.pfx = this.config.stylePrefix; this.target = o.target || {}; + this.propTarget = o.propTarget || {}; this.open = this.model.get('open'); this.caretR = 'fa-caret-right'; this.caretD = 'fa-caret-down'; @@ -82,6 +83,7 @@ define(['backbone', './PropertiesView', 'text!./../templates/sector.html'], var view = new PropertiesView({ collection : objs, target : this.target, + propTarget : this.propTarget, config : this.config, }); this.$el.append(view.render().el); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index dd472d884..93d3fbb71 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -9,6 +9,33 @@ define(['backbone','./SectorView'], this.config = o.config; this.pfx = this.config.stylePrefix; this.target = o.target || {}; + this.propTarget = {}; + _.extend(this.propTarget, Backbone.Events); + + this.listenTo( this.target ,'change:selectedComponent', this.targetUpdated); + + }, + + /** + * Fired when target is updated + */ + targetUpdated: function() { + var el = this.target.get('selectedComponent'); + var classes = el.get('classes'); + + if(classes.length){ + var cssC = this.target.get('CssComposer'); + var valid = _.filter(classes.models, function(item){ return item.get('active'); }); + var iContainer = cssC.getRule(valid, 'status', 'mediaq'); + if(!iContainer){ + //console.log(valid); + iContainer = cssC.newRule(valid, 'status', 'mediaq'); + //console.log(iContainer.get('selectors').models); + this.propTarget.target = iContainer; + this.propTarget.trigger('update'); + } + } + }, render: function() { @@ -21,6 +48,7 @@ define(['backbone','./SectorView'], name : obj.get('name'), properties : obj.get('properties'), target : this.target, + propTarget : this.propTarget, config : this.config, }); fragment.appendChild(view.render().el); From d7285468559843a4b80c9396b63c8dbb8db3cb9f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Fri, 4 Mar 2016 02:34:26 +0100 Subject: [PATCH 3/8] Update on Css Composer --- src/css_composer/main.js | 28 +++++-- src/css_composer/view/CssRuleView.js | 53 ++++++++++++++ src/css_composer/view/CssRulesView.js | 62 ++++++++++++++++ src/editor/view/EditorView.js | 26 ++++--- .../view/PropertyCompositeView.js | 37 +++++----- src/style_manager/view/PropertyView.js | 73 +++++-------------- src/style_manager/view/SectorsView.js | 23 ++++-- 7 files changed, 207 insertions(+), 95 deletions(-) create mode 100644 src/css_composer/view/CssRuleView.js create mode 100644 src/css_composer/view/CssRulesView.js diff --git a/src/css_composer/main.js b/src/css_composer/main.js index b79c15826..9282ae7f6 100644 --- a/src/css_composer/main.js +++ b/src/css_composer/main.js @@ -1,15 +1,16 @@ define(function(require) { /** - * @class CssManager + * @class CssComposer * @param {Object} config Configurations * * */ - var CssManager = function(config) + var CssComposer = function(config) { var c = config || {}, def = require('./config/config'), CssRule = require('./model/CssRule'), - CssRules = require('./model/CssRules'); + CssRules = require('./model/CssRules'), + CssRulesView = require('./view/CssRulesView'); for (var name in def) { if (!(name in c)) @@ -17,7 +18,11 @@ define(function(require) { } //this.qset = { '' : CssRules, '340px': CssRules }; - var rules = new CssRules([]); + var rules = new CssRules([]), + rulesView = new CssRulesView({ + collection: rules, + config: c, + }); return { @@ -27,7 +32,8 @@ define(function(require) { * * @return {Object} Model class * */ - addRule: function(name){ + addRule: function(Rule){ + return rules.add(Rule); /* var label = name; var c = this.getRule(name); @@ -78,6 +84,7 @@ define(function(require) { * Compare 2 arrays to check if are the same * @param {Array} arr1 * @param {Array} arr2 + * * @return {Boolean} */ same: function(a1, a2){ @@ -107,9 +114,18 @@ define(function(require) { return rules; }, + /** + * Render block of CSS rules + * + * @return {Object} + */ + render: function(){ + return rulesView.render().el; + } + }; }; - return CssManager; + return CssComposer; }); \ No newline at end of file diff --git a/src/css_composer/view/CssRuleView.js b/src/css_composer/view/CssRuleView.js new file mode 100644 index 000000000..32c7169ba --- /dev/null +++ b/src/css_composer/view/CssRuleView.js @@ -0,0 +1,53 @@ +define(['backbone'], + function (Backbone) { + /** + * @class CssRuleView + * */ + return Backbone.View.extend({ + + tagName: 'style', + + initialize: function(o) { + this.config = o.config || {}; + this.listenTo(this.model, 'change:style', this.render); + + }, + + /** + * Returns string of selectors + * @return {String} + */ + renderSelectors: function(){ + var sel = []; + this.model.get('selectors').each(function(m){ + sel.push('.' + m.get('name')); + }); + return sel.join(''); + }, + + /** + * Returns string of properties + * @return {String} + */ + renderProperties: function(){ + var sel = [], + props = this.model.get('style'); + for (var prop in props){ + sel.push(prop + ':' + props[prop] + ';'); + } + return sel.join(''); + }, + + /* + http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript + */ + render : function(){ + if(!this.selStr) + this.selStr = this.renderSelectors(); + var prpStr = this.renderProperties(); + this.$el.html(this.selStr + '{' + prpStr + '}'); + return this; + }, + + }); +}); diff --git a/src/css_composer/view/CssRulesView.js b/src/css_composer/view/CssRulesView.js new file mode 100644 index 000000000..c1c6f9768 --- /dev/null +++ b/src/css_composer/view/CssRulesView.js @@ -0,0 +1,62 @@ +define(['backbone','./CssRuleView'], + function (Backbone, CssRuleView) { + /** + * @class CssRulesView + * */ + return Backbone.View.extend({ + + initialize: function(o) { + this.config = o.config; + this.preview = o.preview; + this.pfx = this.config.stylePrefix; + this.listenTo( this.collection, 'add', this.addTo ); + this.listenTo( this.collection, 'reset', this.render ); + }, + + /** + * Add to collection + * @param {Object} model + * */ + addTo: function(model){ + console.log('Added'); + this.addToCollection(model); + }, + + /** + * Add new object to collection + * @param {Object} model + * @param {Object} fragmentEl + * + * @return {Object} + * */ + addToCollection: function(model, fragmentEl){ + var fragment = fragmentEl || null; + var viewObject = CssRuleView; + + var view = new viewObject({ + model: model, + config: this.config, + }); + var rendered = view.render().el; + + if(fragment) + fragment.appendChild( rendered ); + else + this.$el.append(rendered); + + return rendered; + }, + + render: function() { + var fragment = document.createDocumentFragment(); + this.$el.empty(); + + this.collection.each(function(model){ + this.addToCollection(model, fragment); + }, this); + + this.$el.append(fragment); + return this; + } + }); +}); diff --git a/src/editor/view/EditorView.js b/src/editor/view/EditorView.js index b2d9b153a..a09db6319 100644 --- a/src/editor/view/EditorView.js +++ b/src/editor/view/EditorView.js @@ -4,30 +4,34 @@ function(Backbone){ * @class EditorView * */ return Backbone.View.extend({ - + initialize: function() { - this.cv = this.model.get('Canvas'); - this.pn = this.model.get('Panels'); - this.className = this.model.config.stylePrefix + 'editor'; + this.cv = this.model.get('Canvas'); + this.pn = this.model.get('Panels'); + this.css = this.model.get('CssComposer'); + this.className = this.model.config.stylePrefix + 'editor'; }, render: function(){ this.$el.empty(); - + this.$cont = $('body ' + this.model.config.container); - + this.model.set('$editor', this.$el); - + if(this.cv) this.$el.append(this.cv.render()); - + if(this.pn) this.$el.append(this.pn.render()); - + + if(this.css) + this.$el.append(this.css.render()); + this.$el.attr('class', this.className); - + this.$cont.html(this.$el); - + if(this.pn) this.pn.active(); diff --git a/src/style_manager/view/PropertyCompositeView.js b/src/style_manager/view/PropertyCompositeView.js index af953d2ee..310015858 100644 --- a/src/style_manager/view/PropertyCompositeView.js +++ b/src/style_manager/view/PropertyCompositeView.js @@ -1,36 +1,36 @@ -define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html','require'], +define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html','require'], function (Backbone, PropertyView, propertyTemplate, require) { - /** + /** * @class PropertyCompositeView * */ return PropertyView.extend({ - + template: _.template(propertyTemplate), - + initialize: function(o) { PropertyView.prototype.initialize.apply(this, arguments); _.bindAll(this, 'build'); this.config = o.config; this.className = this.className + ' '+ this.pfx +'composite'; }, - + /** * Renders input - * + * * @return void * */ renderInput: function() { var props = this.model.get('properties'); if(props && props.length){ - if(!this.$input) + if(!this.$input) this.$input = $('', {value: 0, type: 'hidden' }); - + if(!this.props){ var Properties = require('./../model/Properties'); this.props = new Properties(props); this.model.set('properties', this.props); } - + if(!this.$props){ //Avoid style for children this.props.each(function(prop, index){ @@ -43,15 +43,16 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html console.warn(prop.get('property')+' of type composite not yet allowed.'); } },this); - + var PropertiesView = require('./PropertiesView'); var that = this; var propsView = new PropertiesView({ config : this.config, - collection : this.props, + collection : this.props, target : this.target, + propTarget : this.propTarget, onChange : function(el, model){ - var result = that.build(el, model); + var result = that.build(el, model); that.model.set('value', result); }, onInputRender : function(property, mIndex){ @@ -67,10 +68,10 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html } } }, - + /** * Get default value of the property - * + * * @return string * */ getDefaultValue: function(){ @@ -80,12 +81,12 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html }); return str.replace(/ +$/,''); }, - + /** * Extract string from composite value * @param integer Index * @param object Property model - * + * * @return string * */ valueOnIndex: function(index, model){ @@ -102,12 +103,12 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html } return result; }, - + /** * Build composite value * @param Object Selected element * @param Object Property model - * + * * @return string * */ build: function(selectedEl, propertyModel){ diff --git a/src/style_manager/view/PropertyView.js b/src/style_manager/view/PropertyView.js index 2768aaa75..b6ea12a46 100644 --- a/src/style_manager/view/PropertyView.js +++ b/src/style_manager/view/PropertyView.js @@ -5,8 +5,8 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], * */ return Backbone.View.extend({ - template: _.template(propertyTemplate), - templateLabel: _.template(propertyTemplate), + template: _.template(propertyTemplate), + templateLabel: _.template(propertyTemplate), events: { 'change' : 'valueChanged', @@ -30,46 +30,26 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], this.list = this.model.get('list'); this.input = this.$input = null; this.className = this.pfx + 'property'; - this.selectedComponent = this.target.get('selectedComponent'); - - if(this.selectedComponent){ - this.componentValue = this.selectedComponent.get('style')[this.property]; - } - this.listenTo( this.propTarget, 'update', this.targetUpdated); - this.listenTo( this.target ,'change:selectedComponent',this.componentSelected); this.listenTo( this.model ,'change:value', this.valueChanged); - - }, - - /** - * Fired when target is updated - */ - targetUpdated: function() { - console.log('Property: target updated'); }, /** - * Rerender property for the new selected component, if necessary - * @param {Array[Model, value, options]} e - * + * Fired when the target is updated * */ - componentSelected: function(e){ - this.selectedComponent = this.target.get('selectedComponent'); + targetUpdated: function(){ + this.selectedComponent = this.propTarget.model; if(this.selectedComponent){ - //I will rerender it only if the assigned one is different from the actuale value - //console.log('property '+this.property+" view: "+this.componentValue+" model: "+ this.model.get('value')); - if( !this.sameValue() ){ + if(!this.sameValue()) this.renderInputRequest(); - } } }, /** - * Checks if the value from selected component is the same with - * the value of the model + * Checks if the value from selected component is the + * same of the value of the model * - * @return boolean + * @return {Boolean} * */ sameValue: function(){ return this.getComponentValue() == (this.model.get('value') + this.model.get('unit')); @@ -88,7 +68,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], if(this.selectedComponent.get('style')[this.property]) this.componentValue = this.selectedComponent.get('style')[this.property]; else - this.componentValue = this.defaultValue + (this.unit ? this.unit : ''); + this.componentValue = this.defaultValue + (this.unit || ''); // Check if wrap inside function is required if(this.func){ @@ -97,11 +77,11 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], this.componentValue = v; } - //This allow to ovveride the normal flow of selecting component value, - //useful in composite properties + // This allow to ovveride the normal flow of selecting component value, + // useful in composite properties if(this.customValue && typeof this.customValue === "function"){ - var index = this.model.collection.indexOf(this.model); - var t = this.customValue(this, index); + var index = this.model.collection.indexOf(this.model); + var t = this.customValue(this, index); if(t) this.componentValue = t; } @@ -110,7 +90,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], }, /** - * Fetch string from function type value + * Fetch the string from function type value * @param {String} v Function type value * * @return {String} @@ -124,8 +104,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], * @param {Object} e Events * @param {Mixed} val Value * @param {Object} opt Options - * - * @return void * */ valueChanged: function(e, val, opt){ if(!this.selectedComponent) @@ -156,7 +134,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], componentCss[this.property] = value; this.selectedComponent.set('style', componentCss, { avoidStore : avSt}); } - this.selectedValue = value;//TODO ? + this.selectedValue = value; if(this.onChange && typeof this.onChange === "function"){ this.onChange(this.selectedComponent, this.model); @@ -165,9 +143,8 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Set value to the input - * @param String value - * - * @return void + * @param {String} value + * @param {Boolean} force * */ setValue: function(value, force){ var f = force===0 ? 0 : 1; @@ -177,13 +154,11 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], } if(this.$input) this.$input.val(v); - this.model.set({value: v},{silent: true}); + this.model.set({value: v}, {silent: true}); }, /** * Render label - * - * @return void * */ renderLabel: function(){ this.$el.html( this.templateLabel({ @@ -196,8 +171,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Render field property - * - * @return void * */ renderField : function() { this.renderTemplate(); @@ -207,8 +180,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Render loaded template - * - * @return void * */ renderTemplate: function(){ this.$el.append( this.template({ @@ -221,8 +192,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Renders input, to override - * - * @return void * */ renderInput: function(){ console.warn("No render input implemented for '"+this.model.get('type')+"'"); @@ -230,8 +199,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Request to render input of the property - * - * @return void * */ renderInputRequest: function(){ this.renderInput(); @@ -243,8 +210,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'], /** * Clean input - * - * @return void * */ cleanValue: function(){ this.setValue(''); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index 93d3fbb71..b9219a0a0 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -9,10 +9,11 @@ define(['backbone','./SectorView'], this.config = o.config; this.pfx = this.config.stylePrefix; this.target = o.target || {}; + + // 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', this.targetUpdated); }, @@ -21,21 +22,31 @@ define(['backbone','./SectorView'], */ targetUpdated: function() { var el = this.target.get('selectedComponent'); + + if(!el) + return; + var classes = el.get('classes'); + var pt = this.propTarget; if(classes.length){ var cssC = this.target.get('CssComposer'); var valid = _.filter(classes.models, function(item){ return item.get('active'); }); var iContainer = cssC.getRule(valid, 'status', 'mediaq'); if(!iContainer){ - //console.log(valid); iContainer = cssC.newRule(valid, 'status', 'mediaq'); - //console.log(iContainer.get('selectors').models); - this.propTarget.target = iContainer; - this.propTarget.trigger('update'); + // Hydrate styles from component element + iContainer.set('style', el.get('style')); + cssC.addRule(iContainer); + el.set('style', {}); + pt.model = iContainer; + pt.trigger('update'); + return; } } + pt.model = el; + pt.trigger('update'); }, render: function() { From a29a1678d31aba58700d29f6e61d1fb4a6f4051f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 5 Mar 2016 15:29:26 +0100 Subject: [PATCH 4/8] Add istanbul --- package.json | 1 + src/class_manager/config/config.js | 2 +- src/css_composer/main.js | 3 +-- src/style_manager/view/SectorsView.js | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 79293e23e..ab5c98133 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "grunt-contrib-watch": "^0.6.1", "grunt-mocha": "^0.4.15", "grunt-sass": "^1.1.0", + "istanbul": "^0.4.2", "node-sass": "^3.4.2" }, "keywords": [ diff --git a/src/class_manager/config/config.js b/src/class_manager/config/config.js index d337a28b4..12464ffd8 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: 'States', + statesLabel: 'State', }; }); \ No newline at end of file diff --git a/src/css_composer/main.js b/src/css_composer/main.js index 9282ae7f6..29a390807 100644 --- a/src/css_composer/main.js +++ b/src/css_composer/main.js @@ -58,8 +58,7 @@ define(function(require) { if(fRule) return; - var sel = _.pluck(rule.get('selectors'), 'cid'); - + var sel = _.pluck(rule.get('selectors').models, 'cid'); if(this.same(req, sel)) fRule = rule; }, this); diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index b9219a0a0..3d79e8f0f 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -31,7 +31,9 @@ define(['backbone','./SectorView'], if(classes.length){ var cssC = this.target.get('CssComposer'); - var valid = _.filter(classes.models, function(item){ return item.get('active'); }); + var valid = _.filter(classes.models, function(item){ + return item.get('active'); + }); var iContainer = cssC.getRule(valid, 'status', 'mediaq'); if(!iContainer){ iContainer = cssC.newRule(valid, 'status', 'mediaq'); @@ -39,10 +41,10 @@ define(['backbone','./SectorView'], iContainer.set('style', el.get('style')); cssC.addRule(iContainer); el.set('style', {}); - pt.model = iContainer; - pt.trigger('update'); - return; } + pt.model = iContainer; + pt.trigger('update'); + return; } pt.model = el; From 01c4ddb7906bceff19475a8e973ad1f0ee0d0a5a Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 6 Mar 2016 03:46:31 +0100 Subject: [PATCH 5/8] Add tests --- .gitignore | 1 + src/class_manager/view/ClassTagsView.js | 16 +---- test/specs/class_manager/main.js | 3 + .../specs/class_manager/view/ClassTagsView.js | 68 +++++++++++++++++++ 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 test/specs/class_manager/view/ClassTagsView.js diff --git a/.gitignore b/.gitignore index d8c5ed254..b084abea3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ grapes.sublime-workspace img/ private/ vendor/ +coverage/ node_modules/ bower_components/ diff --git a/src/class_manager/view/ClassTagsView.js b/src/class_manager/view/ClassTagsView.js index ad1b66a35..9be82d303 100644 --- a/src/class_manager/view/ClassTagsView.js +++ b/src/class_manager/view/ClassTagsView.js @@ -7,13 +7,11 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], template: _.template(tagsTemplate), - events:{ - 'click .add': 'startNewClass', - }, + events: {}, initialize: function(o) { this.config = o.config || {}; - this.pfx = this.config.stylePrefix; + this.pfx = this.config.stylePrefix || ''; this.className = this.pfx + 'tags'; this.addBtnId = this.pfx + 'add-tag'; this.newInputId = this.pfx + 'new'; @@ -59,16 +57,6 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], this.$input.hide().val(''); }, - - /** - * Add new class tag - * @param {Object} model - * - */ - addTag: function(model){ - - }, - /** * Triggered when component is changed * @param {Object} e diff --git a/test/specs/class_manager/main.js b/test/specs/class_manager/main.js index bac48981f..ae40d3507 100644 --- a/test/specs/class_manager/main.js +++ b/test/specs/class_manager/main.js @@ -4,12 +4,14 @@ define([ 'ClassManager', modulePath + '/model/ClassModels', modulePath + '/view/ClassTagView', + modulePath + '/view/ClassTagsView', modulePath + '/e2e/ClassManager' ], function( ClassManager, Models, ClassTagView, + ClassTagsView, e2e ) { @@ -84,6 +86,7 @@ define([ Models.run(); ClassTagView.run(); + ClassTagsView.run(); e2e.run(); }); diff --git a/test/specs/class_manager/view/ClassTagsView.js b/test/specs/class_manager/view/ClassTagsView.js new file mode 100644 index 000000000..648f43f9b --- /dev/null +++ b/test/specs/class_manager/view/ClassTagsView.js @@ -0,0 +1,68 @@ +var path = 'ClassManager/view/'; +define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'], + function(ClassTagsView, ClassTags) { + + return { + run : function(){ + describe('ClassTagsView', function() { + + before(function () { + this.$fixtures = $("#fixtures"); + this.$fixture = $('
'); + }); + + beforeEach(function () { + var target = {}; + this.coll = new ClassTags(); + _.extend(target, Backbone.Events); + + this.view = new ClassTagsView({ + config : { target: target }, + collection: this.coll + }); + + this.$fixture.empty().appendTo(this.$fixtures); + this.$fixture.html(this.view.render().el); + this.btnAdd = this.view.$el.find('#' + this.view.addBtnId); + this.input = this.view.$el.find('input#' + this.view.newInputId); + }); + + afterEach(function () { + delete this.view.collection; + }); + + after(function () { + this.$fixture.remove(); + }); + + it('Object exists', function() { + ClassTagsView.should.be.exist; + }); + + it('Add new tag triggers correct method', function() { + sinon.stub(this.view, "addToClasses"); + this.coll.add({ name: 'test' }); + this.view.addToClasses.calledOnce.should.equal(true); + }); + + it('Start new tag creation', function() { + this.btnAdd.click(); + (this.btnAdd.css('display') == 'none').should.equal(true); + (this.input.css('display') !== 'none').should.equal(true); + }); + + it('Stop tag creation', function() { + this.btnAdd.click(); + this.input.val('test') + this.input.blur(); + (this.btnAdd.css('display') !== 'none').should.equal(true); + (this.input.css('display') == 'none').should.equal(true); + this.input.val().should.equal(''); + }); + + //this.$el.find('#' + this.addBtnId); + }); + } + }; + +}); \ No newline at end of file From 16d7bf0d4f52a14b49970bbb55d9abf057052a00 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 7 Mar 2016 02:45:58 +0100 Subject: [PATCH 6/8] Add tests Class Manager --- src/class_manager/template/classTag.html | 2 +- src/class_manager/view/ClassTagsView.js | 28 +++---- .../specs/class_manager/view/ClassTagsView.js | 83 ++++++++++++++++++- 3 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/class_manager/template/classTag.html b/src/class_manager/template/classTag.html index ba7844d0c..1a9156134 100644 --- a/src/class_manager/template/classTag.html +++ b/src/class_manager/template/classTag.html @@ -1,3 +1,3 @@ - <%= label %> + <%= label %> \ No newline at end of file diff --git a/src/class_manager/view/ClassTagsView.js b/src/class_manager/view/ClassTagsView.js index 9be82d303..dab8074cc 100644 --- a/src/class_manager/view/ClassTagsView.js +++ b/src/class_manager/view/ClassTagsView.js @@ -38,7 +38,7 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], }, /** - * Start new tag event + * Start tag creation * @param {Object} e * */ @@ -48,7 +48,7 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], }, /** - * Start new tag event + * End tag creation * @param {Object} e * */ @@ -57,16 +57,6 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], this.$input.hide().val(''); }, - /** - * Triggered when component is changed - * @param {Object} e - */ - componentChanged: function(e){ - this.compTarget = this.target.get('selectedComponent'); - var models = this.compTarget ? this.compTarget.get('classes').models : []; - this.collection.reset(models); - }, - /** * Checks what to do on keyup event * @param {Object} e @@ -76,10 +66,16 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'], this.addNewTag(this.$input.val()); else if(e.keyCode === 27) this.endNewTag(); - else{ - //this.searchItem(); - //console.log('search'); - } + }, + + /** + * Triggered when component is changed + * @param {Object} e + */ + componentChanged: function(e){ + this.compTarget = this.target.get('selectedComponent'); + var models = this.compTarget ? this.compTarget.get('classes').models : []; + this.collection.reset(models); }, /** diff --git a/test/specs/class_manager/view/ClassTagsView.js b/test/specs/class_manager/view/ClassTagsView.js index 648f43f9b..deec76b63 100644 --- a/test/specs/class_manager/view/ClassTagsView.js +++ b/test/specs/class_manager/view/ClassTagsView.js @@ -12,12 +12,12 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'], }); beforeEach(function () { - var target = {}; + this.target = { get: function(){} }; this.coll = new ClassTags(); - _.extend(target, Backbone.Events); + _.extend(this.target, Backbone.Events); this.view = new ClassTagsView({ - config : { target: target }, + config : { target: this.target }, collection: this.coll }); @@ -25,6 +25,7 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'], this.$fixture.html(this.view.render().el); this.btnAdd = this.view.$el.find('#' + this.view.addBtnId); this.input = this.view.$el.find('input#' + this.view.newInputId); + this.$tags = this.$fixture.find('#tags-c'); }); afterEach(function () { @@ -39,6 +40,10 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'], ClassTagsView.should.be.exist; }); + it('Not tags inside', function() { + this.$tags.html().should.equal(''); + }); + it('Add new tag triggers correct method', function() { sinon.stub(this.view, "addToClasses"); this.coll.add({ name: 'test' }); @@ -60,7 +65,77 @@ define([path + 'ClassTagsView', 'ClassManager/model/ClassTags'], this.input.val().should.equal(''); }); - //this.$el.find('#' + this.addBtnId); + it('Check keyup of ESC on input', function() { + this.btnAdd.click(); + sinon.stub(this.view, "addNewTag"); + this.input.trigger({ + type: 'keyup', + keyCode: 13 + }); + this.view.addNewTag.calledOnce.should.equal(true); + }); + + it('Check keyup on ENTER on input', function() { + this.btnAdd.click(); + sinon.stub(this.view, "endNewTag"); + this.input.trigger({ + type: 'keyup', + keyCode: 27 + }); + this.view.endNewTag.calledOnce.should.equal(true); + }); + + it('Collection changes on update of target', function() { + this.coll.add({ name: 'test' }); + this.target.trigger('change:selectedComponent'); + this.coll.length.should.equal(0); + }); + + it('Collection reacts on reset', function() { + this.coll.add([{ name: 'test1' }, { name: 'test2' }]); + sinon.stub(this.view, "addToClasses"); + this.coll.trigger('reset'); + this.view.addToClasses.calledTwice.should.equal(true); + }); + + it("Don't accept empty tags", function() { + this.view.addNewTag(''); + this.$tags.html().should.equal(''); + }); + + 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(){} }; + } + }; + this.view.addNewTag('test'); + this.view.addNewTag('test2'); + this.$tags.children().length.should.equal(2); + }); + + it("New tag correctly added", function() { + this.coll.add({ label: 'test' }); + this.$tags.children().first().find('#tag-label').html().should.equal('test'); + }); + + describe('Should be rendered correctly', function() { + it('Has label', function() { + this.view.$el.find('#label').should.have.property(0); + }); + it('Has tags container', function() { + this.view.$el.find('#tags-c').should.have.property(0); + }); + it('Has add button', function() { + this.view.$el.find('#add-tag').should.have.property(0); + }); + }); + }); } }; From 10ad1067e67e8a81d6e19f0ba7383c24b93971fb Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 8 Mar 2016 02:49:01 +0100 Subject: [PATCH 7/8] Add tests Css Composer --- src/css_composer/config/config.js | 5 +- src/css_composer/main.js | 66 +++++++++-------- src/css_composer/model/CssRule.js | 40 ++++++----- test/runner/main.js | 1 + test/specs/css_composer/main.js | 84 ++++++++++++++++++++++ test/specs/css_composer/model/CssModels.js | 60 ++++++++++++++++ 6 files changed, 207 insertions(+), 49 deletions(-) create mode 100644 test/specs/css_composer/main.js create mode 100644 test/specs/css_composer/model/CssModels.js diff --git a/src/css_composer/config/config.js b/src/css_composer/config/config.js index 7ec604b92..ae35a997a 100644 --- a/src/css_composer/config/config.js +++ b/src/css_composer/config/config.js @@ -2,7 +2,10 @@ define(function () { return { // Style prefix - stylePrefix : 'css-', + stylePrefix: 'css-', + + // Default CSS style + 'default': '', }; }); \ No newline at end of file diff --git a/src/css_composer/main.js b/src/css_composer/main.js index 29a390807..3d6099201 100644 --- a/src/css_composer/main.js +++ b/src/css_composer/main.js @@ -10,6 +10,7 @@ define(function(require) { def = require('./config/config'), CssRule = require('./model/CssRule'), CssRules = require('./model/CssRules'), + Selectors = require('./model/Selectors'), CssRulesView = require('./view/CssRulesView'); for (var name in def) { @@ -19,28 +20,46 @@ define(function(require) { //this.qset = { '' : CssRules, '340px': CssRules }; var rules = new CssRules([]), - rulesView = new CssRulesView({ - collection: rules, - config: c, - }); + rulesView = new CssRulesView({ + collection: rules, + config: c, + }); return { + Selectors: Selectors, + /** - * Add new class to collection only if it's not already exists - * @param {String} name Class name + * Create new rule and return it. Don't add it to the collection + * @param {Array} selectors Array of selectors + * @param {String} state Css rule state + * @param {String} width For which device this style is oriented * - * @return {Object} Model class + * @return {Object} + * */ + newRule: function(selectors, state, width) { + var s = state || ''; + var w = width || ''; + var rule = new CssRule({ + state: s, + maxWidth: w, + }); + rule.get('selectors').add(selectors); + return rule; + }, + + /** + * Add new rule to the collection if not yet exists + * @param {Object} rule + * + * @return {Object} * */ - addRule: function(Rule){ - return rules.add(Rule); - /* - var label = name; - var c = this.getRule(name); - if(!c) - return this.classes.add({name: name, label: label}); - return c; - */ + addRule: function(rule){ + var models = rule.get('selectors').models; + var r = this.getRule(models, rule.get('state'), rule.get('maxWidth')); + if(!r) + r = rules.add(rule); + return r; }, /** @@ -57,7 +76,6 @@ define(function(require) { rules.each(function(rule){ if(fRule) return; - var sel = _.pluck(rule.get('selectors').models, 'cid'); if(this.same(req, sel)) fRule = rule; @@ -65,20 +83,6 @@ define(function(require) { return fRule; }, - /** - * Create new rule - * @param {Array} selectors Array of selectors - * @param {String} state Rule status - * @param {String} set Query set - * - * @return {Object} - * */ - newRule: function(selectors, state, set) { - var rule = new CssRule({ state: state }); - rule.get('selectors').add(selectors); - return rule; - }, - /** * Compare 2 arrays to check if are the same * @param {Array} arr1 diff --git a/src/css_composer/model/CssRule.js b/src/css_composer/model/CssRule.js index 54586aa5f..169adfeb4 100644 --- a/src/css_composer/model/CssRule.js +++ b/src/css_composer/model/CssRule.js @@ -1,22 +1,28 @@ define(['backbone', './Selectors'], - function (Backbone, Selectors) { - /** - * @class CssRule - * */ - return Backbone.Model.extend({ + function (Backbone, Selectors) { + /** + * @class CssRule + * */ + return Backbone.Model.extend({ - defaults: { - selectors: {}, - style: {}, - stylable: true, - state: '', - }, + defaults: { + // Css selectors + selectors: {}, + // Css properties style + style: {}, + // On which device width this rule should be rendered, eg. @media (max-width: 1000px) + maxWidth: '', + // State of the rule, eg: hover | pressed | focused + state: '', + // Indicates if the rule is stylable + stylable: true, + }, - initialize: function(c, opt) { - this.config = c || {}; - this.slct = this.config.selectors || []; - this.set('selectors', new Selectors(this.slct)); - }, + initialize: function(c, opt) { + this.config = c || {}; + this.slct = this.config.selectors || []; + this.set('selectors', new Selectors(this.slct)); + }, - }); + }); }); diff --git a/test/runner/main.js b/test/runner/main.js index 24824b47e..45ac312fe 100644 --- a/test/runner/main.js +++ b/test/runner/main.js @@ -13,6 +13,7 @@ require(['../src/config/require-config.js', 'config/config.js'], function() { 'specs/asset_manager/view/FileUploader.js', 'specs/dom_components/main.js', 'specs/class_manager/main.js', + 'specs/css_composer/main.js', ], function(chai) { var should = chai.should(), diff --git a/test/specs/css_composer/main.js b/test/specs/css_composer/main.js new file mode 100644 index 000000000..06a8c7e9b --- /dev/null +++ b/test/specs/css_composer/main.js @@ -0,0 +1,84 @@ +var modulePath = './../../../test/specs/css_composer'; + +define([ + 'CssComposer', + modulePath + '/model/CssModels' + ], + function( + CssComposer, + Models, + Selectors + ) { + + describe('Css Composer', function() { + + describe('Main', function() { + + beforeEach(function () { + this.obj = new CssComposer(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Object exists', function() { + CssComposer.should.be.exist; + }); + + it("Rules are empty", function() { + this.obj.getRules().length.should.equal(0); + }); + + it('Create new rule with correct selectors', function() { + var sel = new this.obj.Selectors(); + var s1 = sel.add({name: 'test1'}); + var rule = this.obj.newRule(sel.models); + rule.get('selectors').at(0).should.deep.equal(s1); + }); + + it('Create new rule correctly', function() { + var sel = new this.obj.Selectors(); + var s1 = sel.add({name: 'test1'}); + var rule = this.obj.newRule(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 this.obj.Selectors([{name: 'test1'}]); + var rule = this.obj.newRule(sel.models, 'state1', 'width1'); + this.obj.addRule(rule); + this.obj.getRules().length.should.equal(1); + this.obj.getRules().at(0).get('selectors').at(0).get('name').should.equal('test1'); + }); + +/* + it("Don't duplicate rules", function() { + var sel1 = new this.obj.Selectors([{name: 'test1'}]); + var rule1 = this.obj.newRule(sel1.models, 'state1', 'width1'); + var aRule1 = this.obj.addRule(rule1); + + var sel2 = new this.obj.Selectors([{name: 'test1'}]); + var rule2 = this.obj.newRule(sel2.models, 'state1', 'width1'); + var aRule2 = this.obj.addRule(rule2); + + console.log(sel1); + console.log(rule1); + console.log(aRule1); + console.log(sel2); + console.log(rule2); + console.log(aRule2); + console.log(this.obj.getRules().length); + + aRule1.should.deep.equal(aRule2); + + }); +*/ + + }); + + Models.run(); + + }); +}); \ No newline at end of file diff --git a/test/specs/css_composer/model/CssModels.js b/test/specs/css_composer/model/CssModels.js new file mode 100644 index 000000000..4eb223da2 --- /dev/null +++ b/test/specs/css_composer/model/CssModels.js @@ -0,0 +1,60 @@ +var path = 'CssComposer/model/'; +define([path + 'CssRule', + path + 'CssRules', + path + 'Selectors', + 'ClassManager/model/ClassTag'], + function(CssRule, CssRules, Selectors, ClassTag) { + + return { + run : function(){ + describe('CssRule', function() { + + beforeEach(function () { + this.obj = new CssRule(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Has selectors property', function() { + this.obj.has('selectors').should.equal(true); + }); + + it('Has style property', function() { + this.obj.has('style').should.equal(true); + }); + + it('Has state property', function() { + this.obj.has('state').should.equal(true); + }); + + it('No default selectors', function() { + this.obj.get('selectors').length.should.equal(0); + }); + + }); + + describe('CssRules', function() { + + it('Creates collection item correctly', function() { + var c = new CssRules(); + var m = c.add({}); + m.should.be.an.instanceOf(CssRule); + }); + + }); + + describe('Selectors', function() { + + it('Creates collection item correctly', function() { + var c = new Selectors(); + var m = c.add({}); + m.should.be.an.instanceOf(ClassTag); + }); + + }); + } + }; + +}); \ No newline at end of file From 9c056947b802d5df18bbdbe73131e9a3f5e46721 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 8 Mar 2016 09:07:50 +0100 Subject: [PATCH 8/8] Bump version --- bower.json | 2 +- package.json | 2 +- src/css_composer/view/CssRulesView.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 298b06499..1b459e631 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "grapesjs", "description": "Open source Web Template Editor", - "version": "0.1.2", + "version": "0.1.5", "author": "Artur Arseniev", "homepage": "http://grapesjs.com", "main": [ diff --git a/package.json b/package.json index ab5c98133..dc450b412 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "grapesjs", "description": "Open source Web Template Editor", - "version": "0.1.2", + "version": "0.1.5", "author": "Artur Arseniev", "license": "BSD-3-Clause", "homepage": "http://grapesjs.com", diff --git a/src/css_composer/view/CssRulesView.js b/src/css_composer/view/CssRulesView.js index c1c6f9768..f08bb24bb 100644 --- a/src/css_composer/view/CssRulesView.js +++ b/src/css_composer/view/CssRulesView.js @@ -18,7 +18,7 @@ define(['backbone','./CssRuleView'], * @param {Object} model * */ addTo: function(model){ - console.log('Added'); + //console.log('Added'); this.addToCollection(model); },