Files
effectcraft/crates/expr/src/prelude.js
T

1298 lines
54 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// EffectCraft expression runtime: the After Effects expression object model and global
// functions, written from the public Expression Language Reference (behaviour only).
//
// Natives (Rust): __h(op, ...) host data request; __wig(...) wiggle offsets; __noise(x, y, z);
// __rnd(...) seeded random. This file is evaluated once per runtime and is NOT passed through
// the array-maths rewriter, so it uses plain JS operators.
var time = 0;
var value = 0;
var __S = null;
var __rc = 0;
var __seed = 0;
var __timeless = false;
// Text Expression Selector inputs (1, 1, [100, 100, 100] outside a selector).
var textIndex = 1;
var textTotal = 1;
var selectorValue = [100, 100, 100];
// Request ops (must match runtime.rs).
var __COMP = 0, __COMPNAME = 1, __LAYER = 2, __LINFO = 3, __CHILD = 4, __PINFO = 5, __VALUE = 6, __XFORM = 7, __RECT = 8, __MARKERS = 9, __DOC = 10;
var __FOOT = 11, __FDATA = 12, __SAMPLE = 13, __PROJ = 14, __SOURCE = 15, __ANCESTOR = 16;
function __begin(t, v, c, l, path, uid, idx, fd, ti, tt, sv) {
time = t;
value = v;
textIndex = ti;
textTotal = tt;
selectorValue = sv;
__S = { c: c, l: l, path: path, uid: uid, idx: idx, fd: fd, comp: null, layer: null, prop: null, vars: {} };
__rc = 0;
__seed = 0;
__timeless = false;
__projectInfo = null;
__layersTrim();
}
function __finish(r) {
// Rethrow from this frame so the runtime's stack unwinds (see `Runtime::script`).
try {
return __result(r);
} catch (e) {
throw e;
}
}
function __result(r) {
r = __v(r);
if (typeof r === 'function' && r.__isLayer) return r.index;
if (r !== null && typeof r === 'object' && r.__isTextStyle) return r.__out();
// `[temp, temp]` where temp is a property (pick-whip 1D → 2D): read the values here, while
// host requests can still be answered and the script re-run.
if (Array.isArray(r)) return r.map(function (x) { return __v(x); });
return r;
}
function __err(msg) {
throw new Error(msg);
}
// ---------------------------------------------------------------------------------------------
// Array maths (targets of the operator rewrite).
function __v(x) {
if (x !== null && typeof x === 'object' && x.__isProp === true) return x.value;
return x;
}
function __bin(a, b, f) {
a = __v(a);
b = __v(b);
var aa = Array.isArray(a), ba = Array.isArray(b), i, r;
if (aa && ba) {
var n = Math.max(a.length, b.length);
r = new Array(n);
for (i = 0; i < n; i++) r[i] = f(i < a.length ? a[i] : 0, i < b.length ? b[i] : 0);
return r;
}
if (aa && (typeof b === 'number' || typeof b === 'boolean')) {
r = new Array(a.length);
for (i = 0; i < a.length; i++) r[i] = f(a[i], b);
return r;
}
if (ba && (typeof a === 'number' || typeof a === 'boolean')) {
r = new Array(b.length);
for (i = 0; i < b.length; i++) r[i] = f(a, b[i]);
return r;
}
return f(a, b);
}
function __fadd(x, y) { return x + y; }
function __fsub(x, y) { return x - y; }
function __fmul(x, y) { return x * y; }
function __fdiv(x, y) { return x / y; }
function __add(a, b) {
if (typeof a === 'number' && typeof b === 'number') return a + b;
if (typeof a === 'string' || typeof b === 'string') return __v(a) + __v(b);
var x = __v(a), y = __v(b);
if (typeof x === 'string' || typeof y === 'string') return x + y;
if ((Array.isArray(x) && typeof y === 'string') || (Array.isArray(y) && typeof x === 'string')) return x + y;
return __bin(x, y, __fadd);
}
function __sub(a, b) {
if (typeof a === 'number' && typeof b === 'number') return a - b;
return __bin(a, b, __fsub);
}
function __mul(a, b) {
if (typeof a === 'number' && typeof b === 'number') return a * b;
return __bin(a, b, __fmul);
}
function __div(a, b) {
if (typeof a === 'number' && typeof b === 'number') return a / b;
return __bin(a, b, __fdiv);
}
function __neg(a) {
if (typeof a === 'number') return -a;
a = __v(a);
if (Array.isArray(a)) return a.map(function (x) { return -x; });
return -a;
}
// ---------------------------------------------------------------------------------------------
// Vector maths.
function add(a, b) { return __bin(a, b, __fadd); }
function sub(a, b) { return __bin(a, b, __fsub); }
function mul(a, b) { return __bin(a, b, __fmul); }
function div(a, b) { return __bin(a, b, __fdiv); }
function clamp(v, a, b) {
v = __v(v); a = __v(a); b = __v(b);
var lo = function (x, y) { return Math.min(Math.max(x, Math.min(y[0], y[1])), Math.max(y[0], y[1])); };
if (Array.isArray(v)) {
return v.map(function (x, i) {
return lo(x, [Array.isArray(a) ? a[i] : a, Array.isArray(b) ? b[i] : b]);
});
}
return lo(v, [a, b]);
}
function dot(a, b) {
a = __v(a); b = __v(b);
var s = 0;
for (var i = 0; i < Math.min(a.length, b.length); i++) s += a[i] * b[i];
return s;
}
function cross(a, b) {
a = __v(a); b = __v(b);
var a2 = a[2] || 0, b2 = b[2] || 0;
return [a[1] * b2 - a2 * b[1], a2 * b[0] - a[0] * b2, a[0] * b[1] - a[1] * b[0]];
}
function length(a, b) {
a = __v(a);
if (b !== undefined) a = sub(a, __v(b));
if (!Array.isArray(a)) return Math.abs(a);
var s = 0;
for (var i = 0; i < a.length; i++) s += a[i] * a[i];
return Math.sqrt(s);
}
function normalize(a) {
a = __v(a);
var l = length(a);
return l === 0 ? a.map(function () { return 0; }) : div(a, l);
}
function lookAt(from, at) {
var d = normalize(sub(__v(at), __v(from)));
var x = d[0], y = d[1], z = d[2] || 0;
var rx = -Math.asin(Math.max(-1, Math.min(1, y))) * 180 / Math.PI;
var ry = Math.atan2(x, z) * 180 / Math.PI;
return [rx, ry, 0];
}
function degreesToRadians(d) { return d * Math.PI / 180; }
function radiansToDegrees(r) { return r * 180 / Math.PI; }
// ---------------------------------------------------------------------------------------------
// Interpolation.
function __interp(shape, args) {
var t, tMin, tMax, v1, v2;
if (args.length <= 3) {
t = args[0]; tMin = 0; tMax = 1; v1 = args[1]; v2 = args[2];
} else {
t = args[0]; tMin = args[1]; tMax = args[2]; v1 = args[3]; v2 = args[4];
}
t = __v(t); v1 = __v(v1); v2 = __v(v2);
var u;
if (tMax === tMin) u = t >= tMax ? 1 : 0;
else u = (t - tMin) / (tMax - tMin);
u = Math.max(0, Math.min(1, u));
u = shape(u);
if (Array.isArray(v1) || Array.isArray(v2)) return add(v1, mul(sub(v2, v1), u));
return v1 + (v2 - v1) * u;
}
function __lin(u) { return u; }
function __ease(u) { return u * u * (3 - 2 * u); }
function __easeIn(u) { return u * u * (2 - u); }
function __easeOut(u) { var w = 1 - u; return 1 - w * w * (2 - w); }
function linear() { return __interp(__lin, arguments); }
function ease() { return __interp(__ease, arguments); }
function easeIn() { return __interp(__easeIn, arguments); }
function easeOut() { return __interp(__easeOut, arguments); }
// Cubic-Bezier easing (the CSS cubic-bezier() shape): [x1, y1, x2, y2], with x1 / x2 in 0..1
// and y1 / y2 free (overshoot included). Solves x(u) = x by Newton–Raphson, then by bisection
// when Newton leaves 0..1 or the derivative vanishes, and returns y(u). A curve that is not four
// finite numbers is linear, never an error.
function __cubicBezier(curve) {
var x1, y1, x2, y2;
if (Array.isArray(curve) && curve.length === 4) {
x1 = curve[0]; y1 = curve[1]; x2 = curve[2]; y2 = curve[3];
}
if (typeof x1 !== 'number' || typeof y1 !== 'number' || typeof x2 !== 'number' || typeof y2 !== 'number' ||
!isFinite(x1) || !isFinite(y1) || !isFinite(x2) || !isFinite(y2)) {
return __lin;
}
x1 = Math.max(0, Math.min(1, x1));
x2 = Math.max(0, Math.min(1, x2));
function bx(u) { var v = 1 - u; return 3 * v * v * u * x1 + 3 * v * u * u * x2 + u * u * u; }
function dbx(u) { var v = 1 - u; return 3 * v * v * x1 + 6 * v * u * (x2 - x1) + 3 * u * u * (1 - x2); }
function by(u) { var v = 1 - u; return 3 * v * v * u * y1 + 3 * v * u * u * y2 + u * u * u; }
function dby(u) { var v = 1 - u; return 3 * v * v * y1 + 6 * v * u * (y2 - y1) + 3 * u * u * (1 - y2); }
return function (x) {
if (x <= 0) return 0;
if (x >= 1) return 1;
var u = x;
for (var i = 0; i < 8; i++) {
var f = bx(u) - x;
var d = dbx(u);
if (Math.abs(d) < 1e-12) break;
// Stop when the *value* is accurate: near the endpoints dy/dx is steep, so a residual in x
// alone can still mean a visible error in y (f * dy/dx estimates it).
if (Math.abs(f * dby(u) / d) < 1e-9) return by(u);
var n = u - f / d;
if (!(n >= 0 && n <= 1)) break;
u = n;
}
var lo = 0, hi = 1;
for (var j = 0; j < 60; j++) {
var mid = (lo + hi) / 2;
if (bx(mid) < x) lo = mid; else hi = mid;
}
return by((lo + hi) / 2);
};
}
// bezier(t, tMin, tMax, value1, value2, curve), or bezier(t, value1, value2, curve): the curve is
// the sixth / fourth argument. Read it by position, not from the end: when it is missing or
// unusable, the interpolation falls back to linear without consuming the last value.
function bezier() {
var a = arguments, n = a.length;
if (n >= 6) return __interp(__cubicBezier(a[5]), [a[0], a[1], a[2], a[3], a[4]]);
if (n === 5) return __interp(__lin, [a[0], a[1], a[2], a[3], a[4]]);
if (n === 4) return __interp(__cubicBezier(a[3]), [a[0], a[1], a[2]]);
return __interp(__lin, [a[0], a[1], a[2]]);
}
// ---------------------------------------------------------------------------------------------
// Random numbers and noise.
function seedRandom(offset, timeless) {
__seed = offset;
__timeless = !!timeless;
}
function __r01() {
return __rnd(__S.idx, __S.uid, __seed, __timeless, time, __rc++);
}
function __scaleRand(r, args) {
if (args.length === 0) return r();
var a = __v(args[0]);
if (args.length === 1) {
if (Array.isArray(a)) return a.map(function (m) { return r() * m; });
return r() * a;
}
var b = __v(args[1]);
if (Array.isArray(a) || Array.isArray(b)) {
var n = Math.max(Array.isArray(a) ? a.length : 1, Array.isArray(b) ? b.length : 1), out = [];
for (var i = 0; i < n; i++) {
var lo = Array.isArray(a) ? (a[i] || 0) : a, hi = Array.isArray(b) ? (b[i] || 0) : b;
out.push(lo + r() * (hi - lo));
}
return out;
}
return a + r() * (b - a);
}
function random() { return __scaleRand(__r01, arguments); }
function __gauss01() {
var u1 = Math.max(__r01(), 1e-12), u2 = __r01();
var z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
// ~90% of results fall in [0, 1].
return 0.5 + z * 0.304;
}
function gaussRandom() { return __scaleRand(__gauss01, arguments); }
function noise(x) {
x = __v(x);
if (Array.isArray(x)) return __noise(x[0] || 0, x[1] || 0, x[2] || 0);
return __noise(x, 0, 0);
}
// ---------------------------------------------------------------------------------------------
// Time conversion.
function timeToFrames(t, fps, isDuration) {
if (t === undefined) t = time + thisComp.displayStartTime;
if (fps === undefined) fps = 1 / __S.fd;
var frames = t * fps;
// The 1e-6 keeps a whole number of frames that floating point overshoots (31/24 - 5/24) whole.
if (isDuration && frames !== 0) return frames < 0 ? Math.floor(frames + 1e-6) : Math.ceil(frames - 1e-6);
return Math.floor(frames + 1e-6);
}
function framesToTime(frames, fps) {
if (fps === undefined) fps = 1 / __S.fd;
return frames / fps;
}
function __pad(n) { return n < 10 ? '0' + n : '' + n; }
function timeToTimecode(t, base, isDuration) {
if (t === undefined) t = time + thisComp.displayStartTime;
if (base === undefined) base = 30;
var neg = t < 0;
var f = Math.floor(Math.abs(t) * base + 1e-6);
var ff = f % base, s = Math.floor(f / base), ss = s % 60, m = Math.floor(s / 60), mm = m % 60, h = Math.floor(m / 60);
return (neg ? '-' : '') + h + ':' + __pad(mm) + ':' + __pad(ss) + ':' + __pad(ff);
}
function timeToFeetAndFrames(t, fps, framesPerFoot, isDuration) {
if (t === undefined) t = time + thisComp.displayStartTime;
if (fps === undefined) fps = 1 / __S.fd;
if (framesPerFoot === undefined) framesPerFoot = 16;
var neg = t < 0;
var f = Math.floor(Math.abs(t) * fps + 1e-6);
var feet = Math.floor(f / framesPerFoot), fr = f % framesPerFoot;
var s = String(fr);
while (s.length < 2) s = '0' + s;
return (neg ? '-' : '') + feet + '+' + s;
}
function timeToCurrentFormat(t, fps) {
if (fps === undefined) fps = Math.round(1 / __S.fd);
return timeToTimecode(t, fps);
}
function posterizeTime(fps) {
if (!(fps > 0)) return;
var t = Math.floor(time * fps + 1e-6) / fps;
if (t !== time) {
var v = thisProperty.valueAtTime(t);
time = t;
value = v;
}
}
// ---------------------------------------------------------------------------------------------
// Colour.
function rgbToHsl(c) {
c = __v(c);
var r = c[0], g = c[1], b = c[2], a = c.length > 3 ? c[3] : 1;
var mx = Math.max(r, g, b), mn = Math.min(r, g, b), l = (mx + mn) / 2, h = 0, s = 0;
if (mx !== mn) {
var d = mx - mn;
s = l > 0.5 ? d / (2 - mx - mn) : d / (mx + mn);
if (mx === r) h = (g - b) / d + (g < b ? 6 : 0);
else if (mx === g) h = (b - r) / d + 2;
else h = (r - g) / d + 4;
h /= 6;
}
return [h, s, l, a];
}
function hslToRgb(c) {
c = __v(c);
var h = c[0], s = c[1], l = c[2], a = c.length > 3 ? c[3] : 1;
if (s === 0) return [l, l, l, a];
var hue = function (p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q;
return [hue(p, q, h + 1 / 3), hue(p, q, h), hue(p, q, h - 1 / 3), a];
}
function hexToRgb(hex) {
var s = String(hex).replace(/^#|^0x/i, '');
if (s.length === 3) s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
var n = parseInt(s.slice(0, 6), 16);
var a = s.length >= 8 ? parseInt(s.slice(6, 8), 16) / 255 : 1;
return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255, a];
}
// ---------------------------------------------------------------------------------------------
// Paths.
function __Path(pts, ins, outs, closed) {
this.__path = true;
this.__pts = pts; this.__in = ins; this.__out = outs; this.__closed = !!closed;
}
__Path.prototype.points = function () { return this.__pts; };
__Path.prototype.inTangents = function () { return this.__in; };
__Path.prototype.outTangents = function () { return this.__out; };
__Path.prototype.isClosed = function () { return this.__closed; };
// pointOnPath / tangentOnPath: a point (or unit tangent) at a fraction of the path's arc length.
__Path.prototype.pointOnPath = function (pct) { return __pathAt(this, pct === undefined ? 0.5 : pct, false); };
__Path.prototype.tangentOnPath = function (pct) { return __pathAt(this, pct === undefined ? 0.5 : pct, true); };
__Path.prototype.normalOnPath = function (pct) { var t = this.tangentOnPath(pct); return [t[1], -t[0]]; };
function __pathSegs(p) {
var n = p.__pts.length, out = [];
var m = p.__closed ? n : n - 1;
for (var i = 0; i < m; i++) {
var j = (i + 1) % n, a = p.__pts[i], b = p.__pts[j];
var o = p.__out[i] || [0, 0], ii = p.__in[j] || [0, 0];
out.push([a, [a[0] + o[0], a[1] + o[1]], [b[0] + ii[0], b[1] + ii[1]], b]);
}
return out;
}
function __bezAt(s, u) {
var v = 1 - u;
return [0, 1].map(function (k) { return v * v * v * s[0][k] + 3 * v * v * u * s[1][k] + 3 * v * u * u * s[2][k] + u * u * u * s[3][k]; });
}
function __bezDer(s, u) {
var v = 1 - u;
return [0, 1].map(function (k) { return 3 * v * v * (s[1][k] - s[0][k]) + 6 * v * u * (s[2][k] - s[1][k]) + 3 * u * u * (s[3][k] - s[2][k]); });
}
function __pathAt(p, pct, tangent) {
var segs = __pathSegs(p);
if (!segs.length) return tangent ? [1, 0] : p.__pts.length ? [p.__pts[0][0], p.__pts[0][1]] : [0, 0];
var N = 32, table = [], total = 0;
for (var k = 0; k < segs.length; k++) {
var prev = __bezAt(segs[k], 0);
for (var s = 1; s <= N; s++) {
var q = __bezAt(segs[k], s / N);
total += Math.sqrt((q[0] - prev[0]) * (q[0] - prev[0]) + (q[1] - prev[1]) * (q[1] - prev[1]));
table.push([total, k, s / N]);
prev = q;
}
}
var target = Math.max(0, Math.min(1, pct)) * total;
var i = 0;
while (i < table.length - 1 && table[i][0] < target) i++;
var e = table[i], seg = e[1];
var l0 = i > 0 ? table[i - 1][0] : 0, u0 = i > 0 && table[i - 1][1] === seg ? table[i - 1][2] : 0;
var u = e[0] > l0 ? u0 + (e[2] - u0) * (target - l0) / (e[0] - l0) : e[2];
if (!tangent) return __bezAt(segs[seg], u);
var d = __bezDer(segs[seg], u), len = Math.sqrt(d[0] * d[0] + d[1] * d[1]);
return len > 0 ? [d[0] / len, d[1] / len] : [1, 0];
}
function createPath(points, inTangents, outTangents, isClosed) {
points = points || [];
var zero = points.map(function () { return [0, 0]; });
return new __Path(points, inTangents || zero, outTangents || zero, isClosed === undefined ? true : isClosed);
}
// ---------------------------------------------------------------------------------------------
// Object model.
function __mod(a, d) { return ((a % d) + d) % d; }
function __child(c, l, group, key) {
var info = __h(__CHILD, c, l, group, key);
if (info === null) return undefined;
return info[1] ? __mkGroup(c, l, info) : __mkProp(c, l, info[0]);
}
// `propertyGroup(countUp)` of the property or group at `path`: the group `countUp` levels above
// it (default 1), the layer at the top (#1619).
function __ancestor(c, l, path, countUp) {
var n = countUp === undefined ? 1 : __v(countUp);
if (typeof n !== 'number' || !(n >= 1) || !isFinite(n)) __err('propertyGroup(' + countUp + '): the count must be a number of 1 or more');
n = Math.floor(n);
var info = __h(__ANCESTOR, c, l, path, n);
if (info === 'layer') return __layer(c, l);
if (info === null) __err('propertyGroup(' + n + ') goes above the layer');
return __mkGroup(c, l, info);
}
// Comp ------------------------------------------------------------------------------------------
function Comp(c) { this.__c = c; this.__i = null; }
Comp.prototype = {
__isComp: true,
get __info() { return this.__i || (this.__i = __h(__COMP, this.__c)); },
get name() { return this.__info[0]; },
get width() { return this.__info[1]; },
get height() { return this.__info[2]; },
get duration() { return this.__info[3]; },
get frameDuration() { return this.__info[4]; },
get numLayers() { return this.__info[5]; },
get pixelAspect() { return this.__info[6]; },
get displayStartTime() { return this.__info[7]; },
get bgColor() { return this.__info[8]; },
get marker() { return new Markers(this.__c, -1); },
// The topmost enabled camera active at the current time.
get activeCamera() {
for (var i = 1; i <= this.numLayers; i++) {
var l = this.layer(i);
if (l.__info[10] === 'Camera' && l.active) return l;
}
return __err('Composition "' + this.name + '" has no active camera');
},
layer: function (a, rel) {
if (typeof a === 'function' && a.__isLayer) return this.layer(a.index + (rel || 0));
var id = __h(__LAYER, this.__c, a);
if (id === null) __err(typeof a === 'string' ? 'Layer named "' + a + '" does not exist' : 'Layer index ' + a + ' out of range');
return __layer(this.__c, id);
},
toString: function () { return '[object Comp]'; },
};
function comp(name) {
var id = __h(__COMPNAME, String(name));
if (id === null) __err('Comp named "' + name + '" does not exist');
return new Comp(id);
}
// Footage (data-driven animation) and the project ------------------------------------------------
function Footage(info) { this.__i = info; this.__d = null; }
Footage.prototype = {
__isFootage: true,
get name() { return this.__i[0]; },
get width() { return this.__i[1]; },
get height() { return this.__i[2]; },
get duration() { return this.__i[3]; },
get frameDuration() { return this.__i[4]; },
get pixelAspect() { return this.__i[5]; },
get ntscDropFrame() { return false; },
// The data file's text (JSON / CSV / TSV), or "" for media.
get sourceText() { return this.__i[7] === null ? '' : this.__i[7]; },
get __data() {
if (this.__d !== null) return this.__d;
var j = __h(__FDATA, this.__i[0]);
if (j === null) __err('Footage "' + this.name + '" has no data (or it could not be parsed)');
return (this.__d = JSON.parse(j));
},
// JSON: the parsed document; CSV/TSV: one object per row keyed by the header row.
get sourceData() { return this.__data.data; },
// CSV/TSV cell by [column, row] (0-based, rows after the header).
dataValue: function (idx) {
idx = __v(idx);
var rows = this.__data.rows;
if (rows === null) {
// JSON: a path of keys / indexes.
var v = this.__data.data;
for (var i = 0; i < idx.length; i++) {
if (v === null || v === undefined) __err('dataValue: no value at ' + JSON.stringify(idx));
v = v[idx[i]];
}
return v;
}
var r = rows[idx[1]];
if (r === undefined || r[idx[0]] === undefined) __err('dataValue: no cell at column ' + idx[0] + ', row ' + idx[1]);
return r[idx[0]];
},
get dataKeyCount() { var r = this.__data.rows; return r === null ? 0 : r.length; },
// The column names (CSV/TSV header row).
get dataKeyNames() { return this.__data.header || []; },
toString: function () { return '[object Footage]'; },
};
function footage(name) {
var info = __h(__FOOT, String(name));
if (info === null) __err('Footage named "' + name + '" does not exist');
return new Footage(info);
}
var __projectInfo = null;
// The project's bits per channel (8, 16 or 32).
__global('colorDepth', function () { return thisProject.bitsPerChannel; });
var thisProject = {
get __info() { return __projectInfo || (__projectInfo = __h(__PROJ)); },
get fullPath() { return ''; },
get bitsPerChannel() { return this.__info[0]; },
get linearBlending() { return this.__info[1]; },
toString: function () { return '[object Project]'; },
};
// Layer -----------------------------------------------------------------------------------------
// A layer is a property group too, as in After Effects: `thisLayer("transform")`, another layer's
// `("effects")("Slider Control")`, `thisLayer(1)` (what the pick whip writes with Compact English
// off). Callable like a group (`__mkGroup`), with the Layer methods.
function Layer(c, l) {
var f = function (k) { return f.__group(k); };
delete f.name;
delete f.length;
Object.setPrototypeOf(f, Layer.prototype);
// Kept across evaluations (`__layer`), so an expression can't change which layer it is, delete
// a field or leave its own on it. Its info is private to `__info`, which reads it again in an
// evaluation whose `__S` isn't the one it was read in (every evaluation has a new `__S`), so
// freezing the layer can't keep its info either.
var info = null, read = null;
Object.defineProperty(f, '__c', { value: c });
Object.defineProperty(f, '__l', { value: l });
Object.defineProperty(f, '__info', {
get: function () {
if (read !== __S) { info = __h(__LINFO, c, l); read = __S; }
return info;
},
});
Object.preventExtensions(f);
return f;
}
// Layer `l` of comp `c`, made once per runtime: a callable Layer is dearer to make than a plain
// object, so it is kept, and its info (`__info`) is read again in each evaluation.
// The kept layers are out of an expression's reach (their maps have no prototype, so a change to
// `Object.prototype` doesn't reach them either), and bounded like the runtime's scripts: once
// `max` are kept, the layers after them aren't, and the next evaluation starts over
// (`__layersTrim`, so a layer stays the same object for a whole evaluation).
var __layer, __layersTrim;
(function () {
var layers = Object.create(null), kept = 0, max = 4096;
__layer = function (c, l) {
// By comp, then layer: a `c + ':' + l` key costs more to build than the lookup saves.
var m = layers[c], f = m && m[l];
if (f) return f;
f = new Layer(c, l);
if (kept < max) {
(m || (layers[c] = Object.create(null)))[l] = f;
kept++;
}
return f;
};
__layersTrim = function () {
if (kept >= max) { layers = Object.create(null); kept = 0; }
};
})();
function __m3(m, p) {
var x = p[0] || 0, y = p[1] || 0;
var X = m[0] * x + m[1] * y + m[2], Y = m[3] * x + m[4] * y + m[5], W = m[6] * x + m[7] * y + m[8];
if (W !== 0 && W !== 1) { X /= W; Y /= W; }
return p.length > 2 ? [X, Y, 0] : [X, Y];
}
function __m3v(m, p) {
var x = p[0] || 0, y = p[1] || 0;
return p.length > 2 ? [m[0] * x + m[1] * y, m[3] * x + m[4] * y, 0] : [m[0] * x + m[1] * y, m[3] * x + m[4] * y];
}
function __m4(m, p, vec) {
var x = p[0] || 0, y = p[1] || 0, z = p[2] || 0, k = vec ? 0 : 1;
var X = m[0] * x + m[1] * y + m[2] * z + m[3] * k;
var Y = m[4] * x + m[5] * y + m[6] * z + m[7] * k;
var Z = m[8] * x + m[9] * y + m[10] * z + m[11] * k;
var W = vec ? 1 : m[12] * x + m[13] * y + m[14] * z + m[15];
if (W !== 0 && W !== 1) { X /= W; Y /= W; Z /= W; }
return [X, Y, Z];
}
Layer.prototype = {
__isLayer: true,
get name() { return this.__info[0]; },
get index() { return this.__info[1]; },
get inPoint() { return this.__info[2]; },
get outPoint() { return this.__info[3]; },
get startTime() { return this.__info[4]; },
get width() { return this.__info[5]; },
get height() { return this.__info[6]; },
get hasParent() { return this.__info[7] !== null; },
get parent() {
var p = this.__info[7];
if (p === null) __err('Layer "' + this.name + '" has no parent');
return __layer(this.__c, p);
},
get hasVideo() { return this.__info[8]; },
get enabled() { return this.__info[11]; },
get active() { return this.__info[11] && time >= this.inPoint && time < this.outPoint; },
get containingComp() { return new Comp(this.__c); },
// The source item (AE's Layer.source): a Comp for a precomp layer, a Footage for footage, solid
// and model layers, null for text, shape, null, camera and light layers (#809).
get source() {
var s = __h(__SOURCE, this.__c, this.__l);
if (s === null) return null;
if (s[0] === 'comp') return new Comp(s[1]);
return new Footage([s[2], s[3], s[4], s[5], s[6], s[7], s[0], null, null]);
},
__group: function (k) {
var g = __child(this.__c, this.__l, '', k);
if (g === undefined) __err('Layer "' + this.name + '" has no property "' + k + '"');
return g;
},
get transform() { return this.__group('transform'); },
get position() { return this.transform.position; },
get scale() { return this.transform.scale; },
get rotation() { return this.transform.rotation; },
get opacity() { return this.transform.opacity; },
get anchorPoint() { return this.transform.anchorPoint; },
get text() { return this.__group('text'); },
get cameraOption() { return this.__group('cameraOptions'); },
get lightOption() { return this.__group('lightOptions'); },
get marker() { return new Markers(this.__c, this.__l); },
effect: function (k) {
var g = __child(this.__c, this.__l, '', 'effects');
var e = g === undefined ? undefined : __child(g.__c, g.__l, g.__p, k);
if (e === undefined) __err('Effect ' + (typeof k === 'string' ? '"' + k + '"' : k) + ' not found on layer "' + this.name + '"');
return e;
},
mask: function (k) { return this.__group('masks').__get(k); },
content: function (k) { return this.__group('contents').__get(k); },
__x: function (t) { return __h(__XFORM, this.__c, this.__l, t === undefined ? time : t); },
toComp: function (p, t) { return __m3(this.__x(t)[0], __v(p)); },
fromComp: function (p, t) { return __m3(this.__x(t)[1], __v(p)); },
toWorld: function (p, t) { return __m4(this.__x(t)[2], __v(p), false); },
fromWorld: function (p, t) { return __m4(this.__x(t)[3], __v(p), false); },
toCompVec: function (p, t) { return __m3v(this.__x(t)[0], __v(p)); },
fromCompVec: function (p, t) { return __m3v(this.__x(t)[1], __v(p)); },
toWorldVec: function (p, t) { return __m4(this.__x(t)[2], __v(p), true); },
fromWorldVec: function (p, t) { return __m4(this.__x(t)[3], __v(p), true); },
sourceRectAtTime: function (t, includeExtents) {
var r = __h(__RECT, this.__c, this.__l, t === undefined ? time : t, !!includeExtents);
return { top: r[0], left: r[1], width: r[2], height: r[3] };
},
// Average straight RGBA of the layer's pixels in `point ± radius` (layer space).
sampleImage: function (point, radius, postEffect, t) {
point = __v(point);
radius = radius === undefined ? [0.5, 0.5] : __v(radius);
if (!Array.isArray(radius)) radius = [radius, radius];
return __h(__SAMPLE, this.__c, this.__l, point[0], point[1], radius[0], radius.length > 1 ? radius[1] : radius[0],
postEffect === undefined ? true : !!__v(postEffect), t === undefined ? time : __v(t));
},
toString: function () { return '[object Layer]'; },
};
// Property groups (callable: `effect("Slider Control")("Slider")`) ---------------------------------
var __GroupProto = {
__isGroup: true,
__get: function (k) {
var r = __child(this.__c, this.__l, this.__p, k);
if (r === undefined) __err('Property ' + (typeof k === 'string' ? '"' + k + '"' : k) + ' not found in "' + this.__info[2] + '"');
return r;
},
param: function (k) { return this.__get(k); },
property: function (k) { return this.__get(k); },
content: function (k) {
var inner = __child(this.__c, this.__l, this.__p, 'contents');
return (inner !== undefined && inner.__isGroup ? inner : this).__get(k);
},
get name() { return this.__info[2]; },
get matchName() { return this.__info[3]; },
get numProperties() { return this.__info[4]; },
get propertyIndex() { return this.__info[5]; },
propertyGroup: function (n) { return __ancestor(this.__c, this.__l, this.__p, n); },
get active() { return true; },
toString: function () { return '[object PropertyGroup]'; },
};
var __groupHandler = {
get: function (t, k) {
if (typeof k !== 'string' || k in t) return t[k];
return __child(t.__c, t.__l, t.__p, k);
},
};
// `Proxy` is private to the object model (see "Stack safety" at the end).
var __mkGroup = (function (P) {
return function (c, l, info) {
var f = function (k) { return f.__get(k); };
delete f.name;
delete f.length;
Object.setPrototypeOf(f, __GroupProto);
f.__c = c; f.__l = l; f.__p = info[0]; f.__info = info;
return new P(f, __groupHandler);
};
})(Proxy);
// Properties ------------------------------------------------------------------------------------
function Prop(c, l, path) {
this.__c = c; this.__l = l; this.__p = path; this.__i = null;
this.__self = __S !== null && c === __S.c && l === __S.l && path === __S.path;
}
function __avg(vals) {
var s = vals[0];
for (var i = 1; i < vals.length; i++) s = add(s, vals[i]);
return div(s, vals.length);
}
function __loop(p, out, type, n, dur) {
type = type === undefined ? 'cycle' : String(type).toLowerCase();
var times = p.__info[4], N = times.length, t = time;
if (N === 0) return p.value;
var tStart, tEnd, h = 1e-3, k, ph;
if (out) {
tEnd = times[N - 1];
if (t <= tEnd) return p.value;
if (dur !== undefined && dur > 0) tStart = tEnd - dur;
else if (n > 0 && N - 1 - n >= 0) tStart = times[N - 1 - n];
else tStart = times[0];
} else {
tStart = times[0];
if (t >= tStart) return p.value;
if (dur !== undefined && dur > 0) tEnd = tStart + dur;
else if (n > 0 && n < N) tEnd = times[n];
else tEnd = times[N - 1];
}
var d = tEnd - tStart;
if (type === 'continue') {
if (out) {
var ve = p.valueAtTime(tEnd);
return add(ve, mul(div(sub(ve, p.valueAtTime(tEnd - h)), h), t - tEnd));
}
var vs = p.valueAtTime(tStart);
return add(vs, mul(div(sub(p.valueAtTime(tStart + h), vs), h), t - tStart));
}
if (!(d > 0)) return p.value;
ph = __mod(t - tStart, d);
if (type === 'pingpong') {
if (out) {
k = Math.floor((t - tStart) / d);
return p.valueAtTime(k % 2 === 1 ? tEnd - ph : tStart + ph);
}
k = Math.floor((tEnd - t) / d);
return p.valueAtTime(k % 2 === 1 ? tStart + __mod(tStart - t, d) : tEnd - __mod(tStart - t, d));
}
if (type === 'offset') {
k = Math.floor((t - tStart) / d);
return add(p.valueAtTime(tStart + ph), mul(sub(p.valueAtTime(tEnd), p.valueAtTime(tStart)), k));
}
return p.valueAtTime(tStart + ph);
}
Prop.prototype = {
__isProp: true,
get __info() { return this.__i || (this.__i = __h(__PINFO, this.__c, this.__l, this.__p)); },
get name() { return this.__info[0]; },
get __uid() { return this.__info[1]; },
get numKeys() { return this.__info[4].length; },
get propertyIndex() { return this.__info[7]; },
get matchName() { return this.__info[8]; },
propertyGroup: function (n) { return __ancestor(this.__c, this.__l, this.__p, n); },
__wrap: function (v) {
var kind = this.__info[2];
if (kind === 'layer') return v === null ? null : __layer(this.__c, v);
if (kind === 'path' && v !== null) return new __Path(v[0], v[1], v[2], v[3]);
return v;
},
get value() { return this.__self ? value : this.valueAtTime(time); },
valueAtTime: function (t) {
t = __v(t);
if (this.__self && t === time) return value;
return this.__wrap(__h(__VALUE, this.__c, this.__l, this.__p, t, this.__self));
},
velocityAtTime: function (t) {
var h = 1e-3;
return div(sub(this.valueAtTime(t + h), this.valueAtTime(t - h)), 2 * h);
},
get velocity() { return this.velocityAtTime(time); },
speedAtTime: function (t) { return length(this.velocityAtTime(t)); },
get speed() { return this.speedAtTime(time); },
key: function (n) {
var info = this.__info, N = info[4].length;
if (!(n >= 1 && n <= N)) __err('Key index ' + n + ' out of range (property has ' + N + ' keys)');
n = Math.floor(n);
return { time: info[4][n - 1], value: this.__wrap(info[5][n - 1]), index: n };
},
nearestKey: function (t) {
var times = this.__info[4];
if (times.length === 0) __err('Property has no keyframes');
var best = 0;
for (var i = 1; i < times.length; i++) if (Math.abs(times[i] - t) < Math.abs(times[best] - t)) best = i;
return this.key(best + 1);
},
__seedIndex: function () { return this.__self ? __S.idx : __layer(this.__c, this.__l).index; },
wiggle: function (freq, amp, octaves, ampMult, t) {
if (octaves === undefined) octaves = 1;
if (ampMult === undefined) ampMult = 0.5;
if (t === undefined) t = time;
var base = this.valueAtTime(t);
var dims = Array.isArray(base) ? base.length : 1;
var off = __wig(this.__seedIndex(), this.__uid, dims, __v(freq), __v(amp), octaves, ampMult, t);
if (!Array.isArray(base)) return base + off[0];
var r = base.slice();
for (var i = 0; i < r.length; i++) r[i] += off[i];
return r;
},
temporalWiggle: function (freq, amp, octaves, ampMult, t) {
if (octaves === undefined) octaves = 1;
if (ampMult === undefined) ampMult = 0.5;
if (t === undefined) t = time;
var off = __wig(this.__seedIndex(), this.__uid, 1, __v(freq), __v(amp), octaves, ampMult, t);
return this.valueAtTime(t + off[0]);
},
smooth: function (width, samples, t) {
if (width === undefined) width = 0.2;
if (samples === undefined) samples = 5;
if (t === undefined) t = time;
samples = Math.max(1, Math.round(samples));
if (samples === 1) return this.valueAtTime(t);
var vals = [];
for (var i = 0; i < samples; i++) vals.push(this.valueAtTime(t - width / 2 + width * i / (samples - 1)));
return __avg(vals);
},
loopOut: function (type, n) { return __loop(this, true, type, n || 0); },
loopIn: function (type, n) { return __loop(this, false, type, n || 0); },
loopOutDuration: function (type, d) { return __loop(this, true, type, 0, d === undefined ? 0 : d); },
loopInDuration: function (type, d) { return __loop(this, false, type, 0, d === undefined ? 0 : d); },
points: function (t) { return this.valueAtTime(t === undefined ? time : t).points(); },
inTangents: function (t) { return this.valueAtTime(t === undefined ? time : t).inTangents(); },
outTangents: function (t) { return this.valueAtTime(t === undefined ? time : t).outTangents(); },
isClosed: function () { return this.value.isClosed(); },
pointOnPath: function (pct, t) { return this.valueAtTime(t === undefined ? time : t).pointOnPath(pct); },
tangentOnPath: function (pct, t) { return this.valueAtTime(t === undefined ? time : t).tangentOnPath(pct); },
normalOnPath: function (pct, t) { return this.valueAtTime(t === undefined ? time : t).normalOnPath(pct); },
// Source Text style API.
__docAt: function (t) {
var d = __h(__DOC, this.__c, this.__l, this.__p, t, this.__self);
if (d === null) __err('Property "' + this.name + '" is not a Source Text property');
return JSON.parse(d);
},
get style() { return this.getStyleAt(0); },
getStyleAt: function (i, t) {
if (t === undefined) t = time;
return new __TextStyle(this.__docAt(__v(t)), i === undefined ? 0 : Math.floor(__v(i)), [], false);
},
createStyle: function () { return new __TextStyle(this.__docAt(time), 0, [], true); },
valueOf: function () { return this.value; },
toString: function () { return String(this.value); },
};
var __propHandler = {
get: function (t, k) {
if (typeof k !== 'string') return t[k];
// 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?.__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 and layers are callable: hand them over as they are, not bound like methods
// (#809).
if (typeof x === 'function' && (x.__isGroup || x.__isLayer)) return x;
return typeof x === 'function' ? x.bind(v) : x;
},
};
var __mkProp = (function (P) {
return function (c, l, path) {
return new P(new Prop(c, l, path), __propHandler);
};
})(Proxy);
// Text styles (the Source Text style API) ------------------------------------------------------
// A TextStyle is the source document (as JSON from the host), the character index it reads its
// attributes at, and the setter calls made on it ([key, value, start, count]). Setters return
// new objects; returning a style from a Source Text expression applies it.
function __TextStyle(doc, idx, ops, empty) { this.__d = doc; this.__i = idx; this.__ops = ops; this.__empty = empty; }
function __styleSetter(key, conv) {
return function (v, start, count) {
v = __v(v);
if (conv) v = conv(v, this);
var s = start === undefined ? -1 : Math.max(0, Math.floor(__v(start)));
var n = count === undefined ? -1 : Math.max(0, Math.floor(__v(count)));
return new __TextStyle(this.__d, this.__i, this.__ops.concat([[key, v, s, n]]), this.__empty);
};
}
function __colorIn(c) {
if (typeof c === 'string') return c;
return [c[0], c[1], c[2]];
}
__TextStyle.prototype = {
__isTextStyle: true,
__base: function () {
var runs = this.__d.runs, pos = 0, i = this.__i;
for (var k = 0; k < runs.length; k++) {
if (i < pos + runs[k].len || k === runs.length - 1) return runs[k].style;
pos += runs[k].len;
}
return {};
},
__para: function () {
var t = this.text, p = 0;
for (var k = 0; k < Math.min(this.__i, t.length); k++) if (t[k] === '\n' || t[k] === '\r') p++;
var paras = this.__d.paras;
return paras[Math.min(p, paras.length - 1)];
},
__get: function (key, para) {
for (var k = this.__ops.length - 1; k >= 0; k--) {
var o = this.__ops[k];
if (o[0] !== key) continue;
if (o[2] < 0 || (this.__i >= o[2] && (o[3] < 0 || this.__i < o[2] + o[3]))) return o[1];
}
return (para ? this.__para() : this.__base())[key];
},
get text() {
var t = this.__d.text;
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]));
}
return t;
},
get font() { return this.__get('font'); },
get fontSize() { return this.__get('size'); },
get isFauxBold() { return this.__get('fauxBold'); },
get isFauxItalic() { return this.__get('fauxItalic'); },
get isAllCaps() { return this.__get('allCaps'); },
get isSmallCaps() { return this.__get('smallCaps'); },
get tracking() { return this.__get('tracking'); },
get autoLeading() { return this.__get('leading') === 'auto'; },
get leading() { var l = this.__get('leading'); return l === 'auto' ? this.fontSize * 1.2 : l; },
get baselineShift() { return this.__get('baselineShift'); },
get applyFill() { return this.__get('applyFill'); },
get fillColor() { var c = this.__get('fill'); return [c[0], c[1], c[2]]; },
get applyStroke() { return this.__get('applyStroke'); },
get strokeColor() { var c = this.__get('stroke'); return [c[0], c[1], c[2]]; },
get strokeWidth() { return this.__get('strokeWidth'); },
get horizontalScaling() { return this.__get('hScale'); },
get verticalScaling() { return this.__get('vScale'); },
get tsume() { return this.__get('tsume'); },
get baselineOption() { return this.__get('baseline'); },
get isSuperscript() { return this.__get('baseline') === 'superscript'; },
get isSubscript() { return this.__get('baseline') === 'subscript'; },
get kerningType() { var k = this.__get('kerning'); return typeof k === 'number' ? 'manual' : k; },
get kerning() { var k = this.__get('kerning'); return typeof k === 'number' ? k : 0; },
get isLigature() { return this.__get('ligatures'); },
get justification() { return this.__get('justify', true); },
get firstLineIndent() { return this.__get('indentFirst', true); },
get startIndent() { return this.__get('indentLeft', true); },
get endIndent() { return this.__get('indentRight', true); },
get spaceBefore() { return this.__get('spaceBefore', true); },
get spaceAfter() { return this.__get('spaceAfter', true); },
get direction() { return this.__get('direction', true); },
get isEveryLineComposer() { return this.__get('composer', true) === 'everyLine'; },
get isHangingRoman() { return this.__get('hangingPunctuation', true); },
setText: __styleSetter('text', function (v) { return String(v); }),
replaceText: __styleSetter('text', function (v) { return String(v); }),
setFont: __styleSetter('font', function (v) { return String(v); }),
setFontSize: __styleSetter('size'),
setFauxBold: __styleSetter('fauxBold', Boolean),
setFauxItalic: __styleSetter('fauxItalic', Boolean),
setAllCaps: __styleSetter('allCaps', Boolean),
setSmallCaps: __styleSetter('smallCaps', Boolean),
setTracking: __styleSetter('tracking'),
setLeading: __styleSetter('leading'),
setAutoLeading: __styleSetter('leading', function (v, s) { return v ? 'auto' : s.leading; }),
setBaselineShift: __styleSetter('baselineShift'),
setApplyFill: __styleSetter('applyFill', Boolean),
setFillColor: __styleSetter('fill', __colorIn),
setApplyStroke: __styleSetter('applyStroke', Boolean),
setStrokeColor: __styleSetter('stroke', __colorIn),
setStrokeWidth: __styleSetter('strokeWidth'),
setHorizontalScaling: __styleSetter('hScale'),
setVerticalScaling: __styleSetter('vScale'),
setTsume: __styleSetter('tsume'),
setBaselineOption: __styleSetter('baseline', function (v) { return String(v).toLowerCase().replace('_baseline', ''); }),
// Canonical baseline operations also make the returned TextStyle's getters reflect
// the pending edit, rather than waiting for the host to apply a superscript flag.
setSuperscript: __styleSetter('baseline', function (v) { return v ? 'superscript' : 'normal'; }),
setSubscript: __styleSetter('baseline', function (v) { return v ? 'subscript' : 'normal'; }),
setKerningType: __styleSetter('kerning', function (v) { v = String(v).toLowerCase(); return v === 'manual' ? 0 : v; }),
setKerning: __styleSetter('kerning', Number),
setLigature: __styleSetter('ligatures', Boolean),
setJustification: __styleSetter('justify', function (v) { return String(v); }),
setFirstLineIndent: __styleSetter('indentFirst'),
setStartIndent: __styleSetter('indentLeft'),
setEndIndent: __styleSetter('indentRight'),
setSpaceBefore: __styleSetter('spaceBefore'),
setSpaceAfter: __styleSetter('spaceAfter'),
setDirection: __styleSetter('direction', function (v) { v = String(v).toLowerCase(); return v.indexOf('right') === 0 || v === 'rtl' ? 'rtl' : 'ltr'; }),
setEveryLineComposer: __styleSetter('composer', function (v) { return v ? 'everyLine' : 'singleLine'; }),
setHangingRoman: __styleSetter('hangingPunctuation', Boolean),
__out: function () {
return { __style: true, __json: JSON.stringify({ doc: this.__empty ? null : this.__d.doc, ops: this.__ops }) };
},
toString: function () { return this.text; },
valueOf: function () { return this.text; },
};
Object.defineProperty(__TextStyle.prototype, 'length', { get: function () { return this.text.length; } });
// Markers ---------------------------------------------------------------------------------------
function Markers(c, l) { this.__c = c; this.__l = l; this.__m = null; }
Markers.prototype = {
get __list() { return this.__m || (this.__m = __h(__MARKERS, this.__c, this.__l)); },
get numKeys() { return this.__list.length; },
key: function (n) {
var list = this.__list, i;
if (typeof n === 'string') {
for (i = 0; i < list.length; i++) if (list[i][2] === n) return this.key(i + 1);
__err('Marker "' + n + '" not found');
}
if (!(n >= 1 && n <= list.length)) __err('Marker index ' + n + ' out of range');
var m = list[n - 1];
var params = {};
for (var k = 0; k < m[8].length; k++) params[m[8][k][0]] = m[8][k][1];
return {
time: m[0], duration: m[1], comment: m[2], chapter: m[3], url: m[4], index: n,
frameTarget: m[5], eventCuePoint: m[6], cuePointName: m[7], parameters: params, protectedRegion: m[9],
};
},
nearestKey: function (t) {
var list = this.__list;
if (list.length === 0) __err('No markers');
var best = 0;
for (var i = 1; i < list.length; i++) if (Math.abs(list[i][0] - t) < Math.abs(list[best][0] - t)) best = i;
return this.key(best + 1);
},
};
// ---------------------------------------------------------------------------------------------
// Unqualified names: thisLayer / thisProperty attributes and the `this*` objects. They're
// accessors on the global object; assigning one (`var width = 10`) shadows it for the current
// evaluation only. They can't be deleted or redefined, so an expression can't take one away from
// the evaluations after it.
function __global(name, get) {
Object.defineProperty(globalThis, name, {
get: function () { return name in __S.vars ? __S.vars[name] : get(); },
set: function (v) { __S.vars[name] = v; },
});
}
__global('thisComp', function () { return __S.comp || (__S.comp = new Comp(__S.c)); });
__global('thisLayer', function () { return __S.layer || (__S.layer = __layer(__S.c, __S.l)); });
__global('thisProperty', function () { return __S.prop || (__S.prop = __mkProp(__S.c, __S.l, __S.path)); });
['name', 'index', 'inPoint', 'outPoint', 'startTime', 'width', 'height', 'hasParent', 'parent', 'hasVideo', 'enabled', 'active',
'transform', 'position', 'scale', 'rotation', 'opacity', 'anchorPoint', 'text', 'marker'].forEach(function (k) {
__global(k, function () { return thisLayer[k]; });
});
['numKeys', 'velocity', 'speed'].forEach(function (k) {
__global(k, function () { return thisProperty[k]; });
});
function effect(k) { return thisLayer.effect(k); }
function mask(k) { return thisLayer.mask(k); }
function content(k) { return thisLayer.content(k); }
function toComp(p, t) { return thisLayer.toComp(p, t); }
function fromComp(p, t) { return thisLayer.fromComp(p, t); }
function toWorld(p, t) { return thisLayer.toWorld(p, t); }
function fromWorld(p, t) { return thisLayer.fromWorld(p, t); }
function toCompVec(p, t) { return thisLayer.toCompVec(p, t); }
function fromCompVec(p, t) { return thisLayer.fromCompVec(p, t); }
function toWorldVec(p, t) { return thisLayer.toWorldVec(p, t); }
function fromWorldVec(p, t) { return thisLayer.fromWorldVec(p, t); }
function sourceRectAtTime(t, e) { return thisLayer.sourceRectAtTime(t, e); }
function wiggle(f, a, o, m, t) { return thisProperty.wiggle(f, a, o, m, t); }
function temporalWiggle(f, a, o, m, t) { return thisProperty.temporalWiggle(f, a, o, m, t); }
function smooth(w, s, t) { return thisProperty.smooth(w, s, t); }
function loopOut(type, n) { return thisProperty.loopOut(type, n); }
function loopIn(type, n) { return thisProperty.loopIn(type, n); }
function loopOutDuration(type, d) { return thisProperty.loopOutDuration(type, d); }
function loopInDuration(type, d) { return thisProperty.loopInDuration(type, d); }
function valueAtTime(t) { return thisProperty.valueAtTime(t); }
function velocityAtTime(t) { return thisProperty.velocityAtTime(t); }
function speedAtTime(t) { return thisProperty.speedAtTime(t); }
function key(n) { return thisProperty.key(n); }
function nearestKey(t) { return thisProperty.nearestKey(t); }
// ---------------------------------------------------------------------------------------------
// Stack safety (#2124, #2125). A stack overflow aborts the whole app, so nothing a script does
// may nest native code without bound. The expression text is checked for nesting before it is
// compiled; source handed to `eval` and the function constructors at run time is checked by
// `__nest`. The natives that walk nested values recursively (`join`, which `toString` and string
// conversion use, `toLocaleString`, `JSON.stringify`, `flat`) call `__stack` once per level,
// which throws once the run has used its share of the stack. `Proxy` (chains of proxies nest
// without any script frame) and `Reflect` are not available to expressions.
(function () {
var nest = __nest, stack = __stack, apply = Reflect.apply, construct = Reflect.construct;
var define = Object.defineProperty, describe = Object.getOwnPropertyDescriptor, proto = Object.getPrototypeOf;
// Replace `obj[name]` keeping its attributes.
function replace(obj, name, f) {
var d = describe(obj, name);
define(obj, name, { value: f, writable: d.writable, enumerable: d.enumerable, configurable: true });
}
function guard(obj, name) {
var f = obj[name];
replace(obj, name, function () { stack(); return apply(f, this, arguments); });
}
// Indirect on purpose: a wrapper can't make a direct eval, so `eval` sees globals and `var`s
// but not `let`/`const` locals of the expression. Keeping direct eval would mean leaving it
// unchecked; expressions practically never need their locals inside eval.
var evalJs = eval;
eval = function (x) {
if (typeof x === 'string') {
stack();
nest(x);
}
return evalJs(x);
};
// `Function` and the generator / async variants (reachable as `(function* () {}).constructor`).
function dynamic(F) {
var w = function () {
var args = [];
for (var i = 0; i < arguments.length; i++) args.push(String(arguments[i]));
stack();
nest(args.slice(0, -1).join(','));
nest(args.length ? args[args.length - 1] : '');
return construct(F, args);
};
define(w, 'name', { value: F.name, configurable: true });
w.prototype = F.prototype;
replace(F.prototype, 'constructor', w);
return w;
}
Function = dynamic(Function);
dynamic(proto(function* () {}).constructor);
dynamic(proto(async function () {}).constructor);
dynamic(proto(async function* () {}).constructor);
guard(Array.prototype, 'join');
guard(Array.prototype, 'toLocaleString');
var flat = Array.prototype.flat;
replace(Array.prototype, 'flat', function (depth) {
stack();
var d = depth === undefined ? 1 : Number(depth);
return apply(flat, this, [d > 64 ? 64 : d]);
});
// A replacer function sees every value before it is serialized: the per-level check. An array
// replacer (a list of keys) becomes one that copies each object's listed keys, in list order.
// Each source object gets one copy per call, so a cycle in the source is a cycle of copies and
// the native check reports it (TypeError); an object shared without a cycle is still fine.
var stringify = JSON.stringify, MapJs = Map;
replace(JSON, 'stringify', function (value, replacer, space) {
var fn = typeof replacer === 'function' ? replacer : null;
var keys = null, copies = new MapJs();
if (Array.isArray(replacer)) {
keys = [];
for (var i = 0; i < replacer.length; i++) {
var k = replacer[i];
if (typeof k === 'number' || k instanceof Number || k instanceof String) k = String(k);
if (typeof k === 'string' && keys.indexOf(k) < 0) keys.push(k);
}
}
return apply(stringify, JSON, [value, function (key, v) {
stack();
if (fn !== null) v = apply(fn, this, [key, v]);
if (keys !== null && v !== null && typeof v === 'object' && !Array.isArray(v) &&
!(v instanceof Number || v instanceof String || v instanceof Boolean)) {
var o = copies.get(v);
if (o === undefined) {
o = {};
for (var j = 0; j < keys.length; j++) if (keys[j] in v) o[keys[j]] = v[keys[j]];
copies.set(v, o);
}
v = o;
}
return v;
}, space]);
});
delete globalThis.Proxy;
delete globalThis.Reflect;
})();
// ---------------------------------------------------------------------------------------------
// The runtime runs this file once and keeps it for the thread's later evaluations, so an
// expression can't change the object model or the internals: the prototypes are frozen, and the
// `__` globals, the constructors and `thisProject` can't be reassigned. The `__` state below is
// set again by `__begin` in each evaluation.
[Comp, Footage, Layer, Prop, Markers, __Path, __TextStyle].forEach(function (f) {
Object.freeze(f.prototype);
Object.freeze(f);
});
[__GroupProto, __groupHandler, __propHandler, thisProject].forEach(Object.freeze);
(function () {
var perEvaluation = { __S: true, __rc: true, __seed: true, __timeless: true, __projectInfo: true };
var model = { Comp: true, Footage: true, Layer: true, Prop: true, Markers: true, thisProject: true };
Object.getOwnPropertyNames(globalThis).forEach(function (k) {
if (!(k.slice(0, 2) === '__' && !perEvaluation[k]) && !model[k]) return;
if ('value' in Object.getOwnPropertyDescriptor(globalThis, k)) Object.defineProperty(globalThis, k, { writable: false, configurable: false });
});
})();