Browse Source

Merge pull request #14 from artf/dev

Added new feature Class Manager and tests
Fixed a number of issues
pull/15/head
Artur Arseniev 10 years ago
parent
commit
bc41cc4c73
  1. 1
      .gitignore
  2. 2
      README.md
  3. 2
      bower.json
  4. 2
      dist/css/grapes.min.css
  5. 28
      dist/grapes.min.js
  6. 3
      package.json
  7. 42
      src/asset_manager/view/AssetsView.js
  8. 11
      src/class_manager/config/config.js
  9. 61
      src/class_manager/main.js
  10. 15
      src/class_manager/model/ClassTag.js
  11. 4
      src/class_manager/template/classTag.html
  12. 16
      src/class_manager/template/classTags.html
  13. 39
      src/class_manager/view/ClassTagView.js
  14. 66
      src/class_manager/view/ClassTagsView.js
  15. 102
      src/code_manager/main.js
  16. 87
      src/code_manager/model/CssGenerator.js
  17. 27
      src/code_manager/model/HtmlGenerator.js
  18. 3
      src/commands/view/ExportTemplate.js
  19. 7
      src/commands/view/OpenStyleManager.js
  20. 1
      src/config/require-config.js
  21. 14
      src/css_composer/config/config.js
  22. 106
      src/css_composer/main.js
  23. 75
      src/css_composer/model/CssRule.js
  24. 27
      src/css_composer/model/CssRules.js
  25. 29
      src/css_composer/model/Selectors.js
  26. 56
      src/css_composer/view/CssRuleView.js
  27. 63
      src/css_composer/view/CssRulesView.js
  28. 2
      src/dom_components/main.js
  29. 42
      src/dom_components/model/Component.js
  30. 25
      src/dom_components/model/Components.js
  31. 1
      src/dom_components/view/ComponentImageView.js
  32. 1
      src/dom_components/view/ComponentTextView.js
  33. 16
      src/dom_components/view/ComponentView.js
  34. 3
      src/editor/config/config.js
  35. 2
      src/editor/main.js
  36. 115
      src/editor/model/Editor.js
  37. 26
      src/editor/view/EditorView.js
  38. 18
      src/storage_manager/main.js
  39. 46
      src/style_manager/view/PropertiesView.js
  40. 37
      src/style_manager/view/PropertyCompositeView.js
  41. 110
      src/style_manager/view/PropertyView.js
  42. 2
      src/style_manager/view/SectorView.js
  43. 41
      src/style_manager/view/SectorsView.js
  44. 59
      styles/css/main.css
  45. 30
      styles/scss/main.scss
  46. 3
      test/runner/main.js
  47. 130
      test/specs/class_manager/e2e/ClassManager.js
  48. 93
      test/specs/class_manager/main.js
  49. 53
      test/specs/class_manager/model/ClassModels.js
  50. 117
      test/specs/class_manager/view/ClassTagView.js
  51. 157
      test/specs/class_manager/view/ClassTagsView.js
  52. 39
      test/specs/code_manager/main.js
  53. 151
      test/specs/code_manager/model/CodeModels.js
  54. 82
      test/specs/css_composer/e2e/CssComposer.js
  55. 143
      test/specs/css_composer/main.js
  56. 84
      test/specs/css_composer/model/CssModels.js
  57. 101
      test/specs/css_composer/view/CssRuleView.js
  58. 54
      test/specs/css_composer/view/CssRulesView.js
  59. 25
      test/specs/panel/model/panelModel.js
  60. 49
      test/specs/panel/view/panelView.js

1
.gitignore

@ -10,5 +10,6 @@ grapes.sublime-workspace
img/
private/
vendor/
coverage/
node_modules/
bower_components/

2
README.md

@ -170,7 +170,7 @@ $ npm run test
## Todos before beta release
* **Class Manager** (*in development*) - Ability to assign different classes to components and style them (beacause CSS with only ids is pretty much a pain)
* **Class Manager** (*in development*) - Ability to assign different classes to components and style them (because CSS with only ids is pretty much a pain)
* **Breakpoint Manager** - Resize canvas according to breakpoints established by user (in simple terms, for responsive templates). Will be put into development immediately after Class Manager
* **Style Manager improvements** - Mainly `stack` type is not yet complete

2
bower.json

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

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

28
dist/grapes.min.js

File diff suppressed because one or more lines are too long

3
package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.1.2",
"version": "0.1.7",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",
@ -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": [

42
src/asset_manager/view/AssetsView.js

@ -1,10 +1,10 @@
define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
function (Backbone, AssetView, AssetImageView, FileUploader) {
/**
/**
* @class AssetsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.options = o;
this.config = o.config;
@ -12,7 +12,7 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
this.listenTo( this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll );
this.className = this.pfx + 'assets';
// Check if storage is required and if Storage Manager is available
if(this.config.stm && this.config.storageType !== ''){
var type = this.config.storageType;
@ -30,20 +30,20 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
}
}
},
/**
* Store collection
*
*
* @return void
* */
store: function(){
if(this.storagePrv)
this.storagePrv.store(this.storeName, JSON.stringify(this.collection.toJSON()) );
},
/**
* Load collection
*
*
* @return {Object}
* */
load: function(){
@ -59,63 +59,63 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
}
return result;
},
/**
* Add asset to collection
* */
addToAsset: function(model){
this.addAsset(model);
},
/**
* Add new asset to collection
* @param Object Model
* @param Object Fragment collection
*
*
* @return Object Object created
* */
addAsset: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = AssetView;
if(model.get('type').indexOf("image") > -1)
viewObject = AssetImageView;
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild( rendered );
}else{
this.$el.prepend(rendered);
}
return rendered;
},
/**
* Deselect all assets
*
*
* @return void
* */
deselectAll: function(){
this.$el.find('.' + this.pfx + 'highlight').removeClass(this.pfx + 'highlight');
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addAsset(model, fragment);
},this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
return this;
}
});

11
src/class_manager/config/config.js

@ -5,13 +5,20 @@ define(function () {
stylePrefix : 'clm-',
// Default classes
classes : [],
defaults : [],
// Label for classes
label: 'Classes',
// Label for states
statesLabel: 'States',
statesLabel: '- State -',
// States
states: [
{ name: 'hover', label: 'Hover' },
{ name: 'active', label: 'Click' },
{ name: 'nth-of-type(2n)', label: 'Even/Odd' }
],
};
});

61
src/class_manager/main.js

