Browse Source

Refactor class manager

pull/36/head
Artur Arseniev 10 years ago
parent
commit
e6bca464ad
  1. 24
      src/class_manager/config/config.js
  2. 90
      src/class_manager/main.js
  3. 26
      src/class_manager/model/ClassTag.js
  4. 11
      src/class_manager/model/ClassTags.js
  5. 5
      src/class_manager/template/classTag.html
  6. 27
      src/class_manager/template/classTags.html
  7. 129
      src/class_manager/view/ClassTagView.js
  8. 247
      src/class_manager/view/ClassTagsView.js
  9. 11
      src/commands/view/OpenStyleManager.js
  10. 2
      src/config/require-config.js
  11. 2
      src/css_composer/model/CssRule.js
  12. 2
      src/css_composer/model/Selectors.js
  13. 10
      src/dom_components/model/Component.js
  14. 2
      src/dom_components/model/Components.js
  15. 4
      src/dom_components/view/ComponentView.js
  16. 6
      src/editor/config/config.js
  17. 4
      src/editor/main.js
  18. 23
      src/editor/model/Editor.js
  19. 4
      src/style_manager/view/SectorsView.js

24
src/class_manager/config/config.js

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

90
src/class_manager/main.js

@ -1,90 +0,0 @@
/**
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [remove](#remove)
* * [store](#store)
* * [load](#load)
* * [render](#render)
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var classManager = editor.ClassManager;
* ```
*
* @module ClassManager
*/
define(function(require) {
return function(config) {
var c = config || {},
def = require('./config/config');
ClassTags = require('./model/ClassTags');
ClassTagsView = require('./view/ClassTagsView');
for (var name in def) {
if (!(name in c))
c[name] = def[name];
}
classes = new ClassTags(c.defaults);
config = c;
return {
config: config,
ClassTags: ClassTags,
ClassTagsView: ClassTagsView,
/**
* Name of the module
* @type {String}
* @private
*/
name: 'ClassManager',
/**
* Indicates if module is public
* @type {Boolean}
* @private
*/
public: true,
/**
* Add new class to collection only if it's not already exists
* @param {String} name Class name
* @return {Model}
* */
addClass: function(name){
var label = name;
var c = this.getClass(name);
if(!c)
return classes.add({name: name, label: label});
return c;
},
/**
* Get class by its name
* @param {String} id Class name
*
* @return {Object|null}
* */
getClass: function(id) {
return classes.where({name: id})[0];
},
/**
* Get the collection of classes
* @return {Collection}
* */
getAllClasses: function() {
return classes;
},
};
};
});

26
src/class_manager/model/ClassTag.js

@ -1,26 +0,0 @@
define(['backbone'],
function (Backbone) {
return Backbone.Model.extend({
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, '-');
},
});
});

11
src/class_manager/model/ClassTags.js

@ -1,11 +0,0 @@
define(['backbone','./ClassTag'],
function (Backbone, ClassTag) {
/**
* @class ClassTags
* */
return Backbone.Collection.extend({
model: ClassTag,
});
});

5
src/class_manager/template/classTag.html

@ -1,5 +0,0 @@
<span id="<%= pfx %>checkbox" class="fa"></span>
<span id="<%= pfx %>tag-label">
<input class="<%= ppfx %>no-app" value="<%= label %>" <%= inputProp %>/>
</span>
<span id="<%= pfx %>close">&Cross;</span>

27
src/class_manager/template/classTags.html

@ -1,27 +0,0 @@
<div id="<%= pfx %>up">
<div id="<%= pfx %>label"><%= label %></div>
<div id="<%= pfx %>status-c">
<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">
<div id="<%= pfx %>tags-c"></div>
<input id="<%= pfx %>new" />
<span id="<%= pfx %>add-tag" class="fa fa-plus"></span>
</div>
<div id="<%= pfx %>sel-help">
<div id="<%= pfx %>label">Selected</div>
<div id="<%= pfx %>sel"></div>
<div style="clear:both"></div>
</div>

129
src/class_manager/view/ClassTagView.js

