B2X5NJ7QAQTOZXHDAGANNGNYHTPN6LWKTJTL5IM6VBP4NETHHS5AC
function dragStart(e) {
// Add new screens when ctrl is pressed
if (e.ctrlKey) {
this.addScreen(this.localizePos(e.pos));
return;
}
if (this.focuses[e.id]) {
return;
}
// Bubble target until reached a valid target or the canvas
var elem = e.target;
while (elem != this.elem && !elem.dataset.id) {
elem = elem.parentElement;
}
// Obtain draggable interface for target
var target;
if (elem != this.elem) {
target = this.screens[elem.dataset.id];
this.view.appendChild(target.elem);
} else {
target = this;
}
// Call target callback
target.elem.classList.add('dragging');
if (target.dragStart) {
e.source = this;
e.delta = [0, 0];
target.dragStart(e);
}
this.focuses[e.id] = {
target: target,
lastPos: e.pos,
};
};
function dragMove(e) {
var focus = this.focuses[e.id];
if (!focus) {
return;
}
// Call target callback
var target = focus.target;
if (target.dragMove) {
e.source = this;
e.delta = vectorSub(e.pos, focus.lastPos);
target.dragMove(e);
}
focus.lastPos = e.pos;
};
function dragEnd(e) {
var focus = this.focuses[e.id];
if (!focus) {
return;
}
// Call target callback
var target = focus.target;
target.elem.classList.remove('dragging');
if (target.dragEnd) {
e.source = this;
e.delta = vectorSub(e.pos, focus.lastPos);
target.dragEnd(e);
}
delete this.focuses[e.id];
};
elem.addEventListener('mousedown', mouseEvent.bind(this, this.dragStart), false);
window.addEventListener('mousemove', mouseEvent.bind(this, this.dragMove), false);
window.addEventListener('mouseup', mouseEvent.bind(this, this.dragEnd), false);
elem.addEventListener('mousedown', mouseEvent.bind(this, dragStart), false);
window.addEventListener('mousemove', mouseEvent.bind(this, dragMove), false);
window.addEventListener('mouseup', mouseEvent.bind(this, dragEnd), false);
// Setup resize event
// window.addEventListener('resize', this.recenter.bind(this), false);
elem.addEventListener('touchstart', touchEvent.bind(this, dragStart), false);
elem.addEventListener('touchmove', touchEvent.bind(this, dragMove), false);
elem.addEventListener('touchend', touchEvent.bind(this, dragEnd), false);
Canvas.prototype.getCenter = function() {
var rect = this.elem.getBoundingClientRect();
return {
x: rect.width / 2,
y: rect.height / 2,
}
Canvas.prototype.setPos = function(pos) {
this.pos = pos;
this.view.style.transform = 'translate(' + this.pos[0] + 'px,' + this.pos[1] + 'px)';
return {
x: pos.x - rect.left - this.pos.x,
y: pos.y - rect.top - this.pos.y,
};
};
return [
pos[0] - rect.left - this.pos[0],
pos[1] - rect.top - this.pos[1],
];
};
Canvas.prototype.getScreens = function() {
return this.screens.filter((screen) => {
// Ignore screens that have focuses on them
for (key in this.focuses) {
if (screen == this.focuses[key].target) {
return false;
}
}
return true;
});
};
Canvas.prototype.recenter = function() {
this.setPos({x: 0, y: 0});
};
Canvas.prototype.dragStart = function(e) {
// Add new screens when ctrl is pressed
if (e.ctrlKey) {
this.addScreen(this.localizePoint(e.pos));
return;
}
if (this.focuses[e.id]) {
return;
}
// Bubble target until reached a valid target or the canvas
var target = e.target;
while (target != this.elem && !target.dataset.id) {
target = target.parentElement;
}
// Obtain draggable interface for target
var dragTarget;
if (target != this.elem) {
dragTarget = this.screens[target.dataset.id];
this.view.appendChild(dragTarget.elem);
} else {
dragTarget = this;
}
dragTarget.elem.classList.add('dragging');
this.focuses[e.id] = {
target: dragTarget,
lastPos: e.pos,
};
};
let focus = this.focuses[e.id];
if (!focus) {
return;
}
focus.target.move({
x: e.pos.x - focus.lastPos.x,
y: e.pos.y - focus.lastPos.y,
});
focus.lastPos = e.pos;
};
Canvas.prototype.dragEnd = function(e) {
let focus = this.focuses[e.id];
if (!focus) {
return;
}
var target = focus.target;
if (target.connectClosest) {
let blarg = this.screens.filter((screen) => {
for (key in this.focuses) {
if (screen == this.focuses[key].target) {
return false;
}
}
return true;
});
target.connectClosest(blarg);
}
focus.target.elem.classList.remove('dragging');
delete this.focuses[e.id];
};
this.setPos(vectorAdd(this.pos, e.delta));
}
this.elem.style.width = this.size.x + 'px';
this.elem.style.height = this.size.y + 'px';
};
Screen.prototype.move = function(d) {
this.setPos({
x: this.pos.x + d.x,
y: this.pos.y + d.y,
});
};
Screen.prototype.distance = function(pos) {
var dx = this.pos.x - pos.x;
var dy = this.pos.y - pos.y;
return Math.sqrt(dx * dx + dy * dy);
this.elem.style.width = this.size[0] + 'px';
this.elem.style.height = this.size[1] + 'px';
Screen.prototype.connect = function(screen) {
var pad = 5;
// Compute edge-edge deltas
var dx = this.pos.x - screen.pos.x;
var dy = this.pos.y - screen.pos.y;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0) {
screen.edges.right = this;
this.edges.left = screen;
this.setPos({x: screen.pos.x + screen.size.x + pad, y: screen.pos.y});
} else {
screen.edges.left = this;
this.edges.right = screen;
this.setPos({x: screen.pos.x - this.size.x - pad, y: screen.pos.y});
}
} else {
if (dy > 0) {
screen.edges.bottom = this;
this.edges.top = screen;
this.setPos({x: screen.pos.x, y: screen.pos.y + screen.size.y + pad});
} else {
screen.edges.top = this;
this.edges.bottom = screen;
this.setPos({x: screen.pos.x, y: screen.pos.y - this.size.y - pad});
Screen.prototype.connect = function(other) {
// Find the dimension with the largest delta
// TODO: Take into account aspect ratio
var delta = vectorSub(this.pos, other.pos);
var dim = delta.reduce((max, curr, dim) => {
if (max == null || Math.abs(curr) > Math.abs(delta[max])) {
return dim;
}
return max;
}, null);
// Determine which sides to connect together
var side = delta[dim] < 0 ? 0 : 1;
other.edges[dim][side] = this;
this.edges[dim][1 - side] = other;
// Walk around the graph to find direct neighbours (only works in 2D)
other.edges[1 - dim].forEach((screen, pathSide) => {
if (!screen) return;
screen = screen.edges[dim][side];
if (!screen) return;
// Found a neighbour
this.edges[1 - dim][pathSide] = screen;
screen.edges[1 - dim][1 - pathSide] = this;
screen = screen.edges[dim][side];
if (!screen) return;
screen = screen.edges[1 - dim][1 - pathSide];
if (!screen) return;
// Found a neighbour (if found by one path, should be found by all paths)
this.edges[dim][side] = screen;
screen.edges[dim][1 - side] = this;
});
// Move this screen adjacent to the other screen
var pos = other.pos.slice();
var offset = ((this.size[dim] + other.size[dim]) / 2 + gridPad);
pos[dim] += (2 * side - 1) * offset;
this.setPos(pos);
};
Screen.prototype.swap = function(dim, side) {
// console.log('swap:', dim, side);
// var other = this.edges[dim][side];
// // Swap edges
// var temp = this.edges;
// this.edges = other.edges;
// this.edges[1 - side] = other;
// other.edges = temp;
// other.edges[side] = this;
// // Reposition other screen
// var pos = other.pos;
// var offset = ((this.size[dim] + other.size[dim]) / 2 + gridPad);
// pos[dim] -= (2 * side - 1) * offset;
// other.setPos(pos);
Screen.prototype.dragMove = function(e) {
this.setPos(vectorAdd(this.pos, e.delta));
// Swap when this center meets the edge of another screen
this.pos.forEach((val, dim) => {
this.edges[dim].forEach((other, side) => {
if (other && val >= other.pos[dim] - (2 * side - 1) * other.size[dim] / 2) {
this.swap(dim, side);
}
});
});
};
Screen.prototype.dragEnd = function(e) {
this.connectClosest(e.source.getScreens());
};