@ -7,38 +7,38 @@ define(function(require) {
var ClassManager = function(config)
{
var c = config || {},
def = require('./config/config'),
ClassTags = require('./model/ClassTags'),
ClassTagsView = require('./view/ClassTagsView');
def = require('./config/config');
this.ClassTags = require('./model/ClassTags');
this.ClassTagsView = require('./view/ClassTagsView');
for (var name in def) {
if (!(name in c))
c[name] = def[name];
}
this.classes = new ClassTags(c.classes);
var obj = {
collection: this.classes,
config: c,
};
this.tagsView = new ClassTagsView(obj);
//this.ClassTagsView = new ClassTagsView(obj);
this.classes = new this.ClassTags(c.defaults);
this.config = c;
};
ClassManager.prototype = {
/**
* Get collection of classes
* Add new class to collection only if it's not already exists
* @param {String} name Class name
*
* @return {Object}
* @return {Object} Model class
* */
getClasses : function() {
return this.classes;
addClass: 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 {String} id Class name
*
* @return {Object|null}
* */
@ -48,35 +48,14 @@ define(function(require) {
},
/**
* Add new class to collection only if it's not already exists
* @param {String} name Class name
* @return {Object} Model class
* Get collection of classes
*
* @return {Object}
* */
addClass: function(name){
var label = name;
var fname = this.escapeName(name);
var c = this.getClass(fname);
if(!c)
return this.classes.add({name: fname, label: label});
return c;
},
/**
* Escape string
* @param {String} name
* @return {String}
*/
escapeName: function(name) {
return name.toLowerCase().replace(/([^a-z0-9\w]+)/gi, '-');
getClasses : function() {
return this.classes;
},
/**
* Renders a collection of class tags. Useful for components classes
* @return {Object} Rendered view
*/
renderTags: function(){
return this.tagsView.render().el;
},
};
return ClassManager;

15
src/class_manager/model/ClassTag.js

@ -8,7 +8,22 @@ define(['backbone'],
defaults: {
label: '',
name: '',
active: true,
},
initialize: function(){
this.set('name', this.escapeName(this.get('name')));
},
/**
* Escape string
* @param {String} name
*
* @return {String}
*/
escapeName: function(name) {
return name.toLowerCase().replace(/([^a-z0-9\w]+)/gi, '-');
},
});
});

4
src/class_manager/template/classTag.html

@ -1 +1,3 @@
<%= label %> <span id="<%= pfx %>close">&Cross;</span>
<span id="<%= pfx %>checkbox" class="fa"></span>
<span id="<%= pfx %>tag-label"><%= label %></span>
<span id="<%= pfx %>close">&Cross;</span>

16
src/class_manager/template/classTags.html

@ -1,8 +1,20 @@
<div id="<%= pfx %>up">
<div id="<%= pfx %>label"><%= label %></div>
<div id="<%= pfx %>status-c">
<%= statesLabel %>
<span id="<%= pfx %>input-c"></span>
<!--
<span id="<%= pfx %>input-c">
<div class="<%= pfx %>field <%= pfx %>select">
<span id="<%= pfx %>input-holder">
<select id="<%= pfx %>states">
<option value=""><%= statesLabel %></option>
</select>
</span>
<div class="<%= pfx %>sel-arrow">
<div class="<%= pfx %>d-s-arrow"></div>
</div>
</div>
</span>
-->
</div>
</div>
<div id="<%= pfx %>tags-field">

39
src/class_manager/view/ClassTagView.js

@ -11,33 +11,68 @@ define(['backbone', 'text!./../template/classTag.html'],
initialize: function(o) {
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
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';
this.labelId = this.pfx + 'tag-label';
this.events['click #' + this.closeId ] = 'removeTag';
this.events['click #' + this.chkId ] = 'changeStatus';
this.events['click #' + this.labelId ] = 'changeStatus';
this.listenTo( this.model, 'change:active', this.updateStatus);
this.delegateEvents();
},
changeStatus: function(){
this.model.set('active', !this.model.get('active'));
this.target.trigger('targetClassUpdated');
},
/**
* Remove tag from the selected component
* @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){
this.coll.remove(this.model);
this.target.trigger('targetClassRemoved');
}
this.remove();
},
/**
* Update status of the checkbox
*/
updateStatus: function(){
if(!this.$chk)
this.$chk = this.$el.find('#' + this.pfx + 'checkbox');
if(this.model.get('active')){
this.$chk.removeClass('fa-circle-o').addClass('fa-dot-circle-o');
this.$el.removeClass('opac50');
}else{
this.$chk.removeClass('fa-dot-circle-o').addClass('fa-circle-o');
this.$el.addClass('opac50');
}
},
/** @inheritdoc */
render : function(){
this.$el.html( this.template({
label: this.model.get('label'),
pfx: this.pfx,
}));
this.updateStatus();
this.$el.attr('class', this.className);
return this;
},

66
src/class_manager/view/ClassTagsView.js

@ -7,19 +7,20 @@ 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';
this.stateInputId = this.pfx + 'states';
this.states = this.config.states || [];
this.events['click #' + this.addBtnId] = 'startNewTag';
this.events['blur #' + this.newInputId] = 'endNewTag';
this.events['keyup #' + this.newInputId] = 'onInputKeyUp';
this.events['change #' + this.stateInputId] = 'stateChanged';
this.target = this.config.target;
@ -31,6 +32,18 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
this.delegateEvents();
},
/**
* Create select input with states
* @return {string} String of options
*/
getStateOptions: function(){
var strInput = '';
for(var i = 0; i < this.states.length; i++){
strInput += '<option value="' + this.states[i].name + '">' + this.states[i].label + '</option>';
}
return strInput;
},
/**
* Add new model
* @param {Object} model
@ -40,7 +53,7 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
},
/**
* Start new tag event
* Start tag creation
* @param {Object} e
*
*/
@ -50,7 +63,7 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
},
/**
* Start new tag event
* End tag creation
* @param {Object} e
*
*/
@ -59,14 +72,15 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
this.$input.hide().val('');
},
/**
* Add new class tag
* @param {Object} model
*
* Checks what to do on keyup event
* @param {Object} e
*/
addTag: function(model){
onInputKeyUp: function(e) {
if (e.keyCode === 13)
this.addNewTag(this.$input.val());
else if(e.keyCode === 27)
this.endNewTag();
},
/**
@ -77,21 +91,15 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
this.compTarget = this.target.get('selectedComponent');
var models = this.compTarget ? this.compTarget.get('classes').models : [];
this.collection.reset(models);
//TODO no classes, hide states
},
/**
* Checks what to do on keyup event
* Triggered when select with states is changed
* @param {Object} e
*/
onInputKeyUp: function(e) {
if (e.keyCode === 13)
this.addNewTag(this.$input.val());
else if(e.keyCode === 27)
this.endNewTag();
else{
//this.searchItem();
//console.log('search');
}
stateChanged: function(e){
console.log(this.$states.val());
},
/**
@ -106,9 +114,16 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
var cm = this.target.get('ClassManager');
var model = cm.addClass(name);
if(this.compTarget)
this.compTarget.get('classes').add(model);
if(this.compTarget){
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();
},
@ -126,6 +141,7 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
var view = new ClassTagView({
model: model,
config: this.config,
coll: this.collection,
});
var rendered = view.render().el;
@ -165,6 +181,8 @@ define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
this.$input = this.$el.find('input#' + this.newInputId);
this.$addBtn = this.$el.find('#' + this.addBtnId);
this.$classes = this.$el.find('#' + this.pfx + 'tags-c');
this.$states = this.$el.find('#' + this.stateInputId);
this.$states.append(this.getStateOptions());
this.renderClasses();
this.$el.attr('class', this.className);
return this;

102
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;
});

87
src/code_manager/model/CssGenerator.js

@ -1,44 +1,89 @@
define(['backbone'],
define(['backbone'],
function (Backbone) {
/**
* @class CssGenerator
* */
return Backbone.Model.extend({
initialize: function(){
this.compCls = [];
},
/** @inheritdoc */
getId : function()
{
return 'css';
return 'css';
},
/** @inheritdoc */
build: function(model)
{
/**
* Get CSS from components
* @return {String}
*/
buildFromComp: function(model){
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');
cls.each(function(m){
this.compCls.push(m.get('name'));
}, this);
if(css && Object.keys(css).length !== 0){
code += '#' + m.cid + '{';
for(var prop in css)
for(var prop in css){
if(css.hasOwnProperty(prop))
code += prop + ': ' + css[prop] + ';';
code += prop + ':' + css[prop] + ';';
}
code += '}';
}
if(cln.length)
code += this.build(cln);
code += this.buildFromComp(cln);
}, this);
return code;
},
/** @inheritdoc */
build: function(model, cssc)
{
this.compCls = [];
var code = this.buildFromComp(model);
var compCls = this.compCls;
if(cssc){
var rules = cssc.getRules();
rules.each(function(rule){
var selectors = rule.get('selectors');
var ruleStyle = rule.get('style');
var strSel = '';
var found = 0;
selectors.each(function(selector){
strSel += '.' + selector.get('name');
if(compCls.indexOf(selector.get('name')) > -1)
found = 1;
});
if(strSel && found){
var strStyle = '';
if(ruleStyle && Object.keys(ruleStyle).length !== 0){
for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
}
}
if(strStyle)
code += strSel + '{' + strStyle + '}';
}
});
}
return code;
},
});
});

27
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);

3
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,7 @@ define(function() {
}
this.htmlEditor.setContent( this.cm.getCode(this.components, 'html') );
this.cssEditor.setContent( this.cm.getCode(this.components, 'css') );
this.cssEditor.setContent( this.cm.getCode(this.components, 'css', this.cssc));
if(this.sender)
this.sender.set('active',false);

7
src/commands/view/OpenStyleManager.js

@ -24,8 +24,11 @@ define(['StyleManager'], function(StyleManager) {
// Class Manager container
this.clm = em.get('ClassManager');
if(this.clm){
//this.$clm = this.clm.renderTags();
//this.$cn2.append(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

1
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', }

14
src/css_composer/config/config.js

@ -0,0 +1,14 @@
define(function () {
return {
// Style prefix
stylePrefix: 'css-',
// Custom CSS string to render on top
'staticRules': '',
// Default CSS style
'defaults': [],
};
});

106
src/css_composer/main.js

@ -0,0 +1,106 @@
define(function(require) {
/**
* @class CssComposer
* @param {Object} config Configurations
*
* */
var CssComposer = function(config)
{
var c = config || {},
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) {
if (!(name in c))
c[name] = def[name];
}
var rules = new CssRules(c.defaults, c),
rulesView = new CssRulesView({
collection: rules,
config: c,
});
return {
Selectors: Selectors,
/**
* 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}
* */
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){
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;
},
/**
* Get class by its name
* @param {Array} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
*
* @return {Object|null}
* */
getRule : function(selectors, state, width) {
fRule = null;
rules.each(function(rule){
if(fRule)
return;
if(rule.compare(selectors, state, width))
fRule = rule;
}, this);
return fRule;
},
/**
* Get collection of css rules
*
* @return {Object}
* */
getRules : function() {
return rules;
},
/**
* Render block of CSS rules
*
* @return {Object}
*/
render: function(){
return rulesView.render().el;
}
};
};
return CssComposer;
});

75
src/css_composer/model/CssRule.js

@ -0,0 +1,75 @@
define(['backbone', './Selectors'],
function (Backbone, Selectors) {
/**
* @class CssRule
* */
return Backbone.Model.extend({
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.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));
},
/**
* Compare the actual model with parameters
* @param {Object} selectors Collection of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
*
* @return {Boolean}
*/
compare: function(selectors, state, width){
var st = state || '';
var wd = width || '';
var cId = 'cid';
var a1 = _.pluck(selectors.models || selectors, cId);
var a2 = _.pluck(this.get('selectors').models, cId);
var f = false;
if(a1.length !== a2.length)
return f;
for (var i = 0; i < a1.length; i++) {
var re = 0;
for (var j = 0; j < a2.length; j++) {
if (a1[i] === a2[j])
re = 1;
}
if(re === 0)
return f;
}
if(this.get('state') !== st)
return f;
if(this.get('maxWidth') !== wd)
return f;
return true;
},
});
});

27
src/css_composer/model/CssRules.js

@ -0,0 +1,27 @@
define(['backbone','./CssRule'],
function (Backbone, CssRule) {
/**
* @class CssRules
* */
return Backbone.Collection.extend({
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;
};
},
});
});

29
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;
};
},
});
});

56
src/css_composer/view/CssRuleView.js

@ -0,0 +1,56 @@
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('');
},
render : function(){
var block = '',
o = '';
if(!this.selStr)
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
if(this.selStr)
block = prpStr !== '' ? '{' + prpStr + '}' : '';
o = this.selStr && block ? this.selStr + block : '';
this.$el.html(o);
return this;
},
});
});

63
src/css_composer/view/CssRulesView.js

@ -0,0 +1,63 @@
define(['backbone','./CssRuleView'],
function (Backbone, CssRuleView) {
/**
* @class CssRulesView
* */
return Backbone.View.extend({
initialize: function(o) {
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 );
},
/**
* 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);
this.$el.attr('class', this.className);
return this;
}
});
});

2
src/dom_components/main.js

@ -35,8 +35,8 @@ define(function(require) {
c.wrapper.style = {};
c.wrapper.style.position = 'relative';
this.component = new Component(c.wrapper, { sm: c.em });
this.component = new Component(c.wrapper);
var obj = {
model: this.component,
config: c,

42
src/dom_components/model/Component.js

@ -22,15 +22,46 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
attributes : {},
},
initialize: function(o) {
initialize: function(o, opt) {
this.sm = opt ? opt.sm || {} : {};
this.config = o || {};
this.defaultC = this.config.components || [];
this.defaultCl = this.config.classes || [];
this.components = new Components(this.defaultC);
this.defaultCl = this.normalizeClasses(this.config.classes || []);
this.components = new Components(this.defaultC, opt);
this.set('components', this.components);
this.set('classes', new ClassTags(this.defaultCl));
},
/**
* Normalize input classes from array to array of objects
* @param {Array} arr
*
* @return {Array}
*/
normalizeClasses: function(arr){
var res = [];
if(!this.sm.get)
return;
var clm = this.sm.get('ClassManager');
if(!clm)
return;
arr.forEach(function(val){
var name = '';
if(typeof val === 'string')
name = val;
else
name = val.name;
var model = clm.addClass(name);
res.push(model);
});
return res;
},
/**
* Override original clone method
*/
@ -48,10 +79,11 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
}
if(cls.length){
cls.each(function(md,i){
attr.classes[i] = md.clone();
attr.classes[i] = md.get('name');
});
}
return new this.constructor(attr);
attr.status = '';
return new this.constructor(attr, {sm: this.sm});
},
/**

25
src/dom_components/model/Components.js

@ -1,16 +1,19 @@
define([ 'backbone', 'require'],
define([ 'backbone', 'require'],
function (Backbone, require) {
/**
* @class Components
* */
return Backbone.Collection.extend({
initialize: function(models, opt){
this.model = function(attrs, options) {
var model;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
switch(attrs.type){
case 'text':
@ -18,24 +21,24 @@ define([ 'backbone', 'require'],
this.mComponentText = require("./ComponentText");
model = new this.mComponentText(attrs, options);
break;
case 'image':
if(!this.mComponentImage)
this.mComponentImage = require("./ComponentImage");
model = new this.mComponentImage(attrs, options);
break;
default:
if(!this.mComponent)
this.mComponent = require("./Component");
model = new this.mComponent(attrs, options);
}
return model;
};
},
});
});

1
src/dom_components/view/ComponentImageView.js

@ -59,6 +59,7 @@ define(['backbone', './ComponentView'],
render: function() {
this.updateAttributes();
this.updateClasses();
return this;
},
});

1
src/dom_components/view/ComponentTextView.js

@ -66,6 +66,7 @@ define(['backbone', './ComponentView'],
render: function() {
this.updateAttributes();
this.updateClasses();
this.$el.html(this.model.get('content'));
return this;
},

16
src/dom_components/view/ComponentView.js

@ -26,6 +26,22 @@ define(['backbone', './ComponentsView'],
this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses);
this.$el.data("model", this.model);
this.$el.data("model-comp", this.components);
if(this.model.get('classes').length)
this.importClasses();
},
/**
* Import, if possible, classes inside main container
* */
importClasses: function(){
var clm = this.config.em.get('ClassManager');
if(clm){
this.model.get('classes').each(function(m){
clm.addClass(m.get('name'));
});
}
},
/**

3
src/editor/config/config.js

@ -64,6 +64,9 @@ define(function () {
//Configurations for Class Manager
classManager : {},
//Configurations for Css Composer
cssComposer : {},
};
return config;
});

2
src/editor/main.js

@ -23,7 +23,7 @@ define(function (require){
config : c,
};
this.editorView = new EditorView(obj);
this.editorView = new EditorView(obj);
};
Grapes.prototype = {

115
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,
@ -39,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();
@ -53,15 +57,76 @@ 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,
df = this.loadRules();
pfx = cfg.stylePrefix || 'css-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
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;
@ -310,10 +375,12 @@ define([
*
* @return {Object}
* */
loadComponents: function(){
loadComponents: function() {
var result = null;
try{
result = JSON.parse(this.stm.load(this.compName));
var r = this.stm.load(this.compName);
if(r)
result = JSON.parse(r);
}catch(err){
console.warn("Error encountered while parsing JSON response");
}
@ -325,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');
@ -333,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
@ -340,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;
@ -349,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);
@ -370,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){

26
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();

18
src/storage_manager/main.js

@ -153,20 +153,30 @@ define(function(require) {
* @param {String} name Name of the resource
* @param {String} value Value of the resource
*
* @return {Object}|void
* @return {Object|null}
* */
store : function(name, value){
return this.getCurrentProvider().store(name, value);
var pv = this.getCurrentProvider();
if(pv)
return pv.store(name, value);
return;
},
/**
* Load resource
* @param {String} name Name of the resource
*
* @return {Object}|void
* @return {Object|null}
* */
load : function(name){
return this.getCurrentProvider().load(name);
var pv = this.getCurrentProvider();
if(pv)
return pv.load(name);
return;
},
/**

46
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);

37
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 = $('<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){

110
src/style_manager/view/PropertyView.js

@ -5,72 +5,61 @@ 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',
},
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.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 || {};
this.func = this.model.get('functionName');
this.defaultValue = this.model.get('defaults');
this.property = this.model.get('property');
this.units = this.model.get('units');
this.min = this.model.get('min') || this.model.get('min')===0 ? this.model.get('min') : -5000;
this.max = this.model.get('max') || this.model.get('max')===0 ? this.model.get('max') : 5000;
this.unit = this.model.get('unit') ? this.model.get('unit') : (this.units.length ? this.units[0] : '');
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.target ,'change:selectedComponent',this.componentSelected);
this.func = this.model.get('functionName');
this.defaultValue = this.model.get('defaults');
this.property = this.model.get('property');
this.units = this.model.get('units');
this.min = this.model.get('min') || this.model.get('min')===0 ? this.model.get('min') : -5000;
this.max = this.model.get('max') || this.model.get('max')===0 ? this.model.get('max') : 5000;
this.unit = this.model.get('unit') ? this.model.get('unit') : (this.units.length ? this.units[0] : '');
this.list = this.model.get('list');
this.input = this.$input = null;
this.className = this.pfx + 'property';
this.listenTo( this.propTarget, 'update', this.targetUpdated);
this.listenTo( this.model ,'change:value', this.valueChanged);
},
/**
* Rerender property for the new selected component, if necessary
* @param Array [Model, value, options]
*
* @return void
* 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'));
return this.getComponentValue() == (this.model.get('value') + this.model.get('unit'));
},
/**
* Get the value from the selected component of this property
*
* @return string
* @return {String}
* */
getComponentValue: function(){
if(!this.selectedComponent)
@ -79,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){
@ -88,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;
}
@ -101,10 +90,10 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
},
/**
* Fetch string from function type value
* @param string Function type value
* Fetch the string from function type value
* @param {String} v Function type value
*
* @return string
* @return {String}
* */
fetchFromFunction: function(v){
return v.substring(v.indexOf("(") + 1, v.lastIndexOf(")"));
@ -113,10 +102,8 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
/**
* Property was changed, so I need to update the component too
* @param {Object} e Events
* @param {Mixed} val Value
* @param {Object} opt Options
*
* @return void
* @param {Mixed} val Value
* @param {Object} opt Options
* */
valueChanged: function(e, val, opt){
if(!this.selectedComponent)
@ -147,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);
@ -156,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;
@ -168,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({
@ -187,8 +171,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
/**
* Render field property
*
* @return void
* */
renderField : function() {
this.renderTemplate();
@ -198,8 +180,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
/**
* Render loaded template
*
* @return void
* */
renderTemplate: function(){
this.$el.append( this.template({
@ -212,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')+"'");
@ -221,8 +199,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
/**
* Request to render input of the property
*
* @return void
* */
renderInputRequest: function(){
this.renderInput();
@ -234,8 +210,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html'],
/**
* Clean input
*
* @return void
* */
cleanValue: function(){
this.setValue('');

2
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);

41
src/style_manager/view/SectorsView.js

@ -9,6 +9,46 @@ 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 targetClassAdded targetClassRemoved targetClassUpdated', this.targetUpdated);
},
/**
* Fired when target is updated
*/
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, '', '');
if(!iContainer){
iContainer = cssC.newRule(valid, '', '');
// 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() {
@ -21,6 +61,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);

59
styles/css/main.css

@ -2587,7 +2587,7 @@ html, body, #wte-app, .wte-editor {
.clear {
clear: both; }
.no-select, .wte-com-no-select, .wte-com-no-select img, .wte-clm-tags #wte-clm-close {
.no-select, .wte-com-no-select, .wte-com-no-select img, .wte-clm-tags #wte-clm-close, .wte-clm-tags #wte-clm-tag-label {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
@ -2967,63 +2967,63 @@ ol.example li.placeholder:before {
cursor: pointer; }
.wte-sm-sector .wte-sm-label, .wte-clm-tags .wte-sm-label {
margin: 5px 5px 2px 0; }
.wte-sm-sector .wte-sm-field, .wte-clm-tags .wte-sm-field {
.wte-sm-sector .wte-sm-field, .wte-clm-tags .wte-sm-field, .wte-sm-sector .wte-clm-field, .wte-clm-tags .wte-clm-field {
width: 100%;
position: relative; }
.wte-sm-sector .wte-sm-field input, .wte-clm-tags .wte-sm-field input {
.wte-sm-sector .wte-sm-field input, .wte-clm-tags .wte-sm-field input, .wte-sm-sector .wte-clm-field input, .wte-clm-tags .wte-clm-field input {
box-sizing: border-box;
color: #d5d5d5;
background: none;
border: none;
padding: 3px 21px 3px 0;
width: 100%; }
.wte-sm-sector .wte-sm-field select, .wte-clm-tags .wte-sm-field select {
.wte-sm-sector .wte-sm-field select, .wte-clm-tags .wte-sm-field select, .wte-sm-sector .wte-clm-field select, .wte-clm-tags .wte-clm-field select {
background: none;
border: none;
color: #d5d5d5;
color: transparent;
width: 100%;
padding: 2px 0;
padding: 2px 10px 2px 0;
text-shadow: 0 0 0 #d5d5d5;
position: relative;
z-index: 1;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none; }
.wte-sm-sector .wte-sm-field select::-ms-expand, .wte-clm-tags .wte-sm-field select::-ms-expand {
.wte-sm-sector .wte-sm-field select::-ms-expand, .wte-clm-tags .wte-sm-field select::-ms-expand, .wte-sm-sector .wte-clm-field select::-ms-expand, .wte-clm-tags .wte-clm-field select::-ms-expand {
display: none; }
.wte-sm-sector .wte-sm-field .wte-sm-unit, .wte-clm-tags .wte-sm-field .wte-sm-unit {
.wte-sm-sector .wte-sm-field .wte-sm-unit, .wte-clm-tags .wte-sm-field .wte-sm-unit, .wte-sm-sector .wte-clm-field .wte-sm-unit, .wte-clm-tags .wte-clm-field .wte-sm-unit {
position: absolute;
right: 10px;
top: 3px;
font-size: 10px;
color: #b1b1b1;
cursor: pointer; }
.wte-sm-sector .wte-sm-field .wte-sm-int-arrows, .wte-clm-tags .wte-sm-field .wte-sm-int-arrows, .wte-sm-sector .wte-sm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-sm-field .wte-sm-sel-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-int-arrows, .wte-clm-tags .wte-sm-field .wte-sm-int-arrows, .wte-sm-sector .wte-clm-field .wte-sm-int-arrows, .wte-clm-tags .wte-clm-field .wte-sm-int-arrows, .wte-sm-sector .wte-sm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-sm-field .wte-sm-sel-arrow, .wte-sm-sector .wte-clm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-clm-field .wte-sm-sel-arrow, .wte-sm-sector .wte-sm-field .wte-clm-sel-arrow, .wte-clm-tags .wte-sm-field .wte-clm-sel-arrow, .wte-sm-sector .wte-clm-field .wte-clm-sel-arrow, .wte-clm-tags .wte-clm-field .wte-clm-sel-arrow {
height: 100%;
width: 9px;
position: absolute;
right: 0;
top: 0;
cursor: ns-resize; }
.wte-sm-sector .wte-sm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-sm-field .wte-sm-sel-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-sm-field .wte-sm-sel-arrow, .wte-sm-sector .wte-clm-field .wte-sm-sel-arrow, .wte-clm-tags .wte-clm-field .wte-sm-sel-arrow {
cursor: pointer; }
.wte-sm-sector .wte-sm-field .wte-sm-u-arrow, .wte-clm-tags .wte-sm-field .wte-sm-u-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-u-arrow, .wte-clm-tags .wte-sm-field .wte-sm-u-arrow, .wte-sm-sector .wte-clm-field .wte-sm-u-arrow, .wte-clm-tags .wte-clm-field .wte-sm-u-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-arrow, .wte-sm-sector .wte-clm-field .wte-sm-d-arrow, .wte-clm-tags .wte-clm-field .wte-sm-d-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-sm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-clm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-clm-d-s-arrow {
position: absolute;
height: 0;
width: 0;
border-left: 3px solid transparent;
border-right: 4px solid transparent;
cursor: pointer; }
.wte-sm-sector .wte-sm-field .wte-sm-u-arrow, .wte-clm-tags .wte-sm-field .wte-sm-u-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-u-arrow, .wte-clm-tags .wte-sm-field .wte-sm-u-arrow, .wte-sm-sector .wte-clm-field .wte-sm-u-arrow, .wte-clm-tags .wte-clm-field .wte-sm-u-arrow {
border-bottom: 4px solid #b1b1b1;
top: 4px; }
.wte-sm-sector .wte-sm-field .wte-sm-d-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-d-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-arrow, .wte-sm-sector .wte-clm-field .wte-sm-d-arrow, .wte-clm-tags .wte-clm-field .wte-sm-d-arrow, .wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-sm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-clm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-clm-d-s-arrow {
border-top: 4px solid #b1b1b1;
bottom: 4px; }
.wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow {
.wte-sm-sector .wte-sm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-sm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-sm-d-s-arrow, .wte-sm-sector .wte-sm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-sm-field .wte-clm-d-s-arrow, .wte-sm-sector .wte-clm-field .wte-clm-d-s-arrow, .wte-clm-tags .wte-clm-field .wte-clm-d-s-arrow {
bottom: 7px; }
.wte-sm-sector .wte-sm-field.wte-sm-integer, .wte-clm-tags .wte-sm-field.wte-sm-integer, .wte-clm-tags #wte-clm-tags-field, .wte-sm-sector .wte-sm-field.wte-sm-select, .wte-clm-tags .wte-sm-field.wte-sm-select, .wte-sm-sector .wte-sm-field.wte-sm-list, .wte-clm-tags .wte-sm-field.wte-sm-list, .wte-sm-sector .wte-sm-field.wte-sm-color, .wte-clm-tags .wte-sm-field.wte-sm-color, .wte-sm-sector .wte-sm-field.wte-sm-composite, .wte-clm-tags .wte-sm-field.wte-sm-composite {
.wte-sm-sector .wte-sm-field.wte-sm-integer, .wte-clm-tags .wte-sm-field.wte-sm-integer, .wte-sm-sector .wte-sm-integer.wte-clm-field, .wte-clm-tags .wte-sm-integer.wte-clm-field, .wte-clm-tags #wte-clm-tags-field, .wte-sm-sector .wte-sm-field.wte-sm-select, .wte-clm-tags .wte-sm-field.wte-sm-select, .wte-sm-sector .wte-sm-select.wte-clm-field, .wte-clm-tags .wte-sm-select.wte-clm-field, .wte-sm-sector .wte-clm-select, .wte-clm-tags .wte-clm-select, .wte-sm-sector .wte-sm-field.wte-sm-list, .wte-clm-tags .wte-sm-field.wte-sm-list, .wte-sm-sector .wte-sm-list.wte-clm-field, .wte-clm-tags .wte-sm-list.wte-clm-field, .wte-sm-sector .wte-sm-field.wte-sm-color, .wte-clm-tags .wte-sm-field.wte-sm-color, .wte-sm-sector .wte-sm-color.wte-clm-field, .wte-clm-tags .wte-sm-color.wte-clm-field, .wte-sm-sector .wte-sm-field.wte-sm-composite, .wte-clm-tags .wte-sm-field.wte-sm-composite, .wte-sm-sector .wte-sm-composite.wte-clm-field, .wte-clm-tags .wte-sm-composite.wte-clm-field {
background-color: #333333;
/*353535*/
border: 1px solid #292929;
@ -3034,35 +3034,35 @@ ol.example li.placeholder:before {
border-radius: 2px;
box-sizing: border-box;
padding: 0 5px; }
.wte-sm-sector .wte-sm-field.wte-sm-select, .wte-clm-tags .wte-sm-field.wte-sm-select {
.wte-sm-sector .wte-sm-field.wte-sm-select, .wte-clm-tags .wte-sm-field.wte-sm-select, .wte-sm-sector .wte-sm-select.wte-clm-field, .wte-clm-tags .wte-sm-select.wte-clm-field, .wte-sm-sector .wte-clm-select, .wte-clm-tags .wte-clm-select {
padding: 0; }
.wte-sm-sector .wte-sm-field.wte-sm-select select, .wte-clm-tags .wte-sm-field.wte-sm-select select {
.wte-sm-sector .wte-sm-field.wte-sm-select select, .wte-clm-tags .wte-sm-field.wte-sm-select select, .wte-sm-sector .wte-sm-select.wte-clm-field select, .wte-clm-tags .wte-sm-select.wte-clm-field select, .wte-sm-sector .wte-clm-select select, .wte-clm-tags .wte-clm-select select {
height: 20px; }
.wte-sm-sector .wte-sm-field.wte-sm-select option, .wte-clm-tags .wte-sm-field.wte-sm-select option {
.wte-sm-sector .wte-sm-field.wte-sm-select option, .wte-clm-tags .wte-sm-field.wte-sm-select option, .wte-sm-sector .wte-sm-select.wte-clm-field option, .wte-clm-tags .wte-sm-select.wte-clm-field option, .wte-sm-sector .wte-clm-select option, .wte-clm-tags .wte-clm-select option {
margin: 5px 0; }
.wte-sm-sector .wte-sm-field.wte-sm-composite, .wte-clm-tags .wte-sm-field.wte-sm-composite {
.wte-sm-sector .wte-sm-field.wte-sm-composite, .wte-clm-tags .wte-sm-field.wte-sm-composite, .wte-sm-sector .wte-sm-composite.wte-clm-field, .wte-clm-tags .wte-sm-composite.wte-clm-field {
background-color: transparent;
border: 1px solid #333333; }
.wte-sm-sector .wte-sm-field.wte-sm-list, .wte-clm-tags .wte-sm-field.wte-sm-list {
.wte-sm-sector .wte-sm-field.wte-sm-list, .wte-clm-tags .wte-sm-field.wte-sm-list, .wte-sm-sector .wte-sm-list.wte-clm-field, .wte-clm-tags .wte-sm-list.wte-clm-field {
width: auto;
padding: 0;
overflow: hidden;
float: left; }
.wte-sm-sector .wte-sm-field.wte-sm-list input, .wte-clm-tags .wte-sm-field.wte-sm-list input {
.wte-sm-sector .wte-sm-field.wte-sm-list input, .wte-clm-tags .wte-sm-field.wte-sm-list input, .wte-sm-sector .wte-sm-list.wte-clm-field input, .wte-clm-tags .wte-sm-list.wte-clm-field input {
display: none; }
.wte-sm-sector .wte-sm-field.wte-sm-list label, .wte-clm-tags .wte-sm-field.wte-sm-list label {
.wte-sm-sector .wte-sm-field.wte-sm-list label, .wte-clm-tags .wte-sm-field.wte-sm-list label, .wte-sm-sector .wte-sm-list.wte-clm-field label, .wte-clm-tags .wte-sm-list.wte-clm-field label {
cursor: pointer;
padding: 5px;
display: block; }
.wte-sm-sector .wte-sm-field.wte-sm-list .wte-sm-radio:checked + label, .wte-clm-tags .wte-sm-field.wte-sm-list .wte-sm-radio:checked + label {
.wte-sm-sector .wte-sm-field.wte-sm-list .wte-sm-radio:checked + label, .wte-clm-tags .wte-sm-field.wte-sm-list .wte-sm-radio:checked + label, .wte-sm-sector .wte-sm-list.wte-clm-field .wte-sm-radio:checked + label, .wte-clm-tags .wte-sm-list.wte-clm-field .wte-sm-radio:checked + label {
background-color: #5b5b5b;
/*5b5b5b*/ }
.wte-sm-sector .wte-sm-field.wte-sm-list .wte-sm-icon, .wte-clm-tags .wte-sm-field.wte-sm-list .wte-sm-icon {
.wte-sm-sector .wte-sm-field.wte-sm-list .wte-sm-icon, .wte-clm-tags .wte-sm-field.wte-sm-list .wte-sm-icon, .wte-sm-sector .wte-sm-list.wte-clm-field .wte-sm-icon, .wte-clm-tags .wte-sm-list.wte-clm-field .wte-sm-icon {
background-repeat: no-repeat;
background-position: center;
text-shadow: none;
line-height: normal; }
.wte-sm-sector .wte-sm-field.wte-sm-integer select, .wte-clm-tags .wte-sm-field.wte-sm-integer select, .wte-clm-tags #wte-clm-tags-field select {
.wte-sm-sector .wte-sm-field.wte-sm-integer select, .wte-clm-tags .wte-sm-field.wte-sm-integer select, .wte-sm-sector .wte-sm-integer.wte-clm-field select, .wte-clm-tags .wte-sm-integer.wte-clm-field select, .wte-clm-tags #wte-clm-tags-field select {
width: auto;
padding: 0;
color: transparent; }
@ -3208,7 +3208,7 @@ ol.example li.placeholder:before {
font-size: 11px;
padding: 10px 5px; }
.wte-clm-tags #wte-clm-label {
padding-bottom: 5px;
padding: 7px 0;
float: left; }
.wte-clm-tags #wte-clm-status-c {
float: right; }
@ -3238,7 +3238,8 @@ ol.example li.placeholder:before {
display: inline-block;
border-radius: 3px;
margin: 0 3px 3px 0;
padding: 4px; }
padding: 4px;
cursor: default; }
.wte-clm-tags #wte-clm-close {
font-size: 20px;
line-height: 0;
@ -3246,6 +3247,12 @@ ol.example li.placeholder:before {
.wte-clm-tags #wte-clm-close:hover {
opacity: 0.7;
filter: alpha(opacity=70); }
.wte-clm-tags #wte-clm-checkbox {
vertical-align: middle;
cursor: pointer;
font-size: 9px; }
.wte-clm-tags #wte-clm-tag-label {
cursor: pointer; }
/********* END Class manager **********/
/********* Modal dialog **********/

30
styles/scss/main.scss

@ -478,7 +478,7 @@ $arrowColor: darken($fontColor,24%); /*b1b1b1*/
background: none; border: none;
color: $inputFontColor;
color: transparent;
width: 100%; padding: 2px 0;
width: 100%; padding: 2px 10px 2px 0;
text-shadow: 0 0 0 $inputFontColor;
position: relative;
z-index:1;
@ -495,7 +495,7 @@ $arrowColor: darken($fontColor,24%); /*b1b1b1*/
cursor:pointer;
}
.#{$sm-prefix}int-arrows, .#{$sm-prefix}sel-arrow{
.#{$sm-prefix}int-arrows, .#{$sm-prefix}sel-arrow, .#{$clm-prefix}sel-arrow{
height: 100%; width: 9px;
position: absolute;
right: 0; top: 0;
@ -503,7 +503,7 @@ $arrowColor: darken($fontColor,24%); /*b1b1b1*/
}
.#{$sm-prefix}sel-arrow{ cursor:pointer }
.#{$sm-prefix}u-arrow, .#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow{
.#{$sm-prefix}u-arrow, .#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{
position: absolute;
height: 0; width: 0;
border-left: 3px solid transparent;
@ -515,11 +515,11 @@ $arrowColor: darken($fontColor,24%); /*b1b1b1*/
border-bottom: 4px solid $arrowColor;
top: 4px;
}
.#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow{
.#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{
border-top: 4px solid $arrowColor;
bottom: 4px;
}
.#{$sm-prefix}d-s-arrow{ bottom: 7px; }
.#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{ bottom: 7px; }
&.#{$sm-prefix}integer, &.#{$sm-prefix}select, &.#{$sm-prefix}list, &.#{$sm-prefix}color, &.#{$sm-prefix}composite {
background-color: darken($mainDkColor, 2.5%);/*353535*/
@ -728,13 +728,20 @@ $addBtnBg: lighten($mainDkColor, 10%);
$paddElClm: 5px 6px;
$tagBg: #804f7b;
.#{$clm-prefix}field{
@extend .#{$sm-prefix}field
}
.#{$clm-prefix}select{
@extend .#{$sm-prefix}field.#{$sm-prefix}select;
}
.#{$clm-prefix}tags{
@extend .#{$sm-prefix}sector;
font-size: 11px;
padding: 10px 5px;
##{$clm-prefix}label{
padding-bottom: 5px;
padding: 7px 0;
float:left;
}
##{$clm-prefix}status-c{
@ -777,6 +784,7 @@ $tagBg: #804f7b;
border-radius:3px;
margin: 0 3px 3px 0;
padding: 4px;
cursor: default;
}
##{$clm-prefix}close {
@extend .opac50;
@ -788,6 +796,16 @@ $tagBg: #804f7b;
@include opacity(0.7);
}
}
##{$clm-prefix}checkbox {
vertical-align: middle;
cursor: pointer;
font-size: 9px;
}
##{$clm-prefix}tag-label {
cursor: pointer;
@extend .no-select;
}
}
/********* END Class manager **********/

3
test/runner/main.js

@ -12,6 +12,9 @@ require(['../src/config/require-config.js', 'config/config.js'], function() {
'specs/asset_manager/view/AssetImageView.js',
'specs/asset_manager/view/FileUploader.js',
'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(),

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

@ -0,0 +1,130 @@
define(
function(require) {
return {
run : function(){
describe('E2E tests', function() {
/**
* Create tags viewer
* @param {Object} ctx
*/
var instClassTagViewer = function(ctx){
var $clm;
var clm = ctx.gjs.editor.get('ClassManager');
clm.config.target = ctx.gjs.editor;
if(clm){
$clm = new clm.ClassTagsView({
collection: new clm.ClassTags([]),
config: clm.config,
}).render();
ctx.$fixture.append($clm.el);
}
return $clm;
};
before(function () {
this.$fixtures = $("#fixtures");
this.$fixture = $('<div id="ClassManager-fixture"></div>');
});
beforeEach(function () {
var Grapes = require('editor/main');
this.gjs = new Grapes({
stylePrefix: '',
storageType: 'none',
storageManager: {
storageType: 'none',
},
assetManager: {
storageType: 'none',
},
container: '#ClassManager-fixture',
});
this.$fixture.empty().appendTo(this.$fixtures);
this.gjs.render();
});
afterEach(function () {
delete this.gjs;
});
after(function () {
this.$fixture.remove();
});
describe('Interaction with Components', function() {
beforeEach(function () {
this.wrapper = this.gjs.editor.get('Components').getWrapper().get('components');
this.$clm = instClassTagViewer(this);
});
afterEach(function () {
delete this.wrapper;
delete this.$clm;
});
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');
});
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);
});
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('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');
});
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);
});
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);
});
});
});
}
};
});

