littlejsengine 1.13.4 → 1.14.2
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/README.md +2 -1
- package/copyToGithub.bat +28 -0
- package/dist/littlejs.d.ts +346 -240
- package/dist/littlejs.esm.js +1370 -723
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1350 -709
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1333 -692
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +1 -1
- package/examples/breakout/game.js +30 -25
- package/examples/electron/index.html +2 -2
- package/examples/index.html +207 -96
- package/examples/platformer/gameEffects.js +19 -18
- package/examples/platformer/gameLevel.js +6 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/flappyGame.js +2 -1
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/music.js +106 -0
- package/examples/shorts/parallax.js +71 -0
- package/examples/shorts/piano.js +7 -8
- package/examples/shorts/postProcess.js +25 -8
- package/examples/shorts/shapes.js +1 -9
- package/examples/shorts/song.mp3 +0 -0
- package/examples/shorts/uiSystem.js +15 -8
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +8 -3
- package/examples/style.css +14 -4
- package/examples/uiSystem/game.js +11 -11
- package/package.json +1 -1
- package/plugins/box2d.js +10 -8
- package/plugins/drawUtilities.js +5 -5
- package/plugins/newgrounds.js +6 -6
- package/plugins/pluginExport.js +1 -1
- package/plugins/uiSystem.js +103 -79
- package/plugins/zzfxm.js +10 -10
- package/reference.md +90 -54
- package/src/engine.js +22 -22
- package/src/engineAudio.js +284 -109
- package/src/engineBuild.js +9 -9
- package/src/engineDebug.js +26 -26
- package/src/engineDraw.js +148 -97
- package/src/engineExport.js +22 -16
- package/src/engineInput.js +57 -67
- package/src/engineMedals.js +25 -25
- package/src/engineObject.js +25 -22
- package/src/engineParticles.js +59 -59
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +20 -13
- package/src/engineTileLayer.js +34 -34
- package/src/engineUtilities.js +62 -55
- package/src/engineWebGL.js +447 -65
- /package/examples/shorts/{playSound.js → sound.js} +0 -0
package/examples/box2d/game.js
CHANGED
|
@@ -136,7 +136,7 @@ function gameUpdatePost()
|
|
|
136
136
|
///////////////////////////////////////////////////////////////////////////////
|
|
137
137
|
function gameRender()
|
|
138
138
|
{
|
|
139
|
-
// draw a grey square in the background without using
|
|
139
|
+
// draw a grey square in the background without using WebGL
|
|
140
140
|
LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8), 0, false);
|
|
141
141
|
|
|
142
142
|
if (Scenes.scene == 5)
|
|
@@ -20,7 +20,7 @@ const restitution = .2;
|
|
|
20
20
|
|
|
21
21
|
export function spawnBox(pos, size=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDynamic, applyTexture=true, angle=0)
|
|
22
22
|
{
|
|
23
|
-
size = typeof size
|
|
23
|
+
size = typeof size === 'number' ? vec2(size) : size; // square
|
|
24
24
|
const o = new LJS.Box2dObject(pos, size, applyTexture && Game.spriteAtlas.squareOutline, angle, color, type);
|
|
25
25
|
o.drawSize = size.scale(1.02); // slightly enlarge to cover gaps
|
|
26
26
|
o.addBox(size, vec2(), 0, density, friction, restitution);
|
|
@@ -122,38 +122,43 @@ function setupPostProcess()
|
|
|
122
122
|
p=fract(p*.3197);
|
|
123
123
|
return fract(1.+sin(51.*p.x+73.*p.y)*13753.3);
|
|
124
124
|
}
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);
|
|
128
|
-
return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+1.),u.x),u.y);
|
|
129
|
-
}
|
|
125
|
+
|
|
130
126
|
void mainImage(out vec4 c, vec2 p)
|
|
131
127
|
{
|
|
132
|
-
//
|
|
128
|
+
// setup the shader
|
|
129
|
+
vec2 uv = p;
|
|
133
130
|
p /= iResolution.xy;
|
|
134
|
-
|
|
135
|
-
// apply fuzz as horizontal offset
|
|
136
|
-
const float fuzz = .0005;
|
|
137
|
-
const float fuzzScale = 800.;
|
|
138
|
-
const float fuzzSpeed = 9.;
|
|
139
|
-
p.x += fuzz*(noise(vec2(p.y*fuzzScale, iTime*fuzzSpeed))*2.-1.);
|
|
140
|
-
|
|
141
|
-
// init output color
|
|
142
131
|
c = texture(iChannel0, p);
|
|
143
132
|
|
|
144
|
-
//
|
|
145
|
-
const float
|
|
146
|
-
|
|
147
|
-
c
|
|
148
|
-
|
|
149
|
-
// tv static noise
|
|
150
|
-
const float staticNoise = .1;
|
|
151
|
-
c += staticNoise * hash(p + mod(iTime, 1e3));
|
|
133
|
+
// static noise
|
|
134
|
+
const float staticAlpha = .1;
|
|
135
|
+
const float staticScale = .002;
|
|
136
|
+
c += staticAlpha * hash(floor(p/staticScale) + mod(iTime*500., 1e3));
|
|
152
137
|
|
|
153
138
|
// scan lines
|
|
154
|
-
const float scanlineScale =
|
|
155
|
-
const float scanlineAlpha = .
|
|
156
|
-
c *= 1.
|
|
139
|
+
const float scanlineScale = 2.;
|
|
140
|
+
const float scanlineAlpha = .3;
|
|
141
|
+
c *= 1. - scanlineAlpha*cos(p.y*2.*iResolution.y/scanlineScale);
|
|
142
|
+
|
|
143
|
+
{
|
|
144
|
+
// bloom effect
|
|
145
|
+
const float blurSize = .002;
|
|
146
|
+
const float bloomIntensity = .2;
|
|
147
|
+
|
|
148
|
+
// 5-tap Gaussian blur
|
|
149
|
+
vec4 bloom = vec4(0);
|
|
150
|
+
bloom += texture(iChannel0, p + vec2(-2.*blurSize, 0)) * .12;
|
|
151
|
+
bloom += texture(iChannel0, p + vec2( -blurSize, 0)) * .24;
|
|
152
|
+
bloom += texture(iChannel0, p) * .28;
|
|
153
|
+
bloom += texture(iChannel0, p + vec2( blurSize, 0)) * .24;
|
|
154
|
+
bloom += texture(iChannel0, p + vec2( 2.*blurSize, 0)) * .12;
|
|
155
|
+
bloom += texture(iChannel0, p + vec2(0, -2.*blurSize)) * .12;
|
|
156
|
+
bloom += texture(iChannel0, p + vec2(0, -blurSize)) * .24;
|
|
157
|
+
bloom += texture(iChannel0, p) * .28;
|
|
158
|
+
bloom += texture(iChannel0, p + vec2(0, blurSize)) * .24;
|
|
159
|
+
bloom += texture(iChannel0, p + vec2(0, 2.*blurSize)) * .12;
|
|
160
|
+
c += bloom * bloomIntensity;
|
|
161
|
+
}
|
|
157
162
|
|
|
158
163
|
// black vignette around edges
|
|
159
164
|
const float vignette = 2.;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?
|
|
10
|
+
<script src=../../dist/littlejs.js?1136></script>
|
|
11
11
|
|
|
12
12
|
<!-- Add your game scripts here -->
|
|
13
|
-
<script src=game.js?
|
|
13
|
+
<script src=game.js?1136></script>
|
package/examples/index.html
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<!DOCTYPE html><head>
|
|
2
2
|
<title>LittleJS Example Browser</title>
|
|
3
3
|
<link rel=icon type=image/png href=favicon.png>
|
|
4
|
-
<link rel=stylesheet href=style.css?
|
|
4
|
+
<link rel=stylesheet href=style.css?9>
|
|
5
5
|
<meta charset=utf-8>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
7
|
</head><body>
|
|
7
8
|
<div id=container1>
|
|
8
9
|
<div id=container3>
|
|
9
10
|
<p id=titleInfo>LittleJS Example Browser</p>
|
|
10
11
|
<p id=exampleInfo></p>
|
|
11
12
|
<a id=exampleLink target='_blank'></a>
|
|
13
|
+
<div id=divCodeOptions>
|
|
12
14
|
<hr>
|
|
13
|
-
<div>
|
|
14
15
|
Theme:
|
|
15
16
|
<select style=width:120px onchange=loadTheme() id=selectTheme></select>
|
|
16
17
|
<select style=width:40px onchange=loadTheme() id=selectFontSize>
|
|
@@ -20,24 +21,31 @@ Theme:
|
|
|
20
21
|
<option value=20px>L</option>
|
|
21
22
|
<option value=24px>XL</option>
|
|
22
23
|
</select>
|
|
23
|
-
<input type=checkbox id=checkboxLiveEdit checked>Live Edit
|
|
24
|
-
<input type=checkbox id=
|
|
25
|
-
</div>
|
|
24
|
+
<span class=nowrap><input type=checkbox id=checkboxLiveEdit checked>Live Edit</span>
|
|
25
|
+
<span class=nowrap><input type=checkbox id=checkboxJumpToErrors>Jump to Errors</span>
|
|
26
|
+
</div>
|
|
27
|
+
<div id=divTextareas>
|
|
26
28
|
<textarea id=textareaCode oninput=codeInput() spellcheck=false></textarea>
|
|
27
29
|
<textarea id=textareaError readonly spellcheck=false></textarea>
|
|
28
30
|
<textarea id=textareaConsole readonly spellcheck=false></textarea>
|
|
29
31
|
</div>
|
|
32
|
+
</div>
|
|
30
33
|
<div id=container2>
|
|
34
|
+
<div style='display:flex;justify-content:center'>
|
|
31
35
|
<div id=iframeContainer></div>
|
|
36
|
+
</div>
|
|
32
37
|
<div>
|
|
33
|
-
<input id=inputSearch placeholder='Search examples...' oninput=filterExamples() onkeydown='if(event.key
|
|
38
|
+
<input id=inputSearch placeholder='Search examples...' oninput=filterExamples() onkeydown='if(event.key==="Escape")filterExamples(1)'>
|
|
34
39
|
<button id=buttonRestart onclick=restartCode()>Restart</button>
|
|
35
40
|
<button id=buttonPause>Pause</button>
|
|
36
|
-
<
|
|
41
|
+
<span id=container4>
|
|
37
42
|
<button id=buttonFullscreen>Fullscreen</button>
|
|
38
|
-
<
|
|
43
|
+
<button id=buttonScreenshot>Screenshot</button>
|
|
44
|
+
<span class=nowrap><input type=checkbox id=checkboxWebGL checked>WebGL</span>
|
|
45
|
+
</span>
|
|
39
46
|
</div>
|
|
40
|
-
<
|
|
47
|
+
<span id=selectExampleText class=nowrap>Select an example...</span>
|
|
48
|
+
<select id=selectExample onchange=setExample() size=2></select>
|
|
41
49
|
</div>
|
|
42
50
|
</div>
|
|
43
51
|
<script>
|
|
@@ -66,21 +74,23 @@ const exampleList =
|
|
|
66
74
|
new ExampleInfo('Timers', 'timers.js', 'Timer object and timing functions - [utilities, time]'),
|
|
67
75
|
new ExampleInfo('Texture', 'texture.js', 'Display and manipulate textures - [graphics, sprites]'),
|
|
68
76
|
new ExampleInfo('Particles', 'particles.js', 'Particle system basics - [effects, particles]'),
|
|
69
|
-
new ExampleInfo('
|
|
77
|
+
new ExampleInfo('Sound Effects', 'sound.js', 'Example sound effects with ZzFX - [audio, sound]'),
|
|
78
|
+
new ExampleInfo('Music', 'music.js', 'Example sound loading, play, pause, seek, and stop - [audio, music]'),
|
|
70
79
|
new ExampleInfo('System Font', 'systemFont.js', 'Built-in bitmap font rendering - [text, font]'),
|
|
71
80
|
new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering - [graphics, sprites]'),
|
|
72
81
|
new ExampleInfo('Animation', 'animation.js', 'Sprite animation techniques - [animation, sprites]'),
|
|
73
82
|
new ExampleInfo('Clock', 'clock.js', 'Real-time clock display - [time, ui, utilities]'),
|
|
83
|
+
new ExampleInfo('Medals', 'medals.js', 'Achievement system demo - [ui, achievements]'),
|
|
74
84
|
new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system - [tiles, levels]'),
|
|
75
85
|
new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard - [audio, input, ui]'),
|
|
76
86
|
new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation - [algorithm, generation]'),
|
|
77
|
-
new ExampleInfo('Starfield', 'starfield.js', 'Animated
|
|
78
|
-
new ExampleInfo('
|
|
87
|
+
new ExampleInfo('Starfield', 'starfield.js', 'Animated starfield - [effects, space]'),
|
|
88
|
+
new ExampleInfo('Parallax', 'parallax.js', 'Parallax mountains - [graphics, effects]'),
|
|
79
89
|
new ExampleInfo('--- MINI GAMES ---'),
|
|
80
90
|
new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run platformer - [game, platformer, physics]'),
|
|
81
91
|
new ExampleInfo('Top Down Game', 'topDown.js', 'Adventure with collision - [game, topdown, rpg]'),
|
|
82
92
|
new ExampleInfo('Tilted View', 'tiltedView.js', 'Pseudo 3D tilted perspective - [3d, camera, perspective]'),
|
|
83
|
-
new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship with parallax
|
|
93
|
+
new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship with parallax starfield - [game, space, shooter]'),
|
|
84
94
|
new ExampleInfo('Lander Game', 'landerGame.js', 'Lunar lander physics game - [game, physics, thrust]'),
|
|
85
95
|
new ExampleInfo('Flappy Game', 'flappyGame.js', 'Flappy bird clone - [game, arcade, flying]'),
|
|
86
96
|
new ExampleInfo('Hill Glide Game', 'hillGlideGame.js', 'Tiny wings style glider - [game, physics, flying]'),
|
|
@@ -108,32 +118,90 @@ const exampleList =
|
|
|
108
118
|
///////////////////////////////////////////////////////////////////////////////
|
|
109
119
|
|
|
110
120
|
// global variables
|
|
111
|
-
let iframeExample
|
|
121
|
+
let iframeExample; // iframe of the current loaded example
|
|
122
|
+
let inputTimeout; // timeout to debounce input
|
|
123
|
+
let consoleAutoScroll; // allow console to scroll automatically
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
function initExampleBrowser()
|
|
126
|
+
{
|
|
127
|
+
// load the examples into the list
|
|
128
|
+
filterExamples();
|
|
129
|
+
|
|
130
|
+
// set the selected example from the URL parameters
|
|
131
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
132
|
+
const selectedExample = urlParams.get('example') || '';
|
|
133
|
+
let exampleIndex = exampleList.findIndex((example) => example.filename === selectedExample);
|
|
134
|
+
if (exampleIndex < 0)
|
|
135
|
+
exampleIndex = 1;
|
|
136
|
+
selectExample.selectedIndex = exampleIndex;
|
|
137
|
+
setExample();
|
|
115
138
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (exampleIndex < 0)
|
|
121
|
-
exampleIndex = 1;
|
|
122
|
-
selectExample.selectedIndex = exampleIndex;
|
|
123
|
-
setExample();
|
|
139
|
+
// apply responsive layout
|
|
140
|
+
addEventListener('resize', resizeWindow);
|
|
141
|
+
resizeWindow();
|
|
142
|
+
}
|
|
124
143
|
|
|
125
|
-
|
|
144
|
+
function resizeWindow()
|
|
126
145
|
{
|
|
127
|
-
//
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
// tweak layout for touch devices
|
|
147
|
+
const isTouchDevice = window.ontouchstart !== undefined;
|
|
148
|
+
if (isTouchDevice)
|
|
149
|
+
container4.style.display = 'none';
|
|
150
|
+
else
|
|
151
|
+
selectExampleText.style.display = 'none';
|
|
152
|
+
|
|
153
|
+
const windowAspect = innerWidth / innerHeight;
|
|
154
|
+
const verticalLayout = windowAspect < 1;
|
|
155
|
+
if (verticalLayout)
|
|
156
|
+
{
|
|
157
|
+
// vertical layout for thin screens
|
|
158
|
+
if (container2.parentNode != container3)
|
|
159
|
+
container3.insertBefore(container2, divCodeOptions);
|
|
160
|
+
// resize iframe to the window
|
|
161
|
+
setFrameSize(innerWidth-35);
|
|
162
|
+
|
|
163
|
+
// fix code mirror sizing glitch
|
|
164
|
+
codeMirror && codeMirror.setSize(innerWidth-35, null);
|
|
165
|
+
}
|
|
166
|
+
else
|
|
167
|
+
{
|
|
168
|
+
// horizontal layout for wide screens
|
|
169
|
+
if (container2.parentNode != container1)
|
|
170
|
+
container1.appendChild(container2);
|
|
171
|
+
|
|
172
|
+
// show full controls
|
|
173
|
+
container4.style.display = '';
|
|
174
|
+
|
|
175
|
+
// resize iframe to fit half the window
|
|
176
|
+
setFrameSize(innerWidth/2);
|
|
177
|
+
|
|
178
|
+
// fix code mirror sizing glitch
|
|
179
|
+
codeMirror && codeMirror.setSize(innerWidth/2 - 35, null);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function setFrameSize(w)
|
|
183
|
+
{
|
|
184
|
+
const aspect = 16 / 9; // HD aspect ratio
|
|
185
|
+
w = w | 0;
|
|
186
|
+
const h = w / aspect | 0;
|
|
187
|
+
iframeContainer.style.width = w + 'px';
|
|
188
|
+
iframeContainer.style.height = h + 'px';
|
|
189
|
+
|
|
190
|
+
if (iframeExample)
|
|
191
|
+
{
|
|
192
|
+
// ensure iframe fills container tightly
|
|
193
|
+
iframeExample.style.width = w + 'px';
|
|
194
|
+
iframeExample.style.height = h + 'px';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// fix code mirror after layout changes
|
|
198
|
+
if (codeMirror)
|
|
199
|
+
{
|
|
200
|
+
// fix glitch with code mirror sizing
|
|
201
|
+
setTimeout(()=>codeMirror.refresh(), 500);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
135
204
|
}
|
|
136
|
-
onresize();
|
|
137
205
|
|
|
138
206
|
///////////////////////////////////////////////////////////////////////////////
|
|
139
207
|
// setting examples
|
|
@@ -157,7 +225,7 @@ function setExample()
|
|
|
157
225
|
exampleInfo.innerText = example.name + (example.description ? ' - ' + example.description : '');
|
|
158
226
|
const filename = 'examples/' + example.filename;
|
|
159
227
|
exampleLink.href = 'https://github.com/KilledByAPixel/LittleJS/tree/main/' + filename;
|
|
160
|
-
exampleLink.innerText =
|
|
228
|
+
exampleLink.innerText = 'View code on GitHub';
|
|
161
229
|
|
|
162
230
|
// update URL parameter
|
|
163
231
|
const url = new URL(window.location);
|
|
@@ -175,11 +243,7 @@ async function loadFile(filename, largeExample)
|
|
|
175
243
|
textareaCode.disabled = largeExample;
|
|
176
244
|
|
|
177
245
|
// disable buttons in large example mode
|
|
178
|
-
|
|
179
|
-
buttonRestart.disabled = largeExample;
|
|
180
|
-
buttonScreenshot.disabled = largeExample;
|
|
181
|
-
buttonFullscreen.disabled = largeExample;
|
|
182
|
-
checkboxWebGL.disabled = largeExample;
|
|
246
|
+
setFrameControlsEnabled(!largeExample);
|
|
183
247
|
|
|
184
248
|
if (largeExample)
|
|
185
249
|
{
|
|
@@ -281,9 +345,20 @@ function restartCode()
|
|
|
281
345
|
setCode(code);
|
|
282
346
|
}
|
|
283
347
|
|
|
284
|
-
function
|
|
348
|
+
function setFrameControlsEnabled(enabled=true)
|
|
285
349
|
{
|
|
286
|
-
|
|
350
|
+
buttonPause.disabled = !enabled;
|
|
351
|
+
buttonRestart.disabled = !enabled;
|
|
352
|
+
buttonScreenshot.disabled = !enabled;
|
|
353
|
+
buttonFullscreen.disabled = !enabled;
|
|
354
|
+
checkboxWebGL.disabled = !enabled;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function setCode(code, filename)
|
|
358
|
+
{
|
|
359
|
+
const largeExample = !!filename;
|
|
360
|
+
filename = filename || 'shorts/base.html';
|
|
361
|
+
|
|
287
362
|
clearTimeout(inputTimeout);
|
|
288
363
|
if (iframeExample)
|
|
289
364
|
iframeContainer.removeChild(iframeExample);
|
|
@@ -292,18 +367,26 @@ function setCode(code, textFilename)
|
|
|
292
367
|
unsetConsoleMessage();
|
|
293
368
|
iframeExample = document.createElement('iframe');
|
|
294
369
|
iframeContainer.appendChild(iframeExample);
|
|
370
|
+
|
|
295
371
|
iframeExample.onload = ()=>
|
|
296
372
|
{
|
|
297
373
|
if (largeExample)
|
|
298
374
|
return;
|
|
299
375
|
|
|
376
|
+
// get the iframe content window and document
|
|
300
377
|
const iframeContent = iframeExample.contentWindow;
|
|
301
378
|
const iframeDocument = iframeContent.document;
|
|
379
|
+
if (!iframeContent.engineInit)
|
|
380
|
+
{
|
|
381
|
+
setErrorMessage(`Failed to load ${filename}`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
302
384
|
|
|
303
385
|
// intercept errors
|
|
304
386
|
function getErrorLine(stack)
|
|
305
387
|
{
|
|
306
|
-
// try to extract line number from stack trace
|
|
388
|
+
// try to extract line number from stack trace
|
|
389
|
+
// look for <anonymous> or injectedScript to find user code
|
|
307
390
|
const anonymousMatch = stack.match(/(<anonymous>|injectedScript):(\d+)/);
|
|
308
391
|
return anonymousMatch ? parseInt(anonymousMatch[2]) : -1;
|
|
309
392
|
}
|
|
@@ -332,7 +415,7 @@ function setCode(code, textFilename)
|
|
|
332
415
|
|
|
333
416
|
// intercept asserts
|
|
334
417
|
const originalAssert = iframeContent.console.assert;
|
|
335
|
-
iframeContent.console.assert = function (condition, output)
|
|
418
|
+
iframeContent.console.assert = function (condition, ...output)
|
|
336
419
|
{
|
|
337
420
|
if (!condition)
|
|
338
421
|
{
|
|
@@ -340,7 +423,10 @@ function setCode(code, textFilename)
|
|
|
340
423
|
const errorLine = getErrorLine(stack);
|
|
341
424
|
if (errorLine >= 0)
|
|
342
425
|
setErrorLine(errorLine);
|
|
343
|
-
|
|
426
|
+
|
|
427
|
+
// format output parameters properly
|
|
428
|
+
const outputMessage = output.length > 0 ? output.map(m => stringifyMessage(m)).join(' ') : '';
|
|
429
|
+
setErrorMessage('Assertion failed!\n' + outputMessage + '\n' + stack);
|
|
344
430
|
}
|
|
345
431
|
originalAssert.apply(this, arguments);
|
|
346
432
|
};
|
|
@@ -360,7 +446,7 @@ function setCode(code, textFilename)
|
|
|
360
446
|
['log','info','warn','error','debug'].forEach(f=>interceptConsole(f));
|
|
361
447
|
|
|
362
448
|
{
|
|
363
|
-
//
|
|
449
|
+
// hook up buttons
|
|
364
450
|
buttonScreenshot.onclick = () =>
|
|
365
451
|
{
|
|
366
452
|
if (iframeContent.debugScreenshot)
|
|
@@ -383,22 +469,34 @@ function setCode(code, textFilename)
|
|
|
383
469
|
buttonFullscreen.onclick = ()=> iframeContent.toggleFullscreen();
|
|
384
470
|
}
|
|
385
471
|
|
|
386
|
-
// set WebGL based on checkbox
|
|
387
|
-
iframeContent.setGLEnable(checkboxWebGL.checked);
|
|
388
|
-
checkboxWebGL.onchange = ()=> iframeContent.setGLEnable(checkboxWebGL.checked);
|
|
389
|
-
|
|
390
472
|
// create a script element that overrides the default functions
|
|
391
473
|
const overrideScript = iframeDocument.createElement('script');
|
|
392
474
|
iframeDocument.body.appendChild(overrideScript);
|
|
393
475
|
overrideScript.text = code;
|
|
394
|
-
|
|
395
|
-
if (textareaError.style.display
|
|
396
|
-
return;
|
|
397
|
-
|
|
476
|
+
|
|
477
|
+
if (textareaError.style.display === 'block')
|
|
478
|
+
return;
|
|
479
|
+
|
|
398
480
|
// start LittleJS engine
|
|
399
|
-
iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png?'+Date.now()])
|
|
481
|
+
iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png?'+Date.now()])
|
|
482
|
+
.catch(error =>
|
|
483
|
+
{
|
|
484
|
+
const errorLine = getErrorLine(error.stack);
|
|
485
|
+
if (errorLine >= 0)
|
|
486
|
+
setErrorLine(errorLine);
|
|
487
|
+
setErrorMessage(error.message + '\n' + error.stack);
|
|
488
|
+
throw error;
|
|
489
|
+
})
|
|
490
|
+
.then(()=>
|
|
491
|
+
{
|
|
492
|
+
// setup frame controls
|
|
493
|
+
setFrameControlsEnabled();
|
|
494
|
+
iframeContent.setGLEnable(checkboxWebGL.checked);
|
|
495
|
+
checkboxWebGL.onchange = ()=> iframeContent.setGLEnable(checkboxWebGL.checked);
|
|
496
|
+
})
|
|
400
497
|
}
|
|
401
|
-
|
|
498
|
+
|
|
499
|
+
iframeExample.src = filename + '?' + Date.now();
|
|
402
500
|
}
|
|
403
501
|
|
|
404
502
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -427,9 +525,9 @@ function setMessage(message, element, clear=true)
|
|
|
427
525
|
element.value = message;
|
|
428
526
|
else
|
|
429
527
|
element.value += '\n' + message;
|
|
430
|
-
element.style.display = message ? 'block' : '
|
|
528
|
+
element.style.display = message ? 'block' : '';
|
|
431
529
|
|
|
432
|
-
if (element
|
|
530
|
+
if (element === textareaConsole)
|
|
433
531
|
{
|
|
434
532
|
const maxConsoleLines = 100;
|
|
435
533
|
const lines = element.value.split('\n');
|
|
@@ -448,7 +546,7 @@ function setMessage(message, element, clear=true)
|
|
|
448
546
|
|
|
449
547
|
function unsetMessage(element)
|
|
450
548
|
{
|
|
451
|
-
element.style.display = '
|
|
549
|
+
element.style.display = '';
|
|
452
550
|
element.value = '';
|
|
453
551
|
}
|
|
454
552
|
|
|
@@ -469,13 +567,13 @@ function setErrorLine(lineNumber)
|
|
|
469
567
|
clearErrorLine();
|
|
470
568
|
--lineNumber; // codeMirror uses 0-based line numbers
|
|
471
569
|
errorLineMarker = codeMirror.getDoc().addLineClass(lineNumber, 'background', 'error-line');
|
|
472
|
-
if (
|
|
570
|
+
if (checkboxJumpToErrors.checked)
|
|
473
571
|
{
|
|
474
572
|
codeMirror.scrollIntoView({line: lineNumber, ch: 0});
|
|
475
573
|
codeMirror.focus();
|
|
476
574
|
}
|
|
477
575
|
}
|
|
478
|
-
else if (textareaCode &&
|
|
576
|
+
else if (textareaCode && checkboxJumpToErrors.checked)
|
|
479
577
|
{
|
|
480
578
|
// for textarea, calculate character position and set cursor
|
|
481
579
|
const lines = textareaCode.value.split('\n');
|
|
@@ -485,7 +583,7 @@ function setErrorLine(lineNumber)
|
|
|
485
583
|
charPos += lines[i].length + 1; // +1 for newline character
|
|
486
584
|
}
|
|
487
585
|
textareaCode.setSelectionRange(charPos, charPos);
|
|
488
|
-
if (
|
|
586
|
+
if (checkboxJumpToErrors.checked)
|
|
489
587
|
{
|
|
490
588
|
const line = textareaCode.value.split('\n');
|
|
491
589
|
lineNumber = Math.min(Math.max(0, lineNumber-1), lines.length-1);
|
|
@@ -504,7 +602,8 @@ function setErrorLine(lineNumber)
|
|
|
504
602
|
function setErrorMessage(message)
|
|
505
603
|
{
|
|
506
604
|
// prevent overwriting an existing error message
|
|
507
|
-
const errorMessageIsVisible = textareaError.style.display
|
|
605
|
+
const errorMessageIsVisible = textareaError.style.display === 'block';
|
|
606
|
+
setFrameControlsEnabled(false);
|
|
508
607
|
if (!errorMessageIsVisible)
|
|
509
608
|
setMessage(message, textareaError);
|
|
510
609
|
}
|
|
@@ -520,10 +619,23 @@ function onScrollConsole()
|
|
|
520
619
|
textareaConsole.addEventListener('scroll', onScrollConsole);
|
|
521
620
|
|
|
522
621
|
///////////////////////////////////////////////////////////////////////////////
|
|
523
|
-
//
|
|
524
|
-
|
|
622
|
+
// load saved preferences from localStorage
|
|
525
623
|
const defaultTheme = 'littlejs';
|
|
526
624
|
const defaultFontSize = '16px';
|
|
625
|
+
const savePrefix = 'LittleJSExamples_';
|
|
626
|
+
const savedTheme = localStorage.getItem(savePrefix+'theme') || defaultTheme;
|
|
627
|
+
const savedFontSize = localStorage.getItem(savePrefix+'fontSize') || defaultFontSize;
|
|
628
|
+
selectFontSize.value = savedFontSize;
|
|
629
|
+
textareaCode.style.fontSize = savedFontSize;
|
|
630
|
+
|
|
631
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
632
|
+
// setup code mirror
|
|
633
|
+
|
|
634
|
+
const useCodeMirror = true;
|
|
635
|
+
let codeMirror; // code mirror instance
|
|
636
|
+
let errorLineMarker; // marker for error line in code mirror
|
|
637
|
+
let codeIsJS; // is the current code javascript
|
|
638
|
+
|
|
527
639
|
const themes =
|
|
528
640
|
[
|
|
529
641
|
'littlejs',
|
|
@@ -539,46 +651,40 @@ const themes =
|
|
|
539
651
|
'yonce'
|
|
540
652
|
];
|
|
541
653
|
|
|
542
|
-
//
|
|
543
|
-
const savePrefix = 'LittleJSExamples_';
|
|
544
|
-
const savedTheme = localStorage.getItem(savePrefix+'theme') || defaultTheme;
|
|
545
|
-
const savedFontSize = localStorage.getItem(savePrefix+'fontSize') || defaultFontSize;
|
|
546
|
-
|
|
654
|
+
// load theme and font size
|
|
547
655
|
for (const theme of themes)
|
|
548
656
|
{
|
|
549
657
|
if (theme != 'littlejs')
|
|
550
658
|
addCodeMirrorElement(`theme/${theme}.min.css`, 'link', 'stylesheet');
|
|
551
659
|
const o = new Option(theme);
|
|
552
|
-
o.selected = theme
|
|
660
|
+
o.selected = theme === savedTheme;
|
|
553
661
|
selectTheme.add(o);
|
|
554
662
|
}
|
|
555
663
|
|
|
556
|
-
|
|
557
|
-
selectFontSize.value = savedFontSize;
|
|
558
|
-
textareaCode.style.fontSize = savedFontSize;
|
|
559
|
-
addCodeMirrorElement('codemirror.min.js', 'script').onload = ()=>
|
|
560
|
-
addCodeMirrorElement('codemirror.min.css', 'link', 'stylesheet').onload = ()=>
|
|
561
|
-
addCodeMirrorElement('addon/edit/matchbrackets.js', 'script').onload = ()=>
|
|
562
|
-
addCodeMirrorElement('mode/javascript/javascript.min.js', 'script').onload = ()=>
|
|
563
|
-
initCodeMirror();
|
|
564
|
-
|
|
565
|
-
function initCodeMirror()
|
|
664
|
+
if (useCodeMirror)
|
|
566
665
|
{
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
codeMirror = CodeMirror.fromTextArea(textareaCode,
|
|
666
|
+
addCodeMirrorElement('codemirror.min.js', 'script').onload = ()=>
|
|
667
|
+
addCodeMirrorElement('codemirror.min.css', 'link', 'stylesheet').onload = ()=>
|
|
668
|
+
addCodeMirrorElement('addon/edit/matchbrackets.js', 'script').onload = ()=>
|
|
669
|
+
addCodeMirrorElement('mode/javascript/javascript.min.js', 'script').onload = ()=>
|
|
572
670
|
{
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
671
|
+
if (codeMirror)
|
|
672
|
+
return; // prevent duplicate initialization
|
|
673
|
+
|
|
674
|
+
const textareaCode = document.getElementById('textareaCode');
|
|
675
|
+
codeMirror = CodeMirror.fromTextArea(textareaCode,
|
|
676
|
+
{
|
|
677
|
+
theme: savedTheme,
|
|
678
|
+
indentUnit: 4,
|
|
679
|
+
mode: codeIsJS ? 'javascript' : 'text',
|
|
680
|
+
lineNumbers: true,
|
|
681
|
+
lineWrapping: true,
|
|
682
|
+
matchBrackets: true,
|
|
683
|
+
});
|
|
684
|
+
codeMirror.on('change', codeInput);
|
|
685
|
+
loadTheme();
|
|
686
|
+
resizeWindow(); // fix cursor positioning issue on startup
|
|
687
|
+
}
|
|
582
688
|
}
|
|
583
689
|
|
|
584
690
|
function addCodeMirrorElement(filename, type, rel)
|
|
@@ -587,7 +693,7 @@ function addCodeMirrorElement(filename, type, rel)
|
|
|
587
693
|
const e = document.createElement(type);
|
|
588
694
|
e.rel = rel;
|
|
589
695
|
filename = 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/' + filename;
|
|
590
|
-
if (type
|
|
696
|
+
if (type === 'link')
|
|
591
697
|
e.href = filename;
|
|
592
698
|
else
|
|
593
699
|
e.src = filename;
|
|
@@ -614,6 +720,11 @@ function loadTheme()
|
|
|
614
720
|
}
|
|
615
721
|
}
|
|
616
722
|
|
|
723
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
724
|
+
|
|
725
|
+
// start up the browser
|
|
726
|
+
initExampleBrowser()
|
|
727
|
+
|
|
617
728
|
</script>
|
|
618
729
|
</body>
|
|
619
730
|
</html>
|