let spaceContainer, space; let editing; let dragging; let moved; let movedFrom; let offset; let clicked; document.addEventListener('mouseup', function(event) { clicked = false; }); document.addEventListener('mousemove', function(event) { moved = true; if (dragging) { let left = event.clientX - spaceContainer.offsetLeft - offset.x; let top = event.clientY - spaceContainer.offsetTop - offset.y; dragging.style.left = `${left < 0 ? 0 : left}px`; dragging.style.top = `${top < 0 ? 0 : top}px`; save(dragging); } else if (clicked) { spaceContainer.scrollLeft -= event.movementX; spaceContainer.scrollTop -= event.movementY; } }); let saving; let queue; function save(span) { if (saving) { queue = span; return; } saving = true; window.currentInstance.emit('save_span', { thread: window.currentThread.id, id: span.id ? span.id.slice(4) : '', content: span.innerText, x: span.style.left.slice(0, -2), y: span.style.top.slice(0, -2), scale: span.scale }, msg => { if (!msg.success) console.log('span save failed: ' + msg.message); if (!span.id) span.id = 'span' + msg.id; saving = false; if (queue) { save(queue); queue = null; } }); } function add(s) { let span = document.createElement('span'); span.classList.add('span'); if (s.id) span.id = 'span' + s.id; span.innerText = s.content; span.contentEditable = true; span.spellcheck = false; span.scale = s.scale; span.style.left = `${s.x}px`; span.style.top = `${s.y}px`; span.style.transform = `translate(-50%, -50%) scale(${s.scale})`; span.onkeydown = function(event) { if (event.key === 'Enter' && !event.getModifierState('Shift')) { event.preventDefault(); editing = null; span.blur(); } }; span.oninput = function(event) { save(this); }; span.onblur = function(event) { if (this.innerText) return; this.remove(); if (this.id) save(this); }; span.onwheel = function(event) { event.preventDefault(); event.stopPropagation(); this.scale *= 1 - event.deltaY * .001; if (this.scale > 100) this.scale = 100; this.style.transform = `translate(-50%, -50%) scale(${this.scale})`; save(this); }; span.onmousedown = function(event) { if (dragging || editing === this) return; event.preventDefault(); if (event.button !== 0) return; dragging = this; offset = { x: event.clientX - (spaceContainer.offsetLeft + this.offsetLeft), y: event.clientY - (spaceContainer.offsetTop + this.offsetTop) }; }; span.onmouseup = function(event) { if (event.button !== 0) return; dragging = null; if (moved) return; this.focus(); editing = this; }; space.append(span); return span; } export default function loadSpace(callback) { let instance = window.currentInstance; let scale = 1; if (!instance.spaced) { spaceContainer = document.getElementById('space'); space = document.getElementById('spacediv'); spaceContainer.onwheel = function(event) { if (event.getModifierState('Shift')) return; event.preventDefault(); scale *= 1 - event.deltaY * .001; if (scale < .5) scale = .5; space.style.transform = `scale(${scale})`; spaceContainer.scrollLeft += event.offsetX * (scale - scale / (1 - event.deltaY * .001)); spaceContainer.scrollTop += event.offsetY * (scale - scale / (1 - event.deltaY * .001)); //spaceContainer.scrollLeft += spaceContainer.clientWidth * (1 - spaceContainer.clientWidth / spaceContainer.scrollWidth + event.offsetX / spaceContainer.clientWidth) * (scale - scale / (1 - event.deltaY * .001)); //spaceContainer.scrollTop += spaceContainer.clientHeight * (1 - spaceContainer.clientHeight / spaceContainer.scrollHeight + event.offsetY / spaceContainer.clientHeight) * (scale - scale / (1 - event.deltaY * .001)); }; spaceContainer.onmousedown = function(event) { if (event.button !== 0) return; clicked = true; moved = false; movedFrom = { x: event.offsetX, y: event.offsetY }; }; spaceContainer.onmouseup = function(event) { if (event.button !== 0) return; if (dragging) { dragging.onmouseup(event); return; } if (editing) { if (event.target !== editing) editing = null; return; } if (moved && (event.offsetX - movedFrom.x) * (event.offsetX - movedFrom.x) + (event.offsetY - movedFrom.y) * (event.offsetY - movedFrom.y) > 100) return; editing = add({ x: (event.offsetX + spaceContainer.scrollLeft) / scale, y: (event.offsetY + spaceContainer.scrollTop) / scale, scale: 1, content: '' }); editing.focus(); }; instance.socket.on('span', msg => { if (msg.thread !== window.currentSpace.id || window.currentInstance !== instance) return; let span = document.getElementById('span' + msg.id); if (span) { span.innerText = msg.content; span.style.left = `${msg.x}px`; span.style.top = `${msg.y}px`; span.scale = msg.scale; span.style.transform = `translate(-50%, -50%) scale(${msg.scale})`; } else add(msg); }); instance.spaced = true; } else if (window.currentSpace === window.currentThread) return; window.currentSpace = window.currentThread; space.innerHTML = ''; space.style.transform = ''; instance.emit('get_space', { thread: window.currentSpace.id }, msg => { if (!msg.success) { console.log('get space failed: ' + msg.message); return; } callback && callback(msg.spans); msg.spans.forEach(add); }); };