mirror of https://github.com/artf/grapesjs.git
nocodeframeworkdrag-and-dropsite-buildersite-generatortemplate-builderui-builderweb-builderweb-builder-frameworkwebsite-builderno-codepage-builder
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
806 lines
22 KiB
806 lines
22 KiB
var Backbone = require('backbone');
|
|
|
|
module.exports = Backbone.View.extend({
|
|
|
|
initialize(opt) {
|
|
this.opt = opt || {};
|
|
_.bindAll(this,'startSort','onMove','endMove','rollback', 'udpateOffset', 'moveDragHelper');
|
|
var o = opt || {};
|
|
this.elT = 0;
|
|
this.elL = 0;
|
|
this.borderOffset = o.borderOffset || 10;
|
|
|
|
var el = o.container;
|
|
this.el = typeof el === 'string' ? document.querySelector(el) : el;
|
|
this.$el = $(this.el);
|
|
|
|
this.containerSel = o.containerSel || 'div';
|
|
this.itemSel = o.itemSel || 'div';
|
|
this.draggable = o.draggable || true;
|
|
this.nested = o.nested || 0;
|
|
this.pfx = o.pfx || '';
|
|
this.ppfx = o.ppfx || '';
|
|
this.freezeClass = o.freezeClass || this.pfx + 'freezed';
|
|
this.onStart = o.onStart || '';
|
|
this.onEndMove = o.onEndMove || '';
|
|
this.direction = o.direction || 'v'; // v (vertical), h (horizontal), a (auto)
|
|
this.onMoveClb = o.onMove || '';
|
|
this.relative = o.relative || 0;
|
|
this.plh = o.placer || '';
|
|
// Frame offset
|
|
this.wmargin = o.wmargin || 0;
|
|
this.offTop = o.offsetTop || 0;
|
|
this.offLeft = o.offsetLeft || 0;
|
|
this.document = o.document || document;
|
|
this.$document = $(this.document);
|
|
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);
|
|
this.udpateOffset();
|
|
}
|
|
},
|
|
|
|
getContainerEl() {
|
|
if (!this.el) {
|
|
var el = this.opt.container;
|
|
this.el = typeof el === 'string' ? document.querySelector(el) : el;
|
|
this.$el = $(this.el);
|
|
}
|
|
return this.el;
|
|
},
|
|
|
|
/**
|
|
* Triggered when the offset of the editro is changed
|
|
*/
|
|
udpateOffset() {
|
|
var offset = this.em.get('canvasOffset');
|
|
this.offTop = offset.top;
|
|
this.offLeft = offset.left;
|
|
},
|
|
|
|
/**
|
|
* Set content to drop
|
|
* @param {String|Object} content
|
|
*/
|
|
setDropContent(content) {
|
|
this.dropContent = content;
|
|
},
|
|
|
|
/**
|
|
* Toggle cursor while sorting
|
|
* @param {Boolean} active
|
|
*/
|
|
toggleSortCursor(active) {
|
|
var em = this.em;
|
|
var body = document.body;
|
|
var pfx = this.ppfx || this.pfx;
|
|
var sortCls = pfx + 'grabbing';
|
|
var emBody = em ? em.get('Canvas').getBody() : '';
|
|
if(active) {
|
|
body.className += ' ' + sortCls;
|
|
if(em) {
|
|
emBody.className += ' ' + sortCls;
|
|
}
|
|
} else {
|
|
body.className = body.className.replace(sortCls, '').trim();
|
|
if(em) {
|
|
emBody.className = emBody.className.replace(sortCls, '').trim();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set drag helper
|
|
* @param {HTMLElement} el
|
|
* @param {Event} event
|
|
*/
|
|
setDragHelper(el, event) {
|
|
var ev = event || '';
|
|
var clonedEl = el.cloneNode(1);
|
|
|
|
// Attach style
|
|
var style = '';
|
|
var o = getComputedStyle(el);
|
|
for(var i = 0; i < o.length; i++) {
|
|
style += o[i] + ':' + o.getPropertyValue(o[i])+';';
|
|
}
|
|
clonedEl.setAttribute('style', style);
|
|
clonedEl.className += ' ' + this.pfx + 'bdrag';
|
|
document.body.appendChild(clonedEl);
|
|
this.dragHelper = clonedEl;
|
|
|
|
if(ev) {
|
|
this.moveDragHelper(ev);
|
|
}
|
|
|
|
// Listen mouse move events
|
|
if(this.em) {
|
|
$(this.em.get('Canvas').getBody().ownerDocument)
|
|
.off('mousemove', this.moveDragHelper).on('mousemove', this.moveDragHelper);
|
|
}
|
|
$(document)
|
|
.off('mousemove', this.moveDragHelper).on('mousemove', this.moveDragHelper);
|
|
},
|
|
|
|
/**
|
|
* //TODO Refactor, use canvas.getMouseRelativePos to get mouse's X and Y
|
|
* Update the position of the helper
|
|
* @param {Event} e
|
|
*/
|
|
moveDragHelper(e) {
|
|
if(!this.dragHelper) {
|
|
return;
|
|
}
|
|
var doc = e.target.ownerDocument;
|
|
var win = doc.defaultView || doc.parentWindow;
|
|
var addTop = 0;
|
|
var addLeft = 0;
|
|
var frame = win.frameElement;
|
|
if(frame) {
|
|
var frameRect = frame.getBoundingClientRect(); // maybe to cache ?!?
|
|
addTop = frameRect.top || 0;
|
|
addLeft = frameRect.left || 0;
|
|
}
|
|
var hStyle = this.dragHelper.style;
|
|
hStyle.left = (e.pageX - win.pageXOffset + addLeft) + 'px';
|
|
hStyle.top = (e.pageY - win.pageYOffset + addTop) + 'px';
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns true if the element matches with selector
|
|
* @param {Element} el
|
|
* @param {String} selector
|
|
* @return {Boolean}
|
|
*/
|
|
matches(el, selector, useBody) {
|
|
var startEl = el.parentNode || document.body;
|
|
//startEl = useBody ? startEl.ownerDocument.body : startEl;
|
|
var els = startEl.querySelectorAll(selector);
|
|
var i = 0;
|
|
while (els[i] && els[i] !== el)
|
|
++i;
|
|
return !!els[i];
|
|
},
|
|
|
|
/**
|
|
* Closest parent
|
|
* @param {Element} el
|
|
* @param {String} selector
|
|
* @return {Element|null}
|
|
*/
|
|
closest(el, selector) {
|
|
if(!el)
|
|
return;
|
|
var elem = el.parentNode;
|
|
while (elem && elem.nodeType === 1) {
|
|
if (this.matches(elem, selector))
|
|
return elem;
|
|
elem = elem.parentNode;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Get the offset of the element
|
|
* @param {HTMLElement} el
|
|
* @return {Object}
|
|
*/
|
|
offset(el) {
|
|
var rect = el.getBoundingClientRect();
|
|
return {
|
|
top: rect.top + document.body.scrollTop,
|
|
left: rect.left + document.body.scrollLeft
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Create placeholder
|
|
* @return {HTMLElement}
|
|
*/
|
|
createPlaceholder() {
|
|
var pfx = this.pfx;
|
|
var el = document.createElement('div');
|
|
var ins = document.createElement('div');
|
|
el.className = pfx + 'placeholder';
|
|
el.style.display = 'none';
|
|
el.style['pointer-events'] = 'none';
|
|
ins.className = pfx + "placeholder-int";
|
|
el.appendChild(ins);
|
|
return el;
|
|
},
|
|
|
|
/**
|
|
* Picking component to move
|
|
* @param {HTMLElement} trg
|
|
* */
|
|
startSort(trg) {
|
|
this.moved = 0;
|
|
this.eV = trg;
|
|
|
|
if(trg && !this.matches(trg, this.itemSel + ',' + this.containerSel))
|
|
this.eV = this.closest(trg, this.itemSel);
|
|
|
|
// Create placeholder if not exists
|
|
if(!this.plh) {
|
|
this.plh = this.createPlaceholder();
|
|
this.getContainerEl().appendChild(this.plh);
|
|
}
|
|
|
|
if(trg) {
|
|
var className = trg.getAttribute('class');
|
|
trg.setAttribute('class', `${className} ${this.freezeClass}`);
|
|
this.$document.on('mouseup', this.endMove);
|
|
}
|
|
|
|
this.$el.on('mousemove', this.onMove);
|
|
$(document).on('keydown', this.rollback);
|
|
this.$document.on('keydown', this.rollback);
|
|
|
|
if(typeof this.onStart === 'function')
|
|
this.onStart();
|
|
|
|
// Avoid strange effects on dragging
|
|
if(this.em) {
|
|
this.em.clearSelection();
|
|
}
|
|
|
|
this.toggleSortCursor(1);
|
|
},
|
|
|
|
/**
|
|
* Get the model from HTMLElement target
|
|
* @return {Model|null}
|
|
*/
|
|
getModelFromTarget(el) {
|
|
let elem = el || this.target;
|
|
return $(elem).data('model');
|
|
},
|
|
|
|
/**
|
|
* Highlight target
|
|
* @param {Model|null} model
|
|
*/
|
|
selectTargetModel(model) {
|
|
var prevModel = this.targetModel;
|
|
if (prevModel) {
|
|
prevModel.set('status', '');
|
|
}
|
|
|
|
if (model && model.set) {
|
|
model.set('status', 'selected-parent');
|
|
this.targetModel = model;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* During move
|
|
* @param {Event} e
|
|
* */
|
|
onMove(e) {
|
|
this.moved = 1;
|
|
|
|
// Turn placeholder visibile
|
|
var plh = this.plh;
|
|
var dsp = plh.style.display;
|
|
if(!dsp || dsp === 'none')
|
|
plh.style.display = 'block';
|
|
|
|
// Cache all necessary positions
|
|
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;
|
|
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);
|
|
|
|
let targetModel = this.getModelFromTarget(this.target);
|
|
this.selectTargetModel(targetModel);
|
|
|
|
this.lastDims = dims;
|
|
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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
if(typeof this.onMoveClb === 'function')
|
|
this.onMoveClb(e);
|
|
},
|
|
|
|
/**
|
|
* Returns true if the elements is in flow, so is not in flow where
|
|
* for example the component is with float:left
|
|
* @param {HTMLElement} el
|
|
* @param {HTMLElement} parent
|
|
* @return {Boolean}
|
|
* @private
|
|
* */
|
|
isInFlow(el, parent) {
|
|
if(!el)
|
|
return false;
|
|
|
|
parent = parent || document.body;
|
|
var ch = -1, h;
|
|
var elem = el;
|
|
h = elem.offsetHeight;
|
|
if (/*h < ch || */!this.styleInFlow(elem, parent))
|
|
return false;
|
|
else
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Check if el has style to be in flow
|
|
* @param {HTMLElement} el
|
|
* @param {HTMLElement} parent
|
|
* @return {Boolean}
|
|
* @private
|
|
*/
|
|
styleInFlow(el, parent) {
|
|
var style = el.style;
|
|
var $el = $(el);
|
|
if (style.overflow && style.overflow !== 'visible')
|
|
return;
|
|
if ($el.css('float') !== 'none')
|
|
return;
|
|
if(parent && $(parent).css('display') == 'flex')
|
|
return;
|
|
switch (style.position) {
|
|
case 'static': case 'relative': case '':
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
switch (el.tagName) {
|
|
case 'TR': case 'TBODY': case 'THEAD': case 'TFOOT':
|
|
return true;
|
|
}
|
|
switch ($el.css('display')) {
|
|
case 'block':
|
|
case 'list-item':
|
|
case 'table':
|
|
case 'flex':
|
|
return true;
|
|
}
|
|
return;
|
|
},
|
|
|
|
/**
|
|
* Get dimensions of nodes relative to the coordinates
|
|
* @param {HTMLElement} target
|
|
* @param {number} rX Relative X position
|
|
* @param {number} rY Relative Y position
|
|
* @return {Array<Array>}
|
|
*/
|
|
dimsFromTarget(target, rX, rY) {
|
|
var dims = [];
|
|
|
|
// Select the first valuable target
|
|
// TODO: avoid this check for every standard component,
|
|
// which generally is ok
|
|
if(!this.matches(target, this.itemSel + ',' + this.containerSel))
|
|
target = this.closest(target, this.itemSel);
|
|
|
|
// If draggable is an array the target will be one of those
|
|
if(this.draggable instanceof Array){
|
|
target = this.closest(target, this.draggable.join(','));
|
|
}
|
|
|
|
if(!target)
|
|
return dims;
|
|
|
|
// Check if the target is different from the previous one
|
|
if(this.prevTarget){
|
|
if(this.prevTarget != target){
|
|
this.prevTarget = null;
|
|
}
|
|
}
|
|
|
|
// New target encountered
|
|
if(!this.prevTarget){
|
|
this.targetP = this.closest(target, this.containerSel);
|
|
this.prevTarget = target;
|
|
this.prevTargetDim = this.getDim(target);
|
|
this.cacheDimsP = this.getChildrenDim(this.targetP);
|
|
this.cacheDims = this.getChildrenDim(target);
|
|
}
|
|
|
|
// If the target is the previous one will return the cached dims
|
|
if(this.prevTarget == target)
|
|
dims = this.cacheDims;
|
|
|
|
// Target when I will drop element to sort
|
|
this.target = this.prevTarget;
|
|
// Generally also on every new target the poiner enters near
|
|
// to borders, so have to to check always
|
|
if(this.nearBorders(this.prevTargetDim, rX, rY) ||
|
|
(!this.nested && !this.cacheDims.length)){
|
|
dims = this.cacheDimsP;
|
|
this.target = this.targetP;
|
|
}
|
|
this.lastPos = null;
|
|
return dims;
|
|
},
|
|
|
|
/**
|
|
* Returns dimensions and positions about the element
|
|
* @param {HTMLElement} el
|
|
* @return {Array<number>}
|
|
*/
|
|
getDim(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];
|
|
},
|
|
|
|
/**
|
|
* Get children dimensions
|
|
* @param {HTMLELement} el Element root
|
|
* @retun {Array}
|
|
* */
|
|
getChildrenDim(elem) {
|
|
var dims = [];
|
|
if(!elem)
|
|
return dims;
|
|
|
|
// Get children based on getChildrenContainer
|
|
var $elem = $(elem);
|
|
var elemData = $elem.data('model');
|
|
if (elemData && elemData.view) {
|
|
elem = elemData.view.getChildrenContainer();
|
|
}
|
|
|
|
var ch = elem.children; //TODO filter match
|
|
for (var i = 0, len = ch.length; i < len; i++) {
|
|
var el = ch[i];
|
|
if(!this.matches(el, this.itemSel))
|
|
continue;
|
|
var dim = this.getDim(el);
|
|
var dir = this.direction;
|
|
|
|
if(dir == 'v')
|
|
dir = true;
|
|
else if(dir == 'h')
|
|
dir = false;
|
|
else
|
|
dir = this.isInFlow(el, elem);
|
|
|
|
dim.push(dir);
|
|
dim.push(el);
|
|
dims.push(dim);
|
|
}
|
|
return dims;
|
|
},
|
|
|
|
/**
|
|
* Check if the coordinates are near to the borders
|
|
* @param {Array<number>} dim
|
|
* @param {number} rX Relative X position
|
|
* @param {number} rY Relative Y position
|
|
* @return {Boolean}
|
|
* */
|
|
nearBorders(dim, rX, rY) {
|
|
var result = 0;
|
|
var off = this.borderOffset;
|
|
var x = rX || 0;
|
|
var y = rY || 0;
|
|
var t = dim[0];
|
|
var l = dim[1];
|
|
var h = dim[2];
|
|
var w = dim[3];
|
|
if( ((t + off) > y) || (y > (t + h - off)) ||
|
|
((l + off) > x) || (x > (l + w - off)) )
|
|
result = 1;
|
|
|
|
return !!result;
|
|
},
|
|
|
|
/**
|
|
* Find the position based on passed dimensions and coordinates
|
|
* @param {Array<Array>} dims Dimensions of nodes to parse
|
|
* @param {number} posX X coordindate
|
|
* @param {number} posY Y coordindate
|
|
* @retun {Object}
|
|
* */
|
|
findPosition(dims, posX, posY) {
|
|
var result = {index: 0, method: 'before'};
|
|
var leftLimit = 0, xLimit = 0, dimRight = 0, yLimit = 0, xCenter = 0, yCenter = 0, dimDown = 0, dim = 0;
|
|
// Each dim is: Top, Left, Height, Width
|
|
for(var i = 0, len = dims.length; i < len; i++){
|
|
dim = dims[i];
|
|
// Right position of the element. Left + Width
|
|
dimRight = dim[1] + dim[3];
|
|
// Bottom position of the element. Top + Height
|
|
dimDown = dim[0] + dim[2];
|
|
// X center position of the element. Left + (Width / 2)
|
|
xCenter = dim[1] + (dim[3] / 2);
|
|
// Y center position of the element. Top + (Height / 2)
|
|
yCenter = dim[0] + (dim[2] / 2);
|
|
// Skip if over the limits
|
|
if( (xLimit && dim[1] > xLimit) ||
|
|
(yLimit && yCenter >= yLimit) || // >= avoid issue with clearfixes
|
|
(leftLimit && dimRight < leftLimit) )
|
|
continue;
|
|
result.index = i;
|
|
// If it's not in flow (like 'float' element)
|
|
if(!dim[4]){
|
|
if(posY < dimDown)
|
|
yLimit = dimDown;
|
|
//If x lefter than center
|
|
if(posX < xCenter){
|
|
xLimit = xCenter;
|
|
result.method = "before";
|
|
}else{
|
|
leftLimit = xCenter;
|
|
result.method = "after";
|
|
}
|
|
}else{
|
|
// If y upper than center
|
|
if(posY < yCenter){
|
|
result.method = "before";
|
|
break;
|
|
}else
|
|
result.method = "after"; // After last element
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
|
|
|
|
/**
|
|
* Updates the position of the placeholder
|
|
* @param {HTMLElement} phl
|
|
* @param {Array<Array>} dims
|
|
* @param {Object} pos Position object
|
|
* @param {Array<number>} trgDim target dimensions
|
|
* */
|
|
movePlaceholder(plh, dims, pos, trgDim) {
|
|
var marg = 0, t = 0, l = 0, w = 0, h = 0,
|
|
un = 'px', margI = 5, brdCol = '#62c462', brd = 3,
|
|
method = pos.method;
|
|
var elDim = dims[pos.index];
|
|
plh.style.borderColor = 'transparent ' + brdCol;
|
|
plh.style.borderWidth = brd + un + ' ' + (brd + 2) + un;
|
|
plh.style.margin = '-' + brd + 'px 0 0';
|
|
if(elDim){
|
|
// If it's not in flow (like 'float' element)
|
|
if(!elDim[4]){
|
|
w = 'auto';
|
|
h = elDim[2] - (marg * 2) + un;
|
|
t = elDim[0] + marg;
|
|
l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg);
|
|
plh.style.borderColor = brdCol + ' transparent';
|
|
plh.style.borderWidth = (brd + 2) + un + ' ' + brd + un;
|
|
plh.style.margin = '0 0 0 -' + brd + 'px';
|
|
}else{
|
|
w = elDim[3] + un;
|
|
h = 'auto';
|
|
t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg);
|
|
l = elDim[1];
|
|
}
|
|
}else{
|
|
if(!this.nested){
|
|
plh.style.display = 'none';
|
|
return;
|
|
}
|
|
if(trgDim){
|
|
t = trgDim[0] + margI;
|
|
l = trgDim[1] + margI;
|
|
w = (parseInt(trgDim[3]) - margI * 2) + un;
|
|
h = 'auto';
|
|
}
|
|
}
|
|
plh.style.top = t + un;
|
|
plh.style.left = l + un;
|
|
if(w)
|
|
plh.style.width = w;
|
|
if(h)
|
|
plh.style.height = h;
|
|
},
|
|
|
|
/**
|
|
* Leave item
|
|
* @param event
|
|
*
|
|
* @return void
|
|
* */
|
|
endMove(e) {
|
|
var created;
|
|
this.$el.off('mousemove', this.onMove);
|
|
this.$document.off('mouseup', this.endMove);
|
|
this.$document.off('keydown', this.rollback);
|
|
this.plh.style.display = 'none';
|
|
var clsReg = new RegExp('(?:^|\\s)'+this.freezeClass+'(?!\\S)', 'gi');
|
|
let trg = this.eV;
|
|
|
|
if (trg) {
|
|
var className = (trg.getAttribute('class')+'').replace(clsReg, '');
|
|
trg.setAttribute('class', className);
|
|
}
|
|
|
|
if(this.moved)
|
|
created = this.move(this.target, trg, this.lastPos);
|
|
if(this.plh)
|
|
this.plh.style.display = 'none';
|
|
|
|
if(typeof this.onEndMove === 'function')
|
|
this.onEndMove(created);
|
|
|
|
var dragHelper = this.dragHelper;
|
|
|
|
if(dragHelper) {
|
|
dragHelper.remove();
|
|
this.dragHelper = null;
|
|
}
|
|
|
|
this.selectTargetModel();
|
|
this.toggleSortCursor();
|
|
},
|
|
|
|
/**
|
|
* Move component to new position
|
|
* @param {HTMLElement} dst Destination target
|
|
* @param {HTMLElement} src Element to move
|
|
* @param {Object} pos Object with position coordinates
|
|
* */
|
|
move(dst, src, pos) {
|
|
var em = this.em;
|
|
if (em) em.trigger('component:dragEnd:before', dst, src, pos);
|
|
var warns = [];
|
|
var modelToDrop, modelTemp, created;
|
|
var index = pos.index;
|
|
var model = $(src).data('model');
|
|
var $dst = $(dst);
|
|
var targetModel;
|
|
|
|
while ($dst.length && !targetModel) {
|
|
targetModel = $dst.data('model');
|
|
dst = $dst.get(0);
|
|
|
|
if (targetModel && targetModel.view)
|
|
dst = targetModel.view.el;
|
|
|
|
if (!targetModel)
|
|
$dst = $dst.parent();
|
|
}
|
|
|
|
var targetCollection = $dst.data('collection');
|
|
// Check if the elemenet is DRAGGABLE to the target
|
|
var drag = model && model.get('draggable');
|
|
var draggable = typeof drag !== 'undefined' ? drag : 1;
|
|
var toDrag = draggable;
|
|
|
|
if (this.dropContent instanceof Object) {
|
|
draggable = this.dropContent.draggable;
|
|
draggable = typeof draggable !== 'undefined' ? draggable : 1;
|
|
} else if (typeof this.dropContent === 'string' && targetCollection) {
|
|
var sandboxOpts = {silent: true};
|
|
var sandboxModel = targetCollection.add(this.dropContent, sandboxOpts);
|
|
draggable = sandboxModel.get && sandboxModel.get('draggable');
|
|
draggable = typeof draggable !== 'undefined' ? draggable : 1;
|
|
targetCollection.remove(sandboxModel, sandboxOpts);
|
|
}
|
|
|
|
if(draggable instanceof Array) {
|
|
toDrag = draggable.join(', ');
|
|
draggable = this.matches(dst, toDrag);
|
|
}else if(typeof draggable === 'string') {
|
|
toDrag = draggable;
|
|
draggable = this.matches(dst, toDrag, 1);
|
|
}
|
|
|
|
// Check if the target could accept the element to be DROPPED inside
|
|
var accepted = 1;
|
|
var droppable = targetModel && targetModel.get ? targetModel.get('droppable') : 1;
|
|
var toDrop = draggable;
|
|
if(droppable instanceof Array) {
|
|
// When I drag blocks src is the HTMLElement of the block
|
|
toDrop = droppable.join(', ');
|
|
accepted = this.matches(src, toDrop);
|
|
}else if(typeof droppable === 'string') {
|
|
toDrop = droppable;
|
|
accepted = this.matches(src, toDrop);
|
|
}
|
|
|
|
if(targetCollection && droppable && accepted && draggable) {
|
|
index = pos.method === 'after' ? index + 1 : index;
|
|
var opts = {at: index, noIncrement: 1};
|
|
if(!this.dropContent){
|
|
modelTemp = targetCollection.add({}, opts);
|
|
if(model)
|
|
modelToDrop = model.collection.remove(model);
|
|
|
|
}else{
|
|
modelToDrop = this.dropContent;
|
|
opts.silent = false;
|
|
}
|
|
created = targetCollection.add(modelToDrop, opts);
|
|
if(!this.dropContent){
|
|
targetCollection.remove(modelTemp);
|
|
}else{
|
|
this.dropContent = null;
|
|
}
|
|
// This will cause to recalculate children dimensions
|
|
this.prevTarget = null;
|
|
} else {
|
|
if(!targetCollection){
|
|
warns.push('target collection not found');
|
|
}
|
|
if(!droppable){
|
|
warns.push('target is not droppable');
|
|
}
|
|
if(!draggable){
|
|
warns.push('component not draggable, accepted only by [' + toDrag + ']');
|
|
}
|
|
if(!accepted){
|
|
warns.push('target accepts only [' + toDrop + ']');
|
|
}
|
|
console.warn('Invalid target position: ' + warns.join(', '));
|
|
}
|
|
|
|
if (em)
|
|
em.trigger('component:dragEnd', targetCollection, modelToDrop, warns);
|
|
|
|
return created;
|
|
},
|
|
|
|
/**
|
|
* Rollback to previous situation
|
|
* @param {Event}
|
|
* @param {Bool} Indicates if rollback in anycase
|
|
* */
|
|
rollback(e) {
|
|
$(document).off('keydown', this.rollback);
|
|
this.$document.off('keydown', this.rollback);
|
|
var key = e.which || e.keyCode;
|
|
|
|
if (key == 27) {
|
|
this.moved = false;
|
|
this.endMove();
|
|
}
|
|
return;
|
|
},
|
|
|
|
});
|
|
|