@bigmistqke/view.gl 0.1.11 → 0.1.13
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/.claude/settings.local.json +9 -4
- package/dev/dist/404.html +19 -0
- package/dev/dist/assets/bezier-DuLTb2d1.js +294 -0
- package/dev/dist/assets/game_of_life-D7qHdFcv.js +202 -0
- package/dev/dist/assets/grid-Ie22U_9K.js +173 -0
- package/dev/dist/assets/index-0qrXMlfv.js +109 -0
- package/dev/dist/assets/index-C2N3u8wj.css +12 -0
- package/dev/dist/assets/main-BZ1N6pp6.js +32 -0
- package/dev/dist/assets/overview-DAxtMIw5.js +78 -0
- package/dev/dist/assets/pix_sim-DIhzM_vu.js +501 -0
- package/dev/dist/assets/symbol-BouRL-Ni.js +65 -0
- package/dev/dist/assets/tag-C_bg62fD.js +516 -0
- package/dev/dist/assets/tetris-IMnkSi9p.js +344 -0
- package/dev/dist/assets/utils-CvCgsM08.js +50 -0
- package/dev/dist/index.html +51 -0
- package/dev/src/examples/ray-casting-modules.ts +877 -0
- package/dist/chunk-Z4F7UTKN.mjs +391 -0
- package/dist/chunk-Z4F7UTKN.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +8 -305
- package/dist/index.mjs.map +1 -1
- package/dist/tag.d.mts +9 -9
- package/dist/tag.mjs +8 -340
- package/dist/tag.mjs.map +1 -1
- package/dist/{types-CHXzeNxF.d.mts → types-Cggy6Gkm.d.mts} +48 -119
- package/package.json +7 -11
- package/src/{gl.ts → features.ts} +1 -45
- package/src/index.ts +50 -11
- package/src/tag.ts +2 -3
- package/src/types.ts +154 -211
- package/src/utils/index.ts +46 -0
- package/tsup.config.ts +2 -2
- package/dist/gl.d.mts +0 -8
- package/dist/gl.mjs +0 -71
- package/dist/gl.mjs.map +0 -1
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { a as attribute, u as uniform, g as glsl, c as compile, d as uniformView, e as attributeView } from './tag-C_bg62fD.js';
|
|
2
|
+
|
|
3
|
+
function rotate(data, size, clockwise = true) {
|
|
4
|
+
for (let layer = 0; layer < Math.floor(size / 2); layer++) {
|
|
5
|
+
const first = layer;
|
|
6
|
+
const last = size - 1 - layer;
|
|
7
|
+
for (let i = 0; i < last - first; i++) {
|
|
8
|
+
const top = getColorIndex(data, first, first + i, size);
|
|
9
|
+
const right = getColorIndex(data, first + i, last, size);
|
|
10
|
+
const bottom = getColorIndex(data, last, last - i, size);
|
|
11
|
+
const left = getColorIndex(data, last - i, first, size);
|
|
12
|
+
if (clockwise) {
|
|
13
|
+
setColorIndex(data, first + i, last, size, top);
|
|
14
|
+
setColorIndex(data, last, last - i, size, right);
|
|
15
|
+
setColorIndex(data, last - i, first, size, bottom);
|
|
16
|
+
setColorIndex(data, first, first + i, size, left);
|
|
17
|
+
} else {
|
|
18
|
+
setColorIndex(data, last - i, first, size, top);
|
|
19
|
+
setColorIndex(data, last, last - i, size, left);
|
|
20
|
+
setColorIndex(data, first + i, last, size, bottom);
|
|
21
|
+
setColorIndex(data, first, first + i, size, right);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getColorIndex(buf, x, y, size2) {
|
|
26
|
+
return buf[y * size2 + x];
|
|
27
|
+
}
|
|
28
|
+
function setColorIndex(buf, x, y, size2, pixel) {
|
|
29
|
+
buf[y * size2 + x] = pixel;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function createTimeout() {
|
|
33
|
+
let timeout;
|
|
34
|
+
return {
|
|
35
|
+
set(callback, time) {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
timeout = setTimeout(callback, time);
|
|
38
|
+
},
|
|
39
|
+
clear() {
|
|
40
|
+
clearTimeout(timeout);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const WIDTH = 10;
|
|
45
|
+
const HEIGHT = 20;
|
|
46
|
+
const TETROMINO = {
|
|
47
|
+
I: {
|
|
48
|
+
dimensions: [4, 4],
|
|
49
|
+
pixels: () => new Float32Array([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]),
|
|
50
|
+
color: [0, 1, 1]
|
|
51
|
+
// Cyan
|
|
52
|
+
},
|
|
53
|
+
O: {
|
|
54
|
+
dimensions: [2, 2],
|
|
55
|
+
pixels: () => new Float32Array([2, 2, 2, 2]),
|
|
56
|
+
color: [1, 1, 0]
|
|
57
|
+
// Yellow
|
|
58
|
+
},
|
|
59
|
+
T: {
|
|
60
|
+
dimensions: [3, 3],
|
|
61
|
+
pixels: () => new Float32Array([0, 3, 0, 3, 3, 3, 0, 0, 0]),
|
|
62
|
+
color: [0.6, 0, 1]
|
|
63
|
+
// Purple
|
|
64
|
+
},
|
|
65
|
+
S: {
|
|
66
|
+
dimensions: [3, 3],
|
|
67
|
+
pixels: () => new Float32Array([0, 4, 4, 4, 4, 0, 0, 0, 0]),
|
|
68
|
+
color: [0, 1, 0]
|
|
69
|
+
},
|
|
70
|
+
Z: {
|
|
71
|
+
dimensions: [3, 3],
|
|
72
|
+
pixels: () => new Float32Array([5, 5, 0, 0, 5, 5, 0, 0, 0]),
|
|
73
|
+
color: [1, 0, 0]
|
|
74
|
+
},
|
|
75
|
+
J: {
|
|
76
|
+
dimensions: [3, 3],
|
|
77
|
+
pixels: () => new Float32Array([6, 0, 0, 6, 6, 6, 0, 0, 0]),
|
|
78
|
+
color: [0, 0, 1]
|
|
79
|
+
},
|
|
80
|
+
L: {
|
|
81
|
+
dimensions: [3, 3],
|
|
82
|
+
pixels: () => new Float32Array([0, 0, 7, 7, 7, 7, 0, 0, 0]),
|
|
83
|
+
color: [1, 0.5, 0]
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const TETROMINO_KIND = Object.keys(TETROMINO);
|
|
87
|
+
function getRandomTetrominoKind() {
|
|
88
|
+
return TETROMINO_KIND[Math.floor(Math.random() * TETROMINO_KIND.length)];
|
|
89
|
+
}
|
|
90
|
+
function createTetromino(gl, program, schema) {
|
|
91
|
+
const { a_index, a_pixel } = attributeView(gl, program, schema);
|
|
92
|
+
let pixels = null;
|
|
93
|
+
let dimensions = null;
|
|
94
|
+
let offset = null;
|
|
95
|
+
let pixelCount = null;
|
|
96
|
+
function next(kind = getRandomTetrominoKind()) {
|
|
97
|
+
const template = TETROMINO[kind];
|
|
98
|
+
pixels = template.pixels();
|
|
99
|
+
pixelCount = template.dimensions[0] * template.dimensions[1];
|
|
100
|
+
offset = [Math.ceil((WIDTH - template.dimensions[0]) / 2), HEIGHT - template.dimensions[1]];
|
|
101
|
+
dimensions = template.dimensions;
|
|
102
|
+
a_index.set(new Float32Array(Array.from({ length: pixelCount }, (_, index) => index)));
|
|
103
|
+
a_pixel.set(pixels);
|
|
104
|
+
}
|
|
105
|
+
next();
|
|
106
|
+
return {
|
|
107
|
+
get dimensions() {
|
|
108
|
+
return dimensions;
|
|
109
|
+
},
|
|
110
|
+
get pixels() {
|
|
111
|
+
return pixels;
|
|
112
|
+
},
|
|
113
|
+
get offset() {
|
|
114
|
+
return offset;
|
|
115
|
+
},
|
|
116
|
+
get pixelCount() {
|
|
117
|
+
return pixelCount;
|
|
118
|
+
},
|
|
119
|
+
bindAttributes() {
|
|
120
|
+
a_index.bind();
|
|
121
|
+
a_pixel.bind();
|
|
122
|
+
},
|
|
123
|
+
next,
|
|
124
|
+
rotate(clockwise = true) {
|
|
125
|
+
rotate(pixels, dimensions[1], clockwise);
|
|
126
|
+
a_pixel.set(pixels).bind();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function createBoard(gl, program, schema) {
|
|
131
|
+
const { a_index, a_pixel } = attributeView(gl, program, schema);
|
|
132
|
+
const array = new Float32Array(Array.from({ length: WIDTH * HEIGHT }, () => 0));
|
|
133
|
+
a_index.set(new Float32Array(Array.from({ length: WIDTH * HEIGHT }, (_, index) => index)));
|
|
134
|
+
a_pixel.set(array);
|
|
135
|
+
return {
|
|
136
|
+
bindAttributes() {
|
|
137
|
+
a_index.bind();
|
|
138
|
+
a_pixel.bind();
|
|
139
|
+
},
|
|
140
|
+
array,
|
|
141
|
+
blit({ pixelCount, offset, dimensions, pixels }) {
|
|
142
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
143
|
+
const pixel = pixels[i];
|
|
144
|
+
if (pixel) {
|
|
145
|
+
const x = offset[0] + i % dimensions[0];
|
|
146
|
+
const y = offset[1] + Math.floor(i / dimensions[0]) + 1;
|
|
147
|
+
const index = x + y * WIDTH;
|
|
148
|
+
array[index] = pixel;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
let writeRow = 0;
|
|
152
|
+
for (let y = 0; y < HEIGHT; y++) {
|
|
153
|
+
let isComplete = true;
|
|
154
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
155
|
+
if (array[x + y * WIDTH] === 0) {
|
|
156
|
+
isComplete = false;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!isComplete) {
|
|
161
|
+
if (writeRow !== y) {
|
|
162
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
163
|
+
array[x + writeRow * WIDTH] = array[x + y * WIDTH];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
writeRow++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
a_pixel.set(array);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const vertex = glsl`
|
|
174
|
+
precision mediump float;
|
|
175
|
+
|
|
176
|
+
${attribute.float("a_index", { instanced: true })}
|
|
177
|
+
${attribute.float("a_pixel", { instanced: true })}
|
|
178
|
+
${attribute.vec2("a_vertex")}
|
|
179
|
+
${uniform.vec2("u_offset")}
|
|
180
|
+
${uniform.vec2("u_dimensions")}
|
|
181
|
+
${uniform.vec3("u_palette", { size: 7 })}
|
|
182
|
+
|
|
183
|
+
varying vec3 v_color;
|
|
184
|
+
|
|
185
|
+
void main(){
|
|
186
|
+
float columns = mod(a_index, u_dimensions.x);
|
|
187
|
+
float rows = floor(a_index / u_dimensions.x);
|
|
188
|
+
|
|
189
|
+
vec2 offset = (vec2(columns, rows) + u_offset);
|
|
190
|
+
|
|
191
|
+
vec2 base = offset + 0.5;
|
|
192
|
+
vec2 pos = (a_vertex + base) / vec2(10., 20.);
|
|
193
|
+
|
|
194
|
+
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
195
|
+
|
|
196
|
+
if(a_pixel == 0.){
|
|
197
|
+
v_color = vec3(0., 0., 0.);
|
|
198
|
+
}else{
|
|
199
|
+
v_color = u_palette[int(a_pixel) - 1];
|
|
200
|
+
}
|
|
201
|
+
}`;
|
|
202
|
+
const fragment = glsl`
|
|
203
|
+
precision mediump float;
|
|
204
|
+
|
|
205
|
+
varying vec3 v_color;
|
|
206
|
+
|
|
207
|
+
void main() {
|
|
208
|
+
if(v_color.x + v_color.y + v_color.z == 0.){
|
|
209
|
+
discard;
|
|
210
|
+
}
|
|
211
|
+
gl_FragColor = vec4(v_color, 1.0);
|
|
212
|
+
}`;
|
|
213
|
+
function createTetris() {
|
|
214
|
+
const canvas = document.createElement("canvas");
|
|
215
|
+
canvas.width = WIDTH * 30;
|
|
216
|
+
canvas.height = HEIGHT * 30;
|
|
217
|
+
document.body.append(canvas);
|
|
218
|
+
const animationTimeout = createTimeout();
|
|
219
|
+
const gl = canvas.getContext("webgl2");
|
|
220
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
221
|
+
const { program, schema } = compile(gl, vertex, fragment);
|
|
222
|
+
gl.useProgram(program);
|
|
223
|
+
const { u_dimensions, u_offset, u_palette } = uniformView(gl, program, schema.uniforms);
|
|
224
|
+
const { a_vertex, ...localSchema } = schema.attributes;
|
|
225
|
+
const globalAttributes = attributeView(gl, program, { a_vertex });
|
|
226
|
+
const board = createBoard(gl, program, localSchema);
|
|
227
|
+
const tetromino = createTetromino(gl, program, localSchema);
|
|
228
|
+
u_palette.set(new Float32Array(Object.values(TETROMINO).flatMap(({ color }) => color)));
|
|
229
|
+
globalAttributes.a_vertex.set(
|
|
230
|
+
new Float32Array([
|
|
231
|
+
// triangle 1
|
|
232
|
+
-0.5,
|
|
233
|
+
-0.5,
|
|
234
|
+
0.5,
|
|
235
|
+
-0.5,
|
|
236
|
+
-0.5,
|
|
237
|
+
0.5,
|
|
238
|
+
// triangle 2
|
|
239
|
+
-0.5,
|
|
240
|
+
0.5,
|
|
241
|
+
0.5,
|
|
242
|
+
-0.5,
|
|
243
|
+
0.5,
|
|
244
|
+
0.5
|
|
245
|
+
])
|
|
246
|
+
).bind();
|
|
247
|
+
function checkCollision() {
|
|
248
|
+
for (let i = 0; i < tetromino.pixelCount; i++) {
|
|
249
|
+
const x = i % tetromino.dimensions[0] + tetromino.offset[0];
|
|
250
|
+
const y = Math.floor(i / tetromino.dimensions[0]) + tetromino.offset[1];
|
|
251
|
+
let pixel = tetromino.pixels[i];
|
|
252
|
+
if (pixel > 0) {
|
|
253
|
+
if (x < 0) return true;
|
|
254
|
+
if (x >= WIDTH) return true;
|
|
255
|
+
if (y < 0) return true;
|
|
256
|
+
const boardPixel = board.array[x + y * WIDTH];
|
|
257
|
+
if (boardPixel !== 0) {
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function next() {
|
|
264
|
+
board.blit(tetromino);
|
|
265
|
+
tetromino.next();
|
|
266
|
+
if (checkCollision()) {
|
|
267
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
268
|
+
animationTimeout.clear();
|
|
269
|
+
} else {
|
|
270
|
+
render();
|
|
271
|
+
animationTimeout.set(animate, 1e3);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function handleKeyDown(event) {
|
|
275
|
+
switch (event.key) {
|
|
276
|
+
case "ArrowUp":
|
|
277
|
+
tetromino.rotate();
|
|
278
|
+
if (checkCollision()) {
|
|
279
|
+
tetromino.rotate(false);
|
|
280
|
+
} else {
|
|
281
|
+
render();
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
case "ArrowDown":
|
|
285
|
+
tetromino.offset[1]--;
|
|
286
|
+
if (checkCollision()) {
|
|
287
|
+
next();
|
|
288
|
+
} else {
|
|
289
|
+
render();
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
case "ArrowLeft":
|
|
293
|
+
tetromino.offset[0]--;
|
|
294
|
+
if (checkCollision()) {
|
|
295
|
+
tetromino.offset[0]++;
|
|
296
|
+
} else {
|
|
297
|
+
render();
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
case "ArrowRight":
|
|
301
|
+
tetromino.offset[0]++;
|
|
302
|
+
if (checkCollision()) {
|
|
303
|
+
tetromino.offset[0]--;
|
|
304
|
+
} else {
|
|
305
|
+
render();
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
case " ":
|
|
309
|
+
while (true) {
|
|
310
|
+
tetromino.offset[1]--;
|
|
311
|
+
if (checkCollision()) {
|
|
312
|
+
next();
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
319
|
+
function render() {
|
|
320
|
+
gl.clearColor(0, 0.5, 0, 0.3);
|
|
321
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
322
|
+
u_offset.set(0, 0);
|
|
323
|
+
u_dimensions.set(WIDTH, HEIGHT);
|
|
324
|
+
board.bindAttributes();
|
|
325
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, WIDTH * HEIGHT);
|
|
326
|
+
u_offset.set(...tetromino.offset);
|
|
327
|
+
u_dimensions.set(...tetromino.dimensions);
|
|
328
|
+
tetromino.bindAttributes();
|
|
329
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, tetromino.pixelCount);
|
|
330
|
+
}
|
|
331
|
+
function animate() {
|
|
332
|
+
tetromino.offset[1]--;
|
|
333
|
+
if (checkCollision()) {
|
|
334
|
+
next();
|
|
335
|
+
}
|
|
336
|
+
render();
|
|
337
|
+
animationTimeout.set(animate, 1e3);
|
|
338
|
+
}
|
|
339
|
+
setTimeout(animate, 1e3);
|
|
340
|
+
render();
|
|
341
|
+
}
|
|
342
|
+
createTetris();
|
|
343
|
+
|
|
344
|
+
export { TETROMINO };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function cursor(event, callback) {
|
|
2
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
const initialX = event.clientX;
|
|
5
|
+
const initialY = event.clientY;
|
|
6
|
+
let previousX = event.clientX;
|
|
7
|
+
let previousY = event.clientY;
|
|
8
|
+
window.addEventListener(
|
|
9
|
+
"pointermove",
|
|
10
|
+
(event2) => {
|
|
11
|
+
callback(
|
|
12
|
+
Object.assign(event2, {
|
|
13
|
+
distanceX: initialX - event2.clientX,
|
|
14
|
+
distanceY: initialY - event2.clientY,
|
|
15
|
+
deltaX: previousX - event2.clientX,
|
|
16
|
+
deltaY: previousY - event2.clientY
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
previousX = event2.clientX;
|
|
20
|
+
previousY = event2.clientY;
|
|
21
|
+
},
|
|
22
|
+
controller
|
|
23
|
+
);
|
|
24
|
+
window.addEventListener(
|
|
25
|
+
"pointerup",
|
|
26
|
+
() => {
|
|
27
|
+
controller.abort();
|
|
28
|
+
resolve();
|
|
29
|
+
},
|
|
30
|
+
controller
|
|
31
|
+
);
|
|
32
|
+
return promise;
|
|
33
|
+
}
|
|
34
|
+
function createElement(tag, {
|
|
35
|
+
parentElement,
|
|
36
|
+
...options
|
|
37
|
+
} = {}) {
|
|
38
|
+
const element = document.createElement(tag);
|
|
39
|
+
for (const key in options) {
|
|
40
|
+
if (key.startsWith("data-")) {
|
|
41
|
+
element.setAttribute(key, options[key]);
|
|
42
|
+
} else {
|
|
43
|
+
element[key] = options[key];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
(parentElement ?? document.body).appendChild(element);
|
|
47
|
+
return element;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { cursor as a, createElement as c };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<style>
|
|
4
|
+
body {
|
|
5
|
+
--color-foreground: #e9e9e9;
|
|
6
|
+
--color-background: rgb(33, 33, 47);
|
|
7
|
+
--color-border: rgb(87, 87, 87);
|
|
8
|
+
--font-family: monospace;
|
|
9
|
+
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 175px 1fr;
|
|
12
|
+
margin: 0px;
|
|
13
|
+
background-color: var(--color-background);
|
|
14
|
+
color: var(--color-foreground);
|
|
15
|
+
font-family: var(--font-family);
|
|
16
|
+
}
|
|
17
|
+
iframe {
|
|
18
|
+
border: none;
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
21
|
+
}
|
|
22
|
+
nav {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
border-inline-end: 1px solid var(--color-border);
|
|
26
|
+
|
|
27
|
+
& > * {
|
|
28
|
+
margin: 0px;
|
|
29
|
+
border: none;
|
|
30
|
+
border-block-end: 1px solid var(--color-border);
|
|
31
|
+
background: none;
|
|
32
|
+
padding: 7px;
|
|
33
|
+
color: var(--color-foreground);
|
|
34
|
+
font-family: var(--font-family);
|
|
35
|
+
text-align: left;
|
|
36
|
+
|
|
37
|
+
&.active {
|
|
38
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
43
|
+
<script type="module" crossorigin src="/assets/main-BZ1N6pp6.js"></script>
|
|
44
|
+
<link rel="modulepreload" crossorigin href="/assets/utils-CvCgsM08.js">
|
|
45
|
+
<link rel="modulepreload" crossorigin href="/assets/index-0qrXMlfv.js">
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<nav></nav>
|
|
49
|
+
<iframe></iframe>
|
|
50
|
+
</body>
|
|
51
|
+
</html>
|