diff --git a/src/block_manager/view/BlocksView.js b/src/block_manager/view/BlocksView.js index 199d9f0a8..03324099d 100644 --- a/src/block_manager/view/BlocksView.js +++ b/src/block_manager/view/BlocksView.js @@ -40,7 +40,8 @@ function(Backbone, BlockView) { direction: 'a', wmargin: 1, nested: 1, - em: this.em + em: this.em, + canvasRelative: 1, }); } return this.sorter; diff --git a/src/canvas/main.js b/src/canvas/main.js index 2356d767d..2bcc01f35 100644 --- a/src/canvas/main.js +++ b/src/canvas/main.js @@ -283,8 +283,8 @@ define(function(require) { var yOffset = subWinOffset ? win.pageYOffset : 0; var xOffset = subWinOffset ? win.pageXOffset : 0; - if(frame) { - var frameRect = frame.getBoundingClientRect(); // maybe to cache ?!? + if (frame) { + var frameRect = frame.getBoundingClientRect(); addTop = frameRect.top || 0; addLeft = frameRect.left || 0; } @@ -295,6 +295,26 @@ define(function(require) { }; }, + /** + * X and Y mouse position relative to the canvas + * @param {Event} e + * @return {Object} + */ + getMouseRelativeCanvas: function (e, options) { + var opts = options || {}; + var frame = this.getFrameEl(); + var body = this.getBody(); + var addTop = frame.offsetTop || 0; + var addLeft = frame.offsetLeft || 0; + var yOffset = body.scrollTop || 0; + var xOffset = body.scrollLeft || 0; + + return { + y: e.clientY + addTop + yOffset, + x: e.clientX + addLeft + xOffset, + }; + }, + /** * Returns wrapper element * @return {HTMLElement} diff --git a/src/commands/view/MoveComponent.js b/src/commands/view/MoveComponent.js index 400875c9a..294026bd2 100644 --- a/src/commands/view/MoveComponent.js +++ b/src/commands/view/MoveComponent.js @@ -1,148 +1,148 @@ define(['backbone', './SelectComponent','./SelectPosition'], - function(Backbone, SelectComponent, SelectPosition) { - - return _.extend({}, SelectPosition, SelectComponent, { - - init: function(o){ - SelectComponent.init.apply(this, arguments); - _.bindAll(this, 'initSorter','rollback', 'onEndMove'); - this.opt = o; - this.hoverClass = this.ppfx + 'highlighter-warning'; - this.badgeClass = this.ppfx + 'badge-warning'; - this.noSelClass = this.ppfx + 'no-select'; - }, - - enable: function() { - SelectComponent.enable.apply(this, arguments); - this.getBadgeEl().addClass(this.badgeClass); - this.getHighlighterEl().addClass(this.hoverClass); - var wp = this.$wrapper; - wp.css('cursor','move'); - wp.on('mousedown', this.initSorter); - - // Avoid strange moving behavior - wp.addClass(this.noSelClass); - }, - - /** - * Overwrite for doing nothing - * @private - */ - toggleClipboard: function(){}, - - /** - * Delegate sorting - * @param {Event} e - * @private - * */ - initSorter: function(e){ - var el = $(e.target).data('model'); - var drag = el.get('draggable'); - if(!drag) - return; - - // Avoid badge showing on move - this.cacheEl = null; - this.startSelectPosition(e.target, this.frameEl.contentDocument); - this.sorter.draggable = drag; - this.sorter.onEndMove = this.onEndMove.bind(this); - this.stopSelectComponent(); - this.$wrapper.off('mousedown', this.initSorter); - this.getContentWindow().on('keydown', this.rollback); - }, - - /** - * Init sorter from model - * @param {Object} model - * @private - */ - initSorterFromModel: function(model) { - var drag = model.get('draggable'); - if(!drag) - return; - // Avoid badge showing on move - this.cacheEl = null; - var el = model.view.el; - this.startSelectPosition(el, this.frameEl.contentDocument); - this.sorter.draggable = drag; - this.sorter.onEndMove = this.onEndMoveFromModel.bind(this); - - /* - this.sorter.setDragHelper(el); - var dragHelper = this.sorter.dragHelper; - dragHelper.className = this.ppfx + 'drag-helper'; - dragHelper.innerHTML = ''; - dragHelper.backgroundColor = 'white'; - */ - - this.stopSelectComponent(); - this.getContentWindow().on('keydown', this.rollback); - }, - - onEndMoveFromModel: function() { - this.getContentWindow().off('keydown', this.rollback); - }, - - /** - * Callback after sorting - * @private - */ - onEndMove: function(){ - this.enable(); - this.getContentWindow().off('keydown', this.rollback); - }, - - /** - * Say what to do after the component was selected (selectComponent) - * @param {Event} e - * @param {Object} Selected element - * @private - * */ - onSelect: function(e,el){}, - - /** - * Used to bring the previous situation before start moving the component - * @param {Event} e - * @param {Boolean} Indicates if rollback in anycase - * @private - * */ - rollback: function(e, force){ - var key = e.which || e.keyCode; - if(key == this.opt.ESCAPE_KEY || force){ - this.sorter.moved = false; - this.sorter.endMove(); - } - return; - }, - - /** - * Returns badge element - * @return {HTMLElement} - * @private - */ - getBadgeEl: function(){ - if(!this.$badge) - this.$badge = $(this.getBadge()); - return this.$badge; - }, - - /** - * Returns highlighter element - * @return {HTMLElement} - * @private - */ - getHighlighterEl: function(){ - if(!this.$hl) - this.$hl = $(this.canvas.getHighlighter()); - return this.$hl; - }, - - stop: function(){ - SelectComponent.stop.apply(this, arguments); - this.getBadgeEl().removeClass(this.badgeClass); - this.getHighlighterEl().removeClass(this.hoverClass); - var wp = this.$wrapper; - wp.css('cursor', '').unbind().removeClass(this.noSelClass); - } - }); - }); + function(Backbone, SelectComponent, SelectPosition) { + + return _.extend({}, SelectPosition, SelectComponent, { + + init: function(o){ + SelectComponent.init.apply(this, arguments); + _.bindAll(this, 'initSorter','rollback', 'onEndMove'); + this.opt = o; + this.hoverClass = this.ppfx + 'highlighter-warning'; + this.badgeClass = this.ppfx + 'badge-warning'; + this.noSelClass = this.ppfx + 'no-select'; + }, + + enable: function() { + SelectComponent.enable.apply(this, arguments); + this.getBadgeEl().addClass(this.badgeClass); + this.getHighlighterEl().addClass(this.hoverClass); + var wp = this.$wrapper; + wp.css('cursor','move'); + wp.on('mousedown', this.initSorter); + + // Avoid strange moving behavior + wp.addClass(this.noSelClass); + }, + + /** + * Overwrite for doing nothing + * @private + */ + toggleClipboard: function(){}, + + /** + * Delegate sorting + * @param {Event} e + * @private + * */ + initSorter: function(e){ + var el = $(e.target).data('model'); + var drag = el.get('draggable'); + if(!drag) + return; + + // Avoid badge showing on move + this.cacheEl = null; + this.startSelectPosition(e.target, this.frameEl.contentDocument); + this.sorter.draggable = drag; + this.sorter.onEndMove = this.onEndMove.bind(this); + this.stopSelectComponent(); + this.$wrapper.off('mousedown', this.initSorter); + this.getContentWindow().on('keydown', this.rollback); + }, + + /** + * Init sorter from model + * @param {Object} model + * @private + */ + initSorterFromModel: function(model) { + var drag = model.get('draggable'); + if(!drag) + return; + // Avoid badge showing on move + this.cacheEl = null; + var el = model.view.el; + this.startSelectPosition(el, this.frameEl.contentDocument); + this.sorter.draggable = drag; + this.sorter.onEndMove = this.onEndMoveFromModel.bind(this); + + /* + this.sorter.setDragHelper(el); + var dragHelper = this.sorter.dragHelper; + dragHelper.className = this.ppfx + 'drag-helper'; + dragHelper.innerHTML = ''; + dragHelper.backgroundColor = 'white'; + */ + + this.stopSelectComponent(); + this.getContentWindow().on('keydown', this.rollback); + }, + + onEndMoveFromModel: function() { + this.getContentWindow().off('keydown', this.rollback); + }, + + /** + * Callback after sorting + * @private + */ + onEndMove: function(){ + this.enable(); + this.getContentWindow().off('keydown', this.rollback); + }, + + /** + * Say what to do after the component was selected (selectComponent) + * @param {Event} e + * @param {Object} Selected element + * @private + * */ + onSelect: function(e,el){}, + + /** + * Used to bring the previous situation before start moving the component + * @param {Event} e + * @param {Boolean} Indicates if rollback in anycase + * @private + * */ + rollback: function(e, force){ + var key = e.which || e.keyCode; + if(key == this.opt.ESCAPE_KEY || force){ + this.sorter.moved = false; + this.sorter.endMove(); + } + return; + }, + + /** + * Returns badge element + * @return {HTMLElement} + * @private + */ + getBadgeEl: function(){ + if(!this.$badge) + this.$badge = $(this.getBadge()); + return this.$badge; + }, + + /** + * Returns highlighter element + * @return {HTMLElement} + * @private + */ + getHighlighterEl: function(){ + if(!this.$hl) + this.$hl = $(this.canvas.getHighlighter()); + return this.$hl; + }, + + stop: function(){ + SelectComponent.stop.apply(this, arguments); + this.getBadgeEl().removeClass(this.badgeClass); + this.getHighlighterEl().removeClass(this.hoverClass); + var wp = this.$wrapper; + wp.css('cursor', '').unbind().removeClass(this.noSelClass); + } + }); + }); diff --git a/src/commands/view/SelectPosition.js b/src/commands/view/SelectPosition.js index afcbff1ad..8781d811f 100644 --- a/src/commands/view/SelectPosition.js +++ b/src/commands/view/SelectPosition.js @@ -1,102 +1,103 @@ define(function() { - return { + return { - /** - * Start select position event - * @param {HTMLElement} trg - * @private - * */ - startSelectPosition: function(trg, doc) { - this.isPointed = false; - var utils = this.editorModel.get('Utils'); - if(utils && !this.sorter) - this.sorter = new utils.Sorter({ - container: this.getCanvasBody(), - placer: this.canvas.getPlacerEl(), - containerSel: '*', - itemSel: '*', - pfx: this.ppfx, - direction: 'a', - document: doc, - wmargin: 1, - nested: 1, - em: this.editorModel, - }); - this.sorter.startSort(trg); - }, + /** + * Start select position event + * @param {HTMLElement} trg + * @private + * */ + startSelectPosition: function(trg, doc) { + this.isPointed = false; + var utils = this.editorModel.get('Utils'); + if(utils && !this.sorter) + this.sorter = new utils.Sorter({ + container: this.getCanvasBody(), + placer: this.canvas.getPlacerEl(), + containerSel: '*', + itemSel: '*', + pfx: this.ppfx, + direction: 'a', + document: doc, + wmargin: 1, + nested: 1, + em: this.editorModel, + canvasRelative: 1, + }); + this.sorter.startSort(trg); + }, - /** - * Get frame position - * @return {Object} - * @private - */ - getOffsetDim: function() { - var frameOff = this.offset(this.canvas.getFrameEl()); - var canvasOff = this.offset(this.canvas.getElement()); - var top = frameOff.top - canvasOff.top; - var left = frameOff.left - canvasOff.left; - return { top: top, left: left }; - }, + /** + * Get frame position + * @return {Object} + * @private + */ + getOffsetDim: function() { + var frameOff = this.offset(this.canvas.getFrameEl()); + var canvasOff = this.offset(this.canvas.getElement()); + var top = frameOff.top - canvasOff.top; + var left = frameOff.left - canvasOff.left; + return { top: top, left: left }; + }, - /** - * Stop select position event - * @private - * */ - stopSelectPosition: function() { - this.posTargetCollection = null; - this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize - if(this.sorter){ - this.sorter.moved = 0; - this.sorter.endMove(); - } - if(this.cDim){ - this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length; - this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) : - (!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) )); - this.posTargetModel = this.posTargetEl.data("model"); - this.posTargetCollection = this.posTargetEl.data("model-comp"); - } - }, + /** + * Stop select position event + * @private + * */ + stopSelectPosition: function() { + this.posTargetCollection = null; + this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize + if(this.sorter){ + this.sorter.moved = 0; + this.sorter.endMove(); + } + if(this.cDim){ + this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length; + this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) : + (!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) )); + this.posTargetModel = this.posTargetEl.data("model"); + this.posTargetCollection = this.posTargetEl.data("model-comp"); + } + }, - /** - * Enabel select position - * @private - */ - enable: function() { - this.startSelectPosition(); - }, + /** + * Enabel select position + * @private + */ + enable: function() { + this.startSelectPosition(); + }, - /** - * Check if the pointer is near to the float component - * @param {number} index - * @param {string} method - * @param {Array} dims - * @return {Boolean} - * @private - * */ - nearFloat: function(index, method, dims) { - var i = index || 0; - var m = method || 'before'; - var len = dims.length; - var isLast = len !== 0 && m == 'after' && i == len; - if(len !== 0 && ( - (!isLast && !dims[i][4]) || - (dims[i-1] && !dims[i-1][4]) || - (isLast && !dims[i-1][4]) ) ) - return 1; - return 0; - }, + /** + * Check if the pointer is near to the float component + * @param {number} index + * @param {string} method + * @param {Array} dims + * @return {Boolean} + * @private + * */ + nearFloat: function(index, method, dims) { + var i = index || 0; + var m = method || 'before'; + var len = dims.length; + var isLast = len !== 0 && m == 'after' && i == len; + if(len !== 0 && ( + (!isLast && !dims[i][4]) || + (dims[i-1] && !dims[i-1][4]) || + (isLast && !dims[i-1][4]) ) ) + return 1; + return 0; + }, - run: function() { - this.enable(); - }, + run: function() { + this.enable(); + }, - stop: function() { - this.stopSelectPosition(); - this.$wrapper.css('cursor',''); - this.$wrapper.unbind(); - } - }; -}); \ No newline at end of file + stop: function() { + this.stopSelectPosition(); + this.$wrapper.css('cursor',''); + this.$wrapper.unbind(); + } + }; +}); diff --git a/src/utils/Sorter.js b/src/utils/Sorter.js index 5f231a5f5..ad11adbc6 100644 --- a/src/utils/Sorter.js +++ b/src/utils/Sorter.js @@ -38,6 +38,7 @@ define(function(require) { this.dropContent = null; this.em = o.em || ''; this.dragHelper = null; + this.canvasRelative = o.canvasRelative || 0; if(this.em && this.em.on){ this.em.on('change:canvasOffset', this.udpateOffset); @@ -269,21 +270,34 @@ define(function(require) { var eO = this.offset(this.el); this.elT = this.wmargin ? Math.abs(eO.top) : eO.top; this.elL = this.wmargin ? Math.abs(eO.left): eO.left; - this.rY = (e.pageY - this.elT) + this.el.scrollTop; - this.rX = (e.pageX - this.elL) + this.el.scrollLeft; - var dims = this.dimsFromTarget(e.target, this.rX, this.rY); + var rY = (e.pageY - this.elT) + this.el.scrollTop; + var rX = (e.pageX - this.elL) + this.el.scrollLeft; + + if (this.canvasRelative && this.em) { + var mousePos = this.em.get('Canvas').getMouseRelativeCanvas(e); + rX = mousePos.x; + rY = mousePos.y; + } + + var dims = this.dimsFromTarget(e.target, rX, rY); this.lastDims = dims; - var pos = this.findPosition(dims, this.rX, this.rY); + var pos = this.findPosition(dims, rX, rY); // If there is a significant changes with the pointer if( !this.lastPos || (this.lastPos.index != pos.index || this.lastPos.method != pos.method)){ this.movePlaceholder(this.plh, dims, pos, this.prevTargetDim); if(!this.$plh) this.$plh = $(this.plh); - if(this.offTop) - this.$plh.css('top', '+=' + this.offTop + 'px'); - if(this.offLeft) - this.$plh.css('left', '+=' + this.offLeft + 'px'); + + // With canvasRelative the offset is calculated automatically for + // each element + if (!this.canvasRelative) { + if(this.offTop) + this.$plh.css('top', '+=' + this.offTop + 'px'); + if(this.offLeft) + this.$plh.css('left', '+=' + this.offLeft + 'px'); + } + this.lastPos = pos; } @@ -411,11 +425,26 @@ define(function(require) { * @param {HTMLElement} el * @return {Array} */ - getDim: function(el){ - var o = this.offset(el); - var top = this.relative ? el.offsetTop : o.top - (this.wmargin ? -1 : 1) * this.elT; - var left = this.relative ? el.offsetLeft : o.left - (this.wmargin ? -1 : 1) * this.elL; - return [top, left, el.offsetHeight, el.offsetWidth]; + getDim: function(el) { + var top, left, height, width; + + if (this.canvasRelative && this.em) { + var pos = this.em.get('Canvas').getElementPos(el); + top = pos.top; + left = pos.left; + height = pos.height; + width = pos.width; + } else { + var o = this.offset(el); + top = this.relative ? el.offsetTop : o.top - (this.wmargin ? -1 : 1) * this.elT; + left = this.relative ? el.offsetLeft : o.left - (this.wmargin ? -1 : 1) * this.elL; + height = el.offsetHeight; + width = el.offsetWidth; + } + + //console.log('get dim', top, left, this.canvasRelative); + + return [top, left, height, width]; }, /**