diff --git a/src/canvas/main.js b/src/canvas/main.js index 32cc9f411..1095837b9 100644 --- a/src/canvas/main.js +++ b/src/canvas/main.js @@ -41,30 +41,91 @@ define(function(require) { * Get wrapper * * @return {Object} - * */ + * * getWrapper: function() { return this.canvas.get('wrapper').getComponent(); }, + */ + /** + * Returns canvas element + * @return {HTMLElement} + */ + getElement: function(){ + return this.CanvasView.el; + }, - getFrameWrapperEl: function(){ - return this.CanvasView.frame.getWrapper(); + /** + * Returns frame element of the canvas + * @return {HTMLElement} + */ + getFrameEl: function(){ + return this.CanvasView.frame.el; }, /** * Returns body element of the frame - * @return {[type]} [description] + * @return {HTMLElement} */ getBody: function(){ return this.CanvasView.frame.el.contentDocument.body; }, + /** + * Returns body wrapper element of the frame + * @return {HTMLElement} + */ + getWrapperEl: function(){ + return this.getBody().querySelector('#wrapper'); + }, + + /** + * Returns element containing canvas tools + * @return {HTMLElement} + */ + getToolsEl: function(){ + return this.CanvasView.toolsEl; + }, + + /** + * Returns highlighter element + * @return {HTMLElement} + */ + getHighlighter: function(){ + return this.CanvasView.hlEl; + }, + + /** + * Returns selector highlighter element + * @return {HTMLElement} + */ + getHighlighterSel: function(){ + return this.CanvasView.hlSelEl; + }, + + /** + * Returns badge element + * @return {HTMLElement} + */ + getBadgeEl: function(){ + return this.CanvasView.badgeEl; + }, + /** * Render canvas * */ render: function() { return this.CanvasView.render().el; }, + + /** + * Returns wrapper element + * @return {HTMLElement} + * ???? + */ + getFrameWrapperEl: function(){ + return this.CanvasView.frame.getWrapper(); + }, }; return Canvas; diff --git a/src/canvas/view/CanvasView.js b/src/canvas/view/CanvasView.js index b1ced97a3..0bd021cc0 100644 --- a/src/canvas/view/CanvasView.js +++ b/src/canvas/view/CanvasView.js @@ -6,11 +6,18 @@ function(Backbone, FrameView) { return Backbone.View.extend({ initialize: function(o) { - this.config = o.config; + this.config = o.config || {}; + this.ppfx = this.config.pStylePrefix || ''; this.className = this.config.stylePrefix + 'canvas'; this.frame = new FrameView({ model: this.model.get('frame') }); + this.toolsEl = $('
', { id: this.ppfx + 'tools' }).get(0); + this.hlEl = $('
', { class: this.ppfx + 'highlighter' }).get(0); + this.hlSelEl = $('
', { class: this.ppfx + 'highlighter-sel' }).get(0); + this.badgeEl = $('
', {class: this.ppfx + 'badge'}).get(0); + this.toolsEl.appendChild(this.hlEl); + this.toolsEl.appendChild(this.hlSelEl); }, render: function() { @@ -24,6 +31,7 @@ function(Backbone, FrameView) { frame.el.onload = function(){ frame.renderWrapper(); }; } + this.$el.append(this.toolsEl); this.$el.attr({class: this.className, id: this.config.canvasId}); return this; }, diff --git a/src/commands/view/CommandAbstract.js b/src/commands/view/CommandAbstract.js index 5631767af..18c69346d 100644 --- a/src/commands/view/CommandAbstract.js +++ b/src/commands/view/CommandAbstract.js @@ -23,22 +23,78 @@ define(['backbone'], this.plhClass = this.pfx + 'placeholder'; this.freezClass = this.ppfx + 'freezed'; + this.canvas = this.editorModel.Canvas; + if(this.editorModel.get) - this.setElement(this.editorModel.get('$editor').find('#'+this.canvasId)); + //this.setElement(this.editorModel.get('$editor').find('#'+this.canvasId)); + this.setElement(this.getCanvas()); - //TODO refactor - var fbody = this.editorModel.Canvas.getBody(); - this.setElement(fbody); + this.$canvas = this.$el; + //this.$wrapper = this.$canvas.find('#'+this.wrapperId); + this.$wrapper = $(this.getCanvasWrapper()); - this.$canvas = this.$el; - this.$wrapper = this.$canvas.find('#'+this.wrapperId); + this.init(this.config); - //TODO refactor - this.$wrapper = $(fbody.querySelector('#wrapper')); + this.frameEl = this.canvas.getFrameEl(); + this.canvasTool = this.getCanvasTools(); + this.bodyEl = this.getCanvasBody(); - this.init(this.config); + //frameEl.contentWindow.onscroll = this.onFrameScroll.bind(this); + }, + + /** + * On frame scroll callback + * @param {[type]} e [description] + * @return {[type]} [description] + */ + onFrameScroll: function(e){}, + + /** + * Returns canval element + * @return {HTMLElement} + */ + getCanvas: function(){ + //return this.editorModel.get('$editor').find('#'+this.canvasId); + return this.canvas.getElement(); }, + /** + * Get canvas body element + * @return {HTMLElement} + */ + getCanvasBody: function(){ + return this.canvas.getBody(); + }, + + /** + * Get canvas wrapper element + * @return {HTMLElement} + */ + getCanvasWrapper: function(){ + return this.canvas.getWrapperEl(); + }, + + /** + * Get canvas wrapper element + * @return {HTMLElement} + */ + getCanvasTools: function(){ + return this.canvas.getToolsEl(); + }, + + /** + * Get the offset of the element + * @param {HTMLElement} el + * @return {Object} + */ + offset: function(el){ + var rect = el.getBoundingClientRect(); + return { + top: rect.top + document.body.scrollTop, + left: rect.left + document.body.scrollLeft + }; + }, + /** * Callback triggered after initialize * @param {Object} o Options @@ -60,7 +116,7 @@ define(['backbone'], * @param {Object} sender Button sender * @private * */ - stop: function(em, sender) {} + stop: function(em, sender) {}, }); }); \ No newline at end of file diff --git a/src/commands/view/SelectComponent.js b/src/commands/view/SelectComponent.js index 25885a0a4..610a87892 100644 --- a/src/commands/view/SelectComponent.js +++ b/src/commands/view/SelectComponent.js @@ -9,11 +9,14 @@ define(function() { _.bindAll(this, 'onHover', 'onOut', 'onClick', 'onKeyPress'); }, + enable: function() { _.bindAll(this,'copyComp','pasteComp'); - var confMain = this.config.em.get('Config'); + this.frameEl.contentWindow.onscroll = this.onFrameScroll.bind(this); + var config = this.config.em.get('Config'); this.startSelectComponent(); - if(confMain.copyPaste){ + + if(config.copyPaste){ key('⌘+c, ctrl+c', this.copyComp); key('⌘+v, ctrl+v', this.pasteComp); } @@ -24,10 +27,10 @@ define(function() { * @private */ copyComp: function() { - var sel = this.editorModel.get('selectedComponent'); + var el = this.editorModel.get('selectedComponent'); - if(sel && sel.get('copyable')) - this.editorModel.set('clipboard', sel); + if(el && el.get('copyable')) + this.editorModel.set('clipboard', el); }, /** @@ -35,11 +38,11 @@ define(function() { * @private */ pasteComp: function() { - var clp = this.editorModel.get('clipboard'), - sel = this.editorModel.get('selectedComponent'); + var clp = this.editorModel.get('clipboard'), + sel = this.editorModel.get('selectedComponent'); if(clp && sel && sel.collection){ var index = sel.collection.indexOf(sel), - clone = clp.clone(); + clone = clp.clone(); sel.collection.add(clone, { at: index + 1 }); } }, @@ -49,16 +52,11 @@ define(function() { * @private * */ startSelectComponent: function() { - var that = this; - // TODO - //this.setElement(this.editorModel.Canvas.getFrameWrapperEl()); - //$el should be "canvas" - this.$el.find('*') - .on('mouseover',this.onHover) + this.selEl = $(this.getCanvasBody()).find('*'); + this.selEl.on('mouseover',this.onHover) .on('mouseout', this.onOut) .on('click', this.onClick); - this.selEl = this.$el.find('*'); - $(document).on('keydown', this.onKeyPress); + $(this.frameEl.contentWindow).on('keydown', this.onKeyPress); }, /** @@ -68,7 +66,7 @@ define(function() { onKeyPress: function(e) { var key = e.which || e.keyCode; var comp = this.editorModel.get('selectedComponent'); - var focused = document.activeElement.tagName !== 'BODY'; + var focused = this.frameEl.contentDocument.activeElement.tagName !== 'BODY'; if(key == 8 || key == 46) { if(!focused) e.preventDefault(); @@ -91,8 +89,14 @@ define(function() { */ onHover: function(e) { e.stopPropagation(); - $(e.target).addClass(this.hoverClass); - this.attachBadge(e.target); + var trg = e.target; + // Adjust tools scroll top + if(!this.adjScroll){ + this.adjScroll = 1; + this.onFrameScroll(e); + } + this.updateBadge(trg); + this.updateHighlighter(trg); }, /** @@ -102,9 +106,10 @@ define(function() { */ onOut: function(e) { e.stopPropagation(); - $(e.target).removeClass(this.hoverClass); if(this.badge) this.badge.css({ left: -10000, top:-10000 }); + if(this.hl) + this.hl.css({ left: -10000, top:-10000 }); }, /** @@ -119,6 +124,17 @@ define(function() { this.onSelect(e, e.target); }, + /** + * Update highlighter element + * @param {HTMLElement} el + */ + updateHighlighter: function(el){ + if(!this.hl) + this.hl = $(this.canvas.getHighlighter()); + var elPos = this.getElementPos($(el)); + this.hl.css({ left: elPos.left, top: elPos.topP, height: elPos.height, width: elPos.width }); + }, + /** Stop select component event * @param Event * @private @@ -131,8 +147,8 @@ define(function() { /** * Say what to do after the component was selected - * @param {Object} e - * @param {Object} el + * @param {Object} e + * @param {Object} el * @private * */ onSelect: function(e, el) { @@ -140,10 +156,15 @@ define(function() { var md = this.editorModel.get('selectedComponent'); if(md) this.cleanPrevious(md); - var nMd = $(el).data('model'); + var $el = $(el); + var nMd = $el.data('model'); if(nMd){ this.editorModel.set('selectedComponent', nMd); nMd.set('status','selected'); + if(!this.hlSel) + this.hlSel = $(this.canvas.getHighlighterSel()); + var elP = this.getElementPos($el); + this.hlSel.css({left: elP.left, top: elP.topP, height: elP.height, width: elP.width }); } }, @@ -152,33 +173,61 @@ define(function() { * @private * */ clean: function() { - this.$el.find('*').removeClass(this.hoverClass); + this.selEl.removeClass(this.hoverClass); }, - /** Attach badge to component + /** + * Update badge for the component * @param Object Component * @private * */ - attachBadge: function(el) { - var model = $(el).data("model"); + updateBadge: function(el) { + console.log('Hover'); + var $el = $(el); + this.cacheEl = $el; + var model = $el.data("model"); if(!model || !model.get('badgable')) return; if(!this.badge) this.createBadge(); - var badgeH = this.badge.outerHeight(); this.updateBadgeLabel(model); - var $el = $(el); - if(!this.wrapper) - this.wrapper = this.$wrapper; - if(!this.wrapperTop) - this.wrapperTop = this.wrapper.offset() ? this.wrapper.offset().top : 0; - if(!this.wrapperLeft) - this.wrapperLeft= this.wrapper.offset() ? this.wrapper.offset().left : 0; - var relativeT = ($el.offset().top - this.wrapperTop) + this.wrapper.scrollTop(); - var relativeL = ($el.offset().left- this.wrapperLeft) + this.wrapper.scrollLeft(); - if( (relativeT-badgeH) > this.wrapperTop) //If it's possible set badge to top - relativeT -= badgeH; - this.badge.css({ left: relativeL, top:relativeT }); + var elPos = this.getElementPos($el); + this.badge.css({ left: elPos.left, top: elPos.top }); + }, + + /** + * On frame scroll callback + * @param {[type]} e [description] + * @return {[type]} [description] + */ + onFrameScroll: function(e){ + this.canvasTool.style.top = '-' + this.bodyEl.scrollTop + 'px'; + var elPos = this.getElementPos(this.cacheEl); + this.badge.css({ left: elPos.left, top: elPos.top }); + }, + + /** + * Returns element's data info + * @param {HTMLElement} el + * @return {Object} + */ + getElementPos: function(el){ + if(!this.frameOff) + this.frameOff = this.offset(this.canvas.getFrameEl()); + if(!this.canvasOff) + this.canvasOff = this.offset(this.canvas.getElement()); + var eo = el.offset();//this.offset(el); + var bodyEl = this.getCanvasBody(); + var badgeH = this.badge.outerHeight(); + var top = eo.top + this.frameOff.top - this.canvasOff.top;// - bodyEl.scrollTop + var left = eo.left + this.frameOff.left - bodyEl.scrollLeft - this.canvasOff.left; + var topScroll = this.frameOff.top + bodyEl.scrollTop; + var topP = top; + if( (top - badgeH) < topScroll) + top = topScroll; + else + top -= badgeH; + return {topP: topP, top: top, left: left, height: el.height(), width: el.width() }; }, /** @@ -186,7 +235,7 @@ define(function() { * @private * */ createBadge: function () { - this.badge = $('
', {class: this.badgeClass + " no-dots"}).appendTo(this.$wrapper); + this.badge = $('
', {class: this.badgeClass + " no-dots"}).appendTo(this.getCanvasTools()); }, /** @@ -202,12 +251,12 @@ define(function() { /** * Updates badge label - * @param Object Model + * @param {Object} Model * @private * */ updateBadgeLabel: function (model) { if(model) - this.badge.html( model.getName() ); + this.badge.html(model.getName()); }, /** @@ -224,17 +273,25 @@ define(function() { run: function(em, sender) { this.enable(); - this.render(); }, stop: function() { + if(!this.selEl) + this.selEl = $(this.getCanvasBody()).find('*'); + if(this.hlSel) + this.hlSel.css({ left: -10000, top:-10000 }); + this.frameOff = this.canvasOff = this.adjScroll = null; + $(this.frameEl.contentWindow).off('keydown'); + + var frameEl = this.canvas.getFrameEl(); + frameEl.contentWindow.onscroll = null; var sel = this.editorModel.get('selectedComponent'); if(sel) this.cleanPrevious(sel); this.$el.unbind(); //removes all attached events this.removeBadge(); this.clean(); - this.$el.find('*').unbind('mouseover').unbind('mouseout').unbind('click'); + this.selEl.unbind('mouseover').unbind('mouseout').unbind('click'); this.editorModel.set('selectedComponent',null); key.unbind('⌘+c, ctrl+c'); key.unbind('⌘+v, ctrl+v'); diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 5bed74d2b..46f2a999e 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -253,6 +253,7 @@ define([ initCanvas: function() { var cfg = this.config.canvas, pfx = cfg.stylePrefix || 'cv-'; + cfg.pStylePrefix = this.config.stylePrefix; cfg.stylePrefix = this.config.stylePrefix + pfx; cfg.canvasId = this.config.idCanvas; this.cv = new Canvas(this.config.canvas); diff --git a/styles/css/main.css b/styles/css/main.css index 52ef46ab1..a959b6717 100644 --- a/styles/css/main.css +++ b/styles/css/main.css @@ -2636,9 +2636,21 @@ See http://bgrins.github.io/spectrum/themes/ for instructions. height: 100%; top: 0; left: 3.5%; - overflow: auto; + overflow: hidden; z-index: 1; /* This simulate body behaviour */ } + .wte-cv-canvas .wte-highlighter, .wte-cv-canvas .wte-highlighter-sel { + position: absolute; + outline: 1px solid #3b97e3; + pointer-events: none; } + .wte-cv-canvas .wte-highlighter-sel { + outline: 3px solid #3b97e3; } + .wte-cv-canvas #wte-tools { + width: 100%; + position: absolute; + top: 0; + left: 0; + outline: none; } .wte-cv-canvas > div:first-child { background-color: #fff; position: relative; @@ -2731,7 +2743,7 @@ ol.example li.placeholder:before { *.wte-com-hover-move, div.wte-com-hover-move { outline: 3px solid #ffca6f; } -.wte-com-badge, .wte-com-badge-red, .wte-com-badge-yellow { +.wte-com-badge, .wte-com-badge-red, .wte-com-badge-yellow, .wte-badge { pointer-events: none; background-color: #3b97e3; color: #fff; diff --git a/styles/scss/main.scss b/styles/scss/main.scss index 07736c644..ab7aa0ec6 100644 --- a/styles/scss/main.scss +++ b/styles/scss/main.scss @@ -123,9 +123,26 @@ $imageCompDim: 50px; width: 80%; height: 100%; top: 0; left: 3.5%; - overflow: auto; + overflow: hidden; z-index:1; + .#{$app-prefix}highlighter, .#{$app-prefix}highlighter-sel{ + position: absolute; + outline: 1px solid $colorBlue; + pointer-events: none; + } + + .#{$app-prefix}highlighter-sel{ + outline: 3px solid $colorBlue; + } + + ##{$app-prefix}tools{ + width: 100%; + position: absolute; + top: 0; left: 0; + outline: none; + } + /* This simulate body behaviour */ > div:first-child { background-color: #fff; @@ -135,6 +152,7 @@ $imageCompDim: 50px; width: 100%; } } + .#{$cv-prefix}canvas *{box-sizing: border-box;} .btn-cl { @@ -203,7 +221,7 @@ ol.example li.placeholder:before {position: absolute;} *.#{$com-prefix}hover-move, div.#{$com-prefix}hover-move{ outline: 3px solid $colorYell; } -.#{$com-prefix}badge{ +.#{$com-prefix}badge, .#{$app-prefix}badge{ pointer-events: none; background-color: $colorBlue; color: #fff;