p5-phone 1.4.4
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.
- package/LICENSE +21 -0
- package/README.md +509 -0
- package/dist/p5-phone.js +1062 -0
- package/dist/p5-phone.min.js +10 -0
- package/examples/Phone Sensor Examples/microphone/01_mic_level/index.html +19 -0
- package/examples/Phone Sensor Examples/microphone/01_mic_level/sketch.js +117 -0
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/01_orientation_basic/sketch.js +123 -0
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/02_rotational_velocity/sketch.js +144 -0
- package/examples/Phone Sensor Examples/movement/03_acceleration/index.html +28 -0
- package/examples/Phone Sensor Examples/movement/03_acceleration/sketch.js +87 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/index.html +31 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/sketch.js +225 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples/sound/01_dual_audio/tracks/audio2.mp3 +0 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/index.html +31 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/sketch.js +269 -0
- package/examples/Phone Sensor Examples/sound/02_volume_touches/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples/touch/01_touch_basic/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/01_touch_basic/sketch.js +94 -0
- package/examples/Phone Sensor Examples/touch/02_touch_zones/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/02_touch_zones/sketch.js +220 -0
- package/examples/Phone Sensor Examples/touch/03_touch_count/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/03_touch_count/sketch.js +93 -0
- package/examples/Phone Sensor Examples/touch/04_touch_distance/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/04_touch_distance/sketch.js +120 -0
- package/examples/Phone Sensor Examples/touch/05_touch_angle/index.html +28 -0
- package/examples/Phone Sensor Examples/touch/05_touch_angle/sketch.js +117 -0
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/microphone/01_mic_level/sketch.js +72 -0
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/01_orientation_basic/sketch.js +51 -0
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/02_rotational_velocity/sketch.js +70 -0
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/movement/03_acceleration/sketch.js +83 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/sketch.js +96 -0
- package/examples/Phone Sensor Examples - Minimal/sound/01_sound_basic/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/index.html +19 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/sketch.js +118 -0
- package/examples/Phone Sensor Examples - Minimal/sound/02_sound_amplitude/tracks/audio1.mp3 +0 -0
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/01_touch_basic/sketch.js +58 -0
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/02_touch_zones/sketch.js +78 -0
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/03_touch_count/sketch.js +64 -0
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/04_touch_distance/sketch.js +69 -0
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/index.html +18 -0
- package/examples/Phone Sensor Examples - Minimal/touch/05_touch_angle/sketch.js +85 -0
- package/examples/Phone and Gif/collision/README.md +31 -0
- Gif/collision/gifs/spaceSuit2.png +0 -0
- package/examples/Phone and Gif/collision/index.html +19 -0
- package/examples/Phone and Gif/collision/sketch.js +226 -0
- Gif/fetch/gifs/corgiswimflip.gif +0 -0
- package/examples/Phone and Gif/fetch/index.html +18 -0
- package/examples/Phone and Gif/fetch/sketch.js +139 -0
- Gif/fly/gifs/comparison.gif +0 -0
- package/examples/Phone and Gif/fly/index.html +18 -0
- package/examples/Phone and Gif/fly/sketch.js +103 -0
- Gif/roll/gifs/how-penciles-are-made.gif +0 -0
- package/examples/Phone and Gif/roll/index.html +18 -0
- package/examples/Phone and Gif/roll/sketch.js +121 -0
- package/examples/UXcompare/button-vs-movement/index.html +45 -0
- package/examples/UXcompare/button-vs-movement/sketch.js +355 -0
- package/examples/UXcompare/button-vs-orientation/index.html +25 -0
- package/examples/UXcompare/button-vs-orientation/sketch.js +317 -0
- package/examples/UXcompare/button-vs-shake/index.html +45 -0
- package/examples/UXcompare/button-vs-shake/sketch.js +320 -0
- package/examples/UXcompare/gyroscope-demo/index.html +78 -0
- package/examples/UXcompare/gyroscope-demo/sketch.js +166 -0
- package/examples/UXcompare/index.html +419 -0
- package/examples/UXcompare/microphone-demo/index.html +79 -0
- package/examples/UXcompare/microphone-demo/sketch.js +210 -0
- package/examples/UXcompare/slider-vs-angle/index.html +25 -0
- package/examples/UXcompare/slider-vs-angle/sketch.js +429 -0
- package/examples/UXcompare/slider-vs-distance/index.html +25 -0
- package/examples/UXcompare/slider-vs-distance/sketch.js +401 -0
- package/examples/UXcompare/slider-vs-microphone/index.html +26 -0
- package/examples/UXcompare/slider-vs-microphone/sketch.js +336 -0
- package/examples/UXcompare/slider-vs-touches/index.html +25 -0
- package/examples/UXcompare/slider-vs-touches/sketch.js +376 -0
- package/examples/UXcompare/sliders-vs-acceleration/index.html +25 -0
- package/examples/UXcompare/sliders-vs-acceleration/sketch.js +361 -0
- package/examples/UXcompare/sliders-vs-rotation/index.html +25 -0
- package/examples/UXcompare/sliders-vs-rotation/sketch.js +375 -0
- package/examples/blankTemplate/index.html +31 -0
- package/examples/blankTemplate/sketch.js +55 -0
- package/examples/homepage/index.html +506 -0
- package/package.json +73 -0
- package/src/p5-phone.js +1062 -0
- package/src/permissionMic.js +240 -0
- package/src/permissionsGesture.js +213 -0
- package/src/permissionsGyro.js +246 -0
package/src/p5-phone.js
ADDED
|
@@ -0,0 +1,1062 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* p5-phone v1.4.4
|
|
3
|
+
* Simplified mobile hardware access for p5.js - handle sensors, microphone, touch, and browser gestures with ease
|
|
4
|
+
* https://github.com/DigitalFuturesOCADU/p5-phone
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2025 Nick Puckett
|
|
7
|
+
* Released under the MIT License
|
|
8
|
+
* https://opensource.org/licenses/MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================
|
|
12
|
+
// P5-PHONE - Mobile Hardware Access for p5.js
|
|
13
|
+
// Clean API for enabling permissions in p5.js sketches
|
|
14
|
+
// =============================================
|
|
15
|
+
|
|
16
|
+
// Set up global error handling immediately when script loads
|
|
17
|
+
(function() {
|
|
18
|
+
// Store original console methods before any overrides
|
|
19
|
+
window._originalConsoleError = console.error;
|
|
20
|
+
window._originalConsoleWarn = console.warn;
|
|
21
|
+
|
|
22
|
+
// Only set up once
|
|
23
|
+
if (window._debugErrorHandlersSet) return;
|
|
24
|
+
window._debugErrorHandlersSet = true;
|
|
25
|
+
|
|
26
|
+
// Initialize early error storage
|
|
27
|
+
window._earlyErrors = window._earlyErrors || [];
|
|
28
|
+
|
|
29
|
+
// Global error handler for JavaScript errors
|
|
30
|
+
window.addEventListener('error', function(event) {
|
|
31
|
+
const errorMsg = event.error?.message || event.message || 'Unknown error';
|
|
32
|
+
const fileName = event.filename ? event.filename.split('/').pop() : 'unknown file';
|
|
33
|
+
const line = event.lineno || 'unknown line';
|
|
34
|
+
|
|
35
|
+
const fullError = `${errorMsg} (${fileName}:${line})`;
|
|
36
|
+
|
|
37
|
+
console.error('🚨 Error caught:', fullError);
|
|
38
|
+
if (event.error?.stack) {
|
|
39
|
+
console.error('Stack:', event.error.stack);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Store error for debug panel
|
|
43
|
+
window._earlyErrors.push({
|
|
44
|
+
type: 'error',
|
|
45
|
+
message: 'JavaScript Error: ' + fullError,
|
|
46
|
+
stack: event.error?.stack
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Auto-show debug panel when an error occurs (if SHOW_DEBUG is true)
|
|
50
|
+
if (window.SHOW_DEBUG !== false && !window._debugVisible) {
|
|
51
|
+
// Try to show debug panel automatically
|
|
52
|
+
if (typeof showDebug === 'function') {
|
|
53
|
+
showDebug();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// If debug panel is already visible, show immediately
|
|
58
|
+
if (window._debugVisible && typeof debugError === 'function') {
|
|
59
|
+
debugError('JavaScript Error:', fullError);
|
|
60
|
+
if (event.error?.stack) {
|
|
61
|
+
debugError('Stack trace:', event.error.stack);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Global handler for unhandled promise rejections
|
|
67
|
+
window.addEventListener('unhandledrejection', function(event) {
|
|
68
|
+
const errorMsg = event.reason?.message || event.reason || 'Unknown promise rejection';
|
|
69
|
+
|
|
70
|
+
console.error('🚨 Promise rejection caught:', errorMsg);
|
|
71
|
+
|
|
72
|
+
window._earlyErrors.push({
|
|
73
|
+
type: 'error',
|
|
74
|
+
message: 'Unhandled Promise Rejection: ' + errorMsg
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (window._debugVisible && typeof debugError === 'function') {
|
|
78
|
+
debugError('Unhandled Promise Rejection:', errorMsg);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
})();
|
|
82
|
+
|
|
83
|
+
// Global state flags
|
|
84
|
+
window.sensorsEnabled = false;
|
|
85
|
+
window.micEnabled = false;
|
|
86
|
+
window.soundEnabled = false;
|
|
87
|
+
window.gesturesLocked = false;
|
|
88
|
+
|
|
89
|
+
// Internal state
|
|
90
|
+
let _permissionsInitialized = false;
|
|
91
|
+
let _micInstance = null;
|
|
92
|
+
|
|
93
|
+
// =========================================
|
|
94
|
+
// PUBLIC API - CALL THESE FROM YOUR P5 SKETCH
|
|
95
|
+
// =========================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Lock mobile gestures to prevent browser interference
|
|
99
|
+
* Call this in your setup() function
|
|
100
|
+
*/
|
|
101
|
+
function lockGestures() {
|
|
102
|
+
if (window.gesturesLocked) return;
|
|
103
|
+
|
|
104
|
+
console.log('🔒 Locking mobile gestures...');
|
|
105
|
+
_initializeGestureBlocking();
|
|
106
|
+
_initializeP5TouchOverrides();
|
|
107
|
+
window.gesturesLocked = true;
|
|
108
|
+
console.log('✅ Mobile gestures locked');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Enable gyroscope with a button interface
|
|
113
|
+
* Creates a start button that user must click
|
|
114
|
+
*/
|
|
115
|
+
function enableGyroButton(buttonText = 'ENABLE MOTION SENSORS', statusText = 'Requesting motion sensors...') {
|
|
116
|
+
_createPermissionButton(buttonText, statusText, async () => {
|
|
117
|
+
await _requestMotionPermissions();
|
|
118
|
+
console.log('✅ Gyroscope enabled via button');
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Enable gyroscope with tap-to-start
|
|
124
|
+
* User taps anywhere on screen to enable
|
|
125
|
+
*/
|
|
126
|
+
function enableGyroTap(message = 'Tap screen to enable motion sensors') {
|
|
127
|
+
_createTapToEnable(message, async () => {
|
|
128
|
+
await _requestMotionPermissions();
|
|
129
|
+
console.log('✅ Gyroscope enabled via tap');
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Enable microphone with a button interface
|
|
135
|
+
* Creates a start button that user must click
|
|
136
|
+
*/
|
|
137
|
+
function enableMicButton(buttonText = 'ENABLE MICROPHONE', statusText = 'Requesting microphone access...') {
|
|
138
|
+
_createPermissionButton(buttonText, statusText, async () => {
|
|
139
|
+
await _requestMicrophonePermissions();
|
|
140
|
+
console.log('✅ Microphone enabled via button');
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Enable microphone with tap-to-start
|
|
146
|
+
* User taps anywhere on screen to enable
|
|
147
|
+
*/
|
|
148
|
+
function enableMicTap(message = 'Tap screen to enable microphone') {
|
|
149
|
+
_createTapToEnable(message, async () => {
|
|
150
|
+
await _requestMicrophonePermissions();
|
|
151
|
+
console.log('✅ Microphone enabled via tap');
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Enable sound output with a button interface
|
|
157
|
+
* Creates a start button that user must click
|
|
158
|
+
* Use this for playing sounds without needing microphone input
|
|
159
|
+
*/
|
|
160
|
+
function enableSoundButton(buttonText = 'ENABLE SOUND', statusText = 'Enabling audio...') {
|
|
161
|
+
_createPermissionButton(buttonText, statusText, async () => {
|
|
162
|
+
await _requestSoundOutput();
|
|
163
|
+
console.log('✅ Sound output enabled via button');
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Enable sound output with tap-to-start
|
|
169
|
+
* User taps anywhere on screen to enable
|
|
170
|
+
* Use this for playing sounds without needing microphone input
|
|
171
|
+
*/
|
|
172
|
+
function enableSoundTap(message = 'Tap screen to enable sound') {
|
|
173
|
+
_createTapToEnable(message, async () => {
|
|
174
|
+
await _requestSoundOutput();
|
|
175
|
+
console.log('✅ Sound output enabled via tap');
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Enable both motion sensors and microphone with a button interface
|
|
181
|
+
* Creates a start button that user must click to enable both
|
|
182
|
+
*/
|
|
183
|
+
function enableAllButton(buttonText = 'ENABLE MOTION & MICROPHONE', statusText = 'Requesting permissions...') {
|
|
184
|
+
_createPermissionButton(buttonText, statusText, async () => {
|
|
185
|
+
await _requestMotionPermissions();
|
|
186
|
+
await _requestMicrophonePermissions();
|
|
187
|
+
console.log('✅ Motion sensors and microphone enabled via button');
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Enable both motion sensors and microphone with tap-to-start
|
|
193
|
+
* User taps anywhere on screen to enable both
|
|
194
|
+
*/
|
|
195
|
+
function enableAllTap(message = 'Tap screen to enable motion sensors & microphone') {
|
|
196
|
+
_createTapToEnable(message, async () => {
|
|
197
|
+
await _requestMotionPermissions();
|
|
198
|
+
await _requestMicrophonePermissions();
|
|
199
|
+
console.log('✅ Motion sensors and microphone enabled via tap');
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// =========================================
|
|
204
|
+
// INTERNAL PERMISSION HANDLERS
|
|
205
|
+
// =========================================
|
|
206
|
+
|
|
207
|
+
async function _requestMotionPermissions() {
|
|
208
|
+
try {
|
|
209
|
+
// Request motion sensor permissions (iOS 13+)
|
|
210
|
+
if (typeof DeviceOrientationEvent !== 'undefined' &&
|
|
211
|
+
typeof DeviceOrientationEvent.requestPermission === 'function') {
|
|
212
|
+
|
|
213
|
+
const orientationPermission = await DeviceOrientationEvent.requestPermission();
|
|
214
|
+
console.log('Orientation permission:', orientationPermission);
|
|
215
|
+
|
|
216
|
+
if (typeof DeviceMotionEvent !== 'undefined' &&
|
|
217
|
+
typeof DeviceMotionEvent.requestPermission === 'function') {
|
|
218
|
+
const motionPermission = await DeviceMotionEvent.requestPermission();
|
|
219
|
+
console.log('Motion permission:', motionPermission);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
window.sensorsEnabled = true;
|
|
224
|
+
_notifySketchReady();
|
|
225
|
+
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('Motion sensor permission error:', error);
|
|
228
|
+
if (_debugVisible) {
|
|
229
|
+
debugError('Motion sensor permission error:', error);
|
|
230
|
+
}
|
|
231
|
+
// Enable anyway for non-iOS devices
|
|
232
|
+
window.sensorsEnabled = true;
|
|
233
|
+
_notifySketchReady();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function _requestMicrophonePermissions() {
|
|
238
|
+
try {
|
|
239
|
+
// Start audio context for p5.sound
|
|
240
|
+
if (typeof userStartAudio !== 'undefined') {
|
|
241
|
+
await userStartAudio();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// If there's a global mic object, start it
|
|
245
|
+
if (typeof mic !== 'undefined' && mic && mic.start) {
|
|
246
|
+
mic.start();
|
|
247
|
+
_micInstance = mic;
|
|
248
|
+
window.micEnabled = true;
|
|
249
|
+
} else {
|
|
250
|
+
console.warn('No microphone object found. Create one with: mic = new p5.AudioIn();');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_notifySketchReady();
|
|
254
|
+
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('Microphone permission error:', error);
|
|
257
|
+
if (_debugVisible) {
|
|
258
|
+
debugError('Microphone permission error:', error);
|
|
259
|
+
}
|
|
260
|
+
_notifySketchReady();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function _requestSoundOutput() {
|
|
265
|
+
try {
|
|
266
|
+
// Start audio context for p5.sound (enables sound playback)
|
|
267
|
+
if (typeof userStartAudio !== 'undefined') {
|
|
268
|
+
await userStartAudio();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
window.soundEnabled = true;
|
|
272
|
+
_notifySketchReady();
|
|
273
|
+
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error('Sound output error:', error);
|
|
276
|
+
if (_debugVisible) {
|
|
277
|
+
debugError('Sound output error:', error);
|
|
278
|
+
}
|
|
279
|
+
window.soundEnabled = true; // Enable anyway since no permission needed
|
|
280
|
+
_notifySketchReady();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function _notifySketchReady() {
|
|
285
|
+
// Call userSetupComplete if it exists
|
|
286
|
+
if (typeof userSetupComplete === 'function') {
|
|
287
|
+
userSetupComplete();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Trigger a custom event for more advanced use cases
|
|
291
|
+
window.dispatchEvent(new CustomEvent('permissionsReady', {
|
|
292
|
+
detail: {
|
|
293
|
+
sensors: window.sensorsEnabled,
|
|
294
|
+
microphone: window.micEnabled,
|
|
295
|
+
sound: window.soundEnabled,
|
|
296
|
+
gestures: window.gesturesLocked
|
|
297
|
+
}
|
|
298
|
+
}));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// =========================================
|
|
302
|
+
// UI CREATION HELPERS
|
|
303
|
+
// =========================================
|
|
304
|
+
|
|
305
|
+
function _createPermissionButton(buttonText, statusText, onClickHandler) {
|
|
306
|
+
// Remove existing button if present
|
|
307
|
+
_removeExistingUI();
|
|
308
|
+
|
|
309
|
+
// Create button
|
|
310
|
+
const button = document.createElement('button');
|
|
311
|
+
button.id = 'permissionButton';
|
|
312
|
+
button.textContent = buttonText;
|
|
313
|
+
button.style.cssText = `
|
|
314
|
+
position: fixed;
|
|
315
|
+
top: 50%;
|
|
316
|
+
left: 50%;
|
|
317
|
+
transform: translate(-50%, -50%);
|
|
318
|
+
padding: 20px 40px;
|
|
319
|
+
font-size: 18px;
|
|
320
|
+
font-weight: bold;
|
|
321
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
322
|
+
color: white;
|
|
323
|
+
border: none;
|
|
324
|
+
border-radius: 12px;
|
|
325
|
+
cursor: pointer;
|
|
326
|
+
z-index: 999999;
|
|
327
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
328
|
+
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
|
329
|
+
transition: transform 0.2s ease;
|
|
330
|
+
touch-action: manipulation;
|
|
331
|
+
`;
|
|
332
|
+
|
|
333
|
+
// Create status text
|
|
334
|
+
const status = document.createElement('div');
|
|
335
|
+
status.id = 'permissionStatus';
|
|
336
|
+
status.textContent = statusText;
|
|
337
|
+
status.style.cssText = `
|
|
338
|
+
position: fixed;
|
|
339
|
+
top: 60%;
|
|
340
|
+
left: 50%;
|
|
341
|
+
transform: translate(-50%, 0);
|
|
342
|
+
color: white;
|
|
343
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
344
|
+
text-align: center;
|
|
345
|
+
z-index: 999998;
|
|
346
|
+
display: none;
|
|
347
|
+
`;
|
|
348
|
+
|
|
349
|
+
// Add hover effect
|
|
350
|
+
button.addEventListener('mouseenter', () => {
|
|
351
|
+
button.style.transform = 'translate(-50%, -50%) scale(1.05)';
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
button.addEventListener('mouseleave', () => {
|
|
355
|
+
button.style.transform = 'translate(-50%, -50%) scale(1)';
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Add multiple event handlers to ensure responsiveness
|
|
359
|
+
const handleButtonClick = async () => {
|
|
360
|
+
if (button.parentNode) {
|
|
361
|
+
button.style.display = 'none';
|
|
362
|
+
status.style.display = 'block';
|
|
363
|
+
|
|
364
|
+
await onClickHandler();
|
|
365
|
+
|
|
366
|
+
status.style.display = 'none';
|
|
367
|
+
_removeExistingUI();
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Add click, touch, and pointer handlers
|
|
372
|
+
button.addEventListener('click', handleButtonClick);
|
|
373
|
+
button.addEventListener('touchend', function(e) {
|
|
374
|
+
e.preventDefault();
|
|
375
|
+
e.stopPropagation();
|
|
376
|
+
handleButtonClick();
|
|
377
|
+
});
|
|
378
|
+
button.addEventListener('pointerup', function(e) {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
e.stopPropagation();
|
|
381
|
+
handleButtonClick();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
document.body.appendChild(button);
|
|
385
|
+
document.body.appendChild(status);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function _createTapToEnable(message, onTapHandler) {
|
|
389
|
+
// Remove existing UI if present
|
|
390
|
+
_removeExistingUI();
|
|
391
|
+
|
|
392
|
+
// Create overlay
|
|
393
|
+
const overlay = document.createElement('div');
|
|
394
|
+
overlay.id = 'tapOverlay';
|
|
395
|
+
overlay.style.cssText = `
|
|
396
|
+
position: fixed;
|
|
397
|
+
top: 0;
|
|
398
|
+
left: 0;
|
|
399
|
+
width: 100%;
|
|
400
|
+
height: 100%;
|
|
401
|
+
background: rgba(0, 0, 0, 0.8);
|
|
402
|
+
display: flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
justify-content: center;
|
|
405
|
+
z-index: 999999;
|
|
406
|
+
cursor: pointer;
|
|
407
|
+
touch-action: manipulation;
|
|
408
|
+
`;
|
|
409
|
+
|
|
410
|
+
// Create message
|
|
411
|
+
const messageDiv = document.createElement('div');
|
|
412
|
+
messageDiv.textContent = message;
|
|
413
|
+
messageDiv.style.cssText = `
|
|
414
|
+
color: white;
|
|
415
|
+
font-size: 24px;
|
|
416
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
417
|
+
text-align: center;
|
|
418
|
+
padding: 40px;
|
|
419
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
420
|
+
border-radius: 12px;
|
|
421
|
+
background: rgba(255, 255, 255, 0.1);
|
|
422
|
+
backdrop-filter: blur(10px);
|
|
423
|
+
`;
|
|
424
|
+
|
|
425
|
+
overlay.appendChild(messageDiv);
|
|
426
|
+
|
|
427
|
+
// Add multiple event handlers to ensure responsiveness
|
|
428
|
+
const handleActivation = async () => {
|
|
429
|
+
if (overlay.parentNode) {
|
|
430
|
+
messageDiv.textContent = 'Enabling...';
|
|
431
|
+
await onTapHandler();
|
|
432
|
+
if (overlay.parentNode) {
|
|
433
|
+
document.body.removeChild(overlay);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// Add both click and touch handlers
|
|
439
|
+
overlay.addEventListener('click', handleActivation);
|
|
440
|
+
overlay.addEventListener('touchend', function(e) {
|
|
441
|
+
e.preventDefault();
|
|
442
|
+
e.stopPropagation();
|
|
443
|
+
handleActivation();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Also add pointer events for wider compatibility
|
|
447
|
+
overlay.addEventListener('pointerup', function(e) {
|
|
448
|
+
e.preventDefault();
|
|
449
|
+
e.stopPropagation();
|
|
450
|
+
handleActivation();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
document.body.appendChild(overlay);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function _removeExistingUI() {
|
|
457
|
+
const button = document.getElementById('permissionButton');
|
|
458
|
+
const status = document.getElementById('permissionStatus');
|
|
459
|
+
const overlay = document.getElementById('tapOverlay');
|
|
460
|
+
|
|
461
|
+
if (button) button.remove();
|
|
462
|
+
if (status) status.remove();
|
|
463
|
+
if (overlay) overlay.remove();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// =========================================
|
|
467
|
+
// GESTURE BLOCKING IMPLEMENTATION
|
|
468
|
+
// =========================================
|
|
469
|
+
|
|
470
|
+
function _initializeGestureBlocking() {
|
|
471
|
+
// Prevent back navigation
|
|
472
|
+
window.history.pushState(null, '', window.location.href);
|
|
473
|
+
window.onpopstate = function() {
|
|
474
|
+
window.history.pushState(null, '', window.location.href);
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// Warn before leaving
|
|
478
|
+
window.addEventListener('beforeunload', function(e) {
|
|
479
|
+
e.preventDefault();
|
|
480
|
+
e.returnValue = '';
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
_initializeEdgeSwipePrevention();
|
|
484
|
+
_initializeOtherGesturePrevention();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function _initializeEdgeSwipePrevention() {
|
|
488
|
+
let touchStartX = 0;
|
|
489
|
+
let touchStartY = 0;
|
|
490
|
+
const edgeThreshold = 20;
|
|
491
|
+
|
|
492
|
+
document.addEventListener('touchstart', function(e) {
|
|
493
|
+
if (e.touches && e.touches.length > 0) {
|
|
494
|
+
touchStartX = e.touches[0].clientX;
|
|
495
|
+
touchStartY = e.touches[0].clientY;
|
|
496
|
+
|
|
497
|
+
// Prevent edge swipes
|
|
498
|
+
if (touchStartX < edgeThreshold ||
|
|
499
|
+
touchStartX > window.innerWidth - edgeThreshold) {
|
|
500
|
+
e.preventDefault();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}, { passive: false, capture: true });
|
|
504
|
+
|
|
505
|
+
document.addEventListener('touchmove', function(e) {
|
|
506
|
+
if (!e.touches || e.touches.length === 0) return;
|
|
507
|
+
|
|
508
|
+
let currentX = e.touches[0].clientX;
|
|
509
|
+
let currentY = e.touches[0].clientY;
|
|
510
|
+
let deltaX = currentX - touchStartX;
|
|
511
|
+
let deltaY = currentY - touchStartY;
|
|
512
|
+
|
|
513
|
+
// Prevent horizontal edge swipes (back/forward)
|
|
514
|
+
if ((touchStartX < edgeThreshold && deltaX > 0) ||
|
|
515
|
+
(touchStartX > window.innerWidth - edgeThreshold && deltaX < 0)) {
|
|
516
|
+
e.preventDefault();
|
|
517
|
+
e.stopPropagation();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Prevent pull-to-refresh
|
|
521
|
+
if (window.pageYOffset === 0 && deltaY > 0) {
|
|
522
|
+
e.preventDefault();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Prevent canvas touches but not on permission UI
|
|
526
|
+
if (e.target && e.target.tagName === 'CANVAS' &&
|
|
527
|
+
!document.getElementById('tapOverlay') &&
|
|
528
|
+
!document.getElementById('permissionButton')) {
|
|
529
|
+
e.preventDefault();
|
|
530
|
+
}
|
|
531
|
+
}, { passive: false, capture: true });
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function _initializeOtherGesturePrevention() {
|
|
535
|
+
// Prevent pinch zoom
|
|
536
|
+
document.addEventListener('gesturestart', function(e) {
|
|
537
|
+
e.preventDefault();
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
document.addEventListener('gesturechange', function(e) {
|
|
541
|
+
e.preventDefault();
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
document.addEventListener('gestureend', function(e) {
|
|
545
|
+
e.preventDefault();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Prevent double-tap zoom
|
|
549
|
+
let lastTouchEnd = 0;
|
|
550
|
+
document.addEventListener('touchend', function(e) {
|
|
551
|
+
// Don't prevent clicks on permission UI elements
|
|
552
|
+
if (e.target && (
|
|
553
|
+
e.target.id === 'tapOverlay' ||
|
|
554
|
+
e.target.closest('#tapOverlay') ||
|
|
555
|
+
e.target.id === 'permissionButton' ||
|
|
556
|
+
e.target.id === 'permissionStatus' ||
|
|
557
|
+
e.target.closest('#permissionButton') ||
|
|
558
|
+
e.target.closest('#permissionStatus')
|
|
559
|
+
)) {
|
|
560
|
+
return; // Allow clicks on permission UI
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const now = Date.now();
|
|
564
|
+
if (now - lastTouchEnd <= 300) {
|
|
565
|
+
e.preventDefault();
|
|
566
|
+
}
|
|
567
|
+
lastTouchEnd = now;
|
|
568
|
+
}, false);
|
|
569
|
+
|
|
570
|
+
// Prevent long-press context menu
|
|
571
|
+
window.oncontextmenu = function(e) {
|
|
572
|
+
e.preventDefault();
|
|
573
|
+
e.stopPropagation();
|
|
574
|
+
return false;
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function _initializeP5TouchOverrides() {
|
|
579
|
+
// Wait for p5 to be ready
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
if (window._setupDone) {
|
|
582
|
+
_overrideP5Touch();
|
|
583
|
+
} else {
|
|
584
|
+
// Try again after setup
|
|
585
|
+
const checkP5 = setInterval(() => {
|
|
586
|
+
if (window._setupDone) {
|
|
587
|
+
_overrideP5Touch();
|
|
588
|
+
clearInterval(checkP5);
|
|
589
|
+
}
|
|
590
|
+
}, 100);
|
|
591
|
+
}
|
|
592
|
+
}, 100);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function _overrideP5Touch() {
|
|
596
|
+
const origTouchStarted = window.touchStarted || function() {};
|
|
597
|
+
const origTouchMoved = window.touchMoved || function() {};
|
|
598
|
+
const origTouchEnded = window.touchEnded || function() {};
|
|
599
|
+
const origMousePressed = window.mousePressed || function() {};
|
|
600
|
+
const origMouseDragged = window.mouseDragged || function() {};
|
|
601
|
+
const origMouseReleased = window.mouseReleased || function() {};
|
|
602
|
+
|
|
603
|
+
// Ensure all touch functions return false to prevent default behaviors
|
|
604
|
+
window.touchStarted = function(e) {
|
|
605
|
+
origTouchStarted(e);
|
|
606
|
+
return false;
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
window.touchMoved = function(e) {
|
|
610
|
+
origTouchMoved(e);
|
|
611
|
+
return false;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
window.touchEnded = function(e) {
|
|
615
|
+
origTouchEnded(e);
|
|
616
|
+
return false;
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
window.mousePressed = function(e) {
|
|
620
|
+
origMousePressed(e);
|
|
621
|
+
return false;
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
window.mouseDragged = function(e) {
|
|
625
|
+
origMouseDragged(e);
|
|
626
|
+
return false;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
window.mouseReleased = function(e) {
|
|
630
|
+
origMouseReleased(e);
|
|
631
|
+
return false;
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// =========================================
|
|
636
|
+
// LEGACY COMPATIBILITY
|
|
637
|
+
// =========================================
|
|
638
|
+
|
|
639
|
+
// Initialize gesture blocking on DOM load for backward compatibility
|
|
640
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
641
|
+
// Check for old-style HTML elements
|
|
642
|
+
const startButton = document.getElementById('startButton');
|
|
643
|
+
const statusText = document.getElementById('statusText');
|
|
644
|
+
|
|
645
|
+
if (startButton && statusText) {
|
|
646
|
+
console.warn('⚠️ Legacy HTML elements detected. Consider using the new API functions instead.');
|
|
647
|
+
// Maintain backward compatibility
|
|
648
|
+
startButton.addEventListener('click', async () => {
|
|
649
|
+
startButton.classList.add('hidden');
|
|
650
|
+
statusText.classList.remove('hidden');
|
|
651
|
+
statusText.textContent = 'Requesting permissions...';
|
|
652
|
+
|
|
653
|
+
await _requestMotionPermissions();
|
|
654
|
+
await _requestMicrophonePermissions();
|
|
655
|
+
|
|
656
|
+
statusText.classList.add('hidden');
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
lockGestures(); // Auto-lock gestures for legacy mode
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// =========================================
|
|
664
|
+
// DEBUG SYSTEM - ON-SCREEN CONSOLE
|
|
665
|
+
// =========================================
|
|
666
|
+
|
|
667
|
+
// Debug system state
|
|
668
|
+
let _debugPanel = null;
|
|
669
|
+
let _debugVisible = false;
|
|
670
|
+
let _debugMessages = [];
|
|
671
|
+
const MAX_DEBUG_MESSAGES = 20;
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Show the on-screen debug panel
|
|
675
|
+
*/
|
|
676
|
+
function showDebug() {
|
|
677
|
+
_createDebugPanel();
|
|
678
|
+
_debugPanel.style.display = 'block';
|
|
679
|
+
_debugVisible = true;
|
|
680
|
+
window._debugVisible = true; // Global flag
|
|
681
|
+
|
|
682
|
+
// Set up console overrides for future calls
|
|
683
|
+
_setupConsoleOverrides();
|
|
684
|
+
|
|
685
|
+
// Immediately show any early errors that might have been caught
|
|
686
|
+
_displayEarlyErrors();
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Hide the on-screen debug panel
|
|
691
|
+
*/
|
|
692
|
+
function hideDebug() {
|
|
693
|
+
if (_debugPanel) {
|
|
694
|
+
_debugPanel.style.display = 'none';
|
|
695
|
+
_debugVisible = false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Toggle the debug panel visibility
|
|
701
|
+
*/
|
|
702
|
+
function toggleDebug() {
|
|
703
|
+
if (_debugVisible) {
|
|
704
|
+
hideDebug();
|
|
705
|
+
} else {
|
|
706
|
+
showDebug();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Debug function - works like console.log but shows on screen
|
|
712
|
+
* Also logs to browser console
|
|
713
|
+
*/
|
|
714
|
+
function debug(...args) {
|
|
715
|
+
// Also log to browser console
|
|
716
|
+
console.log(...args);
|
|
717
|
+
|
|
718
|
+
// Format arguments like console.log does
|
|
719
|
+
const message = args.map(arg => {
|
|
720
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
721
|
+
try {
|
|
722
|
+
return JSON.stringify(arg, null, 2);
|
|
723
|
+
} catch (e) {
|
|
724
|
+
return String(arg);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return String(arg);
|
|
728
|
+
}).join(' ');
|
|
729
|
+
|
|
730
|
+
_addDebugMessage(message, 'log');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Error function - shows errors on screen with red styling
|
|
735
|
+
* Also logs to browser console as error
|
|
736
|
+
*/
|
|
737
|
+
function debugError(...args) {
|
|
738
|
+
// Use original console.error to avoid infinite loop
|
|
739
|
+
const originalError = window._originalConsoleError || console.error;
|
|
740
|
+
originalError.apply(console, args);
|
|
741
|
+
|
|
742
|
+
// Format arguments like console.log does
|
|
743
|
+
const message = args.map(arg => {
|
|
744
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
745
|
+
try {
|
|
746
|
+
return JSON.stringify(arg, null, 2);
|
|
747
|
+
} catch (e) {
|
|
748
|
+
return String(arg);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return String(arg);
|
|
752
|
+
}).join(' ');
|
|
753
|
+
|
|
754
|
+
_addDebugMessage(`❌ ERROR: ${message}`, 'error');
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Warning function - shows warnings on screen with yellow styling
|
|
759
|
+
* Also logs to browser console as warning
|
|
760
|
+
*/
|
|
761
|
+
function debugWarn(...args) {
|
|
762
|
+
// Use original console.warn to avoid infinite loop
|
|
763
|
+
const originalWarn = window._originalConsoleWarn || console.warn;
|
|
764
|
+
originalWarn.apply(console, args);
|
|
765
|
+
|
|
766
|
+
// Format arguments like console.log does
|
|
767
|
+
const message = args.map(arg => {
|
|
768
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
769
|
+
try {
|
|
770
|
+
return JSON.stringify(arg, null, 2);
|
|
771
|
+
} catch (e) {
|
|
772
|
+
return String(arg);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return String(arg);
|
|
776
|
+
}).join(' ');
|
|
777
|
+
|
|
778
|
+
_addDebugMessage(`⚠️ WARNING: ${message}`, 'warning');
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Internal function to add messages to debug panel
|
|
783
|
+
*/
|
|
784
|
+
function _addDebugMessage(message, type = 'log') {
|
|
785
|
+
// Add timestamp
|
|
786
|
+
const timestamp = new Date().toLocaleTimeString('en-US', {
|
|
787
|
+
hour12: false,
|
|
788
|
+
hour: '2-digit',
|
|
789
|
+
minute: '2-digit',
|
|
790
|
+
second: '2-digit',
|
|
791
|
+
fractionalSecondDigits: 3
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
const timestampedMessage = {
|
|
795
|
+
text: `[${timestamp}] ${message}`,
|
|
796
|
+
type: type
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
// Add to message history
|
|
800
|
+
_debugMessages.push(timestampedMessage);
|
|
801
|
+
if (_debugMessages.length > MAX_DEBUG_MESSAGES) {
|
|
802
|
+
_debugMessages.shift();
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Update display if panel exists
|
|
806
|
+
if (_debugPanel) {
|
|
807
|
+
_updateDebugDisplay();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Clear all debug messages
|
|
813
|
+
*/
|
|
814
|
+
debug.clear = function() {
|
|
815
|
+
_debugMessages = [];
|
|
816
|
+
if (_debugPanel) {
|
|
817
|
+
_updateDebugDisplay();
|
|
818
|
+
}
|
|
819
|
+
console.clear();
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
// Make debug functions globally accessible
|
|
823
|
+
window.debug = debug;
|
|
824
|
+
window.debugError = debugError;
|
|
825
|
+
window.debugWarn = debugWarn;
|
|
826
|
+
window.showDebug = showDebug;
|
|
827
|
+
window.hideDebug = hideDebug;
|
|
828
|
+
window.toggleDebug = toggleDebug;
|
|
829
|
+
|
|
830
|
+
// Make permission functions globally accessible
|
|
831
|
+
window.lockGestures = lockGestures;
|
|
832
|
+
window.enableGyroTap = enableGyroTap;
|
|
833
|
+
window.enableGyroButton = enableGyroButton;
|
|
834
|
+
window.enableMicTap = enableMicTap;
|
|
835
|
+
window.enableMicButton = enableMicButton;
|
|
836
|
+
window.enableSoundTap = enableSoundTap;
|
|
837
|
+
window.enableSoundButton = enableSoundButton;
|
|
838
|
+
window.enableAllTap = enableAllTap;
|
|
839
|
+
window.enableAllButton = enableAllButton;
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Set up console overrides to capture console.error and console.warn
|
|
843
|
+
*/
|
|
844
|
+
function _setupConsoleOverrides() {
|
|
845
|
+
// Only override once
|
|
846
|
+
if (window._consoleOverrideSet) return;
|
|
847
|
+
window._consoleOverrideSet = true;
|
|
848
|
+
|
|
849
|
+
// Store original console methods for debug functions to use
|
|
850
|
+
window._originalConsoleError = console.error;
|
|
851
|
+
window._originalConsoleWarn = console.warn;
|
|
852
|
+
|
|
853
|
+
// Override console.error to also show in debug panel
|
|
854
|
+
console.error = function(...args) {
|
|
855
|
+
window._originalConsoleError.apply(console, args);
|
|
856
|
+
if (_debugVisible) {
|
|
857
|
+
debugError(...args);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// Override console.warn to also show in debug panel
|
|
862
|
+
console.warn = function(...args) {
|
|
863
|
+
window._originalConsoleWarn.apply(console, args);
|
|
864
|
+
if (_debugVisible) {
|
|
865
|
+
debugWarn(...args);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Display any errors that were caught before the debug panel was ready
|
|
872
|
+
*/
|
|
873
|
+
function _displayEarlyErrors() {
|
|
874
|
+
if (window._earlyErrors && window._earlyErrors.length > 0) {
|
|
875
|
+
debugError(`🚨 Found ${window._earlyErrors.length} early error(s):`);
|
|
876
|
+
window._earlyErrors.forEach(error => {
|
|
877
|
+
debugError(error.message);
|
|
878
|
+
if (error.stack) {
|
|
879
|
+
debugError('Stack trace:', error.stack);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
window._earlyErrors = []; // Clear after displaying
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Create the debug panel DOM element
|
|
888
|
+
*/
|
|
889
|
+
function _createDebugPanel() {
|
|
890
|
+
if (_debugPanel) return;
|
|
891
|
+
|
|
892
|
+
_debugPanel = document.createElement('div');
|
|
893
|
+
_debugPanel.id = 'mobile-debug-panel';
|
|
894
|
+
_debugPanel.innerHTML = `
|
|
895
|
+
<div id="mobile-debug-header">
|
|
896
|
+
<span>Debug</span>
|
|
897
|
+
<button id="mobile-debug-close">×</button>
|
|
898
|
+
</div>
|
|
899
|
+
<div id="mobile-debug-content"></div>
|
|
900
|
+
`;
|
|
901
|
+
|
|
902
|
+
// Add styles
|
|
903
|
+
const style = document.createElement('style');
|
|
904
|
+
style.textContent = `
|
|
905
|
+
#mobile-debug-panel {
|
|
906
|
+
position: fixed;
|
|
907
|
+
top: 20px;
|
|
908
|
+
right: 20px;
|
|
909
|
+
width: 350px;
|
|
910
|
+
max-width: calc(100vw - 40px);
|
|
911
|
+
max-height: 400px;
|
|
912
|
+
background: rgba(0, 0, 0, 0.9);
|
|
913
|
+
color: #ffffff;
|
|
914
|
+
font-family: 'Courier New', monospace;
|
|
915
|
+
font-size: 12px;
|
|
916
|
+
border-radius: 8px;
|
|
917
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
918
|
+
z-index: 10000;
|
|
919
|
+
display: none;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
#mobile-debug-header {
|
|
923
|
+
background: rgba(255, 255, 255, 0.1);
|
|
924
|
+
padding: 8px 12px;
|
|
925
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
|
926
|
+
display: flex;
|
|
927
|
+
justify-content: space-between;
|
|
928
|
+
align-items: center;
|
|
929
|
+
border-radius: 8px 8px 0 0;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
#mobile-debug-header span {
|
|
933
|
+
font-weight: bold;
|
|
934
|
+
font-size: 13px;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
#mobile-debug-close {
|
|
938
|
+
background: none;
|
|
939
|
+
border: none;
|
|
940
|
+
color: #ffffff;
|
|
941
|
+
font-size: 18px;
|
|
942
|
+
cursor: pointer;
|
|
943
|
+
padding: 0;
|
|
944
|
+
width: 20px;
|
|
945
|
+
height: 20px;
|
|
946
|
+
display: flex;
|
|
947
|
+
align-items: center;
|
|
948
|
+
justify-content: center;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
#mobile-debug-close:hover {
|
|
952
|
+
background: rgba(255, 255, 255, 0.1);
|
|
953
|
+
border-radius: 4px;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
#mobile-debug-content {
|
|
957
|
+
padding: 12px;
|
|
958
|
+
max-height: 340px;
|
|
959
|
+
overflow-y: auto;
|
|
960
|
+
word-wrap: break-word;
|
|
961
|
+
line-height: 1.4;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
.debug-message {
|
|
965
|
+
margin-bottom: 4px;
|
|
966
|
+
white-space: pre-wrap;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.debug-message.error {
|
|
970
|
+
color: #ff6b6b;
|
|
971
|
+
background: rgba(255, 107, 107, 0.1);
|
|
972
|
+
padding: 4px;
|
|
973
|
+
border-radius: 3px;
|
|
974
|
+
border-left: 3px solid #ff6b6b;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.debug-message.warning {
|
|
978
|
+
color: #ffd93d;
|
|
979
|
+
background: rgba(255, 217, 61, 0.1);
|
|
980
|
+
padding: 4px;
|
|
981
|
+
border-radius: 3px;
|
|
982
|
+
border-left: 3px solid #ffd93d;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.debug-timestamp {
|
|
986
|
+
color: #888;
|
|
987
|
+
font-size: 10px;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
@media (max-width: 480px) {
|
|
991
|
+
#mobile-debug-panel {
|
|
992
|
+
width: calc(100vw - 20px);
|
|
993
|
+
right: 10px;
|
|
994
|
+
top: 10px;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
`;
|
|
998
|
+
|
|
999
|
+
document.head.appendChild(style);
|
|
1000
|
+
document.body.appendChild(_debugPanel);
|
|
1001
|
+
|
|
1002
|
+
// Add close button functionality
|
|
1003
|
+
document.getElementById('mobile-debug-close').onclick = hideDebug;
|
|
1004
|
+
|
|
1005
|
+
// Update display with existing messages
|
|
1006
|
+
_updateDebugDisplay();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Update the debug panel display with current messages
|
|
1011
|
+
*/
|
|
1012
|
+
function _updateDebugDisplay() {
|
|
1013
|
+
if (!_debugPanel) return;
|
|
1014
|
+
|
|
1015
|
+
const content = document.getElementById('mobile-debug-content');
|
|
1016
|
+
if (!content) return;
|
|
1017
|
+
|
|
1018
|
+
content.innerHTML = _debugMessages
|
|
1019
|
+
.map(msg => {
|
|
1020
|
+
// Handle both old string format and new object format
|
|
1021
|
+
if (typeof msg === 'string') {
|
|
1022
|
+
return `<div class="debug-message">${msg}</div>`;
|
|
1023
|
+
} else {
|
|
1024
|
+
return `<div class="debug-message ${msg.type}">${msg.text}</div>`;
|
|
1025
|
+
}
|
|
1026
|
+
})
|
|
1027
|
+
.join('');
|
|
1028
|
+
|
|
1029
|
+
// Auto-scroll to bottom
|
|
1030
|
+
content.scrollTop = content.scrollHeight;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// =========================================
|
|
1034
|
+
// P5.JS NAMESPACE SUPPORT
|
|
1035
|
+
// =========================================
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Add functions to p5.js prototype for namespace support
|
|
1039
|
+
* This allows using both global functions and p5.prototype.functionName
|
|
1040
|
+
*/
|
|
1041
|
+
if (typeof p5 !== 'undefined' && p5.prototype) {
|
|
1042
|
+
// Core permission functions
|
|
1043
|
+
p5.prototype.lockGestures = lockGestures;
|
|
1044
|
+
p5.prototype.enableGyroTap = enableGyroTap;
|
|
1045
|
+
p5.prototype.enableGyroButton = enableGyroButton;
|
|
1046
|
+
p5.prototype.enableMicTap = enableMicTap;
|
|
1047
|
+
p5.prototype.enableMicButton = enableMicButton;
|
|
1048
|
+
p5.prototype.enableSoundTap = enableSoundTap;
|
|
1049
|
+
p5.prototype.enableSoundButton = enableSoundButton;
|
|
1050
|
+
p5.prototype.enableAllTap = enableAllTap;
|
|
1051
|
+
p5.prototype.enableAllButton = enableAllButton;
|
|
1052
|
+
|
|
1053
|
+
// Debug functions
|
|
1054
|
+
p5.prototype.showDebug = showDebug;
|
|
1055
|
+
p5.prototype.hideDebug = hideDebug;
|
|
1056
|
+
p5.prototype.toggleDebug = toggleDebug;
|
|
1057
|
+
p5.prototype.debug = debug;
|
|
1058
|
+
p5.prototype.debugError = debugError;
|
|
1059
|
+
p5.prototype.debugWarn = debugWarn;
|
|
1060
|
+
|
|
1061
|
+
console.log('✅ Mobile p5.js Permissions: p5.prototype functions registered');
|
|
1062
|
+
}
|