diff --git a/src/dom_components/main.js b/src/dom_components/main.js index f3d75369a..0f1ea6600 100644 --- a/src/dom_components/main.js +++ b/src/dom_components/main.js @@ -36,18 +36,37 @@ define(function(require) { return function (){ var c = {}, - defaults = require('./config/config'), - Component = require('./model/Component'), - ComponentText = require('./model/ComponentText'), - ComponentImage = require('./model/ComponentImage'), - ComponentLink = require('./model/ComponentLink'), - ComponentMap = require('./model/ComponentMap'), - ComponentView = require('./view/ComponentView'), - ComponentImageView = require('./view/ComponentImageView'), - ComponentTextView = require('./view/ComponentTextView'), - ComponentMapView = require('./view/ComponentMapView'), - ComponentLinkView = require('./view/ComponentLinkView'); + componentTypes = {}, + defaults = require('./config/config'), + Component = require('./model/Component'), + ComponentView = require('./view/ComponentView'); var component, componentView; + var defaultTypes = { + 'default': { + model: Component, + view: ComponentView, + }, + 'text': { + model: require('./model/ComponentText'), + view: require('./view/ComponentTextView'), + }, + 'image': { + model: require('./model/ComponentImage'), + view: require('./view/ComponentImageView'), + }, + 'link': { + model: require('./model/ComponentLink'), + view: require('./view/ComponentLinkView'), + }, + 'map': { + model: require('./model/ComponentMap'), + view: require('./view/ComponentMapView'), + }, + 'video': { + model: require('./model/ComponentVideo'), + view: require('./view/ComponentVideoView'), + } + }; return { @@ -100,12 +119,19 @@ define(function(require) { c.am = c.em.get('AssetManager') || ''; } - component = new Component(c.wrapper, { sm: c.em, config: c }); + component = new Component(c.wrapper, { + sm: c.em, + config: c, + defaultTypes: defaultTypes, + componentTypes: componentTypes, + }); component.set({ attributes: {id: 'wrapper'}}); component.get('components').add(c.components); componentView = new ComponentView({ model: component, config: c, + defaultTypes: defaultTypes, + componentTypes: componentTypes, }); return this; }, @@ -290,7 +316,8 @@ define(function(require) { * @private */ addComponentType: function(type, methods) { - + componentTypes[type] = methods; + return this; } }; diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 0b547da9c..fec61cd4b 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -20,6 +20,7 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana content: '', style: {}, attributes: {}, + classes: '', traits: ['id', 'title'], }, @@ -31,7 +32,7 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana this.sm = opt ? opt.sm || {} : {}; this.config = o || {}; this.defaultC = this.config.components || []; - this.defaultCl = this.normalizeClasses(this.config.classes || []); + this.defaultCl = this.normalizeClasses(this.get('classes') || this.config.classes || []); this.components = new Components(this.defaultC, opt); this.set('components', this.components); this.set('classes', new Selectors(this.defaultCl)); @@ -132,13 +133,7 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana sTag = m.get('void'), attrId = ''; // Build the string of attributes - var attr = ''; - _.each(attrs, function(value, prop){ - // TODO: to refactor - if(prop == 'onmousedown') - return; - attr += value && prop!='style' ? ' ' + prop + '="' + value + '"' : ''; - }); + var attr = this.toAttrHTML(); // Build the string of classes var strCls = ''; m.get('classes').each(function(m){ @@ -146,32 +141,37 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana }); strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : ''; - /* - // TODO: to refactor - if(m.get('type') == 'image'){ - tag = 'img'; - sTag = 1; - attr += ' src="' + m.get('src') + '"'; - } - */ - // If style is not empty I need an ID attached to the component - // TODO: need to refactor in case of 'ID Trait' - if(!_.isEmpty(m.get('style'))) + // If style is not empty I need an ID attached to the component + // TODO: need to refactor in case of 'ID Trait' + if(!_.isEmpty(m.get('style'))) attrId = ' id="' + m.cid + '" '; code += '<' + tag + strCls + attrId + attr + (sTag ? '/' : '') + '>' + m.get('content'); - var cln = m.get('components'); - if(cln.length) - code += this.toHTML(cln); + m.get('components').each(function(m) { + code += m.toHTML(); + }); if(!sTag) code += ''; return code; + }, + + /** + * Returns attributes string in HTML + * @return {string} + * @private + */ + toAttrHTML: function() { + var attr = ''; + _.each(this.get('attributes'), function(val, prop){ + attr += (val && prop != 'style') ? ' ' + prop + '="' + val + '"' : ''; + }); + return attr; } - }, { + },{ /** * Detect if the passed element is a valid component. @@ -181,7 +181,7 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana * @return {Object} * @private */ - isValidEl: function(el) { + isComponent: function(el) { var result = ''; if(el.tagName == 'DIV') result = {tagName: 'div'}; diff --git a/src/dom_components/model/ComponentImage.js b/src/dom_components/model/ComponentImage.js index f2cb65ffe..519183f7e 100644 --- a/src/dom_components/model/ComponentImage.js +++ b/src/dom_components/model/ComponentImage.js @@ -4,10 +4,32 @@ define(['./Component'], return Component.extend({ defaults: _.extend({}, Component.prototype.defaults, { - src: '', - droppable: false, - traits: ['alt'], + tagName: 'img', + src: '', + void: 1, + droppable: false, + traits: ['alt'], }), + /** + * Returns attributes string in HTML + * @return {string} + * @private + */ + toAttrHTML: function() { + var attr = ''; + _.each(this.get('attributes'), function(value, prop){ + if(prop == 'onmousedown') + return; + attr += value && prop!='style' ? ' ' + prop + '="' + value + '"' : ''; + }); + + var src = this.get('src'); + if(src) + attr += ' src="' + src + '"'; + + return attr; + } + }); }); diff --git a/src/dom_components/model/ComponentText.js b/src/dom_components/model/ComponentText.js index 4fc08a037..b34f10291 100644 --- a/src/dom_components/model/ComponentText.js +++ b/src/dom_components/model/ComponentText.js @@ -4,8 +4,7 @@ define(['./Component'], return Component.extend({ defaults: _.extend({}, Component.prototype.defaults, { - content : '', - droppable : false, + droppable: false, }), }); diff --git a/src/dom_components/model/ComponentVideo.js b/src/dom_components/model/ComponentVideo.js index 98cacc977..2dbce5530 100644 --- a/src/dom_components/model/ComponentVideo.js +++ b/src/dom_components/model/ComponentVideo.js @@ -7,7 +7,7 @@ define(['./ComponentImage'], type: 'video', tagName: 'video', provider: '', // on change of provider, traits are switched - traits: this.getSourceTraits(), + sources: [], }), // Listen provider change and switch traits, in TraitView listen traits change diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index 4399f22aa..a12ef5961 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -22,40 +22,22 @@ define([ 'backbone', 'require'], if(opt && opt.config) options.config = opt.config; - switch(attrs.type){ - - case 'text': - if(!this.mComponentText) - this.mComponentText = require("./ComponentText"); - model = new this.mComponentText(attrs, options); - break; - - case 'link': - if(!this.mComponentLink) - this.mComponentLink = require("./ComponentLink"); - model = new this.mComponentLink(attrs, options); - break; - - case 'image': - if(!this.mComponentImage) - this.mComponentImage = require("./ComponentImage"); - model = new this.mComponentImage(attrs, options); - break; - - case 'map': - if(!this.mComponentMap) - this.mComponentMap = require("./ComponentMap"); - model = new this.mComponentMap(attrs, options); - break; - - default: - if(!this.mComponent) - this.mComponent = require("./Component"); - model = new this.mComponent(attrs, options); + if(opt && opt.defaultTypes) + options.defaultTypes = opt.defaultTypes; + if(opt && opt.componentTypes) + options.componentTypes = opt.componentTypes; + + var df = opt.defaultTypes; + var cf = opt.componentTypes; + + if(df[attrs.type]){ + model = df[attrs.type].model; + }else{ + model = df.default.model; } - return model; + return new model(attrs, options); }; }, diff --git a/src/dom_components/view/ComponentVideoView.js b/src/dom_components/view/ComponentVideoView.js new file mode 100644 index 000000000..6ef1f2eaf --- /dev/null +++ b/src/dom_components/view/ComponentVideoView.js @@ -0,0 +1,16 @@ +define(['backbone', './ComponentView'], + function (Backbone, ComponentView) { + + return ComponentView.extend({ + + tagName: 'video', + + events: {}, + + initialize: function(o){ + ComponentView.prototype.initialize.apply(this, arguments); + console.log('Video view loaded'); + }, + + }); +}); diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 61ab586e7..f23d08cd6 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -12,6 +12,7 @@ define(['backbone', './ComponentsView'], }, initialize: function(opt){ + this.opts = opt || {}; this.config = opt.config || {}; this.pfx = this.config.stylePrefix || ''; this.ppfx = this.config.pStylePrefix || ''; @@ -180,6 +181,8 @@ define(['backbone', './ComponentsView'], var view = new ComponentsView({ collection: this.components, config: this.config, + defaultTypes: this.opts.defaultTypes, + componentTypes: this.opts.componentTypes, }); // With childNodes lets avoid wrapping 'div' diff --git a/src/dom_components/view/ComponentsView.js b/src/dom_components/view/ComponentsView.js index bab0293c9..76f794dee 100644 --- a/src/dom_components/view/ComponentsView.js +++ b/src/dom_components/view/ComponentsView.js @@ -4,7 +4,8 @@ function(Backbone, require) { return Backbone.View.extend({ initialize: function(o) { - this.config = o.config; + this.opts = o || {}; + this.config = o.config; this.listenTo( this.collection, 'add', this.addTo ); this.listenTo( this.collection, 'reset', this.render ); }, @@ -34,34 +35,19 @@ function(Backbone, require) { if(!this.compView) this.compView = require('./ComponentView'); var fragment = fragmentEl || null, - viewObject = this.compView; + viewObject = this.compView; - switch(model.get('type')){ - case 'text': - if(!this.compViewText) - this.compViewText = require('./ComponentTextView'); - viewObject = this.compViewText; - break; - case 'image': - if(!this.compViewImage) - this.compViewImage = require('./ComponentImageView'); - viewObject = this.compViewImage; - break; - case 'link': - if(!this.compViewLink) - this.compViewLink = require('./ComponentLinkView'); - viewObject = this.compViewLink; - break; - case 'map': - if(!this.compViewMap) - this.compViewMap = require('./ComponentMapView'); - viewObject = this.compViewMap; - break; - } + var dt = this.opts.defaultTypes; + var ct = this.opts.componentTypes; + + var type = model.get('type'); + viewObject = dt[type] ? dt[type].view : dt.default.view; - var view = new viewObject({ - model : model, - config : this.config, + var view = new viewObject({ + model: model, + config: this.config, + defaultTypes: dt, + componentTypes: ct, }); var rendered = view.render().el; diff --git a/src/editor/config/config.js b/src/editor/config/config.js index f4f2283d4..7a6e2aa75 100644 --- a/src/editor/config/config.js +++ b/src/editor/config/config.js @@ -193,6 +193,14 @@ define(function () { type: 'map', style: {height: '350px'} }, + },{ + id: 'video', + label: 'Video', + attributes: {class:'fa fa-youtube-play'}, + content: { + type: 'video', + style: {height: '350px'} + }, }], }, diff --git a/test/specs/dom_components/model/Component.js b/test/specs/dom_components/model/Component.js index a6b561a61..22fee3bb4 100644 --- a/test/specs/dom_components/model/Component.js +++ b/test/specs/dom_components/model/Component.js @@ -53,6 +53,48 @@ define(['DomComponents/model/Component', obj.getName().should.equal('TestType 999'); }); + it('Component toHTML', function() { + obj.toHTML().should.equal('
'); + }); + + it('Component toHTML with attributes', function() { + obj = new Component({ + tagName: 'article', + attributes: { + 'data-test1': 'value1', + 'data-test2': 'value2' + } + }); + obj.toHTML().should.equal('
'); + }); + + it('Component toHTML with classes', function() { + obj = new Component({ + tagName: 'article' + }); + ['class1', 'class2'].forEach(function(item){ + obj.get('classes').add({name: item}); + }); + obj.toHTML().should.equal('
'); + }); + + it('Component toHTML with children', function() { + obj = new Component({tagName: 'article'}); + obj.get('components').add({tagName: 'span'}); + obj.toHTML().should.equal('
'); + }); + + it('Component toHTML with more children', function() { + obj = new Component({tagName: 'article'}); + obj.get('components').add([{tagName: 'span'}, {tagName: 'div'}]); + obj.toHTML().should.equal('
'); + }); + + it('Component toHTML with no closing tag', function() { + obj = new Component({void: 1}); + obj.toHTML().should.equal('
'); + }); + }); describe('Image Component', function() { @@ -73,6 +115,19 @@ define(['DomComponents/model/Component', obj.get('droppable').should.equal(false); }); + it('ComponentImage toHTML', function() { + obj = new ComponentImage(); + obj.toHTML().should.equal(''); + }); + + it('Component toHTML with attributes', function() { + obj = new ComponentImage({ + attributes: {'alt': 'AltTest'}, + src: 'testPath' + }); + obj.toHTML().should.equal('AltTest'); + }); + }); describe('Text Component', function() { @@ -93,6 +148,14 @@ define(['DomComponents/model/Component', obj.get('droppable').should.equal(false); }); + it('Component toHTML with attributes', function() { + obj = new ComponentText({ + attributes: {'data-test': 'value'}, + content: 'test content' + }); + obj.toHTML().should.equal('
test content
'); + }); + }); describe('Components', function() {