93
test/specs/class_manager/main.js

@ -0,0 +1,93 @@
var modulePath = './../../../test/specs/class_manager';
define([
'ClassManager',
modulePath + '/model/ClassModels',
modulePath + '/view/ClassTagView',
modulePath + '/view/ClassTagsView',
modulePath + '/e2e/ClassManager'
],
function(
ClassManager,
Models,
ClassTagView,
ClassTagsView,
e2e
) {
describe('Class Manager', function() {
describe('Main', function() {
beforeEach(function () {
this.obj = new ClassManager();
});
afterEach(function () {
delete this.obj;
});
it('Object exists', function() {
ClassManager.should.be.exist;
});
it('No classes inside', function() {
this.obj.getClasses().length.should.equal(0);
});
it('Able to add default classes', function() {
var cm = new ClassManager({
defaults: ['test1', 'test2', 'test3'],
});
cm.getClasses().length.should.equal(3);
});
it('Add new class', function() {
this.obj.addClass('test');
this.obj.getClasses().length.should.equal(1);
});
it('Check name property', function() {
var className = 'test';
var obj = this.obj.addClass(className);
obj.get('name').should.equal(className);
});
it('Add 2 classes', function() {
this.obj.addClass('test');
this.obj.addClass('test2');
this.obj.getClasses().length.should.equal(2);
});
it('Add 2 same classes', function() {
this.obj.addClass('test');
this.obj.addClass('test');
this.obj.getClasses().length.should.equal(1);
});
it('Get class', function() {
var className = 'test';
var obj = this.obj.addClass(className);
(this.obj.getClass(className) === null).should.equal(false);
});
it('Get empty class', function() {
(this.obj.getClass('test') === null).should.equal(true);
});
it('Get same class', function() {
var className = 'test';
var obj = this.obj.addClass(className);
var obj2 = this.obj.getClass(className);
obj2.should.deep.equal(obj);
});
});
Models.run();
ClassTagView.run();
ClassTagsView.run();
e2e.run();
});
});

