q5 4.4.5 → 4.5.3
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/deno.json +2 -1
- package/package.json +29 -6
- package/q5.d.ts +3 -1
- package/q5.js +210 -43
- package/q5.min.js +3 -2
- package/q5.min.js.map +1 -0
- package/.prettierignore +0 -2
- package/teach/accessibility.html +0 -503
package/deno.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q5/q5",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.3",
|
|
4
4
|
"license": "LGPL-3.0-only",
|
|
5
5
|
"description": "Beginner friendly graphics powered by WebGPU, optimized for interactive art!",
|
|
6
6
|
"author": "quinton-ashley",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"home",
|
|
25
25
|
"lang",
|
|
26
26
|
"learn",
|
|
27
|
+
"teach",
|
|
27
28
|
"test",
|
|
28
29
|
".npmignore",
|
|
29
30
|
".prettierignore",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "q5",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.3",
|
|
4
4
|
"description": "Beginner friendly graphics powered by WebGPU, optimized for interactive art!",
|
|
5
5
|
"author": "quinton-ashley",
|
|
6
6
|
"contributors": [
|
|
@@ -12,8 +12,27 @@
|
|
|
12
12
|
],
|
|
13
13
|
"license": "LGPL-3.0-only",
|
|
14
14
|
"homepage": "https://q5js.org/home",
|
|
15
|
-
"main": "q5-server.js",
|
|
16
15
|
"types": "q5.d.ts",
|
|
16
|
+
"browser": {
|
|
17
|
+
".": "./q5.js",
|
|
18
|
+
"node:process": false
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./q5.d.ts",
|
|
23
|
+
"browser": "./q5.js",
|
|
24
|
+
"deno": "./q5-deno-server.js",
|
|
25
|
+
"node": "./q5-server.js",
|
|
26
|
+
"default": "./q5.js"
|
|
27
|
+
},
|
|
28
|
+
"./q5.min.js": "./q5.min.js",
|
|
29
|
+
"./q5.js": "./q5.js",
|
|
30
|
+
"./q5-server.js": {
|
|
31
|
+
"browser": null,
|
|
32
|
+
"deno": "./q5-deno-server.js",
|
|
33
|
+
"default": "./q5-server.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
17
36
|
"funding": [
|
|
18
37
|
{
|
|
19
38
|
"type": "patreon",
|
|
@@ -29,8 +48,8 @@
|
|
|
29
48
|
}
|
|
30
49
|
],
|
|
31
50
|
"scripts": {
|
|
32
|
-
"bundle": "cat src/q5-core.js src/q5-canvas.js src/q5-c2d-canvas.js src/q5-c2d-shapes.js src/q5-c2d-image.js src/q5-c2d-soft-filters.js src/q5-c2d-text.js src/q5-color.js src/q5-display.js src/q5-dom.js src/q5-fes.js src/q5-input.js src/q5-math.js src/q5-record.js src/q5-sound.js src/q5-util.js src/q5-vector.js src/q5-webgpu.js src/q5-lang.js > q5.js",
|
|
33
|
-
"min": "terser q5.js --compress ecma=
|
|
51
|
+
"bundle": "cat src/q5-core.js src/q5-canvas.js src/q5-c2d-canvas.js src/q5-c2d-shapes.js src/q5-c2d-image.js src/q5-c2d-soft-filters.js src/q5-c2d-text.js src/q5-color.js src/q5-display.js src/q5-dom.js src/q5-fes.js src/q5-input.js src/q5-math.js src/q5-record.js src/q5-sound.js src/q5-util.js src/q5-vector.js src/q5-webgpu.js src/q5-lang.js src/q5-python.js > q5.js",
|
|
52
|
+
"min": "terser q5.js --compress ecma=2026 --mangle --output q5.min.js --source-map url=q5.min.js.map",
|
|
34
53
|
"dist": "bun bundle && bun min",
|
|
35
54
|
"tests": "jest test",
|
|
36
55
|
"bld": "node lang/build.js",
|
|
@@ -61,6 +80,10 @@
|
|
|
61
80
|
"jest-cli": "^29.7.0",
|
|
62
81
|
"jsdom": "^25.0.1",
|
|
63
82
|
"json2csv": "^6.0.0-alpha.2",
|
|
64
|
-
"skia-canvas": "^1.0.2"
|
|
65
|
-
|
|
83
|
+
"skia-canvas": "^1.0.2",
|
|
84
|
+
"terser": "^5.46.1"
|
|
85
|
+
},
|
|
86
|
+
"trustedDependencies": [
|
|
87
|
+
"skia-canvas"
|
|
88
|
+
]
|
|
66
89
|
}
|
package/q5.d.ts
CHANGED
|
@@ -2500,6 +2500,8 @@ declare global {
|
|
|
2500
2500
|
* - If one numerical input is provided, returns a number between 0 and the provided value.
|
|
2501
2501
|
* - If two numerical inputs are provided, returns a number between the two values.
|
|
2502
2502
|
* - If an array is provided, returns a random element from the array.
|
|
2503
|
+
*
|
|
2504
|
+
* Return value can be the lower bound but can never exactly be the upper bound.
|
|
2503
2505
|
* @param {number | any[]} [low] lower bound (inclusive) or an array
|
|
2504
2506
|
* @param {number} [high] upper bound (exclusive)
|
|
2505
2507
|
* @returns {number | any} a random number or element
|
|
@@ -4035,7 +4037,7 @@ declare global {
|
|
|
4035
4037
|
*
|
|
4036
4038
|
* q5.draw = function () {
|
|
4037
4039
|
* shader(stripes);
|
|
4038
|
-
*
|
|
4040
|
+
* plane(0, 0, width, height);
|
|
4039
4041
|
*
|
|
4040
4042
|
* resetShader();
|
|
4041
4043
|
* triangle(-50, -50, 0, 50, 50, -50);
|
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 4.
|
|
3
|
+
* @version 4.5
|
|
4
4
|
* @author quinton-ashley
|
|
5
5
|
* @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
|
|
6
6
|
* @license LGPL-3.0
|
|
@@ -298,6 +298,7 @@ function Q5(scope, parent, renderer) {
|
|
|
298
298
|
if (Q5[name]) $[name] = Q5[name];
|
|
299
299
|
else {
|
|
300
300
|
Object.defineProperty(Q5, name, {
|
|
301
|
+
configurable: true,
|
|
301
302
|
get: () => $[name],
|
|
302
303
|
set: (fn) => ($[name] = fn)
|
|
303
304
|
});
|
|
@@ -307,9 +308,9 @@ function Q5(scope, parent, renderer) {
|
|
|
307
308
|
|
|
308
309
|
function wrapWithFES(name) {
|
|
309
310
|
const fn = t[name] || $[name];
|
|
310
|
-
$[name] = (
|
|
311
|
+
$[name] = function (...args) {
|
|
311
312
|
try {
|
|
312
|
-
return fn(
|
|
313
|
+
return fn.apply(this, args);
|
|
313
314
|
} catch (e) {
|
|
314
315
|
if ($._fes) $._fes(e);
|
|
315
316
|
throw e;
|
|
@@ -321,6 +322,7 @@ function Q5(scope, parent, renderer) {
|
|
|
321
322
|
await runHooks('presetup');
|
|
322
323
|
|
|
323
324
|
readyResolve();
|
|
325
|
+
if ($._removed) return;
|
|
324
326
|
|
|
325
327
|
if (t.preload || $.preload) {
|
|
326
328
|
wrapWithFES('preload');
|
|
@@ -352,9 +354,9 @@ function Q5(scope, parent, renderer) {
|
|
|
352
354
|
})
|
|
353
355
|
]);
|
|
354
356
|
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
if ($._removed) return;
|
|
358
|
+
if (!$._disablePreload) await $.loadAll();
|
|
359
|
+
if ($._removed) return;
|
|
358
360
|
|
|
359
361
|
$.setup ??= t.setup || (() => {});
|
|
360
362
|
wrapWithFES('setup');
|
|
@@ -366,10 +368,12 @@ function Q5(scope, parent, renderer) {
|
|
|
366
368
|
millisStart = performance.now();
|
|
367
369
|
await $.setup();
|
|
368
370
|
$._setupDone = true;
|
|
371
|
+
|
|
372
|
+
if ($._removed) return;
|
|
369
373
|
if ($.ctx === null) $.createCanvas(200, 200);
|
|
370
374
|
await runHooks('postsetup');
|
|
371
375
|
|
|
372
|
-
if ($.frameCount) return;
|
|
376
|
+
if ($.frameCount || $._removed) return;
|
|
373
377
|
|
|
374
378
|
$._lastFrameTime = performance.now() - 15;
|
|
375
379
|
raf(_draw);
|
|
@@ -449,7 +453,7 @@ Q5.preloadMethods = {};
|
|
|
449
453
|
Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.preloadMethods[n] = fn[n]);
|
|
450
454
|
|
|
451
455
|
function Canvas(w, h, opt) {
|
|
452
|
-
if (Q5._hasGlobal) return;
|
|
456
|
+
if (Q5._hasGlobal) return Promise.resolve(Q5.instances[0].canvas);
|
|
453
457
|
|
|
454
458
|
let useC2D = w == 'c2d' || h == 'c2d' || opt == 'c2d' || opt?.renderer == 'c2d' || !Q5._esm;
|
|
455
459
|
|
|
@@ -491,7 +495,7 @@ if (typeof window == 'object') {
|
|
|
491
495
|
window.addEventListener('pagehide', cleanup);
|
|
492
496
|
} else global.window = 0;
|
|
493
497
|
|
|
494
|
-
Q5.version = Q5.VERSION = '4.
|
|
498
|
+
Q5.version = Q5.VERSION = '4.5';
|
|
495
499
|
|
|
496
500
|
if (typeof document == 'object') {
|
|
497
501
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -583,7 +587,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
583
587
|
if (!el) {
|
|
584
588
|
// reattach canvas to the DOM
|
|
585
589
|
document.getElementById(c.id)?.remove();
|
|
586
|
-
|
|
590
|
+
$._addCanvas();
|
|
587
591
|
}
|
|
588
592
|
|
|
589
593
|
if (window.IntersectionObserver) {
|
|
@@ -714,7 +718,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
714
718
|
}
|
|
715
719
|
};
|
|
716
720
|
|
|
717
|
-
|
|
721
|
+
$._addCanvas = () => {
|
|
718
722
|
let el = $._parent;
|
|
719
723
|
el ??= document.getElementsByTagName('main')[0];
|
|
720
724
|
if (!el) {
|
|
@@ -729,8 +733,8 @@ Q5.modules.canvas = ($, q) => {
|
|
|
729
733
|
if (document.body) document.body.appendChild(el);
|
|
730
734
|
});
|
|
731
735
|
}
|
|
732
|
-
}
|
|
733
|
-
|
|
736
|
+
};
|
|
737
|
+
$._addCanvas();
|
|
734
738
|
}
|
|
735
739
|
|
|
736
740
|
$.resizeCanvas = (w, h) => {
|
|
@@ -3455,16 +3459,12 @@ Q5.modules.fes = ($) => {
|
|
|
3455
3459
|
try {
|
|
3456
3460
|
let res = await (await fetch(fileUrl)).text(),
|
|
3457
3461
|
lines = res.split('\n'),
|
|
3458
|
-
errLine = lines[lineNum - 1]?.trim()
|
|
3459
|
-
bug = ['🐛', '🐞', '🐜', '🦗', '🦋', '🪲'][Math.floor(Math.random() * 6)],
|
|
3460
|
-
inIframe = window.self !== window.top,
|
|
3461
|
-
prefix = `q5.js ${bug}`,
|
|
3462
|
-
errorMsg = ` Error in ${fileBase} on line ${lineNum}:\n\n${errLine}`;
|
|
3462
|
+
errLine = lines[lineNum - 1]?.trim();
|
|
3463
3463
|
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3464
|
+
let type = '';
|
|
3465
|
+
if (e instanceof SyntaxError || e.name === 'SyntaxError') type = 'syntax';
|
|
3466
|
+
|
|
3467
|
+
Q5.friendlyError(fileBase, lineNum, errLine, type);
|
|
3468
3468
|
} catch (err) {}
|
|
3469
3469
|
};
|
|
3470
3470
|
|
|
@@ -3477,7 +3477,7 @@ Q5.modules.fes = ($) => {
|
|
|
3477
3477
|
let match = line.match(/(https?:\/\/[^\s)]+\.js|\b\/[^\s)]+\.js)/);
|
|
3478
3478
|
if (match) {
|
|
3479
3479
|
let file = match[1];
|
|
3480
|
-
if (!/q5|p5play/i.test(file)) {
|
|
3480
|
+
if (!/q5|p5play|q5play|brython/i.test(file)) {
|
|
3481
3481
|
$._sketchFile = file;
|
|
3482
3482
|
break;
|
|
3483
3483
|
}
|
|
@@ -3516,6 +3516,19 @@ Q5.modules.fes = ($) => {
|
|
|
3516
3516
|
checkLatestVersion();
|
|
3517
3517
|
}
|
|
3518
3518
|
};
|
|
3519
|
+
|
|
3520
|
+
Q5.friendlyError = (file, lineNum, detail) => {
|
|
3521
|
+
let bug = ['🐛', '🐞', '🐜', '🦗', '🦋', '🪲'][Math.floor(Math.random() * 6)],
|
|
3522
|
+
inIframe = window.self !== window.top,
|
|
3523
|
+
prefix = `q5 ${bug}`,
|
|
3524
|
+
msg = `Error in ${file} on line ${lineNum}`;
|
|
3525
|
+
|
|
3526
|
+
if (detail) msg += ':\n\n' + detail;
|
|
3527
|
+
|
|
3528
|
+
if (inIframe) return console.log(prefix + msg);
|
|
3529
|
+
|
|
3530
|
+
console.log(`%c${prefix}%c ${msg}`, 'background: #b7ebff; color: #000;', '');
|
|
3531
|
+
};
|
|
3519
3532
|
Q5.modules.input = ($, q) => {
|
|
3520
3533
|
if ($._isGraphics) return;
|
|
3521
3534
|
|
|
@@ -9348,17 +9361,17 @@ const userLangs = `
|
|
|
9348
9361
|
update -> es:actualizar
|
|
9349
9362
|
draw -> es:dibujar
|
|
9350
9363
|
postProcess -> es:postProcesar
|
|
9351
|
-
mousePressed -> es:
|
|
9352
|
-
mouseReleased -> es:
|
|
9353
|
-
mouseMoved -> es:
|
|
9354
|
-
mouseDragged -> es:
|
|
9364
|
+
mousePressed -> es:alPresionarRaton
|
|
9365
|
+
mouseReleased -> es:alSoltarRaton
|
|
9366
|
+
mouseMoved -> es:alMoverRaton
|
|
9367
|
+
mouseDragged -> es:alArrastrarRaton
|
|
9355
9368
|
doubleClicked -> es:dobleClic
|
|
9356
9369
|
keyPressed -> es:alPresionarTecla
|
|
9357
9370
|
keyReleased -> es:alSoltarTecla
|
|
9358
9371
|
touchStarted -> es:alEmpezarToque
|
|
9359
9372
|
touchEnded -> es:alTerminarToque
|
|
9360
9373
|
touchMoved -> es:alMoverToque
|
|
9361
|
-
mouseWheel -> es:
|
|
9374
|
+
mouseWheel -> es:ruedaRaton
|
|
9362
9375
|
`;
|
|
9363
9376
|
|
|
9364
9377
|
const classLangs = {
|
|
@@ -9439,20 +9452,19 @@ Object.defineProperty(Q5, 'lang', {
|
|
|
9439
9452
|
|
|
9440
9453
|
for (let className in classLangs) {
|
|
9441
9454
|
let target = className == 'Q5' ? Q5 : Q5[className] ? Q5[className].prototype : null;
|
|
9442
|
-
if (target)
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
}
|
|
9455
|
+
if (!target) continue;
|
|
9456
|
+
let map = parseLangs(classLangs[className], val);
|
|
9457
|
+
for (let name in map) {
|
|
9458
|
+
let translatedName = map[name];
|
|
9459
|
+
if (target.hasOwnProperty(translatedName)) continue;
|
|
9460
|
+
Object.defineProperty(target, translatedName, {
|
|
9461
|
+
get: function () {
|
|
9462
|
+
return this[name];
|
|
9463
|
+
},
|
|
9464
|
+
set: function (v) {
|
|
9465
|
+
this[name] = v;
|
|
9466
|
+
}
|
|
9467
|
+
});
|
|
9456
9468
|
}
|
|
9457
9469
|
}
|
|
9458
9470
|
|
|
@@ -9511,6 +9523,13 @@ Q5.addHook('init', (q) => {
|
|
|
9511
9523
|
for (let name in m) {
|
|
9512
9524
|
let translatedName = m[name];
|
|
9513
9525
|
q[translatedName] = q[name];
|
|
9526
|
+
|
|
9527
|
+
if (Q5._lang == 'es') {
|
|
9528
|
+
let unaccentedName = translatedName.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
9529
|
+
if (unaccentedName != translatedName) {
|
|
9530
|
+
q[unaccentedName] = q[name];
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9514
9533
|
}
|
|
9515
9534
|
});
|
|
9516
9535
|
|
|
@@ -9523,6 +9542,8 @@ Q5.addHook('predraw', (q) => {
|
|
|
9523
9542
|
'frameCount',
|
|
9524
9543
|
'mouseX',
|
|
9525
9544
|
'mouseY',
|
|
9545
|
+
'pmouseX',
|
|
9546
|
+
'pmouseY',
|
|
9526
9547
|
'movedX',
|
|
9527
9548
|
'movedY',
|
|
9528
9549
|
'mouseIsPressed',
|
|
@@ -9535,6 +9556,152 @@ Q5.addHook('predraw', (q) => {
|
|
|
9535
9556
|
|
|
9536
9557
|
// sync properties
|
|
9537
9558
|
for (let p of props) {
|
|
9538
|
-
if (m[p])
|
|
9559
|
+
if (!m[p]) continue;
|
|
9560
|
+
q[m[p]] = q[p];
|
|
9561
|
+
if (Q5._lang == 'es') {
|
|
9562
|
+
let unaccentedName = m[p].normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
9563
|
+
if (unaccentedName != m[p]) {
|
|
9564
|
+
q[unaccentedName] = q[p];
|
|
9565
|
+
}
|
|
9566
|
+
}
|
|
9539
9567
|
}
|
|
9540
9568
|
});
|
|
9569
|
+
const runPython = async function () {
|
|
9570
|
+
let scripts = [...document.getElementsByTagName('script')];
|
|
9571
|
+
scripts = scripts.filter((s) => s.type == 'q5-python' || s.type == 'text/q5-python');
|
|
9572
|
+
if (!scripts.length) return;
|
|
9573
|
+
|
|
9574
|
+
if (!window.brython) {
|
|
9575
|
+
const loadScript = (src) =>
|
|
9576
|
+
new Promise((resolve, reject) => {
|
|
9577
|
+
const script = document.createElement('script');
|
|
9578
|
+
script.src = src;
|
|
9579
|
+
script.onload = resolve;
|
|
9580
|
+
script.onerror = reject;
|
|
9581
|
+
document.head.appendChild(script);
|
|
9582
|
+
});
|
|
9583
|
+
|
|
9584
|
+
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.14.0/brython.min.js');
|
|
9585
|
+
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.14.0/brython_stdlib.min.js');
|
|
9586
|
+
}
|
|
9587
|
+
|
|
9588
|
+
let code = '';
|
|
9589
|
+
for (const script of scripts) {
|
|
9590
|
+
code += script.src ? await (await fetch(script.src)).text() : script.innerText;
|
|
9591
|
+
}
|
|
9592
|
+
|
|
9593
|
+
const useWebGPU = !code.slice(0, code.indexOf('\n')).includes('C2D'),
|
|
9594
|
+
q5py = useWebGPU ? await Q5.WebGPU() : new Q5();
|
|
9595
|
+
|
|
9596
|
+
// `window.Canvas` returns a promise that resolves when Q5 is ready
|
|
9597
|
+
// but `q5py.Canvas` returns the renderer synchronously
|
|
9598
|
+
// so to make Brython happy with `await Canvas()` we need to make it async
|
|
9599
|
+
const Canvas = q5py.Canvas;
|
|
9600
|
+
q5py.Canvas = async (...a) => Canvas(...a);
|
|
9601
|
+
|
|
9602
|
+
code = code.replaceAll('\n', '\n\t');
|
|
9603
|
+
|
|
9604
|
+
code = `
|
|
9605
|
+
async def __run():
|
|
9606
|
+
${code}
|
|
9607
|
+
|
|
9608
|
+
_q5_state_vars = ["mouseX", "mouseY", "pmouseX", "pmouseY", "width", "height", "frameCount", "deltaTime", "mouseIsPressed", "mouseButton", "keyIsPressed", "key", "keyCode", "touches", "movedX", "movedY"]
|
|
9609
|
+
|
|
9610
|
+
def _sync_and_call(fn):
|
|
9611
|
+
def _wrapper(*args):
|
|
9612
|
+
try:
|
|
9613
|
+
for var in _q5_state_vars:
|
|
9614
|
+
if hasattr(q5py, var):
|
|
9615
|
+
ns[var] = getattr(q5py, var)
|
|
9616
|
+
return fn(*args)
|
|
9617
|
+
except Exception as e:
|
|
9618
|
+
window._pyErr(_err())
|
|
9619
|
+
raise e
|
|
9620
|
+
return _wrapper
|
|
9621
|
+
|
|
9622
|
+
for fn_name in ["update", "draw", "mousePressed", "mouseReleased", "mouseMoved", "mouseDragged", "mouseClicked", "doubleClicked", "mouseWheel", "keyPressed", "keyReleased", "keyTyped", "touchStarted", "touchMoved", "touchEnded", "windowResized"]:
|
|
9623
|
+
if fn_name in locals():
|
|
9624
|
+
setattr(window, fn_name, _sync_and_call(locals()[fn_name]))
|
|
9625
|
+
`;
|
|
9626
|
+
|
|
9627
|
+
window._pyErr = (err, lineNum) => {
|
|
9628
|
+
if (typeof err === 'string' && err.includes('Traceback')) {
|
|
9629
|
+
let lines = err.split('\n');
|
|
9630
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9631
|
+
const match = lines[i].match(/File "<string>", line (\d+)/);
|
|
9632
|
+
if (match) {
|
|
9633
|
+
lineNum = parseInt(match[1]);
|
|
9634
|
+
lines = lines.slice(i + 1);
|
|
9635
|
+
// de-indent the first two lines based on the first line's indentation
|
|
9636
|
+
const indentMatch = lines[0].match(/^\s+/);
|
|
9637
|
+
if (indentMatch) {
|
|
9638
|
+
const indent = indentMatch[0];
|
|
9639
|
+
for (let j = 0; j < Math.min(2, lines.length); j++) {
|
|
9640
|
+
lines[j] = lines[j].slice(indent.length);
|
|
9641
|
+
}
|
|
9642
|
+
}
|
|
9643
|
+
err = lines.join('\n');
|
|
9644
|
+
break;
|
|
9645
|
+
}
|
|
9646
|
+
}
|
|
9647
|
+
}
|
|
9648
|
+
|
|
9649
|
+
let file = scripts[0].src.split('/').at(-1);
|
|
9650
|
+
lineNum -= 2; // adjust for the wrapper code lines
|
|
9651
|
+
if (Q5.friendlyError) Q5.friendlyError(file, lineNum, err);
|
|
9652
|
+
else console.error(`Error in ${file} on line ${lineNum}:\n\n${err}`);
|
|
9653
|
+
};
|
|
9654
|
+
|
|
9655
|
+
brython();
|
|
9656
|
+
|
|
9657
|
+
// hide brython's internal logs by temporarily overriding console.log
|
|
9658
|
+
let log = console.log;
|
|
9659
|
+
console.log = function () {};
|
|
9660
|
+
|
|
9661
|
+
__BRYTHON__.runPythonSource(`
|
|
9662
|
+
from browser import window, aio
|
|
9663
|
+
import traceback
|
|
9664
|
+
import io
|
|
9665
|
+
|
|
9666
|
+
def _err():
|
|
9667
|
+
f = io.StringIO()
|
|
9668
|
+
traceback.print_exc(file=f)
|
|
9669
|
+
return f.getvalue()
|
|
9670
|
+
|
|
9671
|
+
async def _run_py(q5py, code):
|
|
9672
|
+
ns = globals().copy()
|
|
9673
|
+
ns['ns'] = ns
|
|
9674
|
+
ns['q5py'] = q5py
|
|
9675
|
+
|
|
9676
|
+
for attr in dir(q5py):
|
|
9677
|
+
if not attr.startswith('_'):
|
|
9678
|
+
try:
|
|
9679
|
+
ns[attr] = getattr(q5py, attr)
|
|
9680
|
+
except Exception:
|
|
9681
|
+
pass
|
|
9682
|
+
|
|
9683
|
+
try:
|
|
9684
|
+
exec(code, ns)
|
|
9685
|
+
except SyntaxError as e:
|
|
9686
|
+
return window._pyErr(_err(), e.lineno)
|
|
9687
|
+
except Exception as e:
|
|
9688
|
+
return window._pyErr(_err())
|
|
9689
|
+
|
|
9690
|
+
try:
|
|
9691
|
+
await ns["__run"]()
|
|
9692
|
+
except Exception as e:
|
|
9693
|
+
window._pyErr(_err())
|
|
9694
|
+
|
|
9695
|
+
window._runPy = _run_py
|
|
9696
|
+
`);
|
|
9697
|
+
|
|
9698
|
+
console.log = log;
|
|
9699
|
+
|
|
9700
|
+
await window._runPy(q5py, code);
|
|
9701
|
+
};
|
|
9702
|
+
|
|
9703
|
+
if (typeof document == 'object') {
|
|
9704
|
+
if (document.readyState == 'loading') {
|
|
9705
|
+
document.addEventListener('DOMContentLoaded', runPython);
|
|
9706
|
+
} else runPython();
|
|
9707
|
+
}
|