diff --git a/src/canvas/view/CanvasView.js b/src/canvas/view/CanvasView.js index 34b71a282..836bd3f67 100644 --- a/src/canvas/view/CanvasView.js +++ b/src/canvas/view/CanvasView.js @@ -1,22 +1,22 @@ define(['backbone','./FrameView'], function(Backbone, FrameView) { - /** - * @class CanvasView - * */ - return Backbone.View.extend({ + /** + * @class CanvasView + * */ + return Backbone.View.extend({ - initialize: function(o) { + initialize: function(o) { _.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff'); - this.config = o.config || {}; + this.config = o.config || {}; this.em = this.config.em || {}; - this.ppfx = this.config.pStylePrefix || ''; - this.className = this.config.stylePrefix + 'canvas'; - this.listenTo(this.em, 'change:canvasOffset', this.clearOff); - this.frame = new FrameView({ + this.ppfx = this.config.pStylePrefix || ''; + this.className = this.config.stylePrefix + 'canvas'; + this.listenTo(this.em, 'change:canvasOffset', this.clearOff); + this.frame = new FrameView({ model: this.model.get('frame'), config: this.config }); - }, + }, /** * Update tools position @@ -27,36 +27,38 @@ function(Backbone, FrameView) { var body = this.frame.el.contentDocument.body; this.toolsEl.style.top = '-' + body.scrollTop + u; this.toolsEl.style.left = '-' + body.scrollLeft + u; - this.em.trigger('canvasScroll'); + this.em.trigger('canvasScroll'); }, - /** - * Render inside frame's body - * @private - */ - renderBody: function(){ - var wrap = this.model.get('frame').get('wrapper'); - if(wrap){ - var ppfx = this.ppfx; + /** + * Render inside frame's body + * @private + */ + renderBody: function(){ + var wrap = this.model.get('frame').get('wrapper'); + var em = this.config.em; + if(wrap) { + var ppfx = this.ppfx; var body = this.frame.$el.contents().find('body'); - var cssc = this.config.em.get('CssComposer'); - var conf = this.config.em.get('Config'); + var cssc = em.get('CssComposer'); + var conf = em.get('Config'); body.append(wrap.render()).append(cssc.render()); var protCss = conf.protectedCss; var frameCss = '.' + ppfx + 'dashed :not([contenteditable]) > *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' + - '.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + + '.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + '.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ '.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + - '.' + ppfx + 'no-pointer{pointer-events: none}' + + '.' + ppfx + 'no-pointer{pointer-events: none}' + '.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}' + - '.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' + - '* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' + - '* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' + - '* ::-webkit-scrollbar {width: 10px}' + - (conf.canvasCss || ''); + '.' + ppfx + 'grabbing{cursor: grabbing; cursor: -webkit-grabbing}' + + '* ::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.1)}' + + '* ::-webkit-scrollbar-thumb {background: rgba(255, 255, 255, 0.2)}' + + '* ::-webkit-scrollbar {width: 10px}' + + (conf.canvasCss || ''); if(protCss) - body.append(''); - this.config.em.trigger('loaded'); + body.append(''); + body.append(this.getJsContainer()); + em.trigger('loaded'); this.frame.el.contentWindow.onscroll = this.onFrameScroll; this.frame.udpateOffset(); @@ -71,7 +73,7 @@ function(Backbone, FrameView) { doc.dispatchEvent(new KeyboardEvent(e.type, e)); }); } - }, + }, /** * Get the offset of the element @@ -87,111 +89,146 @@ function(Backbone, FrameView) { }; }, - /** - * Cleare cached offsets - * @private - */ - clearOff: function(){ - this.frmOff = null; - this.cvsOff = null; - }, - - /** - * Return frame offset - * @return {Object} - * @private - */ - getFrameOffset: function () { - if(!this.frmOff) - this.frmOff = this.offset(this.frame.el); - return this.frmOff; - }, - - /** - * Return canvas offset - * @return {Object} - * @private - */ - getCanvasOffset: function () { - if(!this.cvsOff) - this.cvsOff = this.offset(this.el); - return this.cvsOff; - }, - - /** - * Returns element's data info - * @param {HTMLElement} el - * @return {Object} - * @private - */ - getElementPos: function(el) { - var frmOff = this.getFrameOffset(); - var cvsOff = this.getCanvasOffset(); - var eo = this.offset(el); - var top = eo.top + frmOff.top - cvsOff.top; - var left = eo.left + frmOff.left - cvsOff.left; - return { - top: top, - left: left, - height: el.offsetHeight, - width: el.offsetWidth - }; - }, - - /** - * Returns position data of the canvas element - * @return {Object} obj Position object - */ - getPosition: function() { - var bEl = this.frame.el.contentDocument.body; - var fo = this.getFrameOffset(); - var co = this.getCanvasOffset(); - return { - top: fo.top + bEl.scrollTop - co.top, - left: fo.left + bEl.scrollLeft - co.left - }; - }, - - - render: function() { - this.wrapper = this.model.get('wrapper'); - - if(this.wrapper && typeof this.wrapper.render == 'function'){ - this.model.get('frame').set('wrapper', this.wrapper); - this.$el.append(this.frame.render().el); - var frame = this.frame; - frame.el.onload = this.renderBody; - } - var ppfx = this.ppfx; - toolsEl = $('
', { id: ppfx + 'tools' }).get(0); - this.hlEl = $('
', { class: ppfx + 'highlighter' }).get(0); - this.badgeEl = $('
', {class: ppfx + 'badge'}).get(0); - this.placerEl = $('
', {class: ppfx + 'placeholder'}).get(0); - this.placerIntEl = $('
', {class: ppfx + 'placeholder-int'}).get(0); - this.ghostEl = $('
', {class: ppfx + 'ghost'}).get(0); - this.toolbarEl = $('
', {class: ppfx + 'toolbar'}).get(0); - this.resizerEl = $('
', {class: ppfx + 'resizer'}).get(0); + /** + * Cleare cached offsets + * @private + */ + clearOff: function(){ + this.frmOff = null; + this.cvsOff = null; + }, + + /** + * Return frame offset + * @return {Object} + * @private + */ + getFrameOffset: function () { + if(!this.frmOff) + this.frmOff = this.offset(this.frame.el); + return this.frmOff; + }, + + /** + * Return canvas offset + * @return {Object} + * @private + */ + getCanvasOffset: function () { + if(!this.cvsOff) + this.cvsOff = this.offset(this.el); + return this.cvsOff; + }, + + /** + * Returns element's data info + * @param {HTMLElement} el + * @return {Object} + * @private + */ + getElementPos: function(el) { + var frmOff = this.getFrameOffset(); + var cvsOff = this.getCanvasOffset(); + var eo = this.offset(el); + var top = eo.top + frmOff.top - cvsOff.top; + var left = eo.left + frmOff.left - cvsOff.left; + return { + top: top, + left: left, + height: el.offsetHeight, + width: el.offsetWidth + }; + }, + + /** + * Returns position data of the canvas element + * @return {Object} obj Position object + * @private + */ + getPosition: function() { + var bEl = this.frame.el.contentDocument.body; + var fo = this.getFrameOffset(); + var co = this.getCanvasOffset(); + return { + top: fo.top + bEl.scrollTop - co.top, + left: fo.left + bEl.scrollLeft - co.left + }; + }, + + /** + * Update javascript of a specific component passed by its View + * @param {View} view Component's View + * @private + */ + updateScript: function(view) { + var scriptsContainer = this.getJsContainer(); + + if(!view.scriptContainer) { + view.scriptContainer = $('
'); + scriptsContainer.append(view.scriptContainer.get(0)); + } + + var id = view.model.cid; + var script = view.model.get('script'); + + view.el.id = id; + view.scriptContainer.html(''); + view.scriptContainer.append(''); + }, + + /** + * Get javascript container + * @private + */ + getJsContainer: function () { + if (!this.jsContainer) { + this.jsContainer = $('
', {class: this.ppfx + 'js-cont'}).get(0); + } + return this.jsContainer; + }, + + + render: function() { + this.wrapper = this.model.get('wrapper'); + + if(this.wrapper && typeof this.wrapper.render == 'function'){ + this.model.get('frame').set('wrapper', this.wrapper); + this.$el.append(this.frame.render().el); + var frame = this.frame; + frame.el.onload = this.renderBody; + } + var ppfx = this.ppfx; + toolsEl = $('
', { id: ppfx + 'tools' }).get(0); + this.hlEl = $('
', { class: ppfx + 'highlighter' }).get(0); + this.badgeEl = $('
', {class: ppfx + 'badge'}).get(0); + this.placerEl = $('
', {class: ppfx + 'placeholder'}).get(0); + this.placerIntEl = $('
', {class: ppfx + 'placeholder-int'}).get(0); + this.ghostEl = $('
', {class: ppfx + 'ghost'}).get(0); + this.toolbarEl = $('
', {class: ppfx + 'toolbar'}).get(0); + this.resizerEl = $('
', {class: ppfx + 'resizer'}).get(0); this.offsetEl = $('
', {class: ppfx + 'offset-v'}).get(0); this.fixedOffsetEl = $('
', {class: ppfx + 'offset-fixed-v'}).get(0); - this.placerEl.appendChild(this.placerIntEl); - toolsEl.appendChild(this.hlEl); - toolsEl.appendChild(this.badgeEl); - toolsEl.appendChild(this.placerEl); - toolsEl.appendChild(this.ghostEl); - toolsEl.appendChild(this.toolbarEl); - toolsEl.appendChild(this.resizerEl); + this.placerEl.appendChild(this.placerIntEl); + toolsEl.appendChild(this.hlEl); + toolsEl.appendChild(this.badgeEl); + toolsEl.appendChild(this.placerEl); + toolsEl.appendChild(this.ghostEl); + toolsEl.appendChild(this.toolbarEl); + toolsEl.appendChild(this.resizerEl); toolsEl.appendChild(this.offsetEl); toolsEl.appendChild(this.fixedOffsetEl); - this.$el.append(toolsEl); + this.$el.append(toolsEl); var rte = this.em.get('rte'); if(rte) toolsEl.appendChild(rte.render()); this.toolsEl = toolsEl; - this.$el.attr({class: this.className}); - return this; - }, + this.$el.attr({class: this.className}); + return this; + }, - }); + }); }); diff --git a/src/demo.js b/src/demo.js index 3305d794c..8daabcfb8 100644 --- a/src/demo.js +++ b/src/demo.js @@ -12,9 +12,40 @@ require(['config/require-config'], function() { noticeOnUnload: 0, container : '#gjs', height: '100%', - fromElement: true, - /* + //fromElement: true, + components: [{ + script: 'this.innerHTML= "test1";', + style: { + background: 'red', + width:'500px', + height:'100px', + margin: '50px auto', + } + },{ + script: 'this.innerHTML= "test1";', + style: { + background: 'blue', + width:'500px', + height:'100px', + margin: '50px auto', + } + },{ + script: 'this.innerHTML= "test2";', + style: { + background: 'green', + width:'500px', + height:'100px', + margin: '50px auto', + } + },{ + style: { + background: 'yellow', + width:'500px', + height:'100px', + margin: '50px auto', + } + },{ type: 'text', style:{ width:'100px', @@ -35,7 +66,7 @@ require(['config/require-config'], function() { type: 'textnode', content: " More text node --- ", }], - }],*/ + }], storageManager:{ autoload: 0, diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index c86a5cb99..a92c98ee8 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -59,6 +59,9 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana // Array of classes classes: '', + // Component's javascript + script: '', + // Traits traits: ['id', 'title'], diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index ef5a08edf..afef70b8e 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -1,280 +1,301 @@ define(['backbone', './ComponentsView'], - function (Backbone, ComponentsView) { - - return Backbone.View.extend({ - - events: { - 'click': 'initResize', - }, - - className : function(){ - return this.getClasses(); - }, - - tagName: function(){ - return this.model.get('tagName'); - }, - - initialize: function(opt){ - this.opts = opt || {}; - this.config = opt.config || {}; - this.pfx = this.config.stylePrefix || ''; - this.ppfx = this.config.pStylePrefix || ''; - this.components = this.model.get('components'); - this.attr = this.model.get("attributes"); - this.classe = this.attr.class || []; - this.listenTo(this.model, 'destroy remove', this.remove); - this.listenTo(this.model, 'change:style', this.updateStyle); - this.listenTo(this.model, 'change:attributes', this.updateAttributes); - this.listenTo(this.model, 'change:status', this.updateStatus); - this.listenTo(this.model, 'change:state', this.updateState); - this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses); - this.$el.data('model', this.model); - this.model.view = this; - this.$el.data("collection", this.components); - - if(this.model.get('classes').length) - this.importClasses(); - - this.init(); - }, - - /** + function (Backbone, ComponentsView) { + + return Backbone.View.extend({ + + events: { + 'click': 'initResize', + }, + + className : function(){ + return this.getClasses(); + }, + + tagName: function(){ + return this.model.get('tagName'); + }, + + initialize: function(opt) { + this.opts = opt || {}; + this.config = this.opts.config || {}; + this.em = this.config.em || ''; + this.pfx = this.config.stylePrefix || ''; + this.ppfx = this.config.pStylePrefix || ''; + this.components = this.model.get('components'); + this.attr = this.model.get("attributes"); + this.classe = this.attr.class || []; + this.listenTo(this.model, 'destroy remove', this.remove); + this.listenTo(this.model, 'change:style', this.updateStyle); + this.listenTo(this.model, 'change:attributes', this.updateAttributes); + this.listenTo(this.model, 'change:status', this.updateStatus); + this.listenTo(this.model, 'change:state', this.updateState); + this.listenTo(this.model, 'change:script', this.render); + this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses); + this.$el.data('model', this.model); + this.model.view = this; + this.$el.data("collection", this.components); + + if(this.model.get('classes').length) + this.importClasses(); + + this.init(); + }, + + /** * Initialize callback */ init: function () {}, - /** - * Import, if possible, classes inside main container - * @private - * */ - importClasses: function(){ - var clm = this.config.em.get('SelectorManager'); - - if(clm){ - this.model.get('classes').each(function(m){ - clm.add(m.get('name')); - }); - } - }, - - /** - * Fires on state update. If the state is not empty will add a helper class - * @param {Event} e - * @private - * */ - updateState: function(e){ - var cl = 'hc-state'; - var state = this.model.get('state'); - - if(state){ - this.$el.addClass(cl); - }else{ - this.$el.removeClass(cl); - } - }, - - /** - * Update item on status change - * @param {Event} e - * @private - * */ - updateStatus: function(e){ - var s = this.model.get('status'), - pfx = this.pfx; - switch(s) { - case 'selected': - this.$el.addClass(pfx + 'selected'); - break; - case 'moving': - break; - default: - this.$el.removeClass(pfx + 'selected'); - } - }, - - /** - * Get classes from attributes. - * This method is called before initialize - * - * @return {Array}|null - * @private - * */ - getClasses: function(){ - var attr = this.model.get("attributes"), - classes = attr['class'] || []; - if(classes.length){ - return classes.join(" "); - }else - return null; - }, - - /** - * Update attributes - * @private - * */ - updateAttributes: function(){ - var attributes = {}, - attr = this.model.get("attributes"); - for(var key in attr) { - if(attr.hasOwnProperty(key)) - attributes[key] = attr[key]; - } - // Update src - if(this.model.get("src")) - attributes.src = this.model.get("src"); - - var styleStr = this.getStyleString(); - - if(styleStr) - attributes.style = styleStr; - - this.$el.attr(attributes); - }, - - /** - * Update style attribute - * @private - * */ - updateStyle: function(){ - this.$el.attr('style', this.getStyleString()); - }, - - /** - * Return style string - * @return {string} - * @private - * */ - getStyleString: function(){ - var style = ''; - this.style = this.model.get('style'); - for(var key in this.style) { - if(this.style.hasOwnProperty(key)) - style += key + ':' + this.style[key] + ';'; - } - - return style; - }, - - /** - * Update classe attribute - * @private - * */ - updateClasses: function(){ - var str = ''; - - this.model.get('classes').each(function(model){ - str += model.get('name') + ' '; - }); - str = str.trim(); - - if(str) - this.$el.attr('class', str); - else - this.$el.removeAttr('class'); - - // Regenerate status class - this.updateStatus(); - }, - - /** - * Reply to event call - * @param object Event that generated the request - * @private - * */ - eventCall: function(event){ - event.viewResponse = this; - }, - - /** - * Init component for resizing - */ - initResize: function () { - var em = this.opts.config.em; - var editor = em ? em.get('Editor') : ''; - var config = em ? em.get('Config') : ''; - var pfx = config.stylePrefix || ''; - var attrName = 'data-' + pfx + 'handler'; - var resizeClass = pfx + 'resizing'; - var model = this.model; - var modelToStyle; - - var toggleBodyClass = function(method, e, opts) { - var handlerAttr = e.target.getAttribute(attrName); - var resizeHndClass = pfx + 'resizing-' + handlerAttr; - var classToAdd = resizeClass;// + ' ' +resizeHndClass; - if (opts.docs) { - opts.docs.find('body')[method](classToAdd); - } - }; - - if(editor && this.model.get('resizable')) { - editor.runCommand('resize', { - el: this.el, - options: { - onStart: function (e, opts) { - toggleBodyClass('addClass', e, opts); - modelToStyle = em.get('StyleManager').getModelToStyle(model); - }, - // Update all positioned elements (eg. component toolbar) - onMove: function () { - editor.trigger('change:canvasOffset'); - }, - onEnd: function (e, opts) { - toggleBodyClass('removeClass', e, opts); - editor.trigger('change:canvasOffset'); - }, - updateTarget: function(el, rect, store) { - if (!modelToStyle) { - return; - } - var unit = 'px'; - var style = _.clone(modelToStyle.get('style')); - var width = rect.w + (store ? 1 : 0); - style.width = width + unit; - style.height = rect.h + unit; - modelToStyle.set('style', style, {avoidStore: 1}); - em.trigger('targetStyleUpdated'); - - // This trick will trigger the Undo Manager. To trigger "change:style" - // on the Model you need to provide a new object and after that - // Undo Manager will trigger only if values are different (this is why - // above I've added + 1 to width if store required) - if(store) { - var style3 = _.clone(style); - style3.width = (width - 1) + unit; - modelToStyle.set('style', style3); - } - } - } - }); - } - }, - - /** - * Prevent default helper - * @param {Event} e - * @private - */ - prevDef: function (e) { - e.preventDefault(); - }, - - render: function() { - this.updateAttributes(); - this.updateClasses(); - this.$el.html(this.model.get('content')); - var view = new ComponentsView({ - collection: this.model.get('components'), - config: this.config, - defaultTypes: this.opts.defaultTypes, - componentTypes: this.opts.componentTypes, - }); - - // With childNodes lets avoid wrapping 'div' - this.$el.append(view.render(this.$el).el.childNodes); - return this; - }, - - }); + /** + * Import, if possible, classes inside main container + * @private + * */ + importClasses: function(){ + var clm = this.config.em.get('SelectorManager'); + + if(clm){ + this.model.get('classes').each(function(m){ + clm.add(m.get('name')); + }); + } + }, + + /** + * Fires on state update. If the state is not empty will add a helper class + * @param {Event} e + * @private + * */ + updateState: function(e){ + var cl = 'hc-state'; + var state = this.model.get('state'); + + if(state){ + this.$el.addClass(cl); + }else{ + this.$el.removeClass(cl); + } + }, + + /** + * Update item on status change + * @param {Event} e + * @private + * */ + updateStatus: function(e){ + var s = this.model.get('status'), + pfx = this.pfx; + switch(s) { + case 'selected': + this.$el.addClass(pfx + 'selected'); + break; + case 'moving': + break; + default: + this.$el.removeClass(pfx + 'selected'); + } + }, + + /** + * Get classes from attributes. + * This method is called before initialize + * + * @return {Array}|null + * @private + * */ + getClasses: function(){ + var attr = this.model.get("attributes"), + classes = attr['class'] || []; + if(classes.length){ + return classes.join(" "); + }else + return null; + }, + + /** + * Update attributes + * @private + * */ + updateAttributes: function(){ + var attributes = {}, + attr = this.model.get("attributes"); + for(var key in attr) { + if(attr.hasOwnProperty(key)) + attributes[key] = attr[key]; + } + // Update src + if(this.model.get("src")) + attributes.src = this.model.get("src"); + + var styleStr = this.getStyleString(); + + if(styleStr) + attributes.style = styleStr; + + this.$el.attr(attributes); + }, + + /** + * Update style attribute + * @private + * */ + updateStyle: function(){ + this.$el.attr('style', this.getStyleString()); + }, + + /** + * Return style string + * @return {string} + * @private + * */ + getStyleString: function(){ + var style = ''; + this.style = this.model.get('style'); + for(var key in this.style) { + if(this.style.hasOwnProperty(key)) + style += key + ':' + this.style[key] + ';'; + } + + return style; + }, + + /** + * Update classe attribute + * @private + * */ + updateClasses: function(){ + var str = ''; + + this.model.get('classes').each(function(model){ + str += model.get('name') + ' '; + }); + str = str.trim(); + + if(str) + this.$el.attr('class', str); + else + this.$el.removeAttr('class'); + + // Regenerate status class + this.updateStatus(); + }, + + /** + * Reply to event call + * @param object Event that generated the request + * @private + * */ + eventCall: function(event){ + event.viewResponse = this; + }, + + /** + * Init component for resizing + */ + initResize: function () { + var em = this.opts.config.em; + var editor = em ? em.get('Editor') : ''; + var config = em ? em.get('Config') : ''; + var pfx = config.stylePrefix || ''; + var attrName = 'data-' + pfx + 'handler'; + var resizeClass = pfx + 'resizing'; + var model = this.model; + var modelToStyle; + + var toggleBodyClass = function(method, e, opts) { + var handlerAttr = e.target.getAttribute(attrName); + var resizeHndClass = pfx + 'resizing-' + handlerAttr; + var classToAdd = resizeClass;// + ' ' +resizeHndClass; + if (opts.docs) { + opts.docs.find('body')[method](classToAdd); + } + }; + + if(editor && this.model.get('resizable')) { + editor.runCommand('resize', { + el: this.el, + options: { + onStart: function (e, opts) { + toggleBodyClass('addClass', e, opts); + modelToStyle = em.get('StyleManager').getModelToStyle(model); + }, + // Update all positioned elements (eg. component toolbar) + onMove: function () { + editor.trigger('change:canvasOffset'); + }, + onEnd: function (e, opts) { + toggleBodyClass('removeClass', e, opts); + editor.trigger('change:canvasOffset'); + }, + updateTarget: function(el, rect, store) { + if (!modelToStyle) { + return; + } + var unit = 'px'; + var style = _.clone(modelToStyle.get('style')); + var width = rect.w + (store ? 1 : 0); + style.width = width + unit; + style.height = rect.h + unit; + modelToStyle.set('style', style, {avoidStore: 1}); + em.trigger('targetStyleUpdated'); + + // This trick will trigger the Undo Manager. To trigger "change:style" + // on the Model you need to provide a new object and after that + // Undo Manager will trigger only if values are different (this is why + // above I've added + 1 to width if store required) + if(store) { + var style3 = _.clone(style); + style3.width = (width - 1) + unit; + modelToStyle.set('style', style3); + } + } + } + }); + } + }, + + /** + * Prevent default helper + * @param {Event} e + * @private + */ + prevDef: function (e) { + e.preventDefault(); + }, + + /** + * Render component's script + * @private + */ + updateScript: function () { + var em = this.em; + if(em) { + var canvas = em.get('Canvas'); + canvas.getCanvasView().updateScript(this); + } + }, + + render: function() { + var model = this.model; + this.updateAttributes(); + this.updateClasses(); + this.$el.html(this.model.get('content')); + var view = new ComponentsView({ + collection: this.model.get('components'), + config: this.config, + defaultTypes: this.opts.defaultTypes, + componentTypes: this.opts.componentTypes, + }); + + // With childNodes lets avoid wrapping 'div' + this.$el.append(view.render(this.$el).el.childNodes); + + // Render script + if(model.get('script')) { + this.updateScript(); + } + + return this; + }, + + }); });