53
test/specs/class_manager/model/ClassModels.js

@ -0,0 +1,53 @@
var path = 'ClassManager/model/';
define([path + 'ClassTag',
path + 'ClassTags'],
function(ClassTag, ClassTags) {
return {
run : function(){
describe('ClassTag', function() {
beforeEach(function () {
this.obj = new ClassTag();
});
afterEach(function () {
delete this.obj;
});
it('Has name property', function() {
this.obj.has('name').should.equal(true);
});
it('Has label property', function() {
this.obj.has('label').should.equal(true);
});
it('Has active property', function() {
this.obj.has('active').should.equal(true);
});
it('escapeName test', function() {
this.obj.escapeName('@Te sT*').should.equal('-te-st-');
});
it('Name is corrected at instantiation', function() {
this.obj = new ClassTag({ name: '@Te sT*'});
this.obj.get('name').should.equal('-te-st-');
});
});
describe('ClassTags', function() {
it('Creates collection item correctly', function() {
var c = new ClassTags();
var m = c.add({});
m.should.be.an.instanceOf(ClassTag);
});
});
}
};
});

117
test/specs/class_manager/view/ClassTagView.js

@ -0,0 +1,117 @@
var path = 'ClassManager/view/';
define([path + 'ClassTagView', 'ClassManager/model/ClassTags'],
function(ClassTagView, ClassTags) {
return {
run : function(){
describe('ClassTagView', function() {
before(function () {
this.$fixtures = $("#fixtures");
this.$fixture = $('<div class="classtag-fixture"></div>');
});
beforeEach(function () {
this.coll = new ClassTags();
this.testLabel = 'TestLabel';
var model = this.coll.add({
name: 'test',
label: this.testLabel,
});
this.view = new ClassTagView({
config : {},
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);
});
afterEach(function () {
this.view.model.destroy();
});
after(function () {
this.$fixture.remove();
});
it('Object exists', function() {
ClassTagView.should.be.exist;
});
it('Not empty', function() {
var $el = this.view.$el;
$el.html().should.not.be.empty;
});
it('Not empty', function() {
var $el = this.view.$el;
$el.html().should.contain(this.testLabel);
});
describe('Should be rendered correctly', function() {
it('Has close button', function() {
var $el = this.view.$el;
$el.find('#close').should.have.property(0);
});
it('Has checkbox', function() {
var $el = this.view.$el;
$el.find('#checkbox').should.have.property(0);
});
it('Has label', function() {
var $el = this.view.$el;
$el.find('#tag-label').should.have.property(0);
});
});
it('Could be removed', function() {
var spy = sinon.spy();
this.view.config.target = { get:function(){} };
sinon.stub(this.view.config.target, 'get').returns(0);
this.view.$el.find('#close').trigger('click');
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);
this.view.model.set('active', true);
this.view.$el.find('#checkbox').trigger('click');
this.view.model.get('active').should.equal(false);
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);
this.view.model.set('active', true);
this.view.$el.find('#tag-label').trigger('click');
this.view.model.get('active').should.equal(false);
spy.called.should.equal(true);
});
});
}
};
});

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

