q5 2.0.17 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,200 @@
1
+ Q5.modules.canvas = ($, q) => {
2
+ $._OffscreenCanvas =
3
+ window.OffscreenCanvas ||
4
+ function () {
5
+ return document.createElement('canvas');
6
+ };
7
+
8
+ if (Q5._nodejs) {
9
+ if (Q5._createNodeJSCanvas) {
10
+ q.canvas = Q5._createNodeJSCanvas(100, 100);
11
+ }
12
+ } else if ($._scope == 'image' || $._scope == 'graphics') {
13
+ q.canvas = new $._OffscreenCanvas(100, 100);
14
+ }
15
+
16
+ if (!$.canvas) {
17
+ if (typeof document == 'object') {
18
+ q.canvas = document.createElement('canvas');
19
+ $.canvas.id = 'q5Canvas' + Q5._instanceCount;
20
+ $.canvas.classList.add('q5Canvas');
21
+ } else $.noCanvas();
22
+ }
23
+
24
+ let c = $.canvas;
25
+ c.width = $.width = 100;
26
+ c.height = $.height = 100;
27
+ if ($._scope != 'image') {
28
+ c.renderer = $._renderer;
29
+ c[$._renderer] = true;
30
+ }
31
+ $._pixelDensity = 1;
32
+
33
+ $._adjustDisplay = () => {
34
+ if (c.style) {
35
+ c.style.width = c.w + 'px';
36
+ c.style.height = c.h + 'px';
37
+ }
38
+ };
39
+
40
+ $.createCanvas = function (w, h, options) {
41
+ options ??= arguments[3];
42
+
43
+ let opt = Object.assign({}, Q5.canvasOptions);
44
+ if (typeof options == 'object') Object.assign(opt, options);
45
+
46
+ if ($._scope != 'image') {
47
+ let pd = $.displayDensity();
48
+ if ($._scope == 'graphics') pd = this._pixelDensity;
49
+ else if (window.IntersectionObserver) {
50
+ new IntersectionObserver((e) => {
51
+ c.visible = e[0].isIntersecting;
52
+ }).observe(c);
53
+ }
54
+ $._pixelDensity = Math.ceil(pd);
55
+ }
56
+
57
+ $._setCanvasSize(w, h);
58
+
59
+ Object.assign(c, opt);
60
+ let rend = $._createCanvas(c.w, c.h, opt);
61
+
62
+ if ($._hooks) {
63
+ for (let m of $._hooks.postCanvas) m();
64
+ }
65
+ return rend;
66
+ };
67
+
68
+ $._save = async (data, name, ext) => {
69
+ name = name || 'untitled';
70
+ ext = ext || 'png';
71
+ if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
72
+ if (data instanceof OffscreenCanvas) {
73
+ const blob = await data.convertToBlob({ type: 'image/' + ext });
74
+ data = await new Promise((resolve) => {
75
+ const reader = new FileReader();
76
+ reader.onloadend = () => resolve(reader.result);
77
+ reader.readAsDataURL(blob);
78
+ });
79
+ } else {
80
+ data = data.toDataURL('image/' + ext);
81
+ }
82
+ } else {
83
+ let type = 'text/plain';
84
+ if (ext == 'json') {
85
+ if (typeof data != 'string') data = JSON.stringify(data);
86
+ type = 'text/json';
87
+ }
88
+ data = new Blob([data], { type });
89
+ data = URL.createObjectURL(data);
90
+ }
91
+ let a = document.createElement('a');
92
+ a.href = data;
93
+ a.download = name + '.' + ext;
94
+ a.click();
95
+ URL.revokeObjectURL(a.href);
96
+ };
97
+ $.save = (a, b, c) => {
98
+ if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
99
+ c = b;
100
+ b = a;
101
+ a = $.canvas;
102
+ }
103
+ if (c) return $._save(a, b, c);
104
+ if (b) {
105
+ b = b.split('.');
106
+ $._save(a, b[0], b.at(-1));
107
+ } else $._save(a);
108
+ };
109
+
110
+ $._setCanvasSize = (w, h) => {
111
+ w ??= window.innerWidth;
112
+ h ??= window.innerHeight;
113
+ c.w = w = Math.ceil(w);
114
+ c.h = h = Math.ceil(h);
115
+ c.hw = w / 2;
116
+ c.hh = h / 2;
117
+ c.width = Math.ceil(w * $._pixelDensity);
118
+ c.height = Math.ceil(h * $._pixelDensity);
119
+
120
+ if (!$._da) {
121
+ q.width = w;
122
+ q.height = h;
123
+ } else $.flexibleCanvas($._dau);
124
+
125
+ if ($.displayMode && !c.displayMode) $.displayMode();
126
+ else $._adjustDisplay();
127
+ };
128
+
129
+ if ($._scope == 'image') return;
130
+
131
+ if (c && $._scope != 'graphics') {
132
+ c.parent = (el) => {
133
+ if (c.parentElement) c.parentElement.removeChild(c);
134
+
135
+ if (typeof el == 'string') el = document.getElementById(el);
136
+ el.append(c);
137
+
138
+ function parentResized() {
139
+ if ($.frameCount > 1) {
140
+ $._shouldResize = true;
141
+ $._adjustDisplay();
142
+ }
143
+ }
144
+ if (typeof ResizeObserver == 'function') {
145
+ if ($._ro) $._ro.disconnect();
146
+ $._ro = new ResizeObserver(parentResized);
147
+ $._ro.observe(el);
148
+ } else if (!$.frameCount) {
149
+ window.addEventListener('resize', parentResized);
150
+ }
151
+ };
152
+
153
+ function addCanvas() {
154
+ let el = $._parent;
155
+ el ??= document.getElementsByTagName('main')[0];
156
+ if (!el) {
157
+ el = document.createElement('main');
158
+ document.body.append(el);
159
+ }
160
+ c.parent(el);
161
+ }
162
+ if (document.body) addCanvas();
163
+ else document.addEventListener('DOMContentLoaded', addCanvas);
164
+ }
165
+
166
+ $.resizeCanvas = (w, h) => {
167
+ if (!$.ctx) return $.createCanvas(w, h);
168
+ if (w == c.w && h == c.h) return;
169
+
170
+ $._resizeCanvas(w, h);
171
+ };
172
+
173
+ $.canvas.resize = $.resizeCanvas;
174
+ $.canvas.save = $.saveCanvas = $.save;
175
+
176
+ $.displayDensity = () => window.devicePixelRatio;
177
+ $.pixelDensity = (v) => {
178
+ if (!v || v == $._pixelDensity) return $._pixelDensity;
179
+ $._pixelDensity = v;
180
+ $._setCanvasSize(c.w, c.h);
181
+ return v;
182
+ };
183
+
184
+ $.flexibleCanvas = (unit = 400) => {
185
+ if (unit) {
186
+ $._da = c.width / (unit * $._pixelDensity);
187
+ q.width = $._dau = unit;
188
+ q.height = (c.h / c.w) * unit;
189
+ } else $._da = 0;
190
+ };
191
+ };
192
+
193
+ Q5.canvasOptions = {
194
+ alpha: false,
195
+ colorSpace: 'display-p3'
196
+ };
197
+
198
+ if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
199
+ Q5.canvasOptions.colorSpace = 'srgb';
200
+ } else Q5.supportsHDR = true;
package/src/q5-color.js CHANGED
@@ -1,19 +1,21 @@
1
- Q5.modules.color = ($, p) => {
1
+ Q5.modules.color = ($, q) => {
2
2
  $.RGB = $.RGBA = $._colorMode = 'rgb';
3
3
  $.OKLCH = 'oklch';
4
4
 
5
- if (Q5.supportsHDR) $.Color = Q5.ColorRGBA_P3;
6
- else $.Color = Q5.ColorRGBA;
7
-
8
- $.colorMode = (mode) => {
5
+ $.colorMode = (mode, format) => {
9
6
  $._colorMode = mode;
7
+ let srgb = $.canvas.colorSpace == 'srgb' || mode == 'srgb';
8
+ format ??= srgb ? 'integer' : 'float';
9
+ $._colorFormat = format;
10
10
  if (mode == 'oklch') {
11
- p.Color = Q5.ColorOKLCH;
12
- } else if (mode == 'rgb') {
13
- if ($.canvas.colorSpace == 'srgb') p.Color = Q5.ColorRGBA;
14
- else p.Color = Q5.ColorRGBA_P3;
15
- } else if (mode == 'srgb') {
16
- p.Color = Q5.ColorRGBA;
11
+ q.Color = Q5.ColorOKLCH;
12
+ } else {
13
+ let srgb = $.canvas.colorSpace == 'srgb';
14
+ if ($._colorFormat == 'integer') {
15
+ q.Color = srgb ? Q5.ColorRGBA_8 : Q5.ColorRGBA_P3_8;
16
+ } else {
17
+ q.Color = srgb ? Q5.ColorRGBA : Q5.ColorRGBA_P3;
18
+ }
17
19
  $._colorMode = 'rgb';
18
20
  }
19
21
  };
@@ -58,27 +60,33 @@ Q5.modules.color = ($, p) => {
58
60
  let args = arguments;
59
61
  if (args.length == 1) {
60
62
  if (typeof c0 == 'string') {
63
+ let r, g, b, a;
61
64
  if (c0[0] == '#') {
62
65
  if (c0.length <= 5) {
63
- return new C(
64
- parseInt(c0[1] + c0[1], 16),
65
- parseInt(c0[2] + c0[2], 16),
66
- parseInt(c0[3] + c0[3], 16),
67
- c0.length == 4 ? null : parseInt(c0[4] + c0[4], 16)
68
- );
66
+ r = parseInt(c0[1] + c0[1], 16);
67
+ g = parseInt(c0[2] + c0[2], 16);
68
+ b = parseInt(c0[3] + c0[3], 16);
69
+ a = c0.length == 4 ? null : parseInt(c0[4] + c0[4], 16);
70
+ } else {
71
+ r = parseInt(c0.slice(1, 3), 16);
72
+ g = parseInt(c0.slice(3, 5), 16);
73
+ b = parseInt(c0.slice(5, 7), 16);
74
+ a = c0.length == 7 ? null : parseInt(c0.slice(7, 9), 16);
69
75
  }
70
- return new C(
71
- parseInt(c0.slice(1, 3), 16),
72
- parseInt(c0.slice(3, 5), 16),
73
- parseInt(c0.slice(5, 7), 16),
74
- c0.length == 7 ? null : parseInt(c0.slice(7, 9), 16)
76
+ } else if ($._namedColors[c0]) [r, g, b] = $._namedColors[c0];
77
+ else {
78
+ console.error(
79
+ "q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
75
80
  );
81
+ return new C(0, 0, 0);
76
82
  }
77
- if ($._namedColors[c0]) return new C(...$._namedColors[c0]);
78
- console.error(
79
- "q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
80
- );
81
- return new C(0, 0, 0);
83
+ if ($._colorFormat != 'integer') {
84
+ r /= 255;
85
+ g /= 255;
86
+ b /= 255;
87
+ if (a != null) a /= 255;
88
+ }
89
+ return new C(r, g, b, a);
82
90
  }
83
91
  if (Array.isArray(c0)) return new C(...c0);
84
92
  }
@@ -148,7 +156,24 @@ Q5.ColorRGBA = class extends Q5.Color {
148
156
  this.r = r;
149
157
  this.g = g;
150
158
  this.b = b;
151
- this.a = a ?? 255;
159
+ this.a = a ?? 1;
160
+ }
161
+ get levels() {
162
+ return [this.r, this.g, this.b, this.a];
163
+ }
164
+ toString() {
165
+ return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
166
+ }
167
+ };
168
+ Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
169
+ toString() {
170
+ return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
171
+ }
172
+ };
173
+ // legacy 8-bit (0-255) integer color format
174
+ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
175
+ constructor(r, g, b, a) {
176
+ super(r, g, b, a ?? 255);
152
177
  }
153
178
  setRed(v) {
154
179
  this.r = v;
@@ -169,9 +194,10 @@ Q5.ColorRGBA = class extends Q5.Color {
169
194
  return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
170
195
  }
171
196
  };
172
- Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
197
+ // p3 10-bit color in integer color format, for backwards compatibility
198
+ Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
173
199
  constructor(r, g, b, a) {
174
- super(r, g, b, a);
200
+ super(r, g, b, a ?? 255);
175
201
  this._edited = true;
176
202
  }
177
203
  get r() {
package/src/q5-core.js CHANGED
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * q5.js
3
- * @version 2.0
3
+ * @version 2.1
4
4
  * @author quinton-ashley, Tezumie, and LingDong-
5
5
  * @license LGPL-3.0
6
6
  * @class Q5
7
7
  */
8
- function Q5(scope, parent) {
8
+ function Q5(scope, parent, renderer) {
9
9
  let $ = this;
10
10
  $._q5 = true;
11
- $._scope = scope;
12
11
  $._parent = parent;
12
+ $._renderer = renderer || 'q2d';
13
13
  $._preloadCount = 0;
14
14
 
15
15
  scope ??= 'global';
@@ -17,13 +17,14 @@ function Q5(scope, parent) {
17
17
  if (!(window.setup || window.draw)) return;
18
18
  scope = 'global';
19
19
  }
20
+ $._scope = scope;
20
21
  let globalScope;
21
22
  if (scope == 'global') {
22
23
  Q5._hasGlobal = $._isGlobal = true;
23
24
  globalScope = !Q5._nodejs ? window : global;
24
25
  }
25
26
 
26
- let p = new Proxy($, {
27
+ let q = new Proxy($, {
27
28
  set: (t, p, v) => {
28
29
  $[p] = v;
29
30
  if ($._isGlobal) globalScope[p] = v;
@@ -41,6 +42,10 @@ function Q5(scope, parent) {
41
42
  $._targetFrameDuration = 16.666666666666668;
42
43
  $._frameRate = $._fps = 60;
43
44
  $._loop = true;
45
+ $._hooks = {
46
+ postCanvas: [],
47
+ preRender: []
48
+ };
44
49
 
45
50
  let millisStart = 0;
46
51
  $.millis = () => performance.now() - millisStart;
@@ -48,7 +53,7 @@ function Q5(scope, parent) {
48
53
  $.noCanvas = () => {
49
54
  if ($.canvas?.remove) $.canvas.remove();
50
55
  $.canvas = 0;
51
- p.ctx = p.drawingContext = 0;
56
+ q.ctx = q.drawingContext = 0;
52
57
  };
53
58
 
54
59
  if (window) {
@@ -57,10 +62,10 @@ function Q5(scope, parent) {
57
62
  $.deviceOrientation = window.screen?.orientation?.type;
58
63
  }
59
64
 
60
- $._incrementPreload = () => p._preloadCount++;
61
- $._decrementPreload = () => p._preloadCount--;
65
+ $._incrementPreload = () => q._preloadCount++;
66
+ $._decrementPreload = () => q._preloadCount--;
62
67
 
63
- function _draw(timestamp) {
68
+ $._draw = (timestamp) => {
64
69
  let ts = timestamp || performance.now();
65
70
  $._lastFrameTime ??= ts - $._targetFrameDuration;
66
71
 
@@ -69,44 +74,43 @@ function Q5(scope, parent) {
69
74
  $._shouldResize = false;
70
75
  }
71
76
 
72
- if ($._loop) looper = raf(_draw);
77
+ if ($._loop) looper = raf($._draw);
73
78
  else if ($.frameCount && !$._redraw) return;
74
79
 
75
80
  if (looper && $.frameCount) {
76
81
  let time_since_last = ts - $._lastFrameTime;
77
- if (time_since_last < $._targetFrameDuration - 1) return;
82
+ if (time_since_last < $._targetFrameDuration - 4) return;
78
83
  }
79
- p.deltaTime = ts - $._lastFrameTime;
84
+ q.deltaTime = ts - $._lastFrameTime;
80
85
  $._frameRate = 1000 / $.deltaTime;
81
- p.frameCount++;
86
+ q.frameCount++;
82
87
  let pre = performance.now();
83
- for (let m of Q5.prototype._methods.pre) m.call($);
84
- if ($.ctx) $.ctx.save();
88
+ if ($._beginRender) $._beginRender();
89
+ if ($.ctx) $.resetMatrix();
90
+ for (let m of Q5.methods.pre) m.call($);
85
91
  $.draw();
86
- for (let m of Q5.prototype._methods.post) m.call($);
87
- if ($.ctx) {
88
- $.ctx.restore();
89
- $.resetMatrix();
90
- }
91
- p.pmouseX = $.mouseX;
92
- p.pmouseY = $.mouseY;
92
+ if ($._render) $._render();
93
+ for (let m of Q5.methods.post) m.call($);
94
+ if ($._finishRender) $._finishRender();
95
+ q.pmouseX = $.mouseX;
96
+ q.pmouseY = $.mouseY;
93
97
  $._lastFrameTime = ts;
94
98
  let post = performance.now();
95
99
  $._fps = Math.round(1000 / (post - pre));
96
- }
100
+ };
97
101
  $.noLoop = () => {
98
102
  $._loop = false;
99
103
  looper = null;
100
104
  };
101
105
  $.loop = () => {
102
106
  $._loop = true;
103
- if (looper == null) _draw();
107
+ if (looper == null) $._draw();
104
108
  };
105
109
  $.isLooping = () => $._loop;
106
110
  $.redraw = (n = 1) => {
107
111
  $._redraw = true;
108
112
  for (let i = 0; i < n; i++) {
109
- _draw();
113
+ $._draw();
110
114
  }
111
115
  $._redraw = false;
112
116
  };
@@ -136,7 +140,12 @@ function Q5(scope, parent) {
136
140
  $.describe = () => {};
137
141
 
138
142
  for (let m in Q5.modules) {
139
- Q5.modules[m]($, p);
143
+ Q5.modules[m]($, q);
144
+ }
145
+
146
+ let r = Q5.renderers[$._renderer];
147
+ for (let m in r) {
148
+ r[m]($, q);
140
149
  }
141
150
 
142
151
  // INIT
@@ -147,12 +156,14 @@ function Q5(scope, parent) {
147
156
  }
148
157
  }
149
158
 
159
+ if (scope == 'graphics') return;
160
+
150
161
  if (scope == 'global') {
151
162
  Object.assign(Q5, $);
152
163
  delete Q5.Q5;
153
164
  }
154
165
 
155
- for (let m of Q5.prototype._methods.init) {
166
+ for (let m of Q5.methods.init) {
156
167
  m.call($);
157
168
  }
158
169
 
@@ -169,8 +180,6 @@ function Q5(scope, parent) {
169
180
 
170
181
  if (typeof scope == 'function') scope($);
171
182
 
172
- if (scope == 'graphics') return;
173
-
174
183
  Q5._instanceCount++;
175
184
 
176
185
  let raf =
@@ -218,8 +227,6 @@ function Q5(scope, parent) {
218
227
 
219
228
  if (!($.setup || $.draw)) return;
220
229
 
221
- $._startDone = false;
222
-
223
230
  async function _start() {
224
231
  $._startDone = true;
225
232
  if ($._preloadCount > 0) return raf(_start);
@@ -229,7 +236,7 @@ function Q5(scope, parent) {
229
236
  if ($.ctx === null) $.createCanvas(100, 100);
230
237
  $._setupDone = true;
231
238
  if ($.ctx) $.resetMatrix();
232
- raf(_draw);
239
+ raf($._draw);
233
240
  }
234
241
 
235
242
  if ((arguments.length && scope != 'namespace') || preloadDefined) {
@@ -243,6 +250,7 @@ function Q5(scope, parent) {
243
250
  }
244
251
  }
245
252
 
253
+ Q5.renderers = {};
246
254
  Q5.modules = {};
247
255
 
248
256
  Q5._nodejs = typeof process == 'object';
@@ -253,13 +261,13 @@ Q5._friendlyError = (msg, func) => {
253
261
  };
254
262
  Q5._validateParameters = () => true;
255
263
 
256
- Q5.prototype._methods = {
264
+ Q5.methods = {
257
265
  init: [],
258
266
  pre: [],
259
267
  post: [],
260
268
  remove: []
261
269
  };
262
- Q5.prototype.registerMethod = (m, fn) => Q5.prototype._methods[m].push(fn);
270
+ Q5.prototype.registerMethod = (m, fn) => Q5.methods[m].push(fn);
263
271
  Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.prototype[n] = fn[n]);
264
272
 
265
273
  if (Q5._nodejs) global.p5 ??= global.Q5 = Q5;
package/src/q5-display.js CHANGED
@@ -84,4 +84,10 @@ main {
84
84
  Object.assign(c, { displayMode, renderQuality, displayScale });
85
85
  $._adjustDisplay();
86
86
  };
87
+
88
+ $.fullscreen = (v) => {
89
+ if (v === undefined) return document.fullscreenElement;
90
+ if (v) document.body.requestFullscreen();
91
+ else document.body.exitFullscreen();
92
+ };
87
93
  };
package/src/q5-input.js CHANGED
@@ -1,4 +1,4 @@
1
- Q5.modules.input = ($, p) => {
1
+ Q5.modules.input = ($, q) => {
2
2
  if ($._scope == 'graphics') return;
3
3
 
4
4
  $.mouseX = 0;
@@ -42,17 +42,26 @@ Q5.modules.input = ($, p) => {
42
42
 
43
43
  $._updateMouse = (e) => {
44
44
  if (e.changedTouches) return;
45
- let rect = $.canvas.getBoundingClientRect();
46
- let sx = $.canvas.scrollWidth / $.width || 1;
47
- let sy = $.canvas.scrollHeight / $.height || 1;
48
- p.mouseX = (e.clientX - rect.left) / sx;
49
- p.mouseY = (e.clientY - rect.top) / sy;
45
+ if (c) {
46
+ let rect = c.getBoundingClientRect();
47
+ let sx = c.scrollWidth / $.width || 1;
48
+ let sy = c.scrollHeight / $.height || 1;
49
+ q.mouseX = (e.clientX - rect.left) / sx;
50
+ q.mouseY = (e.clientY - rect.top) / sy;
51
+ if (c.renderer == 'webgpu') {
52
+ q.mouseX -= c.hw;
53
+ q.mouseY -= c.hh;
54
+ }
55
+ } else {
56
+ q.mouseX = e.clientX;
57
+ q.mouseY = e.clientY;
58
+ }
50
59
  };
51
60
  $._onmousedown = (e) => {
52
61
  $._startAudio();
53
62
  $._updateMouse(e);
54
- p.mouseIsPressed = true;
55
- p.mouseButton = mouseBtns[e.button];
63
+ q.mouseIsPressed = true;
64
+ q.mouseButton = mouseBtns[e.button];
56
65
  $.mousePressed(e);
57
66
  };
58
67
  $._onmousemove = (e) => {
@@ -62,18 +71,15 @@ Q5.modules.input = ($, p) => {
62
71
  };
63
72
  $._onmouseup = (e) => {
64
73
  $._updateMouse(e);
65
- p.mouseIsPressed = false;
74
+ q.mouseIsPressed = false;
66
75
  $.mouseReleased(e);
67
76
  };
68
77
  $._onclick = (e) => {
69
78
  $._updateMouse(e);
70
- p.mouseIsPressed = true;
79
+ q.mouseIsPressed = true;
71
80
  $.mouseClicked(e);
72
- p.mouseIsPressed = false;
81
+ q.mouseIsPressed = false;
73
82
  };
74
- c.addEventListener('mousedown', (e) => $._onmousedown(e));
75
- c.addEventListener('mouseup', (e) => $._onmouseup(e));
76
- c.addEventListener('click', (e) => $._onclick(e));
77
83
 
78
84
  $.cursor = (name, x, y) => {
79
85
  let pfx = '';
@@ -95,17 +101,17 @@ Q5.modules.input = ($, p) => {
95
101
  $._onkeydown = (e) => {
96
102
  if (e.repeat) return;
97
103
  $._startAudio();
98
- p.keyIsPressed = true;
99
- p.key = e.key;
100
- p.keyCode = e.keyCode;
104
+ q.keyIsPressed = true;
105
+ q.key = e.key;
106
+ q.keyCode = e.keyCode;
101
107
  keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = true;
102
108
  $.keyPressed(e);
103
109
  if (e.key.length == 1) $.keyTyped(e);
104
110
  };
105
111
  $._onkeyup = (e) => {
106
- p.keyIsPressed = false;
107
- p.key = e.key;
108
- p.keyCode = e.keyCode;
112
+ q.keyIsPressed = false;
113
+ q.key = e.key;
114
+ q.keyCode = e.keyCode;
109
115
  keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
110
116
  $.keyReleased(e);
111
117
  };
@@ -123,37 +129,43 @@ Q5.modules.input = ($, p) => {
123
129
  }
124
130
  $._ontouchstart = (e) => {
125
131
  $._startAudio();
126
- p.touches = [...e.touches].map(getTouchInfo);
132
+ q.touches = [...e.touches].map(getTouchInfo);
127
133
  if (!$._isTouchAware) {
128
- p.mouseX = $.touches[0].x;
129
- p.mouseY = $.touches[0].y;
130
- p.mouseIsPressed = true;
131
- p.mouseButton = $.LEFT;
134
+ q.mouseX = $.touches[0].x;
135
+ q.mouseY = $.touches[0].y;
136
+ q.mouseIsPressed = true;
137
+ q.mouseButton = $.LEFT;
132
138
  if (!$.mousePressed(e)) e.preventDefault();
133
139
  }
134
140
  if (!$.touchStarted(e)) e.preventDefault();
135
141
  };
136
142
  $._ontouchmove = (e) => {
137
- p.touches = [...e.touches].map(getTouchInfo);
143
+ q.touches = [...e.touches].map(getTouchInfo);
138
144
  if (!$._isTouchAware) {
139
- p.mouseX = $.touches[0].x;
140
- p.mouseY = $.touches[0].y;
145
+ q.mouseX = $.touches[0].x;
146
+ q.mouseY = $.touches[0].y;
141
147
  if (!$.mouseDragged(e)) e.preventDefault();
142
148
  }
143
149
  if (!$.touchMoved(e)) e.preventDefault();
144
150
  };
145
151
  $._ontouchend = (e) => {
146
- p.touches = [...e.touches].map(getTouchInfo);
152
+ q.touches = [...e.touches].map(getTouchInfo);
147
153
  if (!$._isTouchAware && !$.touches.length) {
148
- p.mouseIsPressed = false;
154
+ q.mouseIsPressed = false;
149
155
  if (!$.mouseReleased(e)) e.preventDefault();
150
156
  }
151
157
  if (!$.touchEnded(e)) e.preventDefault();
152
158
  };
153
- c.addEventListener('touchstart', (e) => $._ontouchstart(e));
154
- c.addEventListener('touchmove', (e) => $._ontouchmove(e));
155
- c.addEventListener('touchcancel', (e) => $._ontouchend(e));
156
- c.addEventListener('touchend', (e) => $._ontouchend(e));
159
+
160
+ if (c) {
161
+ c.addEventListener('mousedown', (e) => $._onmousedown(e));
162
+ c.addEventListener('mouseup', (e) => $._onmouseup(e));
163
+ c.addEventListener('click', (e) => $._onclick(e));
164
+ c.addEventListener('touchstart', (e) => $._ontouchstart(e));
165
+ c.addEventListener('touchmove', (e) => $._ontouchmove(e));
166
+ c.addEventListener('touchcancel', (e) => $._ontouchend(e));
167
+ c.addEventListener('touchend', (e) => $._ontouchend(e));
168
+ }
157
169
 
158
170
  if (window) {
159
171
  let l = window.addEventListener;