├── .gitignore
├── LICENSE
├── bin
├── puWorker.js
└── scriptimate.js
├── examples
├── 1_helloworld.smte
├── 2_rotate_scale_opacity.smte
├── 3_parallel_animations.gif
├── 3_parallel_animations.smte
├── 4_constants_and_expressions.smte
├── 5_easing.smte
├── 6_groups.gif
├── 6_groups.smte
├── 7_dashoffset.smte
├── groups
│ └── main.smte
├── groups_adv
│ └── main.smte
├── groups_adv2
│ └── main.smte
└── src
│ ├── boomerang.svg
│ ├── boomerang1.svg
│ ├── boomerang2.svg
│ ├── boomerang3.svg
│ ├── boomerang4.svg
│ └── stroke.svg
├── index.html
├── package-lock.json
├── package.json
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.mp4
2 | *.webm
3 | *.gif
4 | res
5 | node_modules
6 | frames
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Devforth.io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/bin/puWorker.js:
--------------------------------------------------------------------------------
1 |
2 | const util = require('util');
3 | const puppeteer = require('puppeteer');
4 | const fs = require("fs");
5 |
6 | const MAX_FILENAME_DIGS = 7;
7 |
8 |
9 | const input = {
10 | pageW: +process.argv[2],
11 | pageH: +process.argv[3],
12 | index: +process.argv[4],
13 | totalFramesCount: +process.argv[5],
14 | framesDir: process.argv[6],
15 | format: process.argv[7],
16 | quality: process.argv[8],
17 | skipFrames: +process.argv[9],
18 | }
19 |
20 | fs.writeFileSync(`/tmp/scriptimate_debug_test${input.index}.txt`, JSON.stringify(input, null, 2), () => {});
21 |
22 | const fileHtml = fs.readFileSync(`${input.framesDir}/_index${(''+input.index).padStart(MAX_FILENAME_DIGS, '0')}.html`, 'utf8')
23 | const jpegFileName = `${input.framesDir}/${(''+(input.index - input.skipFrames)).padStart(MAX_FILENAME_DIGS, '0')}.${input.format}`;
24 |
25 | const genScreenshots = async () => {
26 | const browser = await puppeteer.launch({args: [
27 | `--window-size=${input.pageW},${input.pageH}`,
28 | '--no-sandbox',
29 | '--disk-cache-dir=/tmp/pup',
30 | ], headless: true,});
31 |
32 | const page = await browser.newPage();
33 | await page.setViewport({width: input.pageW, height: input.pageH, deviceScaleFactor: 1});
34 | await page._client.send('Emulation.clearDeviceMetricsOverride');
35 | await page.setContent(fileHtml);
36 | await page.screenshot({path: jpegFileName, type: input.format, omitBackground: true});
37 |
38 | process.exit();
39 | }
40 | genScreenshots()
41 |
--------------------------------------------------------------------------------
/bin/scriptimate.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const {promises: fs, constants: fsConstants} = require('fs');
4 | const { spawn, execFile } = require('child_process');
5 | const { ArgumentParser } = require('argparse');
6 | const { version } = require('../package.json');
7 | const svgDim = require('svg-dimensions');
8 | const YAML = require('yaml');
9 | const crypto = require('crypto');
10 | const os = require('os');
11 | const jsdom = require("jsdom");
12 | const { JSDOM } = jsdom;
13 |
14 | let uniq;
15 |
16 | function initUnique() {
17 | uniq = 0;
18 | }
19 |
20 | function generateUniqueId() {
21 | return 1237841 + uniq++;
22 | }
23 |
24 | const path = require('path');
25 |
26 | const log = console.log;
27 | const MAX_FILENAME_DIGS = 7;
28 | let FRAMES_DIR = 'frames';
29 |
30 | const parser = new ArgumentParser({
31 | description: `Scriptimate v${version}`
32 | });
33 |
34 | parser.add_argument('-v', '--version', { action: 'version', version });
35 | parser.add_argument('-f', '--format', { help: 'output file format, or multiple via comma: "webm,mp4". Available formats: mov, mp4, gif, webm, default is mp4', default: 'mp4' });
36 | parser.add_argument('-i', '--input', { help: 'Input .smte script file', default: null });
37 | parser.add_argument('-fn', '--filename', { help: 'output filename', default: null });
38 | parser.add_argument('-t', '--threads', { help: 'Threads count used during compiling, defaults to 4', default: 4 });
39 | parser.add_argument('-fs', '--fromsecond', { help: 'Start from defined second (could be used to debug animation faster, also you can use "exis" keyword in smte script)', default: 0 });
40 | parser.add_argument('-d', '--debughtml', { help: 'Create HTML files near image to debug', default: false });
41 | parser.add_argument('-bd', '--basedir', { help: 'Input directory (folder where src subfolder and .smte file is located)', default: './' });
42 | parser.add_argument('-fps', '--fps', { help: 'FPS', default: 25 });
43 | parser.add_argument('-if', '--intermediateFormat', { help: 'Screenshots format used to compile video png|jpeg, defaults to png', default: 'png' });
44 | parser.add_argument('-ijq', '--intermediateJpegQuality', { help: 'JPEG quality 0.0 - 1.0, defaults to 1', default: 1 });
45 | parser.add_argument('-nc', '--nocache', { help: "Don't use screenshots cache (but still generate it), for scriptimate develeopmnt", default: false });
46 |
47 |
48 | const proc_args = parser.parse_args();
49 |
50 | const FPS = +proc_args.fps;
51 |
52 | const FORMAT = proc_args.intermediateFormat;
53 | const QUALITY = +proc_args.intermediateJpegQuality;
54 |
55 |
56 | FRAMES_DIR = proc_args.basedir + '/' + FRAMES_DIR;
57 |
58 | let translationsDict = {};
59 | let parts;
60 | let timers;
61 | let boxholes;
62 |
63 | let groups; // name => Array of lines
64 | let freezer;
65 |
66 | let pageScale, pageW, pageH;
67 | let groupToAddNext;
68 | let skipFrames;
69 | let globalFramesCounter;
70 | let globalLastFrame;
71 | let cntr;
72 | let totalFrames;
73 | let totalFramesCount;
74 |
75 |
76 | function initVariables() {
77 | parts = {};
78 | timers = {};
79 | boxholes = {};
80 | groups = {}; // name => Array of lines
81 | freezer = {};
82 | pageScale = 1;
83 | pageW = 0;
84 | pageH = 0;
85 | groupToAddNext = null;
86 | skipFrames = 0;
87 | globalFramesCounter = 0;
88 | globalLastFrame = null;
89 | cntr = 0;
90 | totalFrames = 0;
91 | totalFramesCount = 0;
92 | frameAbsIndexByHTMLHash = {};
93 | reuseAbsFrameIndexForAbsFrameIndex = {};
94 | frameHashByAbsIndex = {};
95 | }
96 |
97 |
98 | const arrayChunks = (arr, size) => arr.reduce((acc, e, i) => (i % size ? acc[acc.length - 1].push(e) : acc.push([e]), acc), []);
99 |
100 | // want more? copy from here: https://hinty.io/ivictbor/animation-formulas/
101 | const animationHandlersByMode = {
102 | linear: (t, b, c, d) => {
103 | return c*t/d + b;
104 | },
105 | easein: (t, b, c, d) => { // Quadratic easing in
106 | t /= d;
107 | return c*t*t + b;
108 | },
109 | easeout: (t, b, c, d) => { // Quadratic easing out
110 | t /= d;
111 | return -c * t*(t-2) + b;
112 | },
113 | easeinout: (t, b, c, d) => { // Quadratic easing out
114 | t /= d/2;
115 | if (t < 1) return c/2*t*t + b;
116 | t--;
117 | return -c/2 * (t*(t-2) - 1) + b;
118 | },
119 | }
120 |
121 | const ACTION_HANDLERS = {
122 | move: (i, ags_arr, first_frame_in_animate, frames, mode) => {
123 | const svg = ags_arr[0];
124 | if (!parts[svg]) {
125 | return;
126 | }
127 | const dstLeft = ags_arr[1] === '-' ? parts[svg].left : eval(ags_arr[1]);
128 | const dstTop = ags_arr[2] === '-' ? parts[svg].top : eval(ags_arr[2]);
129 | if (first_frame_in_animate) {
130 | freezer[svg] = {...freezer[svg], top: parts[svg].top, left: parts[svg].left};
131 | }
132 |
133 | parts[svg].top = animationHandlersByMode[mode](i, freezer[svg].top, dstTop - freezer[svg].top, frames);
134 | parts[svg].left = animationHandlersByMode[mode](i, freezer[svg].left, dstLeft - freezer[svg].left, frames);
135 | global[`\$${svg}__X`] = parts[svg].left; //todo legacy
136 | global[`\$${svg}__Y`] = parts[svg].top; //todo legacy
137 | global[`\$${svg}__LEFT`] = parts[svg].left;
138 | global[`\$${svg}__TOP`] = parts[svg].top;
139 | },
140 | scale: (i, ags_arr, first_frame_in_animate, frames, mode, cmd) => {
141 | const svg = ags_arr[0];
142 | if (!parts[svg]) {
143 | log(`WARN: scale not applied, part not found: ${svg}, line: \n${cmd}\n`);
144 | return;
145 | }
146 | const dstScale = +eval(ags_arr[1]);
147 | let transformOrigin = null;
148 | if (ags_arr[2]) {
149 | transformOrigin = `${ags_arr[2]}`;
150 | if (ags_arr[3]) {
151 | transformOrigin = `${transformOrigin} ${ags_arr[3]}`;
152 | }
153 | }
154 | if (first_frame_in_animate) {
155 | freezer[svg] = {...freezer[svg], scale: parts[svg].scale};
156 | }
157 | parts[svg].transformOrigin = transformOrigin;
158 | parts[svg].scale = animationHandlersByMode[mode](i, freezer[svg].scale, dstScale - freezer[svg].scale, frames);
159 | },
160 | rotate: (i, ags_arr, first_frame_in_animate, frames, mode, cmd) => {
161 | const svg = ags_arr[0];
162 | if (!parts[svg]) {
163 | log(`WARN: rotate not applied, part not found: ${svg}, line: \n${cmd}\n`);
164 | return;
165 | }
166 | const dstRotate = +eval(ags_arr[1]);
167 | let transformOrigin = null;
168 | if (ags_arr[2]) {
169 | transformOrigin = `${ags_arr[2]}`;
170 | if (ags_arr[3]) {
171 | transformOrigin = `${transformOrigin} ${ags_arr[3]}`;
172 | }
173 | }
174 | parts[svg].transformOrigin = transformOrigin;
175 | if (first_frame_in_animate) {
176 | freezer[svg] = {...freezer[svg], rotate: parts[svg].rotate};
177 | }
178 | parts[svg].rotate = animationHandlersByMode[mode](i, freezer[svg].rotate, dstRotate - freezer[svg].rotate, frames);
179 | },
180 | opacity: (i, ags_arr, first_frame_in_animate, frames, mode, cmd) => {
181 | const svg = ags_arr[0];
182 | if (!parts[svg]) {
183 | log(`WARN: opacity not applied, part not found: ${svg}, line: \n${cmd}\n`);
184 | return;
185 | }
186 | const dstOpacity = +eval(ags_arr[1]);
187 | if (first_frame_in_animate) {
188 | freezer[svg] = {...freezer[svg], opacity: parts[svg].opacity};
189 | }
190 | parts[svg].opacity = animationHandlersByMode[mode](i, freezer[svg].opacity, dstOpacity - freezer[svg].opacity, frames);
191 | },
192 | dashoffset: (i, ags_arr, first_frame_in_animate, frames, mode, cmd) => {
193 | const svg = ags_arr[0];
194 | if (!parts[svg]) {
195 | log(`WARN: dashoffset not applied, part not found: ${svg}, line: \n${cmd}\n`);
196 | return;
197 | }
198 | const dstOffset = +eval(ags_arr[1]);
199 | if (first_frame_in_animate) {
200 | freezer[svg] = {...freezer[svg], dashoffset: parts[svg].dashoffset};
201 | }
202 | parts[svg].dashoffset = animationHandlersByMode[mode](i, freezer[svg].dashoffset, dstOffset - freezer[svg].dashoffset, frames);
203 | },
204 | resize_div: (i, ags_arr, first_frame_in_animate, frames, mode, cmd) => {
205 | const svg = ags_arr[0];
206 | if (!parts[svg]) {
207 | log(`WARN: resize_div not applied, part not found: ${svg}, line: \n${cmd}\n`);
208 | return;
209 | }
210 | if (parts[svg].type !== 'block') {
211 | log(`WARN: resize_div could be applied only to type block: not ${parts[svg].type}, part: ${svg}, line: \n${cmd}\n`);
212 | return;
213 | }
214 | const dstW = ags_arr[1] === '-' ? parts[svg].w : eval(ags_arr[1]);
215 | const dstH = ags_arr[2] === '-' ? parts[svg].h : eval(ags_arr[2]);
216 | if (first_frame_in_animate) {
217 | freezer[svg] = {...freezer[svg], w: parts[svg].w, h: parts[svg].h};
218 | }
219 |
220 | parts[svg].w = animationHandlersByMode[mode](i, freezer[svg].w, dstW - freezer[svg].w, frames);
221 | parts[svg].h = animationHandlersByMode[mode](i, freezer[svg].h, dstH - freezer[svg].h, frames);
222 | },
223 | pause: () =>{
224 |
225 | }
226 | }
227 |
228 | const genHtml = (allParts) => {
229 | const inner = Object.values(allParts).sort((a,b) => {
230 | return a.index - b.index
231 | }).reduce((acc, p) => {
232 | if (p.type === 'part') {
233 | const bh = boxholes[p.toBoxHole] || {left: 0, top:0};
234 |
235 | const partHTML = `
${p.content}
`;
236 | if (p.toBoxHole) {
237 | return `${acc}
238 | ${partHTML}
`;
239 | } else {
240 | return `${acc}${partHTML}`;
241 | }
242 | } else if (p.type === 'block') {
243 | return `${acc}${p.content}
`
244 | }
245 | }, '');
246 | return `
247 |
248 |
259 |
260 | ${inner}
261 | `;
262 | }
263 | String.prototype.replaceAll = function(target, replacement) {
264 | return this.split(target).join(replacement);
265 | };
266 |
267 | function firstDefined(...vals) {
268 | for (let i = 0; i < vals.length; i += 1) {
269 | if (vals[i] !== undefined) {
270 | return vals[i];
271 | }
272 | }
273 | return undefined;
274 | }
275 |
276 | const moveToTop = async ( filename) => {
277 | if (!parts[filename]) {
278 | console.error(`error: can't moveToTop ${filename}, part should be first added, e.g. using "place"`);
279 | return;
280 | }
281 | parts[filename].index = Object.values(parts).reduce((a, p) => Math.max(a, p.index), 0) + 1;
282 | }
283 |
284 | const addPart = async (lang, filename, left, top, opacity, scale, toBoxHole, dashoffset) => {
285 | let f;
286 |
287 | const readFname = async (fn) => {
288 | const filePath = `${proc_args.basedir}/src/${fn}.svg`;
289 | const fileBuffer = await fs.readFile(filePath, { encoding: 'utf-8' });
290 | return fileBuffer.toString();
291 | };
292 | let fname = `${filename}_${lang}`;
293 | try {
294 | f = await readFname(fname);
295 | } catch (e) {
296 | fname = filename;
297 | f = await readFname(fname)
298 | }
299 | await new Promise((resolve) => {
300 | svgDim.get(`${proc_args.basedir}/src/${fname}.svg`, function(err, dimensions) {
301 | if (err) {
302 | console.log(`INFO: can't read ${filename} dimensions`, err);
303 | } else {
304 | global[`\$${filename}__WIDTH`] = dimensions.width;
305 | global[`\$${filename}__HEIGHT`] = dimensions.height;
306 | }
307 | resolve();
308 | });
309 | });
310 | const partIds = {};
311 | let withUniquifiedIDs = f.replace(/id="(.*?)"/g, (_, v) => {
312 | if (!partIds[v]) {
313 | partIds[v] = `id_${generateUniqueId()}`;
314 | }
315 | return `id="${partIds[v]}"`;
316 | });
317 |
318 | // for languages where texts might have unstable width, we need remove box limiting
319 | withUniquifiedIDs = withUniquifiedIDs.replace(//g, (_, v1, mid, v2) => {
320 | return ``;
321 | });
322 |
323 | Object.keys(partIds).forEach((u) => {
324 | withUniquifiedIDs = withUniquifiedIDs.replaceAll(`#${u}`, `#${partIds[u]}`);
325 | });
326 | if (lang !== 'default') {
327 | const strings = translationsDict[lang];
328 | const dom = new JSDOM(withUniquifiedIDs);
329 | for ( let e of dom.window.document.querySelectorAll("tspan") ) {
330 | for (let tr of Object.keys(strings)) {
331 | e.innerHTML = e.innerHTML.replaceAll(tr, strings[tr]);
332 | };
333 | }
334 | withUniquifiedIDs = dom.window.document.querySelector('html').innerHTML;
335 | }
336 | parts[filename] = {
337 | type: 'part',
338 | filename,
339 | content: withUniquifiedIDs,
340 | top: +firstDefined(eval(top), 0),
341 | left: +firstDefined(eval(left), 0),
342 | opacity: +firstDefined(eval(opacity), 1),
343 | index: Object.values(parts).reduce((a, p) => Math.max(a, p.index), 0) + 1,
344 | scale: +firstDefined(eval(scale), 1.0),
345 | rotate: +firstDefined(0, 0),
346 | dashoffset: +firstDefined(eval(dashoffset), 0),
347 | extrastyle: '',
348 | toBoxHole,
349 | };
350 | freezer[filename] = {};
351 | global[`\$${filename}__X`] = parts[filename].left;
352 | global[`\$${filename}__Y`] = parts[filename].top;
353 | global[`\$${filename}__LEFT`] = parts[filename].left;
354 | global[`\$${filename}__TOP`] = parts[filename].top;
355 | };
356 |
357 | const addDiv = (name, left, top, w, h, opacity, c, toBoxHole) => {
358 | //+rest.join(' ').replaceAll('"', '').replaceAll("'", '')
359 | const content = eval(c);
360 | parts[name] = {
361 | type: 'block',
362 | name,
363 | top: +firstDefined(eval(top), 0),
364 | left: +firstDefined(eval(left), 0),
365 | opacity: +firstDefined(opacity, 1),
366 | w: +firstDefined(eval(w), 0),
367 | h: +firstDefined(eval(h), 0),
368 | index: Object.values(parts).reduce((a, p) => Math.max(a, p.index), 0) + 1,
369 | rotate: +firstDefined(0, 0),
370 | dashoffset: +firstDefined(0, 0),
371 | content: content,
372 | toBoxHole,
373 | }
374 | global[`\$${name}__X`] = parts[name].left;
375 | global[`\$${name}__Y`] = parts[name].top;
376 | global[`\$${name}__LEFT`] = parts[name].left;
377 | global[`\$${name}__TOP`] = parts[name].top;
378 | }
379 |
380 | const addBoxHole = (name, left, top, w, h) => {
381 | boxholes[name] = {
382 | name,
383 | top: +firstDefined(eval(top), 0),
384 | left: +firstDefined(eval(left), 0),
385 | w: +firstDefined(eval(w), 0),
386 | h: +firstDefined(eval(h), 0),
387 | }
388 | }
389 |
390 | const setPseudoInterval = (callback, ms) => {
391 | const o = {
392 | t: 0,
393 | tick(passed_ms) {
394 | this.t += passed_ms;
395 | while (this.t >= ms) {
396 | callback();
397 | this.t -= ms;
398 | }
399 | }
400 | }
401 | return o;
402 | }
403 |
404 | const addStyle = (part, style) => {
405 | if (!parts[part]) {
406 | log(`WARN: style not applied, part not found: ${part}`)
407 | return
408 | }
409 | parts[part].extrastyle = style;
410 | }
411 |
412 | const schedule_eval = (name, ms, ...rest) => {
413 | const code = rest.join(' ');
414 | // todo check that timer already scheduled and drop warn
415 |
416 | timers[name] = setPseudoInterval(
417 | () => {
418 | const incr = (part, delta) => {
419 | parts[part].content = +parts[part].content + (delta || 1);
420 | global[part+'_value'] = parts[part].content
421 | }
422 | const get = (part) => {
423 | return parts[part].content;
424 | }
425 | const set = (part, value) => {
426 | parts[part].content = value;
427 | global[part+'_value'] = value;
428 | }
429 | eval(code);
430 | },
431 | +ms
432 | )
433 | }
434 |
435 | const schedule_time = (name, ms = 50) => {
436 | const code = `incrM('${name}_minutes'); if (+get('${name}_minutes') >= 60) { incr('${name}_hours'); set('${name}_minutes', 0)}`;
437 |
438 | timers[name] = setPseudoInterval(
439 | () => {
440 | const incr = (part) => {
441 | parts[part].content = +parts[part].content + 1;
442 | global[part+'_value'] = parts[part].content
443 | }
444 | const incrM = (part) => {
445 | parts[part].content = +parts[part].content + 7;
446 | global[part+'_value'] = parts[part].content
447 | }
448 | const get = (part) => {
449 | return parts[part].content;
450 | }
451 | const set = (part, value) => {
452 | parts[part].content = eval(value);
453 | global[part+'_value'] = eval(value)
454 | }
455 | eval(code)
456 | },
457 | +ms
458 | )
459 | }
460 |
461 | const unschedule = (name) => {
462 | // chack that schedulled and drop warn
463 | delete timers[name];
464 | }
465 |
466 |
467 |
468 | let frameAbsIndexByHTMLHash = {};
469 | let reuseAbsFrameIndexForAbsFrameIndex = {};
470 | let frameHashByAbsIndex = {};
471 | let cacheDir = '';
472 |
473 | const runGeneration = async (lang) => {
474 | initVariables();
475 | initUnique();
476 |
477 | function getFilename() {
478 | const baseFilename = proc_args.filename ? proc_args.filename: proc_args.input.split('.').slice(0, -1).join('.');
479 | let prefix = '';
480 | if (lang !== 'default') {
481 | prefix = `_${lang}`;
482 | }
483 | return `${baseFilename}${prefix}`;
484 | }
485 |
486 | const doFrame = async () => {
487 |
488 | if (cntr < skipFrames || (globalLastFrame && cntr > globalLastFrame)) {
489 | cntr += 1;
490 | return;
491 | }
492 | totalFramesCount += 1;
493 |
494 | const html = genHtml(parts);
495 | const hash = crypto.createHash('sha1').update(html).digest('base64url');
496 | frameHashByAbsIndex[cntr] = hash;
497 |
498 | if (!frameAbsIndexByHTMLHash[hash]) {
499 | frameAbsIndexByHTMLHash[hash] = cntr;
500 |
501 | const screenshotCachedPath = path.join(cacheDir, `${hash}.${FORMAT}`);
502 |
503 | const noHTMLNeeded = false;
504 | try {
505 | await fs.access(screenshotCachedPath, fsConstants.F_OK);
506 | noHTMLNeeded = true;
507 | } catch (e) {
508 | // no file exists to copy, so need generate
509 | }
510 | if (!noHTMLNeeded) {
511 | await fs.writeFile(`${FRAMES_DIR}/_index${(''+cntr).padStart(MAX_FILENAME_DIGS, '0')}.html`, html, function(err) {
512 | if (err) {
513 | return console.log(err);
514 | }
515 | });
516 | }
517 | } else {
518 | reuseAbsFrameIndexForAbsFrameIndex[cntr] = frameAbsIndexByHTMLHash[hash];
519 | }
520 |
521 | cntr += 1;
522 | log(`HTML pages gen: ${(cntr * 100.0 / (totalFrames + 1)).toFixed(2)}%`, '\033[F');
523 | }
524 |
525 | const scriptBuffer = await fs.readFile(proc_args.basedir + '/' + proc_args.input);
526 | const script = scriptBuffer.toString();
527 | if (! script) {
528 | throw "Please specify .smte file e.g. -i demo.smte"
529 | }
530 |
531 | cacheDir = path.join(os.tmpdir(), 'scriptimateCache');
532 | await fs.mkdir(cacheDir, { recursive: true });
533 |
534 | let totalMs = 0;
535 | for (v of script.split('\n')) {
536 | const d1 = v.split(' ');
537 | const cmd = d1[0];
538 | if (cmd.startsWith('animate_')) {
539 | totalMs += +cmd.replace('animate_', '');
540 | totalFrames += Math.round(+cmd.replace('animate_', '') / 1.0e3 * FPS);
541 | }
542 | }
543 |
544 | await fs.rm(FRAMES_DIR, { recursive: true });
545 | await fs.mkdir(FRAMES_DIR, { recursive: true });
546 |
547 | const processed_lines = []
548 | for (const lineIter of script.split('\n')) {
549 | let line = lineIter;
550 | if (!line.trim() || line.trim().startsWith(';')) {
551 | // empty line
552 | continue;
553 | }
554 | if (lang !== 'default') {
555 | const strings = translationsDict[lang];
556 | Object.keys(strings).forEach((tr) => {
557 | // allows to translate constants in script too
558 | line = line.replaceAll(`'${tr}'`, `'${strings[tr]}'`).replaceAll(`"${tr}"`, `"${strings[tr]}"`);
559 | });
560 | }
561 |
562 | if (line.trim().startsWith('&&')) {
563 | processed_lines[processed_lines.length - 1] += ` ${line.trim()} `;
564 | } else {
565 | processed_lines.push(line);
566 | }
567 | }
568 |
569 | for (const [file_line, line] of processed_lines.entries()) {
570 | if (line.startsWith(' ') || line.startsWith('\t')) {
571 | if (groupToAddNext) {
572 | if(!groups[groupToAddNext]) {
573 | groups[groupToAddNext] = [];
574 | }
575 | groups[groupToAddNext].push(line.trim());
576 | continue;
577 | } else {
578 | throw `Line can start with whitespace only if it follows after define_group on line ${file_line + 1}:\n${line}`;
579 | }
580 | } else {
581 | groupToAddNext = null;
582 | }
583 |
584 | const handleActionsInAnimate = (i, argSets, frames, cmd) => {
585 | let atLeastOneFrameMade = false;
586 | const first_frame_in_animate = i === 1;
587 |
588 | for (let ags of argSets) {
589 | const ags_arr = ags.trim().split(' ');
590 | const action = ags_arr.shift();
591 | let mode = 'linear';
592 | if (Object.keys(animationHandlersByMode).includes(ags_arr[0])) {
593 | mode = ags_arr.shift()
594 | }
595 | ACTION_HANDLERS[action](i, ags_arr, first_frame_in_animate, frames, mode, cmd);
596 | atLeastOneFrameMade = true;
597 | }
598 | return atLeastOneFrameMade;
599 | }
600 |
601 | const process_line = async (line, group) => {
602 | const line_splitted_by_whitespace = line.split(' ');
603 | const cmd = line_splitted_by_whitespace[0];
604 | let argSets = line_splitted_by_whitespace.slice(1).join(' ');
605 |
606 | if (argSets) {
607 | argSets = argSets.split('&&');
608 | }
609 | if (cmd === 'set_frame_size' || cmd === 'init_page') { //init_page is legacy
610 | if (pageW) {
611 | return;
612 | }
613 | const args = argSets[0].split(' ');
614 | pageScale = firstDefined(args[2], 1);
615 | pageW = Math.round(eval(args[0]) * pageScale);
616 | pageH = Math.round(eval(args[1]) * pageScale);
617 | if (proc_args.fromsecond) {
618 | skipFrames = proc_args.fromsecond * FPS;
619 | }
620 | log(`🎥 Format selected: ${proc_args.format}
621 | 📁 Filename ${getFilename()}.${proc_args.format}
622 | 📺 Resolution: ${pageW}x${pageH}
623 | ✂ Start from second: ${proc_args.fromsecond}s
624 | \n`);
625 | }
626 | else if (cmd === 'const' || cmd === 'var'){
627 | argSets[0].split(' ').forEach((s)=>{
628 | let v = s.split('=')
629 | global[v[0]] = eval(v[1])
630 | })
631 | } else if (cmd === 'exit') {
632 | globalLastFrame = globalLastFrame || globalFramesCounter;
633 | } else if (cmd === 'place') {
634 | let args = argSets[0].split(' ');
635 | await addPart(lang, ...args);
636 | } else if (cmd === 'moveToTop') {
637 | let args = argSets[0].split(' ');
638 | await moveToTop(...args);
639 | }
640 | else if (cmd === 'place_div') {
641 | let args = argSets[0].split(' ');
642 | addDiv(...args);
643 | }
644 | else if (cmd === 'place_boxhole') {
645 | let args = argSets[0].split(' ');
646 | addBoxHole(...args);
647 | }
648 | else if (cmd === 'schedule_eval') {
649 | let args = argSets[0].split(' ');
650 | schedule_eval(...args);
651 | }
652 | else if (cmd === 'schedule_time') {
653 | let args = argSets[0].split(' ');
654 | schedule_time(...args);
655 | }
656 | else if (cmd === 'unschedule') {
657 | let args = argSets[0].split(' ');
658 | unschedule(...args);
659 | }
660 | else if (cmd === 'addstyle') {
661 | let args = argSets[0].split(' ');
662 | addStyle(args[0], args.slice(1).join(' '));
663 | }
664 | else if (cmd === 'define_group') {
665 | let grpName = argSets[0].trim();
666 | if (grpName.endsWith(':')) {
667 | grpName = grpName.slice(0, -1)
668 | }
669 | groupToAddNext = grpName;
670 | }
671 | else if (cmd.startsWith('animate_')) {
672 | const duration_ms = +cmd.replace('animate_', '');
673 | const frames = Math.round(duration_ms / 1.0e3 * FPS);
674 |
675 | for (let i = 1; i <= frames; i += 1) {
676 | Object.values(timers).forEach((t) => t.tick(1000.0 / FPS));
677 | handleActionsInAnimate(i, argSets, frames, cmd)
678 | await doFrame();
679 | globalFramesCounter += 1;
680 | }
681 | }
682 | else if (cmd.startsWith('run_groups_together')) {
683 | const executing_groups = argSets[0].split(' ').map(g => g.trim());
684 | let needNextIteration = true;
685 | const needOperationByGroup = {};
686 | const animationStateByGroup = {}
687 | while (needNextIteration) {
688 |
689 | let atLeastOneFrameMade = false;
690 | for (const grp of executing_groups) {
691 | if (!needOperationByGroup[grp]) {
692 | while (groups[grp].length) {
693 | needOperationByGroup[grp] = groups[grp].shift()
694 | lineIn = needOperationByGroup[grp]
695 |
696 | if (needOperationByGroup[grp].startsWith('animate_')) {
697 | break; // we found next animate
698 | } else {
699 | await process_line(lineIn, grp);
700 | needOperationByGroup[grp] = null; // something which does not need a frames, omit it
701 | }
702 | }
703 | if (needOperationByGroup[grp]) {
704 | // this is a new next animation
705 | const line_splitted_by_whitespace = needOperationByGroup[grp].split(' ');
706 | const cmd = line_splitted_by_whitespace[0];
707 | let argSets = line_splitted_by_whitespace.slice(1).join(' ');
708 |
709 | if (argSets) {
710 | argSets = argSets.split('&&');
711 | }
712 |
713 | const duration_ms = +cmd.replace('animate_', '');
714 | const frames = Math.round(duration_ms / 1.0e3 * FPS);
715 | animationStateByGroup[grp] = {
716 | argSets,
717 | frames,
718 | i: 1,
719 | }
720 | }
721 | }
722 |
723 | if (needOperationByGroup[grp]) {
724 | const state = animationStateByGroup[grp];
725 |
726 | if (handleActionsInAnimate(state.i, state.argSets, state.frames)) {
727 | atLeastOneFrameMade = true;
728 | }
729 |
730 | state.i += 1;
731 | if (state.i > state.frames) {
732 | needOperationByGroup[grp] = null; // next cycle should pick up something
733 | }
734 | }
735 | }
736 |
737 | needNextIteration = atLeastOneFrameMade;
738 | if (atLeastOneFrameMade) {
739 | Object.values(timers).forEach((t) => t.tick(1000.0 / FPS));
740 | globalFramesCounter += 1;
741 | await doFrame();
742 | }
743 | }
744 |
745 | }
746 | }
747 | await process_line(line);
748 | }
749 |
750 |
751 | log('✅ [2/4] HTML generation done')
752 | log(`🕗 Total duration: ${(globalFramesCounter / FPS).toFixed(1)}s 🎞️ FPS: ${FPS}`)
753 |
754 | const THREADS = + proc_args.threads;
755 | let totalGenCntr = 0;
756 |
757 | async function genScreenshots(index) {
758 | const absoluteIndex = index + skipFrames;
759 | const htmlHash = frameHashByAbsIndex[absoluteIndex];
760 | const screenshotCachedPath = path.join(cacheDir, `${htmlHash}.${FORMAT}`);
761 | const dstFile = `${FRAMES_DIR}/${(''+(index)).padStart(MAX_FILENAME_DIGS, '0')}.${FORMAT}`;
762 |
763 | try {
764 | if (!proc_args.nocache) {
765 | await fs.copyFile(screenshotCachedPath, dstFile);
766 | return;
767 | }
768 | } catch (e) {
769 | // console.log('errr', e);
770 | // no file exists to copy, so need generate
771 | }
772 | await new Promise((resolve) => {
773 | if (!reuseAbsFrameIndexForAbsFrameIndex[absoluteIndex]) {
774 |
775 | const proc = spawn('node', [
776 | path.resolve(__dirname, 'puWorker.js'),
777 | pageW, pageH,
778 | absoluteIndex,
779 | totalFramesCount,
780 | FRAMES_DIR,
781 | FORMAT,
782 | QUALITY, skipFrames || 0
783 | ], { shell: true });
784 | proc.stdout.on('data', (data) => {
785 | // console.log(`NodeOUT: ${data}`);
786 | });
787 | proc.stderr.on('data', (data) => {
788 | console.error(`NodeERR: ${data}`);
789 | });
790 | proc.on('close', async (code) => {
791 | totalGenCntr += 1;
792 | log(`Frames gen: ${(totalGenCntr * 100.0 / totalFramesCount).toFixed(2)}%`, '\033[F');
793 | if (code !== 0) {
794 | log('🔴 node failed')
795 | process.exit(-1)
796 | }
797 | await fs.copyFile(dstFile, screenshotCachedPath);
798 | resolve();
799 | });
800 | } else {
801 | totalGenCntr += 1;
802 | resolve();
803 | }
804 | });
805 | }
806 |
807 | async function copyReusedScreenshots(index) {
808 | const absoluteIndex = index + skipFrames;
809 | if (reuseAbsFrameIndexForAbsFrameIndex[absoluteIndex]) {
810 | const reuseAbsIndex = reuseAbsFrameIndexForAbsFrameIndex[absoluteIndex];
811 | const srcFile = `${FRAMES_DIR}/${(''+(reuseAbsIndex - skipFrames)).padStart(MAX_FILENAME_DIGS, '0')}.${FORMAT}`;
812 | const dstFile = `${FRAMES_DIR}/${(''+(index)).padStart(MAX_FILENAME_DIGS, '0')}.${FORMAT}`;
813 | try {
814 | await fs.copyFile(srcFile, dstFile);
815 | } catch (e) {
816 | log(`🔴 failed to copy frame ${srcFile} to ${dstFile}`)
817 | throw e;
818 | }
819 | }
820 | }
821 |
822 | async function genScreenshotsForChunk(indexesChunk) {
823 | for (let i=0; i < indexesChunk.length; i+=1) {
824 | await genScreenshots(indexesChunk[i]);
825 | }
826 | }
827 |
828 | const indexes = Array.from( Array(totalFramesCount).keys() );
829 |
830 | await Promise.all(
831 | arrayChunks(indexes, Math.round( (indexes.length) / THREADS) ).map(async (indexesChunk) => await genScreenshotsForChunk(indexesChunk))
832 | )
833 |
834 | // another run to copy all duplicate files
835 | indexes.forEach(copyReusedScreenshots);
836 |
837 |
838 |
839 |
840 | log('✅ [3/4] Frames generation done')
841 |
842 | await (new Promise((resolve) => {
843 |
844 | const formats = proc_args.format.split(',');
845 | formats.forEach((format) => {
846 | if (!format.trim()) {
847 | return;
848 | }
849 | let ffmpeg_args = ['-framerate', `${FPS}/1`, '-i', `${FRAMES_DIR}/%0${MAX_FILENAME_DIGS}d.${FORMAT}`, ];
850 | if (format === 'webm') {
851 | ffmpeg_args = [...ffmpeg_args, '-c:v', 'libvpx-vp9', '-crf', '30', '-b:v', '0', '-r', ''+FPS, `${getFilename()}.${format}`, '-y']
852 | } else if (format === 'mp4') {
853 | ffmpeg_args = [...ffmpeg_args, '-c:v', 'libx264', '-r', ''+FPS, `${getFilename()}.${format}`, '-y']
854 | } else if (format === 'mov') {
855 | ffmpeg_args = [...ffmpeg_args, '-c:v', 'hevc_videotoolbox', '-allow_sw', '1', '-alpha_quality', '0.75', '-vtag', 'hvc1', '-r', ''+FPS, `${getFilename()}.${format}`, '-y']
856 | } else if (format === 'gif') {
857 | // to gen palled for each frame use stats_mode=single and add :new=1 to paletteuse options
858 | ffmpeg_args = [...ffmpeg_args, '-vf', `fps=${FPS},split[s0][s1];[s0]palettegen=stats_mode=full[p];[s1][p]paletteuse=dither=sierra2_4a:bayer_scale=5`, '-loop', '0', `${getFilename()}.${format}`, '-y']
859 |
860 | } else {
861 | console.error(`Unknown format: ${format}`);
862 | }
863 | log(`💿 Running encoder:\nffmpeg ${ffmpeg_args.join(' ')}`)
864 |
865 | const ls = spawn('ffmpeg', ffmpeg_args);
866 |
867 | ls.stdout.on('data', (data) => {
868 | console.log(`ffmpeg: ${data}`);
869 | });
870 |
871 | ls.stderr.on('data', (data) => {
872 | console.error(`ffmpeg: ${data}`);
873 | });
874 |
875 | ls.on('close', (code) => {
876 | console.log(`ffmpeg exited with code ${code}`);
877 | if (code === 0) {
878 | log('✅ [4/4] Video encoding done')
879 | } else {
880 | log('🔴 [4/4] Video encoding failed, se output above')
881 | }
882 | resolve();
883 | });
884 | });
885 |
886 | }));
887 | };
888 |
889 | (async () => {
890 | try {
891 | const transBuffer = await fs.readFile(proc_args.basedir + '/translations.yml');
892 | const transStr = transBuffer.toString();
893 | if (transStr) {
894 | translationsDict = YAML.parse(transStr);
895 | }
896 | } catch (e) {
897 | console.log('Running without translations.yml', e)
898 | }
899 |
900 | for (let lang of [...Object.keys(translationsDict), 'default']) {
901 | await runGeneration(lang);
902 | }
903 | })();
904 |
--------------------------------------------------------------------------------
/examples/1_helloworld.smte:
--------------------------------------------------------------------------------
1 | set_frame_size 600 300
2 | place boomerang 0 100
3 | animate_3000 move boomerang 500 -
4 |
5 |
--------------------------------------------------------------------------------
/examples/2_rotate_scale_opacity.smte:
--------------------------------------------------------------------------------
1 | set_frame_size 600 300
2 | place boomerang 0 100
3 | animate_1000 move boomerang 250 -
4 | animate_1000 rotate boomerang 360
5 | animate_1000 scale boomerang 2
6 | animate_1000 opacity boomerang 0
--------------------------------------------------------------------------------
/examples/3_parallel_animations.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devforth/scriptimate/54317f4f693036c1be62cd1f21dc063f332e91e9/examples/3_parallel_animations.gif
--------------------------------------------------------------------------------
/examples/3_parallel_animations.smte:
--------------------------------------------------------------------------------
1 | set_frame_size 600 300
2 | place boomerang 0 100
3 | animate_1000 move boomerang 400 - && rotate boomerang 720 && scale boomerang 2
--------------------------------------------------------------------------------
/examples/4_constants_and_expressions.smte:
--------------------------------------------------------------------------------
1 | const $frameWidth=600 $frameHeight=300
2 | const $numberOfSpins=5
3 | const $ScaleFactor=0.01
4 |
5 | set_frame_size $frameWidth $frameHeight
6 | place boomerang 0 $frameHeight-$boomerang__HEIGHT
7 | animate_1000 move boomerang $frameWidth-$boomerang__WIDTH 0 && rotate boomerang 360*$numberOfSpins && scale boomerang $ScaleFactor
--------------------------------------------------------------------------------
/examples/5_easing.smte:
--------------------------------------------------------------------------------
1 | set_frame_size 600 300
2 |
3 | place boomerang1 0 0
4 | place boomerang2 0 $boomerang1__HEIGHT
5 | place boomerang3 0 $boomerang1__HEIGHT*2
6 | place boomerang4 0 $boomerang1__HEIGHT*3
7 |
8 | animate_3000 move boomerang1 600-$boomerang1__WIDTH -
9 | && move easein boomerang2 600-$boomerang1__WIDTH -
10 | && move easeout boomerang3 600-$boomerang1__WIDTH -
11 | && move easeinout boomerang4 600-$boomerang1__WIDTH -
--------------------------------------------------------------------------------
/examples/6_groups.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devforth/scriptimate/54317f4f693036c1be62cd1f21dc063f332e91e9/examples/6_groups.gif
--------------------------------------------------------------------------------
/examples/6_groups.smte:
--------------------------------------------------------------------------------
1 | const $frameW=600 $frameH=300
2 | set_frame_size $frameW $frameH
3 |
4 | place boomerang1 0 $boomerang1__HEIGHT/2
5 | place boomerang2 0 $boomerang1__HEIGHT*3/2
6 |
7 |
8 | define_group scenario1:
9 | animate_1000 move easein boomerang1 270 -
10 | animate_2000 move easeout boomerang1 $frameW-$boomerang1__WIDTH $frameH-$boomerang1__HEIGHT
11 |
12 |
13 | define_group scenario2:
14 | animate_1000 move boomerang2 250 -
15 | animate_1000 pause
16 | animate_1000 move boomerang2 $frameW-$boomerang1__WIDTH 0
17 |
18 | define_group rotator:
19 | animate_3000 rotate boomerang1 360*4 && rotate boomerang2 360*5
20 |
21 | run_groups_together scenario1 scenario2 rotator
--------------------------------------------------------------------------------
/examples/7_dashoffset.smte:
--------------------------------------------------------------------------------
1 | set_frame_size 300 300
2 |
3 | place stroke 10 10 1 1 0 -1000
4 | addstyle stroke stroke-dasharray:1000;
5 |
6 | animate_2000 dashoffset stroke 0
--------------------------------------------------------------------------------
/examples/groups/main.smte:
--------------------------------------------------------------------------------
1 | init_page 300 160 2
2 |
3 | place bg 0 0
4 |
5 | place blue 0 0
6 | place red 0 50
7 |
8 | define_group g1:
9 | animate_80 move blue 120 0
10 | animate_160 move blue 240 0
11 |
12 |
13 | define_group g2:
14 | animate_120 move red 120 50
15 | animate_120 move red 240 50
16 |
17 |
18 |
19 | run_groups_together g1 g2
20 |
--------------------------------------------------------------------------------
/examples/groups_adv/main.smte:
--------------------------------------------------------------------------------
1 | init_page 500 500 2
2 |
3 | place blue 0 200
4 | place red 0 300
5 |
6 | define_group g1:
7 | animate_500 move blue 300 100
8 | animate_500 move red 450 200
9 |
10 | define_group g2:
11 | animate_500 move red 300 400
12 | animate_500 move blue 450 300
13 |
14 | run_groups_together g1 g2
15 |
--------------------------------------------------------------------------------
/examples/groups_adv2/main.smte:
--------------------------------------------------------------------------------
1 | init_page 500 500 2
2 |
3 | place blue 0 200
4 | place red 0 300
5 |
6 | animate_1000 move easyin blue 400 200 && move red 400 300
7 |
8 |
9 | define_group g1:
10 | animate_400 move red 100 300
11 | animate_300 move red 200 300
12 | animate_100 move red 300 300
13 | animate_50 move red 400 300
14 |
15 | define_group g2:
16 | animate_50 move blue 100 200
17 | animate_100 move blue 200 200
18 | animate_300 move blue 300 200
19 | animate_400 move blue 400 200
20 |
21 | run_groups_together g1 g2
22 |
23 | animate_300 move blue 0 200 && move red 0 300
24 |
25 | define_group g3:
26 | animate_200 move blue 200 0
27 |
28 |
29 | define_group g4:
30 | animate_200 move red 200 400
31 |
32 |
33 | run_groups_together g3 g4
34 |
35 | animate_300 move blue 400 200 && move red 400 200
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/src/boomerang.svg:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/examples/src/boomerang1.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/examples/src/boomerang2.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/examples/src/boomerang3.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/examples/src/boomerang4.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/examples/src/stroke.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scriptimate",
3 | "version": "1.2.28",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@tootallnate/once": {
8 | "version": "2.0.0",
9 | "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
10 | "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
11 | },
12 | "@types/node": {
13 | "version": "17.0.14",
14 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz",
15 | "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==",
16 | "optional": true
17 | },
18 | "@types/yauzl": {
19 | "version": "2.9.2",
20 | "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
21 | "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==",
22 | "optional": true,
23 | "requires": {
24 | "@types/node": "*"
25 | }
26 | },
27 | "abab": {
28 | "version": "2.0.5",
29 | "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
30 | "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
31 | },
32 | "acorn": {
33 | "version": "8.7.0",
34 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
35 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
36 | },
37 | "acorn-globals": {
38 | "version": "6.0.0",
39 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
40 | "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
41 | "requires": {
42 | "acorn": "^7.1.1",
43 | "acorn-walk": "^7.1.1"
44 | },
45 | "dependencies": {
46 | "acorn": {
47 | "version": "7.4.1",
48 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
49 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
50 | }
51 | }
52 | },
53 | "acorn-walk": {
54 | "version": "7.2.0",
55 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
56 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
57 | },
58 | "agent-base": {
59 | "version": "6.0.2",
60 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
61 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
62 | "requires": {
63 | "debug": "4"
64 | }
65 | },
66 | "argparse": {
67 | "version": "2.0.1",
68 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
69 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
70 | },
71 | "asynckit": {
72 | "version": "0.4.0",
73 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
74 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
75 | },
76 | "balanced-match": {
77 | "version": "1.0.2",
78 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
79 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
80 | },
81 | "base64-js": {
82 | "version": "1.5.1",
83 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
84 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
85 | },
86 | "bl": {
87 | "version": "4.1.0",
88 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
89 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
90 | "requires": {
91 | "buffer": "^5.5.0",
92 | "inherits": "^2.0.4",
93 | "readable-stream": "^3.4.0"
94 | }
95 | },
96 | "brace-expansion": {
97 | "version": "1.1.11",
98 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
99 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
100 | "requires": {
101 | "balanced-match": "^1.0.0",
102 | "concat-map": "0.0.1"
103 | }
104 | },
105 | "browser-process-hrtime": {
106 | "version": "1.0.0",
107 | "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
108 | "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
109 | },
110 | "buffer": {
111 | "version": "5.7.1",
112 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
113 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
114 | "requires": {
115 | "base64-js": "^1.3.1",
116 | "ieee754": "^1.1.13"
117 | }
118 | },
119 | "buffer-crc32": {
120 | "version": "0.2.13",
121 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
122 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
123 | },
124 | "chownr": {
125 | "version": "1.1.4",
126 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
127 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
128 | },
129 | "combined-stream": {
130 | "version": "1.0.8",
131 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
132 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
133 | "requires": {
134 | "delayed-stream": "~1.0.0"
135 | }
136 | },
137 | "concat-map": {
138 | "version": "0.0.1",
139 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
140 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
141 | },
142 | "cssom": {
143 | "version": "0.5.0",
144 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
145 | "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
146 | },
147 | "cssstyle": {
148 | "version": "2.3.0",
149 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
150 | "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
151 | "requires": {
152 | "cssom": "~0.3.6"
153 | },
154 | "dependencies": {
155 | "cssom": {
156 | "version": "0.3.8",
157 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
158 | "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
159 | }
160 | }
161 | },
162 | "data-urls": {
163 | "version": "3.0.1",
164 | "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.1.tgz",
165 | "integrity": "sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==",
166 | "requires": {
167 | "abab": "^2.0.3",
168 | "whatwg-mimetype": "^3.0.0",
169 | "whatwg-url": "^10.0.0"
170 | }
171 | },
172 | "debug": {
173 | "version": "4.3.1",
174 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
175 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
176 | "requires": {
177 | "ms": "2.1.2"
178 | }
179 | },
180 | "decimal.js": {
181 | "version": "10.3.1",
182 | "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz",
183 | "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ=="
184 | },
185 | "deep-is": {
186 | "version": "0.1.4",
187 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
188 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
189 | },
190 | "delayed-stream": {
191 | "version": "1.0.0",
192 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
193 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
194 | },
195 | "devtools-protocol": {
196 | "version": "0.0.901419",
197 | "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.901419.tgz",
198 | "integrity": "sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ=="
199 | },
200 | "domexception": {
201 | "version": "4.0.0",
202 | "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
203 | "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
204 | "requires": {
205 | "webidl-conversions": "^7.0.0"
206 | }
207 | },
208 | "end-of-stream": {
209 | "version": "1.4.4",
210 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
211 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
212 | "requires": {
213 | "once": "^1.4.0"
214 | }
215 | },
216 | "escodegen": {
217 | "version": "2.0.0",
218 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
219 | "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
220 | "requires": {
221 | "esprima": "^4.0.1",
222 | "estraverse": "^5.2.0",
223 | "esutils": "^2.0.2",
224 | "optionator": "^0.8.1",
225 | "source-map": "~0.6.1"
226 | }
227 | },
228 | "esprima": {
229 | "version": "4.0.1",
230 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
231 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
232 | },
233 | "estraverse": {
234 | "version": "5.3.0",
235 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
236 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
237 | },
238 | "esutils": {
239 | "version": "2.0.3",
240 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
241 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
242 | },
243 | "extract-zip": {
244 | "version": "2.0.1",
245 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
246 | "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
247 | "requires": {
248 | "@types/yauzl": "^2.9.1",
249 | "debug": "^4.1.1",
250 | "get-stream": "^5.1.0",
251 | "yauzl": "^2.10.0"
252 | }
253 | },
254 | "fast-levenshtein": {
255 | "version": "2.0.6",
256 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
257 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
258 | },
259 | "fd-slicer": {
260 | "version": "1.1.0",
261 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
262 | "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
263 | "requires": {
264 | "pend": "~1.2.0"
265 | }
266 | },
267 | "find-up": {
268 | "version": "4.1.0",
269 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
270 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
271 | "requires": {
272 | "locate-path": "^5.0.0",
273 | "path-exists": "^4.0.0"
274 | }
275 | },
276 | "form-data": {
277 | "version": "4.0.0",
278 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
279 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
280 | "requires": {
281 | "asynckit": "^0.4.0",
282 | "combined-stream": "^1.0.8",
283 | "mime-types": "^2.1.12"
284 | }
285 | },
286 | "fs-constants": {
287 | "version": "1.0.0",
288 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
289 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
290 | },
291 | "fs.realpath": {
292 | "version": "1.0.0",
293 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
294 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
295 | },
296 | "get-stream": {
297 | "version": "5.2.0",
298 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
299 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
300 | "requires": {
301 | "pump": "^3.0.0"
302 | }
303 | },
304 | "glob": {
305 | "version": "7.2.0",
306 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
307 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
308 | "requires": {
309 | "fs.realpath": "^1.0.0",
310 | "inflight": "^1.0.4",
311 | "inherits": "2",
312 | "minimatch": "^3.0.4",
313 | "once": "^1.3.0",
314 | "path-is-absolute": "^1.0.0"
315 | }
316 | },
317 | "html-encoding-sniffer": {
318 | "version": "3.0.0",
319 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
320 | "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
321 | "requires": {
322 | "whatwg-encoding": "^2.0.0"
323 | }
324 | },
325 | "http-proxy-agent": {
326 | "version": "5.0.0",
327 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
328 | "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
329 | "requires": {
330 | "@tootallnate/once": "2",
331 | "agent-base": "6",
332 | "debug": "4"
333 | }
334 | },
335 | "https-proxy-agent": {
336 | "version": "5.0.0",
337 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
338 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
339 | "requires": {
340 | "agent-base": "6",
341 | "debug": "4"
342 | }
343 | },
344 | "iconv-lite": {
345 | "version": "0.6.3",
346 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
347 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
348 | "requires": {
349 | "safer-buffer": ">= 2.1.2 < 3.0.0"
350 | }
351 | },
352 | "ieee754": {
353 | "version": "1.2.1",
354 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
355 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
356 | },
357 | "inflight": {
358 | "version": "1.0.6",
359 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
360 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
361 | "requires": {
362 | "once": "^1.3.0",
363 | "wrappy": "1"
364 | }
365 | },
366 | "inherits": {
367 | "version": "2.0.4",
368 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
369 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
370 | },
371 | "is-potential-custom-element-name": {
372 | "version": "1.0.1",
373 | "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
374 | "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
375 | },
376 | "jsdom": {
377 | "version": "19.0.0",
378 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz",
379 | "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==",
380 | "requires": {
381 | "abab": "^2.0.5",
382 | "acorn": "^8.5.0",
383 | "acorn-globals": "^6.0.0",
384 | "cssom": "^0.5.0",
385 | "cssstyle": "^2.3.0",
386 | "data-urls": "^3.0.1",
387 | "decimal.js": "^10.3.1",
388 | "domexception": "^4.0.0",
389 | "escodegen": "^2.0.0",
390 | "form-data": "^4.0.0",
391 | "html-encoding-sniffer": "^3.0.0",
392 | "http-proxy-agent": "^5.0.0",
393 | "https-proxy-agent": "^5.0.0",
394 | "is-potential-custom-element-name": "^1.0.1",
395 | "nwsapi": "^2.2.0",
396 | "parse5": "6.0.1",
397 | "saxes": "^5.0.1",
398 | "symbol-tree": "^3.2.4",
399 | "tough-cookie": "^4.0.0",
400 | "w3c-hr-time": "^1.0.2",
401 | "w3c-xmlserializer": "^3.0.0",
402 | "webidl-conversions": "^7.0.0",
403 | "whatwg-encoding": "^2.0.0",
404 | "whatwg-mimetype": "^3.0.0",
405 | "whatwg-url": "^10.0.0",
406 | "ws": "^8.2.3",
407 | "xml-name-validator": "^4.0.0"
408 | },
409 | "dependencies": {
410 | "ws": {
411 | "version": "8.5.0",
412 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
413 | "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg=="
414 | }
415 | }
416 | },
417 | "levn": {
418 | "version": "0.3.0",
419 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
420 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
421 | "requires": {
422 | "prelude-ls": "~1.1.2",
423 | "type-check": "~0.3.2"
424 | }
425 | },
426 | "locate-path": {
427 | "version": "5.0.0",
428 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
429 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
430 | "requires": {
431 | "p-locate": "^4.1.0"
432 | }
433 | },
434 | "mime-db": {
435 | "version": "1.51.0",
436 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
437 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
438 | },
439 | "mime-types": {
440 | "version": "2.1.34",
441 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
442 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
443 | "requires": {
444 | "mime-db": "1.51.0"
445 | }
446 | },
447 | "minimatch": {
448 | "version": "3.0.4",
449 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
450 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
451 | "requires": {
452 | "brace-expansion": "^1.1.7"
453 | }
454 | },
455 | "minimist": {
456 | "version": "1.2.5",
457 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
458 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
459 | },
460 | "mkdirp": {
461 | "version": "0.5.5",
462 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
463 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
464 | "requires": {
465 | "minimist": "^1.2.5"
466 | }
467 | },
468 | "ms": {
469 | "version": "2.1.2",
470 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
471 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
472 | },
473 | "node-fetch": {
474 | "version": "2.6.1",
475 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
476 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
477 | },
478 | "nwsapi": {
479 | "version": "2.2.0",
480 | "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
481 | "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ=="
482 | },
483 | "once": {
484 | "version": "1.4.0",
485 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
486 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
487 | "requires": {
488 | "wrappy": "1"
489 | }
490 | },
491 | "optionator": {
492 | "version": "0.8.3",
493 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
494 | "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
495 | "requires": {
496 | "deep-is": "~0.1.3",
497 | "fast-levenshtein": "~2.0.6",
498 | "levn": "~0.3.0",
499 | "prelude-ls": "~1.1.2",
500 | "type-check": "~0.3.2",
501 | "word-wrap": "~1.2.3"
502 | }
503 | },
504 | "p-limit": {
505 | "version": "2.3.0",
506 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
507 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
508 | "requires": {
509 | "p-try": "^2.0.0"
510 | }
511 | },
512 | "p-locate": {
513 | "version": "4.1.0",
514 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
515 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
516 | "requires": {
517 | "p-limit": "^2.2.0"
518 | }
519 | },
520 | "p-try": {
521 | "version": "2.2.0",
522 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
523 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
524 | },
525 | "parse5": {
526 | "version": "6.0.1",
527 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
528 | "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
529 | },
530 | "path-exists": {
531 | "version": "4.0.0",
532 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
533 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
534 | },
535 | "path-is-absolute": {
536 | "version": "1.0.1",
537 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
538 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
539 | },
540 | "pend": {
541 | "version": "1.2.0",
542 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
543 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
544 | },
545 | "pkg-dir": {
546 | "version": "4.2.0",
547 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
548 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
549 | "requires": {
550 | "find-up": "^4.0.0"
551 | }
552 | },
553 | "prelude-ls": {
554 | "version": "1.1.2",
555 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
556 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
557 | },
558 | "progress": {
559 | "version": "2.0.1",
560 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz",
561 | "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg=="
562 | },
563 | "proxy-from-env": {
564 | "version": "1.1.0",
565 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
566 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
567 | },
568 | "psl": {
569 | "version": "1.8.0",
570 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
571 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
572 | },
573 | "pump": {
574 | "version": "3.0.0",
575 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
576 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
577 | "requires": {
578 | "end-of-stream": "^1.1.0",
579 | "once": "^1.3.1"
580 | }
581 | },
582 | "punycode": {
583 | "version": "2.1.1",
584 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
585 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
586 | },
587 | "puppeteer": {
588 | "version": "10.4.0",
589 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.4.0.tgz",
590 | "integrity": "sha512-2cP8mBoqnu5gzAVpbZ0fRaobBWZM8GEUF4I1F6WbgHrKV/rz7SX8PG2wMymZgD0wo0UBlg2FBPNxlF/xlqW6+w==",
591 | "requires": {
592 | "debug": "4.3.1",
593 | "devtools-protocol": "0.0.901419",
594 | "extract-zip": "2.0.1",
595 | "https-proxy-agent": "5.0.0",
596 | "node-fetch": "2.6.1",
597 | "pkg-dir": "4.2.0",
598 | "progress": "2.0.1",
599 | "proxy-from-env": "1.1.0",
600 | "rimraf": "3.0.2",
601 | "tar-fs": "2.0.0",
602 | "unbzip2-stream": "1.3.3",
603 | "ws": "7.4.6"
604 | }
605 | },
606 | "readable-stream": {
607 | "version": "3.6.0",
608 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
609 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
610 | "requires": {
611 | "inherits": "^2.0.3",
612 | "string_decoder": "^1.1.1",
613 | "util-deprecate": "^1.0.1"
614 | }
615 | },
616 | "rimraf": {
617 | "version": "3.0.2",
618 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
619 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
620 | "requires": {
621 | "glob": "^7.1.3"
622 | }
623 | },
624 | "safe-buffer": {
625 | "version": "5.2.1",
626 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
627 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
628 | },
629 | "safer-buffer": {
630 | "version": "2.1.2",
631 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
632 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
633 | },
634 | "sax": {
635 | "version": "1.2.4",
636 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
637 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
638 | },
639 | "saxes": {
640 | "version": "5.0.1",
641 | "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
642 | "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
643 | "requires": {
644 | "xmlchars": "^2.2.0"
645 | }
646 | },
647 | "source-map": {
648 | "version": "0.6.1",
649 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
650 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
651 | "optional": true
652 | },
653 | "string_decoder": {
654 | "version": "1.3.0",
655 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
656 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
657 | "requires": {
658 | "safe-buffer": "~5.2.0"
659 | }
660 | },
661 | "svg-dimensions": {
662 | "version": "1.0.2",
663 | "resolved": "https://registry.npmjs.org/svg-dimensions/-/svg-dimensions-1.0.2.tgz",
664 | "integrity": "sha1-s1oDW91X/tJxaJMbmz1bC7VebtI=",
665 | "requires": {
666 | "xml2js": "^0.4.5"
667 | }
668 | },
669 | "symbol-tree": {
670 | "version": "3.2.4",
671 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
672 | "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
673 | },
674 | "tar-fs": {
675 | "version": "2.0.0",
676 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
677 | "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
678 | "requires": {
679 | "chownr": "^1.1.1",
680 | "mkdirp": "^0.5.1",
681 | "pump": "^3.0.0",
682 | "tar-stream": "^2.0.0"
683 | }
684 | },
685 | "tar-stream": {
686 | "version": "2.2.0",
687 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
688 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
689 | "requires": {
690 | "bl": "^4.0.3",
691 | "end-of-stream": "^1.4.1",
692 | "fs-constants": "^1.0.0",
693 | "inherits": "^2.0.3",
694 | "readable-stream": "^3.1.1"
695 | }
696 | },
697 | "through": {
698 | "version": "2.3.8",
699 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
700 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
701 | },
702 | "tough-cookie": {
703 | "version": "4.0.0",
704 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
705 | "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
706 | "requires": {
707 | "psl": "^1.1.33",
708 | "punycode": "^2.1.1",
709 | "universalify": "^0.1.2"
710 | }
711 | },
712 | "tr46": {
713 | "version": "3.0.0",
714 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
715 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
716 | "requires": {
717 | "punycode": "^2.1.1"
718 | }
719 | },
720 | "type-check": {
721 | "version": "0.3.2",
722 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
723 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
724 | "requires": {
725 | "prelude-ls": "~1.1.2"
726 | }
727 | },
728 | "unbzip2-stream": {
729 | "version": "1.3.3",
730 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz",
731 | "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==",
732 | "requires": {
733 | "buffer": "^5.2.1",
734 | "through": "^2.3.8"
735 | }
736 | },
737 | "universalify": {
738 | "version": "0.1.2",
739 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
740 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
741 | },
742 | "util-deprecate": {
743 | "version": "1.0.2",
744 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
745 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
746 | },
747 | "w3c-hr-time": {
748 | "version": "1.0.2",
749 | "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
750 | "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
751 | "requires": {
752 | "browser-process-hrtime": "^1.0.0"
753 | }
754 | },
755 | "w3c-xmlserializer": {
756 | "version": "3.0.0",
757 | "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
758 | "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==",
759 | "requires": {
760 | "xml-name-validator": "^4.0.0"
761 | }
762 | },
763 | "webidl-conversions": {
764 | "version": "7.0.0",
765 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
766 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
767 | },
768 | "whatwg-encoding": {
769 | "version": "2.0.0",
770 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
771 | "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
772 | "requires": {
773 | "iconv-lite": "0.6.3"
774 | }
775 | },
776 | "whatwg-mimetype": {
777 | "version": "3.0.0",
778 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
779 | "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="
780 | },
781 | "whatwg-url": {
782 | "version": "10.0.0",
783 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz",
784 | "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==",
785 | "requires": {
786 | "tr46": "^3.0.0",
787 | "webidl-conversions": "^7.0.0"
788 | }
789 | },
790 | "word-wrap": {
791 | "version": "1.2.3",
792 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
793 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
794 | },
795 | "wrappy": {
796 | "version": "1.0.2",
797 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
798 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
799 | },
800 | "ws": {
801 | "version": "7.4.6",
802 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
803 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
804 | },
805 | "xml-name-validator": {
806 | "version": "4.0.0",
807 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
808 | "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="
809 | },
810 | "xml2js": {
811 | "version": "0.4.23",
812 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
813 | "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
814 | "requires": {
815 | "sax": ">=0.6.0",
816 | "xmlbuilder": "~11.0.0"
817 | }
818 | },
819 | "xmlbuilder": {
820 | "version": "11.0.1",
821 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
822 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
823 | },
824 | "xmlchars": {
825 | "version": "2.2.0",
826 | "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
827 | "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
828 | },
829 | "yaml": {
830 | "version": "1.10.2",
831 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
832 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
833 | },
834 | "yauzl": {
835 | "version": "2.10.0",
836 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
837 | "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
838 | "requires": {
839 | "buffer-crc32": "~0.2.3",
840 | "fd-slicer": "~1.1.0"
841 | }
842 | }
843 | }
844 | }
845 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scriptimate",
3 | "version": "1.2.29",
4 | "description": "Open-source SVG animation tool",
5 | "main": "bin/index.js",
6 | "keywords": [
7 | "svg animation tool",
8 | "SVG",
9 | "animation",
10 | "vector",
11 | "figma",
12 | "design",
13 | "motion design",
14 | "webm animation",
15 | "mp4 animation"
16 | ],
17 | "scripts": {
18 | "scriptimate": "node --max_old_space_size=7096 іndex.js"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/devforth/scriptimate.git"
23 | },
24 | "author": "Ivan Borshchov",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/devforth/scriptimate/issues"
28 | },
29 | "bin": {
30 | "scriptimate": "bin/scriptimate.js"
31 | },
32 | "homepage": "https://tracklify.com/blog/scriptimate-an-open-source-tool-to-create-svg-animations-in-a-coding-way/",
33 | "dependencies": {
34 | "argparse": "^2.0.1",
35 | "jsdom": "^19.0.0",
36 | "puppeteer": "^10.4.0",
37 | "svg-dimensions": "^1.0.2",
38 | "yaml": "^1.10.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 | > 🙏🙏🙏 Scream for help to Ukraine 🇺🇦
3 | >
4 | > This alert created by company based in Ukraine from shelter.
5 | >
6 | > 24 February 2022, Russia started bombing cities with peacefully civilized population in whole Ukraine. Breaking the laws of war. Their bombs has been killing children and adults. This deserves Hague court.
7 | > - 🏠 If you are from Russia, please stop your government by any means including protests, don't trust local media, they are bribed by the government. They always was. I am sure you already feel lie by unexplainable crazy things in your country caused by world sanctions.
8 | > - 💣 Please spread the information about bombing of Ukraine in all social way you could. Russia treacherously broke into the territory of a sovereign state. Do not trust to anything from Russian media, most likely it will be bullshit
9 | > - 💼 If you have any kind of business cooperation with Russia, please block it now and keep most of money on your side, this is the only possible ethical decision
10 | > - ☢️ Ask your government to stop Russia from spreading invasion in any way. Russia is nuclear threat to the whole world. You think it is not possible? We thought that bombing of independent country with population of 44.13 million is also not possible.
11 |
12 |
13 | # Scriptimate – OpenSource svg animation tool
14 |
15 | Create webm/mp4/gif videos by animating qualitative SVG files (e.g. exported from Figma or any other vector image editor).
16 |
17 | > ⚠️ for now Supported Node version is 16+ (Probably 14, but 12 is not working)
18 |
19 | Works on on 🪟Windows WSL 2 🐧Ubuntu 🍏Mac
20 |
21 |
22 | ## Typical example
23 |
24 | Create text file `demo.smte`:
25 |
26 | ```
27 | set_frame_size 600 300
28 | place boomerang 0 100
29 | animate_1000 move boomerang 400 - && rotate boomerang 720 && scale boomerang 2
30 | ```
31 |
32 | Place `boomerang.svg` into `src/` folder. E.g. this one: [boomerang.svg](./examples/src/boomerang.svg)
33 |
34 | Execute scriptimate to compile video:
35 |
36 | ```
37 | npx scriptimate@latest -i demo.smte -f gif
38 | ```
39 |
40 | You will get:
41 |
42 | 
43 |
44 | [Read guide here](https://tracklify.com/blog/scriptimate-an-open-source-tool-to-create-svg-animations-in-a-coding-way/)
45 |
46 | ## Prerequirements
47 |
48 | You need to have next packages on your system (works for Ubuntu and [Windows WSL2](https://hinty.io/devforth/how-to-install-wsl-2-best-way-to-run-real-linux-on-windows/)):
49 |
50 | ```
51 | sudo apt install libnss3-dev libatk-bridge2.0-0 libcups2 libgtk-3-0 libgbm-dev ffmpeg
52 | ```
53 |
54 | ( All apart `ffmpeg` required to run pupeeter which is used to generate high-qaulity frames, some taken from here https://gist.github.com/winuxue/cfef08e2f5fe9dfc16a1d67a4ad38a01 )
55 |
56 | Required version of `ffmpeg >=4.x` (Will be installed automatically in Ubuntu 20.04+, when in 18.04 it will be 3.x, which is not compatible)
57 |
58 | If you are using custom changable texts, please make sure you have all fonts that you use in styles installed into OS, e.g. on Ubuntu:
59 |
60 | ```
61 | sudo apt install fonts-roboto fonts-open-sans
62 | ```
63 |
64 | ## Getting started
65 |
66 | If you want to start using scriptimate, please read carefully these 2 posts:
67 |
68 | * [Getting started with Scriptimate on Tracklify Blog](https://tracklify.com/blog/scriptimate-an-open-source-tool-to-create-svg-animations-in-a-coding-way/)
69 | * [Figma exort hints on hinty.io](https://hinty.io/ivictbor/prepare-figma-exports-for-scriptimate/)
70 |
71 |
72 | ## How to run examples from this repo
73 |
74 | 1. Pull the repo
75 | 2. `cd example`
76 | 3. Execute `npx scriptimate -i 1_helloworld.smte`
77 |
78 |
79 |
80 | # .smte syntax
81 |
82 | Please start with reading [scriptimate getting started blog post](https://tracklify.com/blog/scriptimate-an-open-source-tool-to-create-svg-animations-in-a-coding-way/).
83 | This reference could be used for advanced use-cases.
84 |
85 | Supported commands:
86 |
87 | ## place: Place part
88 |
89 | Part is svg file which is basic part of animation.
90 | Filename should slug-compatible (latin, no spaces, etc).
91 | Also this filename is used as id of part anywhere
92 |
93 | ```
94 | place