@ -0,0 +1,157 @@
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 = $('<div class="classtag-fixture"></div>');
});
beforeEach(function () {
this.target = { get: function(){} };
this.coll = new ClassTags();
_.extend(this.target, Backbone.Events);
this.view = new ClassTagsView({
config : { target: this.target },
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);
this.input = this.view.$el.find('input#' + this.view.newInputId);
this.$tags = this.$fixture.find('#tags-c');
});
afterEach(function () {
delete this.view.collection;
});
after(function () {
this.$fixture.remove();
});
it('Object exists', function() {
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' });
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('');
});
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(this.targetStub);
this.view.compTarget = this.compTargetStub;
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');
});
it("Output correctly state options", function() {
var view = new ClassTagsView({
config : {
target: this.target,
states: [ { name: 'testName', label: 'testLabel' } ],
},
collection: this.coll
});
view.getStateOptions().should.equal('<option value="testName">testLabel</option>');
});
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);
});
it.skip('Has states input', function() {
this.view.$el.find('#states').should.have.property(0);
});
});
});
}
};
});

39
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();
});
});

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

@ -0,0 +1,151 @@
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('<div></div>');
});
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('<article data-test1="value1" data-test2="value2"></article>');
});
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('<article class="class1 class2" data-test1="value1" data-test2="value2"></article>');
});
});
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;}');
});
it('Build correctly with more classes', function() {
var comp = new Component();
var m1 = comp.get('components').add({tagName: 'article'});
var cls1 = m1.get('classes').add({name: 'class1'});
var cls2 = m1.get('classes').add({name: 'class2'});
var cssc = new CssComposer();
var rule = cssc.newRule([cls1, cls2]);
rule.set('style',{'prop1':'value1', 'prop2':'value2'});
cssc.addRule(rule);
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;prop2:value2;}');
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;prop2:value2;}');
});
it('Build correctly with class styled out', function() {
var comp = new Component();
var m1 = comp.get('components').add({tagName: 'article'});
var cls1 = m1.get('classes').add({name: 'class1'});
var cls2 = m1.get('classes').add({name: 'class2'});
var cssc = new CssComposer();
var rule = cssc.newRule([cls1, cls2]);
rule.set('style',{'prop1':'value1'});
cssc.addRule(rule);
var rule2 = cssc.newRule(cls2);
rule2.set('style',{'prop2':'value2'});
cssc.addRule(rule2);
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;}.class2{prop2:value2;}');
});
it("Avoid useless code", 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);
comp.get('components').remove(m1);
this.obj.build(comp, cssc).should.equal('');
});
})
}
};
});

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');
});
});
}
};
});