@ -1,129 +0,0 @@
define(['backbone', 'text!./../template/classTag.html'],
function (Backbone, tagTemplate) {
/**
* @class ClassTagView
* */
return Backbone.View.extend({
template: _.template(tagTemplate),
events: {},
initialize: function(o) {
this.config = o.config || {};
this.coll = o.coll || null;
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.inputProp = 'readonly';
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['dblclick #' + this.labelId ] = 'startEditTag';
this.events['keypress #' + this.labelId + ' input'] = 'updateInputLabel';
this.events['blur #' + this.labelId + ' input'] = 'endEditTag';
this.listenTo( this.model, 'change:active', this.updateStatus);
this.delegateEvents();
},
/**
* Start editing tag
*/
startEditTag: function(){
this.$labelInput.prop(this.inputProp, false);
},
/**
* End editing tag. If the class typed already exists the
* old one will be restored otherwise will be changed
*/
endEditTag: function(){
var value = this.$labelInput.val();
var next = this.model.escapeName(value);
if(this.target){
var clsm = this.target.ClassManager;
if(clsm){
if(clsm.getClass(next))
this.$labelInput.val(this.model.get('label'));
else
this.model.set({ name: next, label: value});
}
}
this.$labelInput.prop(this.inputProp, true);
},
/**
* Update status of the tag
*/
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.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');
}
},
/**
* Update label's input
*/
updateInputLabel: function(){
if(!this.$labelInput)
this.$labelInput = this.$el.find('input');
var size = this.$labelInput.val().length - 2;
size = size < 1 ? 1 : size;
this.$labelInput.attr('size', size);
},
/** @inheritdoc */
render : function(){
this.$el.html( this.template({
label: this.model.get('label'),
pfx: this.pfx,
ppfx: this.ppfx,
inputProp: this.inputProp,
}));
this.updateStatus();
this.$el.attr('class', this.className);
this.updateInputLabel();
return this;
},
});
});

247
src/class_manager/view/ClassTagsView.js

@ -1,247 +0,0 @@
define(['backbone', 'text!./../template/classTags.html', './ClassTagView'],
function (Backbone, tagsTemplate, ClassTagView) {
/**
* @class ClassTagsView
* */
return Backbone.View.extend({
template: _.template(tagsTemplate),
events: {},
initialize: function(o) {
this.config = o.config || {};
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.stateInputC = this.pfx + 'input-c';
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;
this.listenTo(this.target ,'change:selectedComponent',this.componentChanged);
this.listenTo(this.target, 'targetClassUpdated', this.updateSelector);
this.listenTo(this.collection, 'add', this.addNew);
this.listenTo(this.collection, 'reset', this.renderClasses);
this.listenTo(this.collection, 'remove', this.tagRemoved);
this.delegateEvents();
},
/**
* Triggered when a tag is removed from collection
* @param {Object} model Removed model
*/
tagRemoved: function(model){
this.updateStateVis();
},
/**
* 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
*/
addNew: function(model){
this.addToClasses(model);
},
/**
* Start tag creation
* @param {Object} e
*
*/
startNewTag: function(e) {
this.$addBtn.hide();
this.$input.show().focus();
},
/**
* End tag creation
* @param {Object} e
*
*/
endNewTag: function(e) {
this.$addBtn.show();
this.$input.hide().val('');
},
/**
* Checks what to do on keyup event
* @param {Object} e
*/
onInputKeyUp: function(e) {
if (e.keyCode === 13)
this.addNewTag(this.$input.val());
else if(e.keyCode === 27)
this.endNewTag();
},
/**
* Triggered when component is changed
* @param {Object} e
*/
componentChanged: function(e){
this.compTarget = this.target.get('selectedComponent');
if(this.compTarget)
this.$states.val(this.compTarget.get('state'));
var models = this.compTarget ? this.compTarget.get('classes').models : [];
this.collection.reset(models);
this.updateStateVis();
},
/**
* Update states visibility. Hides states in case there is no tags
* inside collection
*/
updateStateVis: function(){
if(this.collection.length)
this.$statesC.css('display','block');
else
this.$statesC.css('display','none');
this.updateSelector();
},
/**
* Udpate selector helper
* @return {this}
* @private
*/
updateSelector: function(){
this.compTarget = this.target.get('selectedComponent');
if(!this.compTarget || !this.compTarget.get)
return;
var result = '';
var models = this.compTarget.get('classes');
models.each(function(model){
if(model.get('active'))
result += '.' + model.get('name');
});
var state = this.compTarget.get('state');
result = state ? result + ':' + state : result;
var el = this.el.querySelector('#' + this.pfx + 'sel');
if(el)
el.innerHTML = result;
},
/**
* Triggered when the select with states is changed
* @param {Object} e
*/
stateChanged: function(e){
if(this.compTarget){
this.compTarget.set('state', this.$states.val());
if(this.target)
this.target.trigger('targetStateUpdated');
this.updateSelector();
}
},
/**
* Add new tag to collection, if possible, and to the component
* @param {Object} e
*/
addNewTag: function(name){
if(!name)
return;
if(this.target){
var cm = this.target.get('ClassManager');
var model = cm.addClass(name);
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.updateStateVis();
}
}
this.endNewTag();
},
/**
* Add new object to collection
* @param {Object} model Model
* @param {Object} fragmentEl Fragment collection
*
* @return {Object} Object created
* */
addToClasses: function(model, fragmentEl) {
var fragment = fragmentEl || null;
var view = new ClassTagView({
model: model,
config: this.config,
coll: this.collection,
});
var rendered = view.render().el;
if(fragment)
fragment.appendChild( rendered );
else
this.$classes.append(rendered);
return rendered;
},
/**
* Render the collection of classes
* @return {this}
*/
renderClasses: function() {
var fragment = document.createDocumentFragment();
this.collection.each(function(model){
this.addToClasses(model, fragment);
},this);
if(this.$classes)
this.$classes.empty().append(fragment);
return this;
},
/** @inheritdoc */
render : function(){
this.$el.html( this.template({
label: this.config.label,
statesLabel: this.config.statesLabel,
pfx: this.pfx,
}));
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.$statesC = this.$el.find('#' + this.stateInputC);
this.$states.append(this.getStateOptions());
this.renderClasses();
this.$el.attr('class', this.className);
return this;
},
});
});

