From f31a6b0aef5fcc3bbab5f8ee1a2643f7fff7ec02 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 25 Oct 2026 19:37:04 -0400 Subject: [PATCH] sonar: optional chaining (S6582) and for-of loops (S4138) in engine-owned JS (#2273) ExtendScript .jsx files are left alone (ES3, no ?. or for-of). --- apps/effectcraft-web/js/host.js | 28 ++++++------ apps/effectcraft-web/tests/smoke.mjs | 16 +++---- crates/expr/src/prelude.js | 8 ++-- crates/script/src/prelude.js | 68 ++++++++++++++-------------- crates/script/src/scriptui.js | 66 +++++++++++++-------------- 5 files changed, 93 insertions(+), 93 deletions(-) diff --git a/apps/effectcraft-web/js/host.js b/apps/effectcraft-web/js/host.js index 12cb90bf..9346acc2 100644 --- a/apps/effectcraft-web/js/host.js +++ b/apps/effectcraft-web/js/host.js @@ -19,7 +19,7 @@ const enc = (k) => encodeURIComponent(k); const dec = (n) => decodeURIComponent(n); async function opfs() { - if (!navigator.storage || !navigator.storage.getDirectory) throw new Error("no OPFS"); + if (!navigator.storage?.getDirectory) throw new Error("no OPFS"); const root = await navigator.storage.getDirectory(); const dir = await root.getDirectoryHandle(DIR, { create: true }); // Probe a write: some browsers expose OPFS but only write it from workers. @@ -116,7 +116,7 @@ export async function storeOpen(prefer) { try { backend = await make(); break; } catch (e) { console.info(`storage: ${make.name}: ${e.message ?? e}`); } } // Ask the browser not to evict the store under storage pressure (granted for installed apps). - try { if (navigator.storage && navigator.storage.persist) await navigator.storage.persist(); } catch {} + try { if (navigator.storage?.persist) await navigator.storage.persist(); } catch {} return backend.name; } @@ -128,7 +128,7 @@ export async function storeDelete(key) { return backend.del(key); } /// Ask the browser to keep the origin's storage under pressure; resolves with whether it is. export async function storePersist() { try { - if (navigator.storage && navigator.storage.persist) return await navigator.storage.persist(); + if (navigator.storage?.persist) return await navigator.storage.persist(); } catch {} return false; } @@ -227,7 +227,7 @@ export async function audioStart(workletUrl) { } if (gen !== generation) return; node = new AudioWorkletNode(c, "effectcraft-output", { numberOfInputs: 0, outputChannelCount: [2] }); - node.port.onmessage = (e) => { if (e.data && e.data.underrun) underruns += e.data.underrun; }; + node.port.onmessage = (e) => { if (e.data?.underrun) underruns += e.data.underrun; }; kind = "worklet"; } catch (e) { console.warn("audio: AudioWorklet unavailable, using ScriptProcessorNode", e); @@ -318,11 +318,11 @@ function spawn(base) { const w = { worker, files: new Map(), ready: null, gpu: null }; w.ready = new Promise((res, rej) => { worker.onmessage = (e) => { - if (e.data && e.data.type === "gpu") { + if (e.data?.type === "gpu") { w.gpu = e.data.gpu ? { adapter: e.data.gpu } : { cpu: e.data.error || "no GPU" }; jobGpu = w.gpu; } - if (e.data && e.data.type === "ready") res(); + if (e.data?.type === "ready") res(); }; worker.onerror = (e) => rej(new Error(e.message || "worker failed to start")); }); @@ -341,7 +341,7 @@ export function workerRun(id, json, files, base, onMessage) { } catch (e) { // `new Worker` throws at once when the page's origin is opaque (a sandboxed iframe): the job // fails like one whose worker didn't start. - setTimeout(() => onMessage("error", String((e && e.message) || e), null, null)); + setTimeout(() => onMessage("error", String(e?.message || e), null, null)); return; } running.set(id, w); @@ -416,12 +416,12 @@ export function frameWorkerStart(base, onMessage, gpu) { // slot stays, and the failure is reported like a worker that died, after the caller has // recorded the index: frames then render on the page's thread (as with `?noworkers`). frameWorkers.push(null); - setTimeout(() => onMessage(index, undefined, undefined, String((e && e.message) || e || "frame worker failed"))); + setTimeout(() => onMessage(index, undefined, undefined, String(e?.message || e || "frame worker failed"))); return index; } worker.onmessage = (e) => { const m = e.data; - if (m && m.type === "frameReply") onMessage(index, m.json, m.bytes ? new Uint8Array(m.bytes) : null, undefined); + if (m?.type === "frameReply") onMessage(index, m.json, m.bytes ? new Uint8Array(m.bytes) : null, undefined); }; worker.onerror = (e) => { onMessage(index, undefined, undefined, String(e.message || "frame worker failed")); @@ -447,7 +447,7 @@ const cacheDirHandles = {}; async function cacheDir(kind = "frames") { if (cacheDirHandles[kind]) return cacheDirHandles[kind]; - if (!navigator.storage || !navigator.storage.getDirectory) throw new Error("no Origin Private File System"); + if (!navigator.storage?.getDirectory) throw new Error("no Origin Private File System"); let d = await navigator.storage.getDirectory(); for (const n of [CACHE_ROOT, "v1", kind]) d = await d.getDirectoryHandle(n, { create: true }); cacheDirHandles[kind] = d; @@ -544,7 +544,7 @@ export async function cacheClear() { /// Whether this thread has WebGPU (`navigator.gpu`). export function hasWebGpu() { - return !!(self.navigator && self.navigator.gpu); + return !!self.navigator?.gpu; } /// Post a message ({type: "frame", json} or {type: "file", path, bytes}) to frame worker `index`. @@ -652,7 +652,7 @@ export async function browseForget(name) { export async function browseRead(path) { const rel = path.replace(/^\/Folders\//, ""); const folder = folders.get(rel.split("/")[0]); - const h = folder && folder.files.get(rel); + const h = folder?.files.get(rel); if (!h) throw new Error(`${path}: its folder is not open (reconnect it)`); return new Uint8Array(await (await h.getFile()).arrayBuffer()); } @@ -706,7 +706,7 @@ export function showDeviceLost(message, kept) { self.effectcraft .execute("file.save", {}) .catch(() => self.effectcraft.execute("file.saveAs", { path: "/Untitled Project.ecproj" })) - .catch((e) => (text.textContent = `Saving failed: ${(e && e.message) || e}`)); + .catch((e) => (text.textContent = `Saving failed: ${e?.message || e}`)); const reload = document.createElement("button"); reload.textContent = "Reload"; reload.onclick = async () => { @@ -714,7 +714,7 @@ export function showDeviceLost(message, kept) { try { await self.effectcraft.flush(); } catch (e) { - text.textContent = `Writing to browser storage failed (${(e && e.message) || e}). Save the project, or reload anyway and lose the changes.`; + text.textContent = `Writing to browser storage failed (${e?.message || e}). Save the project, or reload anyway and lose the changes.`; reload.textContent = "Reload Anyway"; reload.disabled = false; reload.onclick = () => location.reload(); diff --git a/apps/effectcraft-web/tests/smoke.mjs b/apps/effectcraft-web/tests/smoke.mjs index 3a6c1337..83091a72 100644 --- a/apps/effectcraft-web/tests/smoke.mjs +++ b/apps/effectcraft-web/tests/smoke.mjs @@ -162,7 +162,7 @@ try { const info = await js("effectcraft.info()"); report.steps.gpu = { webgpu: info.webgpu, backend: info.backend, ...info.gpu }; if (info.webgpu && info.backend === "BrowserWebGpu") { - check(info.gpu && info.gpu.compositor, "GPU compositor on WebGPU"); + check(info.gpu?.compositor, "GPU compositor on WebGPU"); check(info.gpu.viewerOnGpu, "viewer frames on the GPU"); } @@ -250,7 +250,7 @@ try { const st = await js(`(async () => { await new Promise((r) => setTimeout(r, 1200)); return effectcraft.execute("storage.info", {}); })()`); report.steps.storage = st; check(st.available && st.quota > 0 && st.usage > 0, `storage.info: ${JSON.stringify(st)}`); - check(st.diskCache && st.diskCache.enabled && st.diskCache.writes > 0, `disk cache in OPFS: ${JSON.stringify(st.diskCache)}`); + check(st.diskCache?.enabled && st.diskCache.writes > 0, `disk cache in OPFS: ${JSON.stringify(st.diskCache)}`); await js(`effectcraft.execute("prefs.open", {page: "disk"})`); await sleep(800); report.steps.storage.screenshot = await shot("01c-storage-manager"); @@ -378,14 +378,14 @@ try { const jobs = gj.workers.jobs || []; const rj = jobs.filter((j) => j.kind === "Render").pop(); const wj = jobs.filter((j) => j.kind === "Warp").pop(); - check(gj.workers.gpu && gj.workers.gpu.adapter, `job workers have a GPU: ${JSON.stringify(gj.workers)}`); + check(gj.workers.gpu?.adapter, `job workers have a GPU: ${JSON.stringify(gj.workers)}`); check(gj.status.every((st) => st === "Done"), `GPU job render: ${JSON.stringify(gj.status)}`); - check(rj && rj.gpu && rj.readbacks >= 3 && rj.passes >= 6, `Render Queue frames on the job worker's GPU, in passes: ${JSON.stringify(rj)}`); + check(rj?.gpu && rj.readbacks >= 3 && rj.passes >= 6, `Render Queue frames on the job worker's GPU, in passes: ${JSON.stringify(rj)}`); check(gj.warp.analyzed, `Warp Stabilizer analysed in a job worker: ${JSON.stringify(gj.warp)}`); - check(wj && wj.gpu && wj.readbacks > 0, `Warp Stabilizer input frames on the job worker's GPU: ${JSON.stringify(wj)}`); + check(wj?.gpu && wj.readbacks > 0, `Warp Stabilizer input frames on the job worker's GPU: ${JSON.stringify(wj)}`); const fj = jobs.filter((j) => j.kind === "ContentFill").pop(); check(!gj.fill.error && gj.fillLayers.length > 0, `Content-Aware Fill in a job worker: ${JSON.stringify(gj.fill)} ${JSON.stringify(gj.fillLayers)}`); - check(fj && fj.gpu && fj.readbacks > 0, `Content-Aware Fill frames on the job worker's GPU: ${JSON.stringify(fj)}`); + check(fj?.gpu && fj.readbacks > 0, `Content-Aware Fill frames on the job worker's GPU: ${JSON.stringify(fj)}`); check(gj.maxGapMs < 400, `event loop blocked ${gj.maxGapMs} ms during the GPU jobs: ${JSON.stringify(gj.gaps)}`); check(gj.gaps.prepareFill < 50, `event loop blocked ${gj.gaps.prepareFill} ms while editing after the analysis (pre-compose, mask): ${JSON.stringify(gj.gaps)}`); } else { @@ -418,7 +418,7 @@ try { check(mb.path === "/Browser Storage" && mb.entries.some((e) => e.name === "tone.wav" && e.kind === "audio"), `Media Browser: ${JSON.stringify(report.steps.mediaBrowser)}`); check(mb.actions.some((a) => a.id === "addFiles"), "Media Browser: Add Files…"); const mbImport = await js(`effectcraft.execute("mediaBrowser.import", {paths: [${JSON.stringify(mb.entries.find((e) => e.name === "tone.wav")?.path ?? "/tone.wav")}]})`); - check(!mbImport.pending && mbImport.items && mbImport.items.length === 1, `Media Browser import: ${JSON.stringify(mbImport)}`); + check(!mbImport.pending && mbImport.items?.length === 1, `Media Browser import: ${JSON.stringify(mbImport)}`); await js(`effectcraft.request("ui.playback", {action: "stop"})`); await js(`effectcraft.execute("time.set", {time: 0})`); await js(`effectcraft.request("ui.playback", {action: "play"})`); @@ -579,7 +579,7 @@ try { report.steps.final2 = { screenshot: await shot("03-offline") }; } catch (e) { failed = true; - report.error = String(e && e.stack || e); + report.error = String(e?.stack || e); try { report.errorShot = await shot("error"); } catch {} } report.console = logs.slice(-40); diff --git a/crates/expr/src/prelude.js b/crates/expr/src/prelude.js index df6ed3ff..6611d36a 100644 --- a/crates/expr/src/prelude.js +++ b/crates/expr/src/prelude.js @@ -840,14 +840,14 @@ var __propHandler = { // A layer-valued property (a Layer Control's Layer) is named as its layer (#809). if (k === 'name' && t.__info[2] === 'layer') { var lv = t.value; - if (lv !== null && lv !== undefined && lv.__isLayer) return lv.name; + if (lv?.__isLayer) return lv.name; } if (k in t) return t[k]; var v = t.value; if (v === null || v === undefined) return undefined; var x = v[k]; // Property groups are callable proxies: hand them over as they are, not bound like methods (#809). - if (x !== null && x !== undefined && x.__isGroup) return x; + if (x?.__isGroup) return x; return typeof x === 'function' ? x.bind(v) : x; }, }; @@ -904,8 +904,8 @@ __TextStyle.prototype = { }, get text() { var t = this.__d.text; - for (var k = 0; k < this.__ops.length; k++) { - var o = this.__ops[k]; + for (const elem of this.__ops) { + var o = elem; if (o[0] !== 'text') continue; if (o[2] < 0) t = String(o[1]); else t = t.slice(0, o[2]) + String(o[1]) + (o[3] < 0 ? '' : t.slice(o[2] + o[3])); diff --git a/crates/script/src/prelude.js b/crates/script/src/prelude.js index 5216db34..ce64e221 100644 --- a/crates/script/src/prelude.js +++ b/crates/script/src/prelude.js @@ -420,7 +420,7 @@ function __callable(o) { delete f.length; Object.setPrototypeOf(f, Object.getPrototypeOf(o)); var keys = Object.keys(o); - for (var i = 0; i < keys.length; i++) f[keys[i]] = o[keys[i]]; + for (const elem of keys) f[elem] = o[elem]; var self = new Proxy(f, { get: __attr.get, apply: function (t, thisArg, args) { return self.property(args[0]); }, @@ -473,7 +473,7 @@ function MarkerValue(comment, chapter, url, frameTarget, cuePointName, params) { } MarkerValue.prototype.getParameters = function () { var o = {}; - for (var i = 0; i < this.__params.length; i++) o[this.__params[i][0]] = this.__params[i][1]; + for (const elem of this.__params) o[elem[0]] = elem[1]; return o; }; MarkerValue.prototype.setParameters = function (o) { @@ -513,8 +513,8 @@ TextDocument.prototype = { set text(v) { this.__text = String(v); this.__textSet = true; }, __style: function (k, d) { if (k in this.__changed) return this.__changed[k]; - if (this.__base && this.__base.first && k in this.__base.first) return this.__base.first[k]; - if (this.__base && this.__base.para && k in this.__base.para) return this.__base.para[k]; + if (this.__base?.first && k in this.__base.first) return this.__base.first[k]; + if (this.__base?.para && k in this.__base.para) return this.__base.para[k]; return d; }, __set: function (k, v) { this.__changed[k] = v; }, @@ -580,14 +580,14 @@ TextDocument.prototype = { }, get tsume() { return this.__style("tsume", 0); }, set tsume(v) { this.__set("tsume", __num(v)); }, - get boxText() { return !!(this.__base && this.__base.box_size); }, + get boxText() { return !!this.__base?.box_size; }, get pointText() { return !this.boxText; }, - get boxTextSize() { return this.__base && this.__base.box_size ? this.__base.box_size.slice() : undefined; }, + get boxTextSize() { return this.__base?.box_size ? this.__base.box_size.slice() : undefined; }, set boxTextSize(v) { var p = this.boxTextPos || [0, 0]; this.__set("box", [p[0], p[1], v[0], v[1]]); }, - get boxTextPos() { return this.__base && this.__base.box_size ? this.__base.box_pos.slice() : undefined; }, + get boxTextPos() { return this.__base?.box_size ? this.__base.box_pos.slice() : undefined; }, get startIndent() { return this.__style("indentLeft", 0); }, set startIndent(v) { this.__set("indentLeft", __num(v)); }, get endIndent() { return this.__style("indentRight", 0); }, @@ -665,7 +665,7 @@ PropertyBase.prototype = { get isMask() { return !!this.__info().isMask; }, get selected() { var c = __get("item", { id: this.__layer.__comp }).selectedProperties; - for (var i = 0; i < c.length; i++) if (c[i][0] === this.__layer.__id && c[i][1] === this.__uid) return true; + for (const elem of c) if (elem[0] === this.__layer.__id && elem[1] === this.__uid) return true; return false; }, set selected(v) { @@ -842,7 +842,7 @@ Property.prototype.property = function () { return null; }; i = __num(i, "keyIndex"); // Only the key asked for: fetching the whole list made every key call linear (#814). var r = i >= 1 && Math.floor(i) === i ? __get("keys", { comp: this.__layer.__comp, layer: this.__layer.__id, uid: this.__uid, index: i - 1 }) : null; - if (!r || !r.key) throw __err("keyIndex " + i + " is out of range 1.." + (r ? r.count : this.numKeys)); + if (!r?.key) throw __err("keyIndex " + i + " is out of range 1.." + (r ? r.count : this.numKeys)); return r.key; }; // Script value → engine JSON for `prop.set` / `prop.addKey`. @@ -853,13 +853,13 @@ Property.prototype.property = function () { return null; }; return __get("textdoc", { comp: this.__layer.__comp, layer: this.__layer.__id, uid: this.__uid, time: time, attrs: td.__attrs() }); } if (t === "SHAPE") { - if (!v || !v.vertices) throw __err("value must be a Shape"); + if (!v?.vertices) throw __err("value must be a Shape"); var n = v.vertices.length; - function fit(a) { var o = []; for (var i = 0; i < n; i++) o.push(a && a[i] ? [a[i][0], a[i][1]] : [0, 0]); return o; } + function fit(a) { var o = []; for (var i = 0; i < n; i++) o.push(a?.[i] ? [a[i][0], a[i][1]] : [0, 0]); return o; } return { vertices: fit(v.vertices), in_tangents: fit(v.inTangents), out_tangents: fit(v.outTangents), closed: v.closed !== false }; } if (t === "LAYER_INDEX") { - var idx = v && v.__id !== undefined ? v.index : __num(v); + var idx = v?.__id !== undefined ? v.index : __num(v); if (!idx) return null; return this.__layer.containingComp.layer(idx).__id; } @@ -1177,7 +1177,7 @@ Layer.prototype = { }, get selectedProperties() { var c = this.containingComp.__info().selectedProperties, out = []; - for (var i = 0; i < c.length; i++) if (c[i][0] === this.__id) out.push(__propOf(this, __get("node", { comp: this.__comp, layer: this.__id, uid: c[i][1] }))); + for (const elem of c) if (elem[0] === this.__id) out.push(__propOf(this, __get("node", { comp: this.__comp, layer: this.__id, uid: elem[1] }))); return out; }, get parent() { var p = this.__info().parent; return p === null ? null : __layer(this.__comp, p); }, @@ -1205,7 +1205,7 @@ Layer.prototype = { var before = this.containingComp.__info().layers; __call("edit.duplicate", this.__p()); var after = this.containingComp.__info().layers; - for (var i = 0; i < after.length; i++) if (before.indexOf(after[i]) < 0) return __layer(this.__comp, after[i]); + for (const elem of after) if (before.indexOf(elem) < 0) return __layer(this.__comp, elem); return null; }, copyToComp: function (comp) { @@ -1310,7 +1310,7 @@ AVLayer.prototype.constructor = AVLayer; // AVLayer.replaceSource). `fixExpressions` is accepted for compatibility: only AV sources are // replaced, so the property tree and the layer's name stay and expressions keep resolving. P.replaceSource = function (newSource, fixExpressions) { - if (!newSource || newSource.__id === undefined) throw __err("replaceSource needs a project item"); + if (newSource?.__id === undefined) throw __err("replaceSource needs a project item"); __call("layer.replaceSource", { comp: this.__comp, layers: [this.__id], item: newSource.__id, fixExpressions: !!fixExpressions }); return null; }; @@ -1371,7 +1371,7 @@ LayerCollection.prototype = { return layer; }, add: function (item, duration) { - if (!item || item.id === undefined) throw __err("add(): expected an item"); + if (item?.id === undefined) throw __err("add(): expected an item"); var l = this.__new(__call("layer.addItem", { comp: this.__comp, item: item.id, time: 0 })); return this.__dur(l, duration); }, @@ -1414,15 +1414,15 @@ LayerCollection.prototype = { }, precompose: function (indices, name, moveAllAttributes) { var ids = this.__ids(), sel = []; - for (var i = 0; i < indices.length; i++) sel.push(ids[indices[i] - 1]); + for (const elem of indices) sel.push(ids[elem - 1]); var r = __call("layer.precompose", { comp: this.__comp, layers: sel, name: String(name), mode: moveAllAttributes === false ? "leave" : "move" }); return __item(r.comp); }, byName: function (name) { var ids = this.__ids(); - for (var i = 0; i < ids.length; i++) { - var l = __get("layer", { comp: this.__comp, layer: ids[i] }); - if (l.name === name) return __layer(this.__comp, ids[i]); + for (const elem of ids) { + var l = __get("layer", { comp: this.__comp, layer: elem }); + if (l.name === name) return __layer(this.__comp, elem); } return null; }, @@ -1493,7 +1493,7 @@ Item.prototype = { get label() { return this.__info().label; }, set label(v) { __call("project.setLabel", { items: [this.__id], label: __num(v, "label") }); }, get parentFolder() { return __item(this.__info().parent); }, - set parentFolder(f) { __call("project.move", { items: [this.__id], folder: f && f.__id ? f.__id : null }); }, + set parentFolder(f) { __call("project.move", { items: [this.__id], folder: f?.__id ? f.__id : null }); }, get selected() { return this.__info().selected; }, set selected(v) { var selection = __get("project").selection; @@ -1527,7 +1527,7 @@ AVItem.prototype = Object.create(Item.prototype); def("hasVideo", function () { return this.__info().hasVideo; }); def("hasAudio", function () { return this.__info().hasAudio; }); def("footageMissing", function () { return !!this.__info().footageMissing; }); - def("useProxy", function () { var p = this.__info().proxy; return !!(p && p.enabled); }, function (v) { + def("useProxy", function () { var p = this.__info().proxy; return !!p?.enabled; }, function (v) { if (!this.__info().proxy) throw __err("\"" + this.name + "\" has no proxy"); __call("file.useProxy", { item: this.__id, on: !!v }); }); @@ -1564,9 +1564,9 @@ CompItem.prototype.constructor = CompItem; }); def("activeCamera", function () { var ids = this.__info().layers; - for (var i = 0; i < ids.length; i++) { - var l = __get("layer", { comp: this.__id, layer: ids[i] }); - if (l.kind === "camera" && l.enabled && l.active) return __layer(this.__id, ids[i]); + for (const elem of ids) { + var l = __get("layer", { comp: this.__id, layer: elem }); + if (l.kind === "camera" && l.enabled && l.active) return __layer(this.__id, elem); } return null; }); @@ -1606,7 +1606,7 @@ CompItem.prototype.constructor = CompItem; if (a < 1 || a > ids.length || Math.floor(a) !== a) throw __err("layer index " + a + " is out of range 1.." + ids.length); return __layer(this.__id, ids[a - 1]); } - if (a && a.__id !== undefined && typeof b === "number") { + if (a?.__id !== undefined && typeof b === "number") { var j = a.index + b; return j >= 1 && j <= ids.length ? __layer(this.__id, ids[j - 1]) : null; } @@ -1619,9 +1619,9 @@ CompItem.prototype.constructor = CompItem; P.__controllers = function () { var out = []; (function walk(list) { - for (var i = 0; i < list.length; i++) { - if (list[i].kind === "property" || list[i].kind === "mirror" || list[i].kind === "media") out.push(list[i]); - if (list[i].children) walk(list[i].children); + for (const elem of list) { + if (elem.kind === "property" || elem.kind === "mirror" || elem.kind === "media") out.push(elem); + if (elem.children) walk(elem.children); } })(this.__eg().controls); return out; @@ -1732,7 +1732,7 @@ FolderItem.prototype.constructor = FolderItem; Object.defineProperty(P, "numItems", { get: function () { return new ItemCollection(this.__id).length; } }); Object.defineProperty(P, "parentFolder", { get: function () { return this.__id === 0 ? null : __item(this.__info().parent); }, - set: function (f) { __call("project.move", { items: [this.__id], folder: f && f.__id ? f.__id : null }); }, + set: function (f) { __call("project.move", { items: [this.__id], folder: f?.__id ? f.__id : null }); }, }); P.item = function (i) { var v = new ItemCollection(this.__id).__at(__num(i, "index")); @@ -1776,7 +1776,7 @@ RenderQueueItem.prototype = { constructor: RenderQueueItem, __info: function () { var l = __call("renderQueue.list", {}).items; - for (var i = 0; i < l.length; i++) if (l[i].id === this.__id) return l[i]; + for (const elem of l) if (elem.id === this.__id) return elem; throw __err("the render queue item no longer exists"); }, get comp() { return __item(this.__info().comp); }, @@ -1812,7 +1812,7 @@ RenderQueueItem.prototype = { var before = __call("renderQueue.list", {}).items.map(function (i) { return i.id; }); __call("renderQueue.duplicate", { item: this.__id }); var after = __call("renderQueue.list", {}).items; - for (var i = 0; i < after.length; i++) if (before.indexOf(after[i].id) < 0) return new RenderQueueItem(after[i].id); + for (const elem of after) if (before.indexOf(elem.id) < 0) return new RenderQueueItem(elem.id); return null; }, applyTemplate: function () {}, @@ -1945,9 +1945,9 @@ Project.prototype = { } } var r = __call("file.import", args); - if (r.errors && r.errors.length) throw __err(r.errors.join("; ")); + if (r.errors?.length) throw __err(r.errors.join("; ")); // Composition imports create several footage items, but callers need the comp. - return __item(r.comps && r.comps.length ? r.comps[0] : r.items[0]); + return __item(r.comps?.length ? r.comps[0] : r.items[0]); }, importFileWithDialog: function () { return null; }, importPlaceholder: function (name, w, h, rate, dur) { diff --git a/crates/script/src/scriptui.js b/crates/script/src/scriptui.js index 52c65efb..4760d058 100644 --- a/crates/script/src/scriptui.js +++ b/crates/script/src/scriptui.js @@ -213,7 +213,7 @@ function __uiResPlain(v) { if (v instanceof Array) return v.map(__uiResPlain); if (v && typeof v === "object" && v.__object) { var o = {}; - for (var k = 0; k < v.__object.length; k++) o[v.__object[k][0]] = __uiResPlain(v.__object[k][1]); + for (const elem of v.__object) o[elem[0]] = __uiResPlain(elem[1]); return o; } return v; @@ -222,8 +222,8 @@ function __uiResPlain(v) { // A resource's own properties and its child elements (in source order). function __uiResProps(res) { var props = {}, kids = []; - for (var k = 0; k < res.list.length; k++) { - var e = res.list[k]; + for (const elem2 of res.list) { + var e = elem2; if (e[1] && typeof e[1] === "object" && e[1].__element) kids.push([e[0], { type: e[1].__element, list: e[1].list }]); else props[e[0]] = __uiResPlain(e[1]); } @@ -239,17 +239,17 @@ function __uiApplyRes(c, res) { // Ranges before values, items before selections. if (props.minvalue !== undefined) c.minvalue = __num(props.minvalue); if (props.maxvalue !== undefined) c.maxvalue = __num(props.maxvalue); - if (props.items instanceof Array && c.items) for (var n = 0; n < props.items.length; n++) c.add("item", props.items[n]); + if (props.items instanceof Array && c.items) for (const elem of props.items) c.add("item", elem); for (var k in props) { if (__uiResSkip[k]) continue; var v = props[k]; if (__uiSizes[k] && v instanceof Array) v = __dim(v[0], v[1]); c[k] = v; } - for (var j = 0; j < pk.kids.length; j++) { - var child = __uiAddRes(c, pk.kids[j][1]); - child.__key = pk.kids[j][0]; - c[pk.kids[j][0]] = child; + for (const elem of pk.kids) { + var child = __uiAddRes(c, elem[1]); + child.__key = elem[0]; + c[elem[0]] = child; } return c; } @@ -268,7 +268,7 @@ Window.alert = function (s) { alert(s); }; Window.confirm = function (s) { return confirm(s); }; Window.prompt = function (m, d) { return prompt(m, d); }; Window.find = function (type, title) { - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].text === title) return __uiWins[i]; + for (const elem of __uiWins) if (elem.text === title) return elem; return null; }; @@ -335,7 +335,7 @@ function __uiColor(c) { return null; } function __uiFontSize(f, g) { - var x = f && f.size !== undefined ? Number(f.size) : g && g.font && g.font.size !== undefined ? Number(g.font.size) : 12; + var x = f?.size !== undefined ? Number(f.size) : g?.font && g.font.size !== undefined ? Number(g.font.size) : 12; return x > 0 ? x : 12; } @@ -371,11 +371,11 @@ function __uiGraphics() { this.__push({ op: "fill", color: brush ? brush.color : null, path: (path || this.currentPath).slice() }); }, strokePath: function (pen, path) { - this.__push({ op: "stroke", color: pen ? pen.color : null, width: pen && pen.lineWidth !== undefined ? pen.lineWidth : 1, path: (path || this.currentPath).slice() }); + this.__push({ op: "stroke", color: pen ? pen.color : null, width: pen?.lineWidth !== undefined ? pen.lineWidth : 1, path: (path || this.currentPath).slice() }); }, drawString: function (text, pen, x, y, font) { var f = font || this.font; - this.__push({ op: "text", text: String(text), color: pen ? pen.color : null, x: __num(x || 0), y: __num(y || 0), size: __uiFontSize(f, this), style: String(f && f.style !== undefined ? f.style : "") }); + this.__push({ op: "text", text: String(text), color: pen ? pen.color : null, x: __num(x || 0), y: __num(y || 0), size: __uiFontSize(f, this), style: String(f?.style !== undefined ? f.style : "") }); }, drawOSControl: function () { this.__push({ op: "os" }); }, drawImage: function (img, x, y, w, h) { this.__push({ op: "image", image: __uiImageJson(img), x: __num(x || 0), y: __num(y || 0), w: w === undefined ? 0 : __num(w), h: h === undefined ? 0 : __num(h) }); }, @@ -384,7 +384,7 @@ function __uiGraphics() { var size = __uiFontSize(font, this); var lines = String(s).split("\n"); var cw = size * 0.58, w = 0; - for (var i = 0; i < lines.length; i++) w = Math.max(w, Math.ceil(lines[i].length * cw)); + for (const elem of lines) w = Math.max(w, Math.ceil(elem.length * cw)); var n = lines.length; if (boundaryWidth > 0 && w > boundaryWidth) { n = Math.ceil(w / boundaryWidth) + lines.length - 1; @@ -405,14 +405,14 @@ function __uiRunDraws(win) { })(win); if (!drawn.length) return; __uiLayout(win); - for (var k = 0; k < drawn.length; k++) { - var c = drawn[k]; + for (const elem of drawn) { + var c = elem; c.__draw = []; c.graphics.__rec = c.__draw; try { c.onDraw.call(c, { type: "draw", target: c }); } catch (e) { - try { writeLn("onDraw: " + (e && e.message ? e.message : e)); } catch (e2) {} + try { writeLn("onDraw: " + (e?.message ? e.message : e)); } catch (e2) {} } c.graphics.__rec = null; } @@ -498,7 +498,7 @@ function __uiProto() { var arg = t === "image" || t === "iconbutton" ? text : typeof text === "object" && text !== null && !(text instanceof Array) ? "" : text; __uiInit(c, t, this.window, this, bounds, arg, props); if ((t === "dropdownlist" || t === "listbox") && text instanceof Array) { - for (var i = 0; i < text.length; i++) c.add("item", text[i]); + for (const elem of text) c.add("item", elem); c.text = ""; } if (t === "slider" || t === "scrollbar") { @@ -521,7 +521,7 @@ function __uiProto() { if (t === "checkbox" || t === "radiobutton") c.value = false; if (t === "edittext" && c.properties.multiline) c.__multiline = true; if (t === "edittext" && c.properties.readonly) c.__readonly = true; - if (c.properties && c.properties.name) c.name = c.properties.name; + if (c.properties?.name) c.name = c.properties.name; this.children.push(c); if (t === "tab" && this.type === "tabbedpanel" && this.__sel.length === 0) this.__sel = [this.children.length - 1]; return c; @@ -608,21 +608,21 @@ function __uiProto() { removeEventListener: function (name, fn, capture) { var k = String(name).toLowerCase(); var m = capture ? this.__capture : this.__listeners; - if (m && m[k]) m[k] = m[k].filter(function (f) { return f !== fn; }); + if (m?.[k]) m[k] = m[k].filter(function (f) { return f !== fn; }); }, dispatchEvent: function (e) { this.__fire("on" + String(e.type || e).charAt(0).toUpperCase() + String(e.type || e).slice(1)); }, __fire: function (name) { var r; if (typeof this[name] === "function") r = this[name].call(this); var l = this.__listeners[name.slice(2).toLowerCase()]; - if (l) for (var i = 0; i < l.length; i++) l[i].call(this, { type: name.slice(2).toLowerCase(), target: this }); + if (l) for (const elem of l) elem.call(this, { type: name.slice(2).toLowerCase(), target: this }); return r; }, __handlers: function () { var out = []; var names = ["onClick", "onChange", "onChanging", "onDoubleClick", "onClose", "onShow", "onActivate", "onDeactivate", "onResize", "onDraw"]; - for (var i = 0; i < names.length; i++) { - var n = names[i]; + for (const elem of names) { + var n = elem; if (typeof this[n] === "function" || (this.__listeners[n.slice(2).toLowerCase()] || []).length) out.push(n); } return out; @@ -634,8 +634,8 @@ function __uiProto() { (function () { var all = ["Window", "Panel", "Group", "Button", "IconButton", "StaticText", "EditText", "Checkbox", "RadioButton", "Slider", "Scrollbar", "Progressbar", "DropDownList", "ListBox", "TabbedPanel", "Tab", "ScriptUIImage"]; - for (var i = 0; i < all.length; i++) { - var C = __ecGlobal[all[i]]; + for (const elem of all) { + var C = __ecGlobal[elem]; var p = __uiProto(); for (var k in p) C.prototype[k] = p[k]; Object.defineProperty(C.prototype, "bounds", { @@ -737,8 +737,8 @@ function __uiMouseListeners(c, type, capture) { // The mouse events that reach a listener at `c`: its own, and its containers' (`inherited`). function __uiMouseWanted(c, inherited) { var out = inherited.slice(); - for (var i = 0; i < __uiMouseKinds.length; i++) { - var t = __uiMouseKinds[i]; + for (const elem of __uiMouseKinds) { + var t = elem; if (out.indexOf(t) < 0 && (__uiMouseListeners(c, t, false).length || __uiMouseListeners(c, t, true).length)) out.push(t); } return out; @@ -793,7 +793,7 @@ function __uiMouseFire(target, type, v) { var l = __uiMouseListeners(c, type, capture); e.currentTarget = c; e.eventPhase = phase; - for (var i = 0; i < l.length; i++) if (typeof l[i] === "function") l[i].call(c, e); + for (const elem of l) if (typeof elem === "function") elem.call(c, e); }; var n = path.length - 1; for (var i = 0; i < n; i++) { @@ -832,7 +832,7 @@ function __uiJson(c, inherited) { if (m) o.margins = m; if (typeof c.spacing === "number") o.spacing = c.spacing; var kids = c.children; - for (var i = 0; i < kids.length; i++) o.children.push(__uiJson(kids[i], mouse)); + for (const elem of kids) o.children.push(__uiJson(elem, mouse)); } if (c.alignment !== null && c.alignment !== undefined) o.alignment = __uiAlign(c.alignment); if (c.type === "checkbox" || c.type === "radiobutton") o.checked = !!c.value; @@ -859,8 +859,8 @@ function __uiJson(c, inherited) { // The open windows of this script, for the host. function __uiSnapshot() { var out = []; - for (var i = 0; i < __uiWins.length; i++) { - var w = __uiWins[i]; + for (const elem of __uiWins) { + var w = elem; if (w.__shown && !w.__closed) __uiRunDraws(w); out.push({ id: w.__wid, kind: w.__kind, title: String(w.text || ""), visible: !!w.__shown && !w.__closed, modal: !!w.__modal, root: __uiJson(w) }); } @@ -871,12 +871,12 @@ function __uiSnapshot() { function __uiOpenCount() { var n = 0; - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].__shown && !__uiWins[i].__closed) n++; + for (const elem of __uiWins) if (elem.__shown && !elem.__closed) n++; return n; } function __uiWin(id) { - for (var i = 0; i < __uiWins.length; i++) if (__uiWins[i].__wid === id) return __uiWins[i]; + for (const elem of __uiWins) if (elem.__wid === id) return elem; return null; } function __uiFind(c, id) { @@ -914,7 +914,7 @@ function __uiDispatch(winId, id, kind, valueJson) { if (!c || c.enabled === false) return; var radio = function () { var sib = c.parent ? c.parent.children : []; - for (var i = 0; i < sib.length; i++) if (sib[i] !== c && sib[i].type === "radiobutton") sib[i].value = false; + for (const elem of sib) if (elem !== c && elem.type === "radiobutton") elem.value = false; c.value = true; }; // A mouse click carries the pointer's details ({x, y, button, detail, …keys}).