143
test/specs/css_composer/main.js

@ -0,0 +1,143 @@
var modulePath = './../../../test/specs/css_composer';
define([
'CssComposer',
modulePath + '/model/CssModels',
modulePath + '/view/CssRuleView',
modulePath + '/view/CssRulesView',
modulePath + '/e2e/CssComposer'
],
function(
CssComposer,
Models,
CssRuleView,
CssRulesView,
e2e
) {
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);
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("Returns correct rule with the same selector", function() {
var sel = new this.obj.Selectors([{name: 'test1'}]);
var rule1 = this.obj.newRule(sel.models);
this.obj.addRule(rule1);
var rule2 = this.obj.getRule(sel.models);
rule1.should.deep.equal(rule2);
});
it("Returns correct rule with the same selectors", function() {
var sel1 = new this.obj.Selectors([{name: 'test1'}]);
var rule1 = this.obj.newRule(sel1.models);
this.obj.addRule(rule1);
var sel2 = new this.obj.Selectors([{name: 'test21'}, {name: 'test22'}]);
var rule2 = this.obj.newRule(sel2.models);
this.obj.addRule(rule2);
var rule3 = this.obj.getRule(sel2.models);
rule3.should.deep.equal(rule2);
});
it("Don't duplicate rules", function() {
var sel = new this.obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = this.obj.newRule([s1, s3]);
var aRule1 = this.obj.addRule(rule1);
var rule2 = this.obj.newRule([s3, s1]);
var aRule2 = this.obj.addRule(rule2);
aRule2.should.deep.equal(aRule1);
});
it("Returns correct rule with the same mixed selectors", function() {
var sel = new this.obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = this.obj.newRule([s1, s3]);
this.obj.addRule(rule1);
var rule2 = this.obj.getRule([s3, s1]);
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors and state", function() {
var sel = new this.obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = this.obj.newRule([s1, s3], 'hover');
this.obj.addRule(rule1);
var rule2 = this.obj.getRule([s3, s1], 'hover');
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors, state and width", function() {
var sel = new this.obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var rule1 = this.obj.newRule([s1], 'hover','1');
this.obj.addRule(rule1);
var rule2 = this.obj.getRule([s1], 'hover', '1');
rule2.should.deep.equal(rule1);
});
it("Renders correctly", function() {
this.obj.render().should.be.ok;
});
});
Models.run();
CssRuleView.run();
CssRulesView.run();
e2e.run();
});
});

84
test/specs/css_composer/model/CssModels.js

@ -0,0 +1,84 @@
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);
});
it('Compare returns true with the same selectors', function() {
var s1 = this.obj.get('selectors').add({ name: 'test1' });
var s2 = this.obj.get('selectors').add({ name: 'test2' });
this.obj.compare([s1, s2]).should.equal(true);
});
it('Compare with different state', function() {
var s1 = this.obj.get('selectors').add({ name: 'test1' });
var s2 = this.obj.get('selectors').add({ name: 'test2' });
this.obj.set('state','hover');
this.obj.compare([s1, s2]).should.equal(false);
this.obj.compare([s1, s2], 'hover').should.equal(true);
});
it('Compare with different maxWidth', function() {
var s1 = this.obj.get('selectors').add({ name: 'test1' });
var s2 = this.obj.get('selectors').add({ name: 'test2' });
this.obj.set('state','hover');
this.obj.set('maxWidth','1000');
this.obj.compare([s1, s2]).should.equal(false);
this.obj.compare([s1, s2], 'hover').should.equal(false);
this.obj.compare([s2, s1], 'hover', '1000').should.equal(true);
});
});
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);
});
});
}
};
});

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