6PSALZKWII5BVJZG7DOPH2SNY2Q4CJR6QSMROXCTVSJ6UPX2RIUAC
.screen {
box-sizing: border-box;
position: absolute;
background-color: #8BC34A;
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
border: 5px solid #689f38;
transition: transform 0.2s ease-out, opacity 0.2s ease-out, box-shadow 0.2s ease-out;
.draggable {
cursor: default;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
while (target != elem && !target.classList.contains('dragabble')) {
target = target.parentElement;
}
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);
if (target == elem) {
var rel = localizePoint(x, y);
var screen = new Screen(rel.x, rel.y);
screens.push(screen);
target = screen.elem;
// Setup touch events
function touchEvent(cb, e) {
for (var i = 0; i < e.changedTouches.length; i++) {
var touch = e.changedTouches[i];
cb.call(this, {
id: touch.identifier,
target: e.target,
pos: {x: touch.clientX, y: touch.clientY},
ctrlKey: e.ctrlKey,
});
var dx = x - focus.lastX;
var dy = y - focus.lastY;
focus.target.style.left = (parseInt(focus.target.style.left) || 0) + dx + 'px';
focus.target.style.top = (parseInt(focus.target.style.top) || 0) + dy + 'px';
focus.lastX = x;
focus.lastY = y;
Canvas.prototype.getCenter = function() {
var rect = this.elem.getBoundingClientRect();
return {
x: rect.width / 2,
y: rect.height / 2,
};
Canvas.prototype.localizePoint = function(pos) {
var rect = this.elem.getBoundingClientRect();
return {
x: pos.x - rect.left - this.pos.x,
y: pos.y - rect.top - this.pos.y,
};
};
Canvas.prototype.addScreen = function(pos) {
var id = this.screens.length;
var screen = new Screen({
id: id,
name: 'gallifrey',
pos: pos,
size: {x: 160, y: 100},
});
screen.connectClosest(this.screens);
this.screens.push(screen);
this.view.appendChild(screen.elem);
return screen;
};
Canvas.prototype.setPos = function(pos) {
this.pos = pos;
this.view.style.transform = 'translate(' + this.pos.x + 'px,' + this.pos.y + 'px)';
};
Canvas.prototype.move = function(d) {
this.setPos({
x: this.pos.x + d.x,
y: this.pos.y + d.y
});
};
function dragEnd(id, x, y) {
let focus = focuses[id];
if (!focus) {
return
}
focus.target.classList.remove('drag');
delete focuses[id];
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;
window.addEventListener('mousemove', function(e) {
dragMove(null, e.clientX, e.clientY);
}, false);
window.addEventListener('mouseup', function(e) {
dragEnd(null, e.clientX, e.clientY);
}, false);
// Bubble target until reached a valid target or the canvas
var target = e.target;
while (target != this.elem && !target.dataset.id) {
target = target.parentElement;
}
// Touch
elem.addEventListener('touchstart', function(e) {
for (var i = 0; i < e.changedTouches.length; i++) {
var touch = e.changedTouches[i];
dragStart(touch.target, touch.identifier, touch.clientX, touch.clientY);
}
e.preventDefault();
}, false);
// 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;
}
elem.addEventListener('touchmove', function(e) {
for (var i = 0; i < e.changedTouches.length; i++) {
var touch = e.changedTouches[i];
dragMove(touch.identifier, touch.clientX, touch.clientY);
}
e.preventDefault();
}, false);
dragTarget.elem.classList.add('dragging');
this.focuses[e.id] = {
target: dragTarget,
lastPos: e.pos,
};
};
Canvas.prototype.dragMove = function(e) {
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,
});
function Screen(x, y) {
var width = 160;
var height = 100;
Canvas.prototype.dragEnd = function(e) {
let focus = this.focuses[e.id];
if (!focus) {
return;
}
var target = focus.target;
if (target.connectClosest) {
target.connectClosest(this.screens.filter((screen) => {
return screen != target;
}));
}
focus.target.elem.classList.remove('dragging');
delete this.focuses[e.id];
};
function Screen(params) {
Screen.prototype.setPos = function(pos) {
this.pos = pos;
this.elem.style.left = this.pos.x - this.size.x / 2 + 'px';
this.elem.style.top = this.pos.y - this.size.y / 2 + 'px';
};
Screen.prototype.setSize = function(size) {
this.size = size;
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);
};
Screen.prototype.closest = function(screens) {
var pos = this.pos;
return screens.reduce((min, curr) => {
var dist = curr.distance(pos);
if (!min || dist < min.dist) {
return {dist: dist, screen: curr};
}
return min;
}, null);
};
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.connectClosest = function(screens) {
let closest = this.closest(screens);
if (closest) this.connect(closest.screen);
};