|
|
|
@ -20,6 +20,12 @@ function des_u16(d) {
@@ -20,6 +20,12 @@ function des_u16(d) {
|
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function des_s16(d) { |
|
|
|
|
const value = d.view.getInt16(d.offset, true); |
|
|
|
|
d.offset += 2; |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function des_u32(d) { |
|
|
|
|
const value = d.view.getUint32(d.offset, true); |
|
|
|
|
d.offset += 4; |
|
|
|
@ -76,8 +82,8 @@ function des_event(d) {
@@ -76,8 +82,8 @@ function des_event(d) {
|
|
|
|
|
case EVENT.IMAGE: |
|
|
|
|
case EVENT.IMAGE_MOVE: { |
|
|
|
|
event.image_id = des_u32(d); |
|
|
|
|
event.x = des_u16(d); |
|
|
|
|
event.y = des_u16(d); |
|
|
|
|
event.x = des_s16(d); // stored as u16, but actually is s16
|
|
|
|
|
event.y = des_s16(d); // stored as u16, but actually is s16
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -133,16 +139,38 @@ async function handle_event(event) {
@@ -133,16 +139,38 @@ async function handle_event(event) {
|
|
|
|
|
case EVENT.UNDO: { |
|
|
|
|
for (let i = storage.events.length - 1; i >=0; --i) { |
|
|
|
|
const other_event = storage.events[i]; |
|
|
|
|
if (other_event.type === EVENT.STROKE && other_event.user_id === event.user_id && !other_event.deleted) { |
|
|
|
|
other_event.deleted = true; |
|
|
|
|
const stats = stroke_stats(other_event.points, storage.cursor.width); |
|
|
|
|
redraw_region(stats.bbox); |
|
|
|
|
break; |
|
|
|
|
} else if (other_event.type === EVENT.IMAGE && other_event.user_id === event.user_id && !other_event.deleted) { |
|
|
|
|
other_event.deleted = true; |
|
|
|
|
const item = document.querySelector(`img[data-image-id="${other_event.image_id}"]`); |
|
|
|
|
if (item) item.remove(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
// Users can only undo their own, undeleted (not already undone) events
|
|
|
|
|
if (other_event.user_id === event.user_id && !other_event.deleted) { |
|
|
|
|
if (other_event.type === EVENT.STROKE) { |
|
|
|
|
other_event.deleted = true; |
|
|
|
|
const stats = stroke_stats(other_event.points, storage.cursor.width); |
|
|
|
|
redraw_region(stats.bbox); |
|
|
|
|
break; |
|
|
|
|
} else if (other_event.type === EVENT.IMAGE) { |
|
|
|
|
other_event.deleted = true; |
|
|
|
|
const item = document.querySelector(`img[data-image-id="${other_event.image_id}"]`); |
|
|
|
|
if (item) item.remove(); |
|
|
|
|
break; |
|
|
|
|
} else if (other_event.type === EVENT.ERASER) { |
|
|
|
|
other_event.deleted = true; |
|
|
|
|
const erased = find_stroke_backwards(other_event.stroke_id); |
|
|
|
|
if (erased) { |
|
|
|
|
erased.deleted = false; |
|
|
|
|
const stats = stroke_stats(erased.points, storage.cursor.width); |
|
|
|
|
redraw_region(stats.bbox); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} else if (other_event.type === EVENT.IMAGE_MOVE) { |
|
|
|
|
const item = document.querySelector(`img[data-image-id="${other_event.image_id}"]`); |
|
|
|
|
|
|
|
|
|
const ix = storage.images[other_event.image_id].x -= other_event.x; |
|
|
|
|
const iy = storage.images[other_event.image_id].y -= other_event.y; |
|
|
|
|
|
|
|
|
|
item.style.transform = `translate(${ix}px, ${iy}px)`; |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -174,16 +202,27 @@ async function handle_event(event) {
@@ -174,16 +202,27 @@ async function handle_event(event) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case EVENT.IMAGE_MOVE: { |
|
|
|
|
const image_id = event.image_id; |
|
|
|
|
const item = document.querySelector(`.floating-image[data-image-id="${image_id}"]`); |
|
|
|
|
item.style.transform = `translate(${event.x}px, ${event.y}px)`; |
|
|
|
|
storage.images[event.image_id] = { |
|
|
|
|
'x': event.x, 'y': event.y |
|
|
|
|
}; |
|
|
|
|
// Already moved due to local prediction
|
|
|
|
|
if (event.user_id !== storage.me.id) { |
|
|
|
|
const image_id = event.image_id; |
|
|
|
|
const item = document.querySelector(`.floating-image[data-image-id="${image_id}"]`); |
|
|
|
|
|
|
|
|
|
const ix = storage.images[event.image_id].x += event.x; |
|
|
|
|
const iy = storage.images[event.image_id].y += event.y; |
|
|
|
|
|
|
|
|
|
if (item) { |
|
|
|
|
item.style.transform = `translate(${ix}px, ${iy}px)`; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case EVENT.ERASER: { |
|
|
|
|
if (event.deleted) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const other_event of storage.events) { |
|
|
|
|
if (other_event.type === EVENT.STROKE && other_event.stroke_id === event.stroke_id) { |
|
|
|
|
// Might already be deleted because of local prediction
|
|
|
|
|