11
src/commands/view/OpenStyleManager.js

@ -25,14 +25,9 @@ define(['StyleManager'], function(StyleManager) {
}
// Class Manager container
var clm = em.ClassManager;
if(clm){
var $clm = new clm.ClassTagsView({
collection: new clm.ClassTags([]),
config: clm.config,
}).render().el;
this.$cn2.append($clm);
}
var clm = em.SelectorManager;
if(clm)
this.$cn2.append(clm.render([]));
this.$cn2.append(em.StyleManager.render());

2
src/config/require-config.js

@ -38,13 +38,13 @@ require.config({
{ name: 'AssetManager', location: 'asset_manager', },
{ name: 'BlockManager', location: 'block_manager', },
{ name: 'StyleManager', location: 'style_manager', },
{ name: 'ClassManager', location: 'class_manager', },
{ name: 'DeviceManager', location: 'device_manager', },
{ name: 'StorageManager', location: 'storage_manager', },
{ name: 'PluginManager', location: 'plugin_manager', },
{ name: 'Navigator', location: 'navigator', },
{ name: 'DomComponents', location: 'dom_components', },
{ name: 'RichTextEditor', location: 'rich_text_editor', },
{ name: 'SelectorManager', location: 'selector_manager', },
{ name: 'ModalDialog', location: 'modal_dialog', },
{ name: 'CodeManager', location: 'code_manager', },
{ name: 'CssComposer', location: 'css_composer', },

2
src/css_composer/model/CssRule.js

@ -26,7 +26,7 @@ define(['backbone', './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[i]));
slct.push(this.sm.get('SelectorManager').add(this.slct[i].name || this.slct[i]));
this.slct = slct;
}

2
src/css_composer/model/Selectors.js

@ -15,7 +15,7 @@ define([ 'backbone', 'require'],
default:
if(!this.ClassTag)
this.ClassTag = require("ClassManager/model/ClassTag");
this.ClassTag = require("SelectorManager/model/Selector");
model = new this.ClassTag(attrs, opts);
}

10
src/dom_components/model/Component.js

@ -1,5 +1,5 @@
define(['backbone','./Components', 'ClassManager/model/ClassTags'],
function (Backbone, Components, ClassTags) {
define(['backbone','./Components', 'SelectorManager/model/Selectors'],
function (Backbone, Components, Selectors) {
return Backbone.Model.extend({
@ -33,7 +33,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
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));
this.set('classes', new Selectors(this.defaultCl));
},
/**
@ -48,7 +48,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
if(!this.sm.get)
return;
var clm = this.sm.get('ClassManager');
var clm = this.sm.get('SelectorManager');
if(!clm)
return;
@ -60,7 +60,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
else
name = val.name;
var model = clm.addClass(name);
var model = clm.add(name);
res.push(model);
});
return res;

2
src/dom_components/model/Components.js

@ -65,7 +65,7 @@ define([ 'backbone', 'require'],
if(!_.isEmpty(style) && this.editor){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('ClassManager').addClass(model.cid);
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.newRule(newClass);

4
src/dom_components/view/ComponentView.js

@ -36,11 +36,11 @@ define(['backbone', './ComponentsView'],
* @private
* */
importClasses: function(){
var clm = this.config.em.get('ClassManager');
var clm = this.config.em.get('SelectorManager');
if(clm){
this.model.get('classes').each(function(m){
clm.addClass(m.get('name'));
clm.add(m.get('name'));
});
}
},

6
src/editor/config/config.js

@ -73,12 +73,12 @@ define(function () {
//Configurations for Commands
commands : {},
//Configurations for Class Manager
classManager : {},
//Configurations for Css Composer
cssComposer : {},
//Configurations for Selector Manager
selectorManager: {},
//Configurations for Device Manager
deviceManager: {
'devices': [{

4
src/editor/main.js

@ -108,9 +108,9 @@ define(function (require){
BlockManager: em.get('BlockManager'),
/**
* @property {ClassManager}
* @property {SelectorManager}
*/
ClassManager: em.get('ClassManager'),
SelectorManager: em.get('SelectorManager'),
/**
* @property {CodeManager}

23
src/editor/model/Editor.js

@ -13,7 +13,7 @@ define([
'Canvas',
'RichTextEditor',
'DomComponents',
'ClassManager',
'SelectorManager',
'StyleManager',
'Panels',
'Parser',
@ -33,7 +33,7 @@ define([
Canvas,
RichTextEditor,
DomComponents,
ClassManager,
SelectorManager,
StyleManager,
Panels,
Parser,
@ -63,7 +63,7 @@ define([
this.initDeviceManager();
this.initParser();
this.initStorage();
this.initClassManager();
this.loadModule('SelectorManager');
this.initModal();
this.loadModule('AssetManager');
this.initUtils();
@ -101,6 +101,8 @@ define([
storables.push(M);
this.set('storables', storables);
}
cfg.em = this;
//cfg.target = this; // refactor
M.init(cfg);
// Bind the module to the editor model if public
@ -245,21 +247,6 @@ define([
}
},
/**
* Initialize Class manager
* @private
* */
initClassManager: function() {
var cfg = this.config.classManager,
pfx = cfg.stylePrefix || 'clm-';
cfg.pStylePrefix = this.config.stylePrefix;
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.target = this;
this.clm = new ClassManager(cfg);
this.ClassManager = this.clm;
this.set('ClassManager', this.clm);
},
/**
* Initialize components
* @private

4
src/style_manager/view/SectorsView.js

@ -67,8 +67,8 @@ define(['backbone', './SectorView'],
// If the state is not empty, there is should be a helper rule in play
// The helper rule will get the same style of the iContainer
if(state){
var clm = this.target.get('ClassManager');
var helperClass = clm.addClass('hc-state');
var clm = this.target.get('SelectorManager');
var helperClass = clm.add('hc-state');
var helperRule = cssC.getRule([helperClass],'','');
if(!helperRule){
helperRule = cssC.newRule([helperClass],'','');

Loading…
Cancel
Save