diff --git a/src/index.js b/src/index.js index a055cdd4..07093e92 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,7 @@ import { PI2, MAT_NUM_WIDTH, CHAR, + MODE_KEYS, } from './resources'; // custom functions! @@ -1096,6 +1097,7 @@ export function present() { * Sets page up for presentation mode. */ function setUpPresentationMode() { + rtv.c.focus(); enterSelect(); // Enter select mode document.body.style.cursor = 'none'; // Hide cursor document.body.style.overflow = 'hidden'; // Disable and hide scrollbars @@ -3584,6 +3586,7 @@ window.addEventListener('load', () => { rtv.c = document.getElementById('viewport'); rtv.c.style.backgroundColor = CANVAS_BG; + rtv.c.focus(); rtv.ctx = rtv.c.getContext('2d'); @@ -3706,125 +3709,104 @@ window.addEventListener('load', () => { rtv.keys.ctrl = false; }); - window.addEventListener('keydown', (evt) => { - const { key } = evt; - - if (key === 'Escape' && rtv.presenting && rtv.tool !== 'camera' && rtv.tool !== 'pen') { - rtv.presenting = false; - document.body.style.cursor = ''; - document.body.style.overflow = 'auto'; // Enable and show scrollbar - return false; - } + rtv.c.addEventListener('keydown', (evt) => { + if (evt.key === 'Escape') { + if (rtv.presenting && rtv.tool !== 'camera' && rtv.tool !== 'pen') { + rtv.presenting = false; + document.body.style.cursor = ''; + document.body.style.overflow = 'auto'; // Enable and show scrollbar + return; + } - if (key === 'Escape') { enterSelect(); } - if (key === 'Tab') { - rtv.keys.tab = true; - } - - if (key === 'Meta') { - rtv.keys.meta = true; - } - - if (key === 'Shift') { - rtv.keys.shift = true; - } - - if (key === 'Control') { - rtv.keys.ctrl = true; + if (rtv.keys.ctrl && evt.key === 'Backspace') { + rtv.objs.forEach((obj) => { + if (obj.is_selected) obj.deleted = true; + }); } - if (key === 'Backspace') { - if (rtv.keys.ctrl) { - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - const obj = rtv.objs[i]; - if (obj.is_selected()) { - obj.deleted = true; - } - } - } - } - - if (key === 'z' && (rtv.keys.meta || rtv.keys.ctrl)) { + if (evt.key === 'z' && (rtv.keys.meta || rtv.keys.ctrl)) { undo(); return; } - if ((rtv.keys.meta || rtv.keys.ctrl) && key === 'Enter') { - present(); - return true; - } - - if (document.getElementById('formula_text') === document.activeElement) { - return true; - } - let captured = false; - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - const obj = rtv.objs[i]; - - if (typeof obj.onkeydown === 'function') { - if (obj.onkeydown(evt)) { - captured = true; - evt.preventDefault(); // Prevent default if event has been handled - if (key === 'ArrowUp' || key === 'ArrowDown') { - // stops text selection from propagating as you iterate the array - break; - } - } + rtv.objs.forEach((obj) => { + if (obj.onkeydown?.(evt)) { + captured = true; + return evt.key === 'ArrowDown'; } - } - - if (captured) { return false; + }); + if (captured) { + evt.preventDefault(); + return; } if (rtv.frames.onkeydown(evt)) { - return false; + evt.preventDefault(); + return; } rtv.cam.onkeydown(evt); rtv.pen.onkeydown(evt); - if (key === ' ') { - return false; - } + if (rtv.tool === 'select' && evt.key in MODE_KEYS) rtv.tool = MODE_KEYS[evt.key]; + }); - if (rtv.tool === 'select' && evt.target === document.body) { - const tools = { - t: 'text', s: 'shape', c: 'camera', v: 'vector', - }; - if (key in tools) { - rtv.tool = tools[key]; - } + window.addEventListener('keydown', (evt) => { + switch (evt.key) { + case 'Tab': + rtv.keys.tab = true; + break; + + case 'Meta': + rtv.keys.meta = true; + break; + + case 'Shift': + rtv.keys.shift = true; + break; + + case 'Control': + rtv.keys.ctrl = true; + break; + + case 'Enter': + if (rtv.keys.meta || rtv.keys.ctrl) present(); + break; + + // no default } }); window.addEventListener('keyup', ({ key }) => { - if (key === 'Tab') { - rtv.keys.tab = false; - } + switch (key) { + case 'Tab': + rtv.keys.tab = false; + break; - if (key === 'Meta') { - rtv.keys.meta = false; - } + case 'Meta': + rtv.keys.meta = false; + break; - if (key === 'Shift') { - rtv.keys.shift = false; - } + case 'Shift': + rtv.keys.shift = false; + break; - if (key === 'Control') { - rtv.keys.ctrl = false; + case 'Control': + rtv.keys.ctrl = false; + break; + + // no default } saveState(); }); - ['mousedown', 'touchstart'].forEach((key) => rtv.c.addEventListener(key, (evt) => { + ['mousedown', 'touchstart'].forEach((evtName) => rtv.c.addEventListener(evtName, (evt) => { rtv.mouse.down = true; rtv.mouse.start = getMousePos(rtv.c, evt); @@ -3832,52 +3814,13 @@ window.addEventListener('load', () => { math.compile('click()').evaluate(parser.scope); } catch { /* Continue */ } - if (rtv.cam.mouse_down(evt)) { - return; - } - - if (rtv.pen.mouse_down(evt)) { - return; - } - - if (rtv.presenting) { - return false; - } - - let captured = false; - for (let i = rtv.objs.length - 1; i >= 0; i--) { - const obj = rtv.objs[i]; - if (typeof obj.mouse_down === 'function') { - if (obj.mouse_down(evt)) { - captured = true; - break; - } - } - } - - if (captured) { - return false; - } - - if (rtv.frames.mouse_down()) { - return; - } - - // didn't touch an obj, if tool is move start a rect - let objSelected = false; - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - if (rtv.objs[i].is_selected()) { - objSelected = true; - } - } - - if (rtv.tool === 'select' && objSelected === false) { - rtv.selecting = true; - } + if (rtv.cam.mouse_down(evt) || rtv.pen.mouse_down(evt) || rtv.presenting) return; + if (rtv.objs.some((obj) => obj.mouse_down?.(evt))) evt.preventDefault(); + if (rtv.frames.mouse_down()) return; + if (rtv.tool === 'select') rtv.selecting = true; })); - ['mousemove', 'touchmove'].forEach((key) => window.addEventListener(key, (evt) => { + ['mousemove', 'touchmove'].forEach((key) => rtv.c.addEventListener(key, (evt) => { // update mouse rtv.mouse.pos = getMousePos(rtv.c, evt); rtv.mouse.grid = constrainToGrid(rtv.mouse.pos); @@ -3886,61 +3829,33 @@ window.addEventListener('load', () => { Text.setVariable('_y', rtv.mouse.graph.x); Text.setVariable('_z', rtv.mouse.graph.y); - if (rtv.pen.mouse_move(evt)) { - return; - } + if (rtv.pen.mouse_move(evt)) return; if (rtv.mouse.down) { - let captured = false; - const N = rtv.objs.length; - for (let i = N - 1; i >= 0; i--) { - const obj = rtv.objs[i]; - if (!captured && typeof obj.mouse_drag === 'function') { - captured = obj.mouse_drag(evt); - } - } - - if (!captured) { - rtv.cam.mouse_drag(evt); + if (!rtv.objs.some((obj) => obj.mouse_drag?.(evt))) { + rtv.cam.mouse_drag(); } } else { - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - const obj = rtv.objs[i]; - if (typeof obj.mouse_move === 'function') { - obj.mouse_move(evt); - } - } + rtv.objs.forEach((obj) => obj.mouse_move?.(evt)); } - if (rtv.presenting) { - rtv.mouse.time = MOUSE_DURATION; - } + if (rtv.presenting) rtv.mouse.time = MOUSE_DURATION; rtv.mouse.last = getMousePos(rtv.c, evt); rtv.mouse.gridLast = constrainToGrid(rtv.mouse.pos); })); - ['mouseup', 'touchend'].forEach((key) => rtv.c.addEventListener(key, (evt) => { + ['mouseup', 'touchend'].forEach((evtName) => rtv.c.addEventListener(evtName, (evt) => { rtv.mouse.down = false; if (rtv.presenting) { // maybe tap some text - let captured = false; - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - const obj = rtv.objs[i]; - if (!captured && typeof obj.mouse_up === 'function') { - captured = obj.mouse_up(evt); - } - } + rtv.objs.some((obj) => obj.mouse_up?.()); - return false; + evt.preventDefault(); } - if (rtv.frames.mouse_up(evt)) { - return; - } + if (rtv.frames.mouse_up(evt)) return; if (rtv.menu.mouse_up(evt)) { rtv.new_line = null; @@ -3955,96 +3870,86 @@ window.addEventListener('load', () => { return; } - if (rtv.tool === 'select') { - let captured = false; - const N = rtv.objs.length; - for (let i = N - 1; i >= 0; i--) { - const obj = rtv.objs[i]; - if (!captured && typeof obj.mouse_up === 'function') { - captured = obj.mouse_up(evt); - } - } - } else if (rtv.tool === 'text') { - // add a num obj at mouse pos - const n = new Text('', rtv.mouse.grid); + switch (rtv.tool) { + case 'select': + rtv.objs.some((obj) => obj.mouse_up?.()); + break; - const N = rtv.objs.length; - for (let i = 0; i < N; i++) { - const obj = rtv.objs[i]; - if (typeof obj.is_selected === 'function') { - obj.selected = false; - } - } + case 'text': { + rtv.objs.forEach((obj) => { obj.selected = false; }); - n.select(); - rtv.objs.push(n); - } else if (rtv.tool === 'shape' || rtv.tool === 'vector') { - // add a num obj at mouse pos - if (rtv.new_line) { - // add a point - rtv.new_line.add_point({ x: rtv.mouse.grid.x, y: rtv.mouse.grid.y }); - } else { - const l = new Shape([0, 0, 0, 1], [{ x: rtv.mouse.grid.x, y: rtv.mouse.grid.y }]); + // add a num obj at mouse pos + const newText = new Text('', rtv.mouse.grid); + newText.select(); + rtv.objs.push(newText); + } break; + + case 'shape': + case 'vector': + // add a num obj at mouse pos + if (rtv.new_line) { + // add a point + rtv.new_line.add_point({ x: rtv.mouse.grid.x, y: rtv.mouse.grid.y }); + } else { + const l = new Shape([0, 0, 0, 1], [{ x: rtv.mouse.grid.x, y: rtv.mouse.grid.y }]); + + switch (rtv.tool) { + case 'vector': + l.properties[rtv.frame].v = true; + break; - if (rtv.tool === 'vector') { - l.properties[rtv.frame].v = true; - } else if (rtv.tool === 'circle') { - l.properties[rtv.frame].circle = true; + case 'circle': + l.properties[rtv.frame].circle = true; + break; + + // no default + } + + rtv.objs.push(l); + rtv.new_line = l; } + return; - rtv.objs.push(l); - rtv.new_line = l; - } + case 'circle': { + const newCircle = new Circle([0, 0, 0, 1], rtv.mouse.grid); + rtv.objs.push(newCircle); + } break; - return; - } else if (rtv.tool === 'circle') { - const newCircle = new Circle([0, 0, 0, 1], rtv.mouse.grid); - rtv.objs.push(newCircle); - } else if (rtv.tool === 'network') { - const n = new Network(rtv.mouse.grid); - rtv.objs.push(n); + case 'network': { + const newNetwork = new Network(rtv.mouse.grid); + rtv.objs.push(newNetwork); + } break; + + // no default } if (rtv.selecting) { rtv.selecting = false; - const { x } = rtv.mouse.start; - const { y } = rtv.mouse.start; - const x2 = rtv.mouse.pos.x; - const y2 = rtv.mouse.pos.y; + const { x, y } = rtv.mouse.start; + const { x: x2, y: y2 } = rtv.mouse.pos; const xx = Math.min(x, x2); const yy = Math.min(y, y2); const xx2 = Math.max(x, x2); const yy2 = Math.max(y, y2); - rtv.selected_objs = []; - - for (let i = 0; i < rtv.objs.length; i++) { - const obj = rtv.objs[i]; + rtv.selected_objs = rtv.objs.filter((obj) => { if (typeof obj.in_rect === 'function') { obj.in_rect(xx, yy, xx2, yy2); - if (obj.is_selected()) { - rtv.selected_objs.push(obj); - } + return obj.is_selected(); } - } - - const scopy = copy(rtv.selected_objs); - for (let i = 0; i < scopy.length; i++) { - const obj = scopy[i]; - const props = copy(obj.properties[rtv.frame]); - obj.properties = { 1: props }; - } + return false; + }); - if (scopy.length > 0) { + if (rtv.selected_objs.length) { // store as text rep - const string = JSON.stringify(scopy); - document.getElementById('selected_objects_text').value = string; + document.getElementById('selected_objects_text').value = JSON.stringify( + rtv.selected_objs.map( + (obj) => ({ ...obj, properties: { 1: obj.properties[rtv.frame] } }), + ), + ); } - - saveState(); - return false; } saveState(); diff --git a/src/resources.js b/src/resources.js index 17e85576..722af9d2 100644 --- a/src/resources.js +++ b/src/resources.js @@ -5,6 +5,13 @@ import './style.css'; export const VERSION = packageJson.version; // Version const IS_MAC = navigator.platform.toUpperCase().includes('MAC'); // Running on macOS? +export const MODE_KEYS = { + c: 'camera', + s: 'shape', + t: 'text', + v: 'vector', +}; + // Text and image border export const BORDER_OPACITY = 0.2; diff --git a/src/template.html b/src/template.html index 98559a85..29aadcb7 100644 --- a/src/template.html +++ b/src/template.html @@ -4,7 +4,7 @@ Anim - +

Formula text: