superfly-timeline
Advanced tools
Comparing version 7.3.1 to 8.0.0
@@ -5,2 +5,29 @@ # Changelog | ||
## [8.0.0](https://github.com/SuperFlyTV/supertimeline/compare/7.3.1...8.0.0) (2020-06-27) | ||
### ⚠ BREAKING CHANGES | ||
* drop node 8 support | ||
### Features | ||
* drop node 8 support ([98f42ca](https://github.com/SuperFlyTV/supertimeline/commit/98f42caf62f0d93364e1cf921fd359437a27fea4)) | ||
* **ci:** prerelease flow & optional audit skip [skip ci] ([a7a4c20](https://github.com/SuperFlyTV/supertimeline/commit/a7a4c20d01971d934253cbb348744ed5d042c2d1)) | ||
* add check in resolveAllStates, optimizing when no changes are detected at all. ([6d02009](https://github.com/SuperFlyTV/supertimeline/commit/6d02009995de2ebaf782774ade5779e321ca15b2)) | ||
* be able to provide enable.instances directly in the timeline. ([0bd3149](https://github.com/SuperFlyTV/supertimeline/commit/0bd31490734d001e5ea4ab1c9e751717c85e1a37)) | ||
* partial resolve of timeline ([eab68aa](https://github.com/SuperFlyTV/supertimeline/commit/eab68aac2d7b70e85ec5bc6a733676358f348ec1)) | ||
### Bug Fixes | ||
* don't hash content, it takes too much time ([ac35795](https://github.com/SuperFlyTV/supertimeline/commit/ac35795ae6d7e74ef2413b0b2391f0206d23343c)) | ||
* optimize cleanInstances for most common situations ([8b9e0b5](https://github.com/SuperFlyTV/supertimeline/commit/8b9e0b51976f47d620af1332a061e74c18549928)) | ||
* performance: cache result of interpretExpression ([ea572cb](https://github.com/SuperFlyTV/supertimeline/commit/ea572cbd791366b44134144292418ed5f057831f)) | ||
* performance: move applyParendInstances ([bb1e952](https://github.com/SuperFlyTV/supertimeline/commit/bb1e952019b085264b501e60c6a950eeb84d041b)) | ||
* performance: refactor _.uniq + _.reduce + _.compact ([d6d7eb5](https://github.com/SuperFlyTV/supertimeline/commit/d6d7eb5cf780707a3a6ebaa545e4ea20d672be75)) | ||
* performance: replace _.each for for-loops where it's most used ([5d1df6a](https://github.com/SuperFlyTV/supertimeline/commit/5d1df6a99042c6233bb6fe76715c73c565933a80)) | ||
* performance: use typeof instead of _.isObject ([14bf545](https://github.com/SuperFlyTV/supertimeline/commit/14bf5453423cbb93e80e0db3bf62cb363c9dd1b4)) | ||
* reworked instances to instead use arrays directly on the .enable property ([ccc8452](https://github.com/SuperFlyTV/supertimeline/commit/ccc845278d3f2e5c279fd3cffe2ebf40f87b651a)) | ||
### [7.3.1](https://github.com/SuperFlyTV/supertimeline/compare/7.3.0...7.3.1) (2019-12-02) | ||
@@ -7,0 +34,0 @@ |
@@ -21,6 +21,8 @@ import { EventType } from './enums'; | ||
resolveInstanceCollisions?: boolean; | ||
/** A cache thet is to persist data between resolves. If provided, will increase performance of resolving when only making small changes to the timeline. */ | ||
cache?: ResolverCache; | ||
} | ||
export interface TimelineObject { | ||
id: ObjectId; | ||
enable: TimelineEnable; | ||
enable: TimelineEnable | TimelineEnable[]; | ||
layer: string | number; | ||
@@ -64,3 +66,3 @@ /** Group children */ | ||
id: string; | ||
enable: TimelineEnable; | ||
enable: TimelineEnable | TimelineEnable[]; | ||
duration?: number | string; | ||
@@ -99,2 +101,4 @@ classes?: Array<string>; | ||
resolvedKeyframeCount: number; | ||
/** How many objects that was actually resolved (is affected when using cache) */ | ||
resolvingCount: number; | ||
}; | ||
@@ -121,2 +125,4 @@ } | ||
isSelfReferencing?: boolean; | ||
/** Ids of all other objects that directly affects this object (ie through direct reference, classes, etc) */ | ||
directReferences: string[]; | ||
}; | ||
@@ -129,3 +135,3 @@ } | ||
isFirst?: boolean; | ||
/** The start time of the instance */ | ||
/** The start time of the instance */ | ||
start: Time; | ||
@@ -211,1 +217,10 @@ /** The end time of the instance (null = infinite) */ | ||
} | ||
export declare type ResolverCache = Partial<ResolverCacheInternal>; | ||
export interface ResolverCacheInternal { | ||
objHashes: { | ||
[id: string]: string; | ||
}; | ||
resolvedTimeline: ResolvedTimeline; | ||
hasOldData?: boolean; | ||
resolvedStates?: ResolvedStates; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tslib_1 = require("tslib"); | ||
const tslib_1 = require("tslib"); | ||
tslib_1.__exportStar(require("./api/enums"), exports); | ||
@@ -5,0 +5,0 @@ var resolver_1 = require("./resolver/resolver"); |
@@ -68,2 +68,5 @@ import { InstanceEvent, TimelineObjectInstance, ResolveOptions, ValueWithReference, Cap } from './api/api'; | ||
export declare function setInstanceStartTime(instance: TimelineObjectInstance, startTime: number): void; | ||
export declare function applyParentInstances(parentInstances: TimelineObjectInstance[] | null, value: TimelineObjectInstance[] | null | ValueWithReference): TimelineObjectInstance[] | null | ValueWithReference; | ||
/** Cache the result of function for a limited time */ | ||
export declare function cacheResult<T>(name: string, fcn: () => T, limitTime?: number): any; | ||
export {}; |
240
dist/lib.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _ = require("underscore"); | ||
const _ = require("underscore"); | ||
/** | ||
@@ -31,3 +31,3 @@ * Somewhat like _.extend, but with strong types & mandated additional properties | ||
function sortEvents(events) { | ||
return events.sort(function (a, b) { | ||
return events.sort((a, b) => { | ||
if (a.time > b.time) | ||
@@ -37,4 +37,4 @@ return 1; | ||
return -1; | ||
var aId = a.data && (a.data.id || (a.data.instance && a.data.instance.id)); | ||
var bId = b.data && (b.data.id || (b.data.instance && b.data.instance.id)); | ||
const aId = a.data && (a.data.id || (a.data.instance && a.data.instance.id)); | ||
const bId = b.data && (b.data.id || (b.data.instance && b.data.instance.id)); | ||
if (aId && bId && aId === bId) { | ||
@@ -59,9 +59,17 @@ // If the event refer to the same ID, let the ending event be first: | ||
*/ | ||
function cleanInstances(instances, allowMerge, allowZeroGaps) { | ||
// if (!allowMerge) throw new Error(`TODO: cleanInstances: allowMerge is temorarily removed`) | ||
if (allowZeroGaps === void 0) { allowZeroGaps = false; } | ||
var events = []; | ||
// let i: number = 1 | ||
_.each(instances, function (instance) { | ||
// const id = 'i' + (i++) | ||
function cleanInstances(instances, allowMerge, allowZeroGaps = false) { | ||
// First, optimize for certain common situations: | ||
if (instances.length === 0) | ||
return []; | ||
if (instances.length <= 1) { | ||
const instance = instances[0]; | ||
if (!instance.end) | ||
instance.end = null; | ||
instance.originalEnd = instance.end; | ||
instance.originalStart = instance.start; | ||
return [instance]; | ||
} | ||
const events = []; | ||
for (let i = 0; i < instances.length; i++) { | ||
const instance = instances[i]; | ||
events.push({ | ||
@@ -81,16 +89,16 @@ time: instance.start, | ||
} | ||
}); | ||
} | ||
return convertEventsToInstances(events, allowMerge, allowZeroGaps); | ||
} | ||
exports.cleanInstances = cleanInstances; | ||
function convertEventsToInstances(events, allowMerge, allowZeroGaps) { | ||
if (allowZeroGaps === void 0) { allowZeroGaps = false; } | ||
function convertEventsToInstances(events, allowMerge, allowZeroGaps = false) { | ||
sortEvents(events); | ||
var activeInstances = {}; | ||
var activeInstanceId = null; | ||
var previousActive = false; | ||
var returnInstances = []; | ||
_.each(events, function (event) { | ||
var eventId = event.data.id || event.data.instance.id; | ||
var lastInstance = _.last(returnInstances); | ||
const activeInstances = {}; | ||
let activeInstanceId = null; | ||
let previousActive = false; | ||
const returnInstances = []; | ||
for (let i = 0; i < events.length; i++) { | ||
const event = events[i]; | ||
const eventId = event.data.id || event.data.instance.id; | ||
const lastInstance = returnInstances[returnInstances.length - 1]; | ||
if (event.value) { | ||
@@ -102,3 +110,3 @@ activeInstances[eventId] = event; | ||
} | ||
if (_.keys(activeInstances).length) { | ||
if (Object.keys(activeInstances).length) { | ||
// There is an active instance | ||
@@ -127,3 +135,3 @@ previousActive = true; | ||
// The active instance stopped playing, but another is still playing | ||
var latestInstance = _.reduce(activeInstances, function (memo, event, id) { | ||
const latestInstance = _.reduce(activeInstances, (memo, event, id) => { | ||
if (memo === null || | ||
@@ -189,3 +197,3 @@ memo.event.time < event.time) { | ||
} | ||
}); | ||
} | ||
return returnInstances; | ||
@@ -197,5 +205,5 @@ } | ||
instances = cleanInstances(instances, true, true); | ||
var invertedInstances_1 = []; | ||
const invertedInstances = []; | ||
if (instances[0].start !== 0) { | ||
invertedInstances_1.push({ | ||
invertedInstances.push({ | ||
id: getId(), | ||
@@ -208,4 +216,4 @@ isFirst: true, | ||
} | ||
_.each(instances, function (instance) { | ||
var last = _.last(invertedInstances_1); | ||
_.each(instances, (instance) => { | ||
const last = _.last(invertedInstances); | ||
if (last) { | ||
@@ -215,3 +223,3 @@ last.end = instance.start; | ||
if (instance.end !== null) { | ||
invertedInstances_1.push({ | ||
invertedInstances.push({ | ||
id: getId(), | ||
@@ -225,3 +233,3 @@ start: instance.end, | ||
}); | ||
return invertedInstances_1; | ||
return invertedInstances; | ||
} | ||
@@ -253,12 +261,12 @@ else { | ||
} | ||
var result = []; | ||
var minLength = Math.min(_.isArray(array0) ? array0.length : Infinity, _.isArray(array1) ? array1.length : Infinity); | ||
for (var i_1 = 0; i_1 < minLength; i_1++) { | ||
var a = (_.isArray(array0) ? | ||
array0[i_1] : | ||
const result = []; | ||
const minLength = Math.min(_.isArray(array0) ? array0.length : Infinity, _.isArray(array1) ? array1.length : Infinity); | ||
for (let i = 0; i < minLength; i++) { | ||
const a = (_.isArray(array0) ? | ||
array0[i] : | ||
{ id: '', start: array0.value, end: array0.value, references: array0.references }); | ||
var b = (_.isArray(array1) ? | ||
array1[i_1] : | ||
const b = (_.isArray(array1) ? | ||
array1[i] : | ||
{ id: '', start: array1.value, end: array1.value, references: array1.references }); | ||
var start = (a.isFirst ? | ||
const start = (a.isFirst ? | ||
{ value: a.start, references: a.references } : | ||
@@ -268,3 +276,3 @@ b.isFirst ? | ||
operate({ value: a.start, references: joinReferences(a.id, a.references) }, { value: b.start, references: joinReferences(b.id, b.references) })); | ||
var end = (a.isFirst ? | ||
const end = (a.isFirst ? | ||
(a.end !== null ? { value: a.end, references: a.references } : null) : | ||
@@ -329,3 +337,3 @@ b.isFirst ? | ||
return instances; | ||
var repeatTime = repeatTime0.value; | ||
const repeatTime = repeatTime0.value; | ||
if (isReference(instances)) { | ||
@@ -339,20 +347,20 @@ instances = [{ | ||
} | ||
var repeatedInstances = []; | ||
_.each(instances, function (instance) { | ||
var startTime = Math.max(options.time - (options.time - instance.start) % repeatTime, instance.start); | ||
var endTime = (instance.end === null ? | ||
const repeatedInstances = []; | ||
_.each(instances, (instance) => { | ||
let startTime = Math.max(options.time - (options.time - instance.start) % repeatTime, instance.start); | ||
let endTime = (instance.end === null ? | ||
null : | ||
instance.end + (startTime - instance.start)); | ||
var cap = (instance.caps ? | ||
_.find(instance.caps, function (cap) { return instance.references.indexOf(cap.id) !== -1; }) | ||
const cap = (instance.caps ? | ||
_.find(instance.caps, (cap) => instance.references.indexOf(cap.id) !== -1) | ||
: null) || null; | ||
var limit = options.limitCount || 2; | ||
for (var i_2 = 0; i_2 < limit; i_2++) { | ||
const limit = options.limitCount || 2; | ||
for (let i = 0; i < limit; i++) { | ||
if (options.limitTime && | ||
startTime >= options.limitTime) | ||
break; | ||
var cappedStartTime = (cap ? | ||
const cappedStartTime = (cap ? | ||
Math.max(cap.start, startTime) : | ||
startTime); | ||
var cappedEndTime = (cap && cap.end !== null && endTime !== null ? | ||
const cappedEndTime = (cap && cap.end !== null && endTime !== null ? | ||
Math.min(cap.end, endTime) : | ||
@@ -385,6 +393,8 @@ endTime); | ||
return instances; | ||
var returnInstances = []; | ||
_.each(instances, function (instance) { | ||
var parent = null; | ||
_.each(parentInstances, function (p) { | ||
const returnInstances = []; | ||
for (let i = 0; i < instances.length; i++) { | ||
const instance = instances[i]; | ||
let parent = null; | ||
for (let j = 0; j < parentInstances.length; j++) { | ||
const p = parentInstances[j]; | ||
if ((instance.start >= p.start && | ||
@@ -398,5 +408,6 @@ instance.start < (p.end || Infinity)) || (instance.start < p.start && | ||
} | ||
}); | ||
} | ||
if (!parent) { | ||
_.each(parentInstances, function (p) { | ||
for (let j = 0; j < parentInstances.length; j++) { | ||
const p = parentInstances[j]; | ||
if ((instance.end || Infinity) > p.start && | ||
@@ -409,7 +420,7 @@ (instance.end || Infinity) <= (p.end || Infinity)) { | ||
} | ||
}); | ||
} | ||
} | ||
if (parent) { | ||
var parent2 = parent; // cast type | ||
var i2 = _.clone(instance); | ||
const parent2 = parent; // cast type | ||
const i2 = _.clone(instance); | ||
if (parent2.end !== null && | ||
@@ -424,3 +435,3 @@ (i2.end || Infinity) > parent2.end) { | ||
} | ||
}); | ||
} | ||
return returnInstances; | ||
@@ -430,3 +441,3 @@ } | ||
function isReference(ref) { | ||
return (_.isObject(ref) && | ||
return (typeof ref === 'object' && | ||
!_.isArray(ref) && | ||
@@ -438,13 +449,26 @@ ref.value !== undefined && | ||
exports.isReference = isReference; | ||
function joinReferences() { | ||
var references = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
references[_i] = arguments[_i]; | ||
function joinReferences(...references) { | ||
const refMap = {}; | ||
const refs = []; | ||
for (let i = 0; i < references.length; i++) { | ||
const reference = references[i]; | ||
if (reference) { | ||
if (typeof reference === 'string') { | ||
if (!refMap[reference]) | ||
refs.push(reference); | ||
refMap[reference] = true; | ||
} | ||
else { | ||
for (let j = 0; j < reference.length; j++) { | ||
const ref = reference[j]; | ||
if (ref) { | ||
if (!refMap[ref]) | ||
refs.push(ref); | ||
refMap[ref] = true; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return _.compact(_.uniq(_.reduce(references, function (memo, ref) { | ||
if (_.isString(ref)) | ||
return memo.concat([ref]); | ||
else | ||
return memo.concat(ref); | ||
}, []))).sort(function (a, b) { | ||
return refs.sort((a, b) => { | ||
if (a > b) | ||
@@ -458,9 +482,5 @@ return 1; | ||
exports.joinReferences = joinReferences; | ||
function addCapsToResuming(instance) { | ||
var caps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
caps[_i - 1] = arguments[_i]; | ||
} | ||
var capsToAdd = []; | ||
_.each(joinCaps.apply(void 0, caps), function (cap) { | ||
function addCapsToResuming(instance, ...caps) { | ||
const capsToAdd = []; | ||
_.each(joinCaps(...caps), (cap) => { | ||
if (cap.end && | ||
@@ -479,19 +499,17 @@ instance.end && | ||
exports.addCapsToResuming = addCapsToResuming; | ||
function joinCaps() { | ||
var caps = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
caps[_i] = arguments[_i]; | ||
function joinCaps(...caps) { | ||
const capMap = {}; | ||
for (let i = 0; i < caps.length; i++) { | ||
const caps2 = caps[i]; | ||
if (caps2) { | ||
for (let j = 0; j < caps2.length; j++) { | ||
const cap2 = caps2[j]; | ||
capMap[cap2.id] = cap2; | ||
} | ||
} | ||
} | ||
return (_.uniq(_.compact(_.reduce(caps, function (memo, cap) { | ||
if (cap !== undefined) { | ||
return (memo || []).concat(cap); | ||
} | ||
else | ||
return memo; | ||
}, [])), false, function (cap) { | ||
return cap.id; | ||
})); | ||
return Object.values(capMap); | ||
} | ||
exports.joinCaps = joinCaps; | ||
var i = 0; | ||
let i = 0; | ||
/** | ||
@@ -522,2 +540,40 @@ * Returns a unique id | ||
exports.setInstanceStartTime = setInstanceStartTime; | ||
function applyParentInstances(parentInstances, value) { | ||
const operate = (a, b) => { | ||
if (a === null || b === null) | ||
return null; | ||
return { | ||
value: a.value + b.value, | ||
references: joinReferences(a.references, b.references) | ||
}; | ||
}; | ||
return operateOnArrays(parentInstances, value, operate); | ||
} | ||
exports.applyParentInstances = applyParentInstances; | ||
const cacheResultCache = {}; | ||
/** Cache the result of function for a limited time */ | ||
function cacheResult(name, fcn, limitTime = 1000) { | ||
if (Math.random() < 0.01) { | ||
setTimeout(cleanCacheResult, 100); | ||
} | ||
const cache = cacheResultCache[name]; | ||
if (!cache || cache.ttl < Date.now()) { | ||
const value = fcn(); | ||
cacheResultCache[name] = { | ||
ttl: Date.now() + limitTime, | ||
value: value | ||
}; | ||
return value; | ||
} | ||
else { | ||
return cache.value; | ||
} | ||
} | ||
exports.cacheResult = cacheResult; | ||
function cleanCacheResult() { | ||
_.each(cacheResultCache, (cache, name) => { | ||
if (cache.ttl < Date.now()) | ||
delete cacheResultCache[name]; | ||
}); | ||
} | ||
//# sourceMappingURL=lib.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _ = require("underscore"); | ||
const _ = require("underscore"); | ||
function addObjectToResolvedTimeline(resolvedTimeline, obj) { | ||
resolvedTimeline.objects[obj.id] = obj; | ||
if (obj.classes) { | ||
_.each(obj.classes, function (className) { | ||
_.each(obj.classes, (className) => { | ||
if (className) { | ||
@@ -9,0 +9,0 @@ if (!resolvedTimeline.classes[className]) |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tslib_1 = require("tslib"); | ||
var _ = require("underscore"); | ||
var lib_1 = require("../lib"); | ||
const _ = require("underscore"); | ||
const lib_1 = require("../lib"); | ||
exports.OPERATORS = ['&', '|', '+', '-', '*', '/', '%', '!']; | ||
const REGEXP_OPERATORS = _.map(exports.OPERATORS, o => '\\' + o).join(''); | ||
function interpretExpression(expr) { | ||
@@ -12,23 +12,24 @@ if (lib_1.isNumeric(expr)) { | ||
else if (_.isString(expr)) { | ||
var operatorList = exports.OPERATORS; | ||
var regexpOperators = _.map(operatorList, function (o) { return '\\' + o; }).join(''); | ||
expr = expr.replace(new RegExp('([' + regexpOperators + '\\(\\)])', 'g'), ' $1 '); // Make sure there's a space between every operator & operand | ||
var words = _.compact(expr.split(' ')); | ||
if (words.length === 0) | ||
return null; // empty expression | ||
// Fix special case: a + - b | ||
for (var i = words.length - 2; i >= 1; i--) { | ||
if ((words[i] === '-' || words[i] === '+') && wordIsOperator(operatorList, words[i - 1])) { | ||
words[i] = words[i] + words[i + 1]; | ||
words.splice(i + 1, 1); | ||
const expr0 = expr; | ||
return lib_1.cacheResult(expr0, () => { | ||
const expr = expr0.replace(new RegExp('([' + REGEXP_OPERATORS + '\\(\\)])', 'g'), ' $1 '); // Make sure there's a space between every operator & operand | ||
const words = _.compact(expr.split(' ')); | ||
if (words.length === 0) | ||
return null; // empty expression | ||
// Fix special case: a + - b | ||
for (let i = words.length - 2; i >= 1; i--) { | ||
if ((words[i] === '-' || words[i] === '+') && wordIsOperator(exports.OPERATORS, words[i - 1])) { | ||
words[i] = words[i] + words[i + 1]; | ||
words.splice(i + 1, 1); | ||
} | ||
} | ||
} | ||
var innerExpression = wrapInnerExpressions(words); | ||
if (innerExpression.rest.length) | ||
throw new Error('interpretExpression: syntax error: parentheses don\'t add up in "' + expr + '".'); | ||
if (innerExpression.inner.length % 2 !== 1) | ||
throw new Error('interpretExpression: operands & operators don\'t add up: "' + innerExpression.inner.join(' ') + '".'); | ||
var expression = words2Expression(operatorList, innerExpression.inner); | ||
validateExpression(operatorList, expression); | ||
return expression; | ||
const innerExpression = wrapInnerExpressions(words); | ||
if (innerExpression.rest.length) | ||
throw new Error('interpretExpression: syntax error: parentheses don\'t add up in "' + expr + '".'); | ||
if (innerExpression.inner.length % 2 !== 1) | ||
throw new Error('interpretExpression: operands & operators don\'t add up: "' + innerExpression.inner.join(' ') + '".'); | ||
const expression = words2Expression(exports.OPERATORS, innerExpression.inner); | ||
validateExpression(exports.OPERATORS, expression); | ||
return expression; | ||
}, 100 * 1000); | ||
} | ||
@@ -45,3 +46,3 @@ else { | ||
function simplifyExpression(expr0) { | ||
var expr = (_.isString(expr0) ? | ||
const expr = (_.isString(expr0) ? | ||
interpretExpression(expr0) : | ||
@@ -52,5 +53,5 @@ expr0); | ||
if (isExpressionObject(expr)) { | ||
var l = simplifyExpression(expr.l); | ||
var o = expr.o; | ||
var r = simplifyExpression(expr.r); | ||
const l = simplifyExpression(expr.l); | ||
const o = expr.o; | ||
const r = simplifyExpression(expr.r); | ||
if (lib_1.isConstant(l) && | ||
@@ -71,5 +72,5 @@ lib_1.isConstant(r) && | ||
l % r : | ||
{ l: l, o: o, r: r }); | ||
{ l, o, r }); | ||
} | ||
return { l: l, o: o, r: r }; | ||
return { l, o, r }; | ||
} | ||
@@ -93,8 +94,8 @@ return expr; | ||
function wrapInnerExpressions(words) { | ||
for (var i = 0; i < words.length; i++) { | ||
for (let i = 0; i < words.length; i++) { | ||
if (words[i] === '(') { | ||
var tmp = wrapInnerExpressions(words.slice(i + 1)); | ||
const tmp = wrapInnerExpressions(words.slice(i + 1)); | ||
// insert inner expression and remove tha | ||
words[i] = tmp.inner; | ||
words.splice.apply(words, tslib_1.__spreadArrays([i + 1, 99999], tmp.rest)); | ||
words.splice(i + 1, 99999, ...tmp.rest); | ||
} | ||
@@ -108,6 +109,6 @@ else if (words[i] === ')') { | ||
else if (words[i] === '!') { | ||
var tmp = wrapInnerExpressions(words.slice(i + 1)); | ||
const tmp = wrapInnerExpressions(words.slice(i + 1)); | ||
// insert inner expression after the '!' | ||
words[i] = ['', '!'].concat(tmp.inner); | ||
words.splice.apply(words, tslib_1.__spreadArrays([i + 1, 99999], tmp.rest)); | ||
words.splice(i + 1, 99999, ...tmp.rest); | ||
} | ||
@@ -129,3 +130,3 @@ } | ||
// Find the operator with the highest priority: | ||
var operatorI = -1; | ||
let operatorI = -1; | ||
_.each(operatorList, function (operator) { | ||
@@ -137,5 +138,5 @@ if (operatorI === -1) { | ||
if (operatorI !== -1) { | ||
var l = words.slice(0, operatorI); | ||
var r = words.slice(operatorI + 1); | ||
var expr = { | ||
const l = words.slice(0, operatorI); | ||
const r = words.slice(operatorI + 1); | ||
const expr = { | ||
l: words2Expression(operatorList, l), | ||
@@ -155,11 +156,11 @@ o: words[operatorI], | ||
if (_.isObject(expr0) && !_.isArray(expr0)) { | ||
var expr = expr0; | ||
const expr = expr0; | ||
if (!_.has(expr, 'l')) | ||
throw new Error("validateExpression: " + breadcrumbs + ".l missing in " + JSON.stringify(expr)); | ||
throw new Error(`validateExpression: ${breadcrumbs}.l missing in ${JSON.stringify(expr)}`); | ||
if (!_.has(expr, 'o')) | ||
throw new Error("validateExpression: " + breadcrumbs + ".o missing in " + JSON.stringify(expr)); | ||
throw new Error(`validateExpression: ${breadcrumbs}.o missing in ${JSON.stringify(expr)}`); | ||
if (!_.has(expr, 'r')) | ||
throw new Error("validateExpression: " + breadcrumbs + ".r missing in " + JSON.stringify(expr)); | ||
throw new Error(`validateExpression: ${breadcrumbs}.r missing in ${JSON.stringify(expr)}`); | ||
if (!_.isString(expr.o)) | ||
throw new Error("validateExpression: " + breadcrumbs + ".o not a string"); | ||
throw new Error(`validateExpression: ${breadcrumbs}.o not a string`); | ||
if (!wordIsOperator(operatorList, expr.o)) | ||
@@ -171,3 +172,3 @@ throw new Error(breadcrumbs + '.o not valid: "' + expr.o + '"'); | ||
else if (!_.isNull(expr0) && !_.isString(expr0) && !_.isNumber(expr0)) { | ||
throw new Error("validateExpression: " + breadcrumbs + " is of invalid type"); | ||
throw new Error(`validateExpression: ${breadcrumbs} is of invalid type`); | ||
} | ||
@@ -174,0 +175,0 @@ return true; |
@@ -1,2 +0,2 @@ | ||
import { TimelineObject, ResolvedTimeline, ResolveOptions, Expression, ResolvedTimelineObject, TimelineObjectInstance, Time, TimelineState, ValueWithReference, ResolvedStates } from '../api/api'; | ||
import { TimelineObject, ResolvedTimeline, ResolveOptions, Expression, ResolvedTimelineObject, TimelineObjectInstance, Time, TimelineState, ValueWithReference, ResolvedStates, ResolverCache } from '../api/api'; | ||
export declare class Resolver { | ||
@@ -11,3 +11,3 @@ /** | ||
/** Calculate the state for all points in time. */ | ||
static resolveAllStates(resolvedTimeline: ResolvedTimeline): ResolvedStates; | ||
static resolveAllStates(resolvedTimeline: ResolvedTimeline, cache?: ResolverCache): ResolvedStates; | ||
/** | ||
@@ -37,3 +37,6 @@ * Calculate the state at a given point in time. | ||
*/ | ||
export declare function lookupExpression(resolvedTimeline: ResolvedTimeline, obj: ResolvedTimelineObject, expr: Expression | null, context: ObjectRefType): Array<TimelineObjectInstance> | ValueWithReference | null; | ||
export declare function lookupExpression(resolvedTimeline: ResolvedTimeline, obj: ResolvedTimelineObject, expr: Expression | null, context: ObjectRefType): { | ||
instances: Array<TimelineObjectInstance> | ValueWithReference | null; | ||
allReferences: string[]; | ||
}; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _ = require("underscore"); | ||
var lib_1 = require("../lib"); | ||
var validate_1 = require("./validate"); | ||
var expression_1 = require("./expression"); | ||
var state_1 = require("./state"); | ||
var common_1 = require("./common"); | ||
var Resolver = /** @class */ (function () { | ||
function Resolver() { | ||
} | ||
const _ = require("underscore"); | ||
const lib_1 = require("../lib"); | ||
const validate_1 = require("./validate"); | ||
const expression_1 = require("./expression"); | ||
const state_1 = require("./state"); | ||
const common_1 = require("./common"); | ||
const cache_1 = require("./cache"); | ||
class Resolver { | ||
/** | ||
@@ -18,3 +17,3 @@ * Go through all objects on the timeline and calculate all the timings. | ||
*/ | ||
Resolver.resolveTimeline = function (timeline, options) { | ||
static resolveTimeline(timeline, options) { | ||
if (!_.isArray(timeline)) | ||
@@ -26,4 +25,4 @@ throw new Error('resolveTimeline: parameter timeline missing'); | ||
lib_1.resetId(); | ||
var resolvedTimeline = { | ||
options: _.clone(options), | ||
const resolvedTimeline = { | ||
options: { ...options }, | ||
objects: {}, | ||
@@ -38,10 +37,11 @@ classes: {}, | ||
resolvedGroupCount: 0, | ||
resolvedKeyframeCount: 0 | ||
resolvedKeyframeCount: 0, | ||
resolvingCount: 0 | ||
} | ||
}; | ||
// Step 1: pre-populate resolvedTimeline with objects | ||
var addToResolvedTimeline = function (obj, levelDeep, parentId, isKeyframe) { | ||
const addToResolvedTimeline = (obj, levelDeep, parentId, isKeyframe) => { | ||
if (resolvedTimeline.objects[obj.id]) | ||
throw Error("All timelineObjects must be unique! (duplicate: \"" + obj.id + "\")"); | ||
var o = lib_1.extendMandadory(_.clone(obj), { | ||
throw Error(`All timelineObjects must be unique! (duplicate: "${obj.id}")`); | ||
const o = lib_1.extendMandadory(_.clone(obj), { | ||
resolved: { | ||
@@ -52,7 +52,10 @@ resolved: false, | ||
levelDeep: levelDeep, | ||
isSelfReferencing: false | ||
isSelfReferencing: false, | ||
directReferences: [] | ||
} | ||
}); | ||
if (parentId) | ||
if (parentId) { | ||
o.resolved.parentId = parentId; | ||
o.resolved.directReferences.push(parentId); | ||
} | ||
if (isKeyframe) | ||
@@ -63,29 +66,179 @@ o.resolved.isKeyframe = true; | ||
if (obj.isGroup && obj.children) { | ||
_.each(obj.children, function (child) { | ||
for (let i = 0; i < obj.children.length; i++) { | ||
const child = obj.children[i]; | ||
addToResolvedTimeline(child, levelDeep + 1, obj.id); | ||
}); | ||
} | ||
} | ||
// Add keyframes: | ||
if (obj.keyframes) { | ||
_.each(obj.keyframes, function (keyframe) { | ||
var kf2 = lib_1.extendMandadory(_.clone(keyframe), { | ||
for (let i = 0; i < obj.keyframes.length; i++) { | ||
const keyframe = obj.keyframes[i]; | ||
const kf2 = lib_1.extendMandadory(_.clone(keyframe), { | ||
layer: '' | ||
}); | ||
addToResolvedTimeline(kf2, levelDeep + 1, obj.id, true); | ||
}); | ||
} | ||
} | ||
}; | ||
_.each(timeline, function (obj) { | ||
for (let i = 0; i < timeline.length; i++) { | ||
const obj = timeline[i]; | ||
addToResolvedTimeline(obj, 0); | ||
}); | ||
} | ||
// Step 2: go though and resolve the objects | ||
_.each(resolvedTimeline.objects, function (obj) { | ||
resolveTimelineObj(resolvedTimeline, obj); | ||
}); | ||
return resolvedTimeline; | ||
}; | ||
if (options.cache) { | ||
// Figure out which objects has changed since last time | ||
const cache = cache_1.initializeCache(options.cache, resolvedTimeline); | ||
// Go through all new objects, and determine whether they have changed: | ||
const allNewObjects = {}; | ||
const changedReferences = {}; | ||
const getAllReferencesThisObjectAffects = (newObj) => { | ||
const references = ['#' + newObj.id]; | ||
if (newObj.classes) { | ||
for (const className of newObj.classes) { | ||
references.push('.' + className); | ||
} | ||
} | ||
if (newObj.layer) | ||
references.push('$' + newObj.layer); | ||
return references; | ||
}; | ||
const addChangedObject = (obj) => { | ||
const references = getAllReferencesThisObjectAffects(obj); | ||
for (const ref of references) { | ||
changedReferences[ref] = true; | ||
} | ||
}; | ||
_.each(resolvedTimeline.objects, (obj) => { | ||
const oldHash = cache.objHashes[obj.id]; | ||
const newHash = cache_1.objectHash(obj); | ||
allNewObjects[obj.id] = true; | ||
if (!oldHash || | ||
oldHash !== newHash) { | ||
cache.objHashes[obj.id] = newHash; | ||
addChangedObject(obj); | ||
const oldObj = cache.resolvedTimeline.objects[obj.id]; | ||
if (oldObj) | ||
addChangedObject(oldObj); | ||
} | ||
}); | ||
if (cache.hasOldData) { | ||
// Go through all old hashes, removing the ones that doesn't exist anymore | ||
for (const objId in cache.resolvedTimeline.objects) { | ||
if (!allNewObjects[objId]) { | ||
const obj = cache.resolvedTimeline.objects[objId]; | ||
delete cache.objHashes[objId]; | ||
addChangedObject(obj); | ||
} | ||
} | ||
// Invalidate objects, by gradually removing the invalidated ones from validObjects | ||
// Prepare validObjects: | ||
const validObjects = {}; | ||
_.each(resolvedTimeline.objects, obj => { | ||
validObjects[obj.id] = obj; | ||
}); | ||
/** All references that depend on another reference (ie objects, classs or layers): */ | ||
const affectReferenceMap = {}; | ||
_.each(resolvedTimeline.objects, obj => { | ||
// Add everything that this object affects: | ||
let affectedReferences = getAllReferencesThisObjectAffects(obj); | ||
const oldObj = cache.resolvedTimeline.objects[obj.id]; | ||
if (oldObj) { | ||
affectedReferences = _.uniq(affectedReferences.concat(getAllReferencesThisObjectAffects(oldObj))); | ||
} | ||
for (let i = 0; i < affectedReferences.length; i++) { | ||
const ref = affectedReferences[i]; | ||
const objRef = '#' + obj.id; | ||
if (ref !== objRef) { | ||
if (!affectReferenceMap[objRef]) | ||
affectReferenceMap[objRef] = []; | ||
affectReferenceMap[objRef].push(ref); | ||
} | ||
} | ||
// Add everything that this object is affected by: | ||
if (changedReferences['#' + obj.id]) { | ||
// The object is directly said to be invalid, no need to add it to referencingObjects, | ||
// since it'll be easily invalidated anyway later | ||
} | ||
else { | ||
// Note: we only have to check for the OLD object, since if the old and the new object differs, | ||
// that would mean it'll be directly invalidated anyway. | ||
const oldObj = cache.resolvedTimeline.objects[obj.id]; | ||
if (oldObj) { | ||
// Fetch all references for the object from the last time it was resolved. | ||
// Note: This can be done, since _if_ the object was changed in any way since last resolve | ||
// it'll be invalidated anyway | ||
const dependOnReferences = cache_1.getObjectReferences(oldObj); | ||
for (let i = 0; i < dependOnReferences.length; i++) { | ||
const ref = dependOnReferences[i]; | ||
if (!affectReferenceMap[ref]) | ||
affectReferenceMap[ref] = []; | ||
affectReferenceMap[ref].push('#' + obj.id); | ||
} | ||
} | ||
} | ||
}); | ||
// Invalidate all changed objects, and recursively invalidate all objects that reference those objects: | ||
const handledReferences = {}; | ||
const invalidateObjectsWithReference = (reference, affectReferenceMap, validObjects) => { | ||
if (handledReferences[reference]) | ||
return; // to avoid infinite loops | ||
handledReferences[reference] = true; | ||
if (reference[0] === '#') { // an id | ||
const objId = reference.slice(1); | ||
if (validObjects[objId]) { | ||
delete validObjects[objId]; | ||
// const obj = validObjects[objId] | ||
// const affectedReferences = getAllReferencesThisObjectAffects(obj) | ||
} | ||
} | ||
// Invalidate all objects that depend on any of the references that this reference affects: | ||
const affectedReferences = affectReferenceMap[reference]; | ||
if (affectedReferences) { | ||
for (let i = 0; i < affectedReferences.length; i++) { | ||
const referencingReference = affectedReferences[i]; | ||
invalidateObjectsWithReference(referencingReference, affectReferenceMap, validObjects); | ||
} | ||
} | ||
}; | ||
_.each(Object.keys(changedReferences), reference => { | ||
invalidateObjectsWithReference(reference, affectReferenceMap, validObjects); | ||
}); | ||
// The objects that are left in validObjects at this point are still valid. | ||
// We can reuse the old resolving for those: | ||
_.each(validObjects, (obj) => { | ||
if (!cache.resolvedTimeline.objects[obj.id]) | ||
throw new Error(`Something went wrong: "${obj.id}" does not exist in cache.resolvedTimeline.objects`); | ||
resolvedTimeline.objects[obj.id] = cache.resolvedTimeline.objects[obj.id]; | ||
}); | ||
} | ||
_.each(resolvedTimeline.objects, (obj) => { | ||
resolveTimelineObj(resolvedTimeline, obj); | ||
}); | ||
// Save for next time: | ||
cache.resolvedTimeline = resolvedTimeline; | ||
cache.hasOldData = true; | ||
// Update statistics, since that's not accurate after having used the cache: | ||
resolvedTimeline.statistics.unresolvedCount = 0; | ||
resolvedTimeline.statistics.resolvedCount = 0; | ||
resolvedTimeline.statistics.resolvedInstanceCount = 0; | ||
resolvedTimeline.statistics.resolvedObjectCount = 0; | ||
resolvedTimeline.statistics.resolvedGroupCount = 0; | ||
resolvedTimeline.statistics.resolvedKeyframeCount = 0; | ||
_.each(resolvedTimeline.objects, obj => { | ||
updateStatistics(resolvedTimeline, obj); | ||
}); | ||
return resolvedTimeline; | ||
} | ||
else { | ||
// If there are no cache provided, just resolve all objects: | ||
_.each(resolvedTimeline.objects, (obj) => { | ||
resolveTimelineObj(resolvedTimeline, obj); | ||
}); | ||
return resolvedTimeline; | ||
} | ||
} | ||
/** Calculate the state for all points in time. */ | ||
Resolver.resolveAllStates = function (resolvedTimeline) { | ||
return state_1.resolveStates(resolvedTimeline); | ||
}; | ||
static resolveAllStates(resolvedTimeline, cache) { | ||
return state_1.resolveStates(resolvedTimeline, undefined, cache); | ||
} | ||
/** | ||
@@ -99,7 +252,6 @@ * Calculate the state at a given point in time. | ||
*/ | ||
Resolver.getState = function (resolved, time, eventLimit) { | ||
static getState(resolved, time, eventLimit) { | ||
return state_1.getState(resolved, time, eventLimit); | ||
}; | ||
return Resolver; | ||
}()); | ||
} | ||
} | ||
exports.Resolver = Resolver; | ||
@@ -110,100 +262,75 @@ function resolveTimelineObj(resolvedTimeline, obj) { | ||
if (obj.resolved.resolving) | ||
throw new Error("Circular dependency when trying to resolve \"" + obj.id + "\""); | ||
throw new Error(`Circular dependency when trying to resolve "${obj.id}"`); | ||
obj.resolved.resolving = true; | ||
var instances = []; | ||
var repeatingExpr = (obj.enable.repeating !== undefined ? | ||
expression_1.interpretExpression(obj.enable.repeating) : | ||
null); | ||
var lookedupRepeating = lookupExpression(resolvedTimeline, obj, repeatingExpr, 'duration'); | ||
if (_.isArray(lookedupRepeating)) { | ||
throw new Error("lookupExpression should never return an array for .duration lookup"); // perhaps tmp? maybe revisit this at some point | ||
} | ||
var start = (obj.enable.while !== undefined ? | ||
obj.enable.while : | ||
obj.enable.start !== undefined ? | ||
obj.enable.start : | ||
''); | ||
if (obj.enable.while + '' === '1') { | ||
start = 'true'; | ||
} | ||
else if (obj.enable.while + '' === '0') { | ||
start = 'false'; | ||
} | ||
var startExpr = expression_1.simplifyExpression(start); | ||
var parentInstances = null; | ||
var hasParent = false; | ||
var referToParent = false; | ||
if (obj.resolved.parentId) { | ||
hasParent = true; | ||
parentInstances = lookupExpression(resolvedTimeline, obj, expression_1.interpretExpression("#" + obj.resolved.parentId), 'start'); // a start-reference will always return an array, or null | ||
if (lib_1.isConstant(startExpr)) { | ||
// Only use parent if the expression resolves to a number (ie doesn't contain any references) | ||
referToParent = true; | ||
resolvedTimeline.statistics.resolvingCount++; | ||
let instances = []; | ||
let directReferences = []; | ||
const enables = (_.isArray(obj.enable) ? | ||
obj.enable : | ||
[obj.enable]); | ||
_.each(enables, enable => { | ||
let newInstances = []; | ||
const repeatingExpr = (enable.repeating !== undefined ? | ||
expression_1.interpretExpression(enable.repeating) : | ||
null); | ||
const lookedRepeating = lookupExpression(resolvedTimeline, obj, repeatingExpr, 'duration'); | ||
const lookedupRepeating = lookedRepeating.instances; | ||
directReferences = directReferences.concat(lookedRepeating.allReferences); | ||
if (_.isArray(lookedupRepeating)) { | ||
throw new Error(`lookupExpression should never return an array for .duration lookup`); // perhaps tmp? maybe revisit this at some point | ||
} | ||
} | ||
var lookedupStarts = lookupExpression(resolvedTimeline, obj, startExpr, 'start'); | ||
var applyParentInstances = function (value) { | ||
var operate = function (a, b) { | ||
if (a === null || b === null) | ||
return null; | ||
return { | ||
value: a.value + b.value, | ||
references: lib_1.joinReferences(a.references, b.references) | ||
}; | ||
}; | ||
return lib_1.operateOnArrays(parentInstances, value, operate); | ||
}; | ||
if (referToParent) { | ||
lookedupStarts = applyParentInstances(lookedupStarts); | ||
} | ||
if (obj.enable.while) { | ||
if (_.isArray(lookedupStarts)) { | ||
instances = lookedupStarts; | ||
let start = (enable.while !== undefined ? | ||
enable.while : | ||
enable.start !== undefined ? | ||
enable.start : | ||
''); | ||
if (enable.while + '' === '1') { | ||
start = 'true'; | ||
} | ||
else if (lookedupStarts !== null) { | ||
instances = [{ | ||
id: lib_1.getId(), | ||
start: lookedupStarts.value, | ||
end: null, | ||
references: lookedupStarts.references | ||
}]; | ||
else if (enable.while + '' === '0') { | ||
start = 'false'; | ||
} | ||
} | ||
else { | ||
var events_1 = []; | ||
var iStart_1 = 0; | ||
var iEnd_1 = 0; | ||
if (_.isArray(lookedupStarts)) { | ||
_.each(lookedupStarts, function (instance) { | ||
events_1.push({ | ||
time: instance.start, | ||
value: true, | ||
data: { instance: instance, id: obj.id + '_' + iStart_1++ }, | ||
references: instance.references | ||
}); | ||
}); | ||
const startExpr = expression_1.simplifyExpression(start); | ||
let parentInstances = null; | ||
let hasParent = false; | ||
let referToParent = false; | ||
if (obj.resolved.parentId) { | ||
hasParent = true; | ||
const lookup = lookupExpression(resolvedTimeline, obj, expression_1.interpretExpression(`#${obj.resolved.parentId}`), 'start'); | ||
parentInstances = lookup.instances; // a start-reference will always return an array, or null | ||
directReferences = directReferences.concat(lookup.allReferences); | ||
if (lib_1.isConstant(startExpr)) { | ||
// Only use parent if the expression resolves to a number (ie doesn't contain any references) | ||
referToParent = true; | ||
} | ||
} | ||
else if (lookedupStarts !== null) { | ||
events_1.push({ | ||
time: lookedupStarts.value, | ||
value: true, | ||
data: { instance: { id: lib_1.getId(), start: lookedupStarts.value, end: null, references: lookedupStarts.references }, id: obj.id + '_' + iStart_1++ }, | ||
references: lookedupStarts.references | ||
}); | ||
const lookupStart = lookupExpression(resolvedTimeline, obj, startExpr, 'start'); | ||
let lookedupStarts = lookupStart.instances; | ||
directReferences = directReferences.concat(lookupStart.allReferences); | ||
if (referToParent) { | ||
lookedupStarts = lib_1.applyParentInstances(parentInstances, lookedupStarts); | ||
} | ||
if (obj.enable.end !== undefined) { | ||
var endExpr = expression_1.interpretExpression(obj.enable.end); | ||
// lookedupEnds will contain an inverted list of instances. Therefore .start means an end | ||
var lookedupEnds = (endExpr ? | ||
lookupExpression(resolvedTimeline, obj, endExpr, 'end') : | ||
null); | ||
if (referToParent && lib_1.isConstant(endExpr)) { | ||
lookedupEnds = applyParentInstances(lookedupEnds); | ||
if (enable.while) { | ||
if (_.isArray(lookedupStarts)) { | ||
newInstances = lookedupStarts; | ||
} | ||
if (_.isArray(lookedupEnds)) { | ||
_.each(lookedupEnds, function (instance) { | ||
events_1.push({ | ||
else if (lookedupStarts !== null) { | ||
newInstances = [{ | ||
id: lib_1.getId(), | ||
start: lookedupStarts.value, | ||
end: null, | ||
references: lookedupStarts.references | ||
}]; | ||
} | ||
} | ||
else { | ||
const events = []; | ||
let iStart = 0; | ||
let iEnd = 0; | ||
if (_.isArray(lookedupStarts)) { | ||
_.each(lookedupStarts, (instance) => { | ||
events.push({ | ||
time: instance.start, | ||
value: false, | ||
data: { instance: instance, id: obj.id + '_' + iEnd_1++ }, | ||
value: true, | ||
data: { instance: instance, id: obj.id + '_' + iStart++ }, | ||
references: instance.references | ||
@@ -213,93 +340,127 @@ }); | ||
} | ||
else if (lookedupEnds !== null) { | ||
events_1.push({ | ||
time: lookedupEnds.value, | ||
value: false, | ||
data: { instance: { id: lib_1.getId(), start: lookedupEnds.value, end: null, references: lookedupEnds.references }, id: obj.id + '_' + iEnd_1++ }, | ||
references: lookedupEnds.references | ||
else if (lookedupStarts !== null) { | ||
events.push({ | ||
time: lookedupStarts.value, | ||
value: true, | ||
data: { instance: { id: lib_1.getId(), start: lookedupStarts.value, end: null, references: lookedupStarts.references }, id: obj.id + '_' + iStart++ }, | ||
references: lookedupStarts.references | ||
}); | ||
} | ||
} | ||
else if (obj.enable.duration !== undefined) { | ||
var durationExpr = expression_1.interpretExpression(obj.enable.duration); | ||
var lookedupDuration = lookupExpression(resolvedTimeline, obj, durationExpr, 'duration'); | ||
if (_.isArray(lookedupDuration) && lookedupDuration.length === 1) { | ||
lookedupDuration = { | ||
value: lookedupDuration[0].start, | ||
references: lookedupDuration[0].references | ||
}; | ||
} | ||
if (_.isArray(lookedupDuration) && !lookedupDuration.length) | ||
lookedupDuration = null; | ||
if (_.isArray(lookedupDuration)) { | ||
throw new Error("lookupExpression should never return an array for .duration lookup"); // perhaps tmp? maybe revisit this at some point | ||
} | ||
else if (lookedupDuration !== null) { | ||
if (lookedupRepeating !== null && | ||
lookedupDuration.value > lookedupRepeating.value) | ||
lookedupDuration.value = lookedupRepeating.value; | ||
var tmpLookedupDuration_1 = lookedupDuration; // cast type | ||
_.each(events_1, function (e) { | ||
if (e.value) { | ||
var time = e.time + tmpLookedupDuration_1.value; | ||
var references = lib_1.joinReferences(e.references, tmpLookedupDuration_1.references); | ||
events_1.push({ | ||
time: time, | ||
if (enable.end !== undefined) { | ||
const endExpr = expression_1.interpretExpression(enable.end); | ||
// lookedupEnds will contain an inverted list of instances. Therefore .start means an end | ||
const lookupEnd = endExpr ? lookupExpression(resolvedTimeline, obj, endExpr, 'end') : null; | ||
let lookedupEnds = lookupEnd ? lookupEnd.instances : null; | ||
if (lookupEnd) | ||
directReferences = directReferences.concat(lookupEnd.allReferences); | ||
if (referToParent && lib_1.isConstant(endExpr)) { | ||
lookedupEnds = lib_1.applyParentInstances(parentInstances, lookedupEnds); | ||
} | ||
if (_.isArray(lookedupEnds)) { | ||
_.each(lookedupEnds, (instance) => { | ||
events.push({ | ||
time: instance.start, | ||
value: false, | ||
data: { id: e.data.id, instance: { id: e.data.instance.id, start: time, end: null, references: references } }, | ||
references: references | ||
data: { instance: instance, id: obj.id + '_' + iEnd++ }, | ||
references: instance.references | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
else if (lookedupEnds !== null) { | ||
events.push({ | ||
time: lookedupEnds.value, | ||
value: false, | ||
data: { instance: { id: lib_1.getId(), start: lookedupEnds.value, end: null, references: lookedupEnds.references }, id: obj.id + '_' + iEnd++ }, | ||
references: lookedupEnds.references | ||
}); | ||
} | ||
} | ||
else if (enable.duration !== undefined) { | ||
const durationExpr = expression_1.interpretExpression(enable.duration); | ||
const lookupDuration = lookupExpression(resolvedTimeline, obj, durationExpr, 'duration'); | ||
let lookedupDuration = lookupDuration.instances; | ||
directReferences = directReferences.concat(lookupDuration.allReferences); | ||
if (_.isArray(lookedupDuration) && lookedupDuration.length === 1) { | ||
lookedupDuration = { | ||
value: lookedupDuration[0].start, | ||
references: lookedupDuration[0].references | ||
}; | ||
} | ||
if (_.isArray(lookedupDuration) && !lookedupDuration.length) | ||
lookedupDuration = null; | ||
if (_.isArray(lookedupDuration)) { | ||
throw new Error(`lookupExpression should never return an array for .duration lookup`); // perhaps tmp? maybe revisit this at some point | ||
} | ||
else if (lookedupDuration !== null) { | ||
if (lookedupRepeating !== null && | ||
lookedupDuration.value > lookedupRepeating.value) | ||
lookedupDuration.value = lookedupRepeating.value; | ||
const tmpLookedupDuration = lookedupDuration; // cast type | ||
_.each(events, (e) => { | ||
if (e.value) { | ||
const time = e.time + tmpLookedupDuration.value; | ||
const references = lib_1.joinReferences(e.references, tmpLookedupDuration.references); | ||
events.push({ | ||
time: time, | ||
value: false, | ||
data: { id: e.data.id, instance: { id: e.data.instance.id, start: time, end: null, references: references } }, | ||
references: references | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
newInstances = lib_1.convertEventsToInstances(events, false); | ||
} | ||
instances = lib_1.convertEventsToInstances(events_1, false); | ||
} | ||
if (hasParent) { | ||
// figure out what parent-instance the instances are tied to, and cap them | ||
var cappedInstances_1 = []; | ||
_.each(instances, function (instance) { | ||
if (parentInstances) { | ||
var parentInstance = _.find(parentInstances, function (parentInstance) { | ||
return instance.references.indexOf(parentInstance.id) !== -1; | ||
}); | ||
if (parentInstance) { | ||
// If the child refers to its parent, there should be one specific instance to cap into | ||
var cappedInstance = lib_1.capInstances([instance], [parentInstance])[0]; | ||
if (cappedInstance) { | ||
if (!cappedInstance.caps) | ||
cappedInstance.caps = []; | ||
cappedInstance.caps.push({ | ||
id: parentInstance.id, | ||
start: parentInstance.start, | ||
end: parentInstance.end | ||
}); | ||
cappedInstances_1.push(cappedInstance); | ||
if (hasParent) { | ||
// figure out what parent-instance the instances are tied to, and cap them | ||
const cappedInstances = []; | ||
for (let i = 0; i < newInstances.length; i++) { | ||
const instance = newInstances[i]; | ||
if (parentInstances) { | ||
const referredParentInstance = _.find(parentInstances, (parentInstance) => { | ||
return instance.references.indexOf(parentInstance.id) !== -1; | ||
}); | ||
if (referredParentInstance) { | ||
// If the child refers to its parent, there should be one specific instance to cap into | ||
const cappedInstance = lib_1.capInstances([instance], [referredParentInstance])[0]; | ||
if (cappedInstance) { | ||
if (!cappedInstance.caps) | ||
cappedInstance.caps = []; | ||
cappedInstance.caps.push({ | ||
id: referredParentInstance.id, | ||
start: referredParentInstance.start, | ||
end: referredParentInstance.end | ||
}); | ||
cappedInstances.push(cappedInstance); | ||
} | ||
} | ||
} | ||
else { | ||
// If the child doesn't refer to its parent, it should be capped within all of its parent instances | ||
_.each(parentInstances, function (parentInstance) { | ||
var cappedInstance = lib_1.capInstances([instance], [parentInstance])[0]; | ||
if (cappedInstance) { | ||
if (parentInstance) { | ||
if (!cappedInstance.caps) | ||
cappedInstance.caps = []; | ||
cappedInstance.caps.push({ | ||
id: parentInstance.id, | ||
start: parentInstance.start, | ||
end: parentInstance.end | ||
}); | ||
else { | ||
// If the child doesn't refer to its parent, it should be capped within all of its parent instances | ||
for (let i = 0; i < parentInstances.length; i++) { | ||
const parentInstance = parentInstances[i]; | ||
const cappedInstance = lib_1.capInstances([instance], [parentInstance])[0]; | ||
if (cappedInstance) { | ||
if (parentInstance) { | ||
if (!cappedInstance.caps) | ||
cappedInstance.caps = []; | ||
cappedInstance.caps.push({ | ||
id: parentInstance.id, | ||
start: parentInstance.start, | ||
end: parentInstance.end | ||
}); | ||
} | ||
cappedInstances.push(cappedInstance); | ||
} | ||
cappedInstances_1.push(cappedInstance); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
}); | ||
instances = cappedInstances_1; | ||
} | ||
instances = lib_1.applyRepeatingInstances(instances, lookedupRepeating, resolvedTimeline.options); | ||
newInstances = cappedInstances; | ||
} | ||
newInstances = lib_1.applyRepeatingInstances(newInstances, lookedupRepeating, resolvedTimeline.options); | ||
instances = instances.concat(newInstances); | ||
}); | ||
// filter out zero-length instances: | ||
instances = _.filter(instances, function (instance) { | ||
instances = _.filter(instances, (instance) => { | ||
return ((instance.end || Infinity) > instance.start); | ||
@@ -310,4 +471,9 @@ }); | ||
obj.resolved.instances = instances; | ||
if (instances.length) { | ||
resolvedTimeline.statistics.resolvedInstanceCount += instances.length; | ||
obj.resolved.directReferences = directReferences; | ||
updateStatistics(resolvedTimeline, obj); | ||
} | ||
exports.resolveTimelineObj = resolveTimelineObj; | ||
function updateStatistics(resolvedTimeline, obj) { | ||
if (obj.resolved.instances.length) { | ||
resolvedTimeline.statistics.resolvedInstanceCount += obj.resolved.instances.length; | ||
resolvedTimeline.statistics.resolvedCount += 1; | ||
@@ -328,3 +494,2 @@ if (obj.isGroup) { | ||
} | ||
exports.resolveTimelineObj = resolveTimelineObj; | ||
/** | ||
@@ -344,8 +509,11 @@ * Look up a reference on the timeline | ||
if (expr === null) | ||
return null; | ||
return { instances: null, allReferences: [] }; | ||
if (_.isString(expr) && | ||
lib_1.isNumeric(expr)) { | ||
return { | ||
value: parseFloat(expr), | ||
references: [] | ||
instances: { | ||
value: parseFloat(expr), | ||
references: [] | ||
}, | ||
allReferences: [] | ||
}; | ||
@@ -355,4 +523,7 @@ } | ||
return { | ||
value: expr, | ||
references: [] | ||
instances: { | ||
value: expr, | ||
references: [] | ||
}, | ||
allReferences: [] | ||
}; | ||
@@ -365,51 +536,62 @@ } | ||
return { | ||
value: 0, | ||
references: [] | ||
instances: { | ||
value: 0, | ||
references: [] | ||
}, | ||
allReferences: [] | ||
}; | ||
} | ||
else if (expr.match(/^false$/i)) { | ||
return []; | ||
return { | ||
instances: [], | ||
allReferences: [] | ||
}; | ||
} | ||
} | ||
// Look up string | ||
var invert = false; | ||
var ignoreFirstIfZero = false; | ||
var referencedObjs_1 = []; | ||
var ref = context; | ||
var rest = ''; | ||
var objIdsToReference = []; | ||
var referenceIsOk = false; | ||
let invert = false; | ||
let ignoreFirstIfZero = false; | ||
let referencedObjs = []; | ||
let ref = context; | ||
let rest = ''; | ||
let objIdsToReference = []; | ||
const allReferences = []; | ||
let referenceIsOk = false; | ||
// Match id, example: "#objectId.start" | ||
var m = expr.match(/^\W*#([^.]+)(.*)/); | ||
const m = expr.match(/^\W*#([^.]+)(.*)/); | ||
if (m) { | ||
var id = m[1]; | ||
const id = m[1]; | ||
rest = m[2]; | ||
referenceIsOk = true; | ||
objIdsToReference = [id]; | ||
allReferences.push('#' + id); | ||
} | ||
else { | ||
// Match class, example: ".className.start" | ||
var m_1 = expr.match(/^\W*\.([^.]+)(.*)/); | ||
if (m_1) { | ||
var className = m_1[1]; | ||
rest = m_1[2]; | ||
const m = expr.match(/^\W*\.([^.]+)(.*)/); | ||
if (m) { | ||
const className = m[1]; | ||
rest = m[2]; | ||
referenceIsOk = true; | ||
objIdsToReference = resolvedTimeline.classes[className] || []; | ||
allReferences.push('.' + className); | ||
} | ||
else { | ||
// Match layer, example: "$layer" | ||
var m_2 = expr.match(/^\W*\$([^.]+)(.*)/); | ||
if (m_2) { | ||
var layer = m_2[1]; | ||
rest = m_2[2]; | ||
const m = expr.match(/^\W*\$([^.]+)(.*)/); | ||
if (m) { | ||
const layer = m[1]; | ||
rest = m[2]; | ||
referenceIsOk = true; | ||
objIdsToReference = resolvedTimeline.layers[layer] || []; | ||
allReferences.push('$' + layer); | ||
} | ||
} | ||
} | ||
_.each(objIdsToReference, function (refObjId) { | ||
for (let i = 0; i < objIdsToReference.length; i++) { | ||
const refObjId = objIdsToReference[i]; | ||
if (refObjId !== obj.id) { | ||
var refObj = resolvedTimeline.objects[refObjId]; | ||
const refObj = resolvedTimeline.objects[refObjId]; | ||
if (refObj) { | ||
referencedObjs_1.push(refObj); | ||
referencedObjs.push(refObj); | ||
} | ||
@@ -423,12 +605,13 @@ } | ||
} | ||
}); | ||
if (!referenceIsOk) | ||
return null; | ||
} | ||
if (!referenceIsOk) { | ||
return { instances: null, allReferences: [] }; | ||
} | ||
if (obj.resolved.isSelfReferencing) { | ||
// Exclude any self-referencing objects: | ||
referencedObjs_1 = _.filter(referencedObjs_1, function (refObj) { | ||
referencedObjs = _.filter(referencedObjs, refObj => { | ||
return !refObj.resolved.isSelfReferencing; | ||
}); | ||
} | ||
if (referencedObjs_1.length) { | ||
if (referencedObjs.length) { | ||
if (rest.match(/start/)) | ||
@@ -442,4 +625,5 @@ ref = 'start'; | ||
// Duration refers to the first object on the resolved timeline | ||
var instanceDurations_1 = []; | ||
_.each(referencedObjs_1, function (referencedObj) { | ||
const instanceDurations = []; | ||
for (let i = 0; i < referencedObjs.length; i++) { | ||
const referencedObj = referencedObjs[i]; | ||
resolveTimelineObj(resolvedTimeline, referencedObj); | ||
@@ -453,9 +637,9 @@ if (referencedObj.resolved.resolved) { | ||
else { | ||
var firstInstance = _.first(referencedObj.resolved.instances); | ||
const firstInstance = _.first(referencedObj.resolved.instances); | ||
if (firstInstance) { | ||
var duration = (firstInstance.end !== null ? | ||
const duration = (firstInstance.end !== null ? | ||
firstInstance.end - firstInstance.start : | ||
null); | ||
if (duration !== null) { | ||
instanceDurations_1.push({ | ||
instanceDurations.push({ | ||
value: duration, | ||
@@ -468,12 +652,12 @@ references: lib_1.joinReferences(referencedObj.id, firstInstance.references) | ||
} | ||
} | ||
let firstDuration = null; | ||
_.each(instanceDurations, (d) => { | ||
if (firstDuration === null || d.value < firstDuration.value) | ||
firstDuration = d; | ||
}); | ||
var firstDuration_1 = null; | ||
_.each(instanceDurations_1, function (d) { | ||
if (firstDuration_1 === null || d.value < firstDuration_1.value) | ||
firstDuration_1 = d; | ||
}); | ||
return firstDuration_1; | ||
return { instances: firstDuration, allReferences: allReferences }; | ||
} | ||
else { | ||
var returnInstances_1 = []; | ||
let returnInstances = []; | ||
if (ref === 'start') { | ||
@@ -487,4 +671,4 @@ // nothing | ||
else | ||
throw Error("Unknown ref: \"" + ref + "\""); | ||
_.each(referencedObjs_1, function (referencedObj) { | ||
throw Error(`Unknown ref: "${ref}"`); | ||
_.each(referencedObjs, (referencedObj) => { | ||
resolveTimelineObj(resolvedTimeline, referencedObj); | ||
@@ -498,23 +682,23 @@ if (referencedObj.resolved.resolved) { | ||
else { | ||
returnInstances_1 = returnInstances_1.concat(referencedObj.resolved.instances); | ||
returnInstances = returnInstances.concat(referencedObj.resolved.instances); | ||
} | ||
} | ||
}); | ||
if (returnInstances_1.length) { | ||
if (returnInstances.length) { | ||
if (invert) { | ||
returnInstances_1 = lib_1.invertInstances(returnInstances_1); | ||
returnInstances = lib_1.invertInstances(returnInstances); | ||
} | ||
else { | ||
returnInstances_1 = lib_1.cleanInstances(returnInstances_1, true, true); | ||
returnInstances = lib_1.cleanInstances(returnInstances, true, true); | ||
} | ||
if (ignoreFirstIfZero) { | ||
var first = _.first(returnInstances_1); | ||
const first = _.first(returnInstances); | ||
if (first && first.start === 0) { | ||
returnInstances_1.splice(0, 1); | ||
returnInstances.splice(0, 1); | ||
} | ||
} | ||
return returnInstances_1; | ||
return { instances: returnInstances, allReferences: allReferences }; | ||
} | ||
else { | ||
return []; | ||
return { instances: [], allReferences: allReferences }; | ||
} | ||
@@ -524,3 +708,3 @@ } | ||
else { | ||
return []; | ||
return { instances: [], allReferences: allReferences }; | ||
} | ||
@@ -530,15 +714,24 @@ } | ||
if (expr) { | ||
var lookupExpr = { | ||
l: lookupExpression(resolvedTimeline, obj, expr.l, context), | ||
const l = lookupExpression(resolvedTimeline, obj, expr.l, context); | ||
const r = lookupExpression(resolvedTimeline, obj, expr.r, context); | ||
const lookupExpr = { | ||
l: l.instances, | ||
o: expr.o, | ||
r: lookupExpression(resolvedTimeline, obj, expr.r, context) | ||
r: r.instances | ||
}; | ||
const allReferences = l.allReferences.concat(r.allReferences); | ||
if (lookupExpr.o === '!') { | ||
// Discard l, invert and return r: | ||
if (lookupExpr.r && _.isArray(lookupExpr.r)) { | ||
return lib_1.invertInstances(lookupExpr.r); | ||
return { | ||
instances: lib_1.invertInstances(lookupExpr.r), | ||
allReferences: allReferences | ||
}; | ||
} | ||
else { | ||
// We can't invert a value | ||
return lookupExpr.r; | ||
return { | ||
instances: lookupExpr.r, | ||
allReferences: allReferences | ||
}; | ||
} | ||
@@ -549,12 +742,12 @@ } | ||
_.isNull(lookupExpr.r)) { | ||
return null; | ||
return { instances: null, allReferences: allReferences }; | ||
} | ||
if (lookupExpr.o === '&' || | ||
lookupExpr.o === '|') { | ||
var events_2 = []; | ||
var addEvents = function (instances, left) { | ||
_.each(instances, function (instance) { | ||
let events = []; | ||
const addEvents = (instances, left) => { | ||
_.each(instances, (instance) => { | ||
if (instance.start === instance.end) | ||
return; // event doesn't actually exist... | ||
events_2.push({ | ||
events.push({ | ||
left: left, | ||
@@ -568,3 +761,3 @@ time: instance.start, | ||
if (instance.end !== null) { | ||
events_2.push({ | ||
events.push({ | ||
left: left, | ||
@@ -584,18 +777,18 @@ time: instance.end, | ||
addEvents(lookupExpr.r, false); | ||
events_2 = lib_1.sortEvents(events_2); | ||
var calcResult = (lookupExpr.o === '&' ? | ||
function (left, right) { return !!(left && right); } : | ||
events = lib_1.sortEvents(events); | ||
const calcResult = (lookupExpr.o === '&' ? | ||
(left, right) => !!(left && right) : | ||
lookupExpr.o === '|' ? | ||
function (left, right) { return !!(left || right); } : | ||
function () { return false; }); | ||
var leftValue = (lib_1.isReference(lookupExpr.l) ? !!lookupExpr.l.value : false); | ||
var rightValue = (lib_1.isReference(lookupExpr.r) ? !!lookupExpr.r.value : false); | ||
var leftInstance = null; | ||
var rightInstance = null; | ||
var resultValue = calcResult(leftValue, rightValue); | ||
var resultReferences = lib_1.joinReferences((lib_1.isReference(lookupExpr.l) ? lookupExpr.l.references : []), (lib_1.isReference(lookupExpr.r) ? lookupExpr.r.references : [])); | ||
var instances_1 = []; | ||
var updateInstance = function (time, value, references, caps) { | ||
(left, right) => !!(left || right) : | ||
() => { return false; }); | ||
let leftValue = (lib_1.isReference(lookupExpr.l) ? !!lookupExpr.l.value : false); | ||
let rightValue = (lib_1.isReference(lookupExpr.r) ? !!lookupExpr.r.value : false); | ||
let leftInstance = null; | ||
let rightInstance = null; | ||
let resultValue = calcResult(leftValue, rightValue); | ||
const resultReferences = lib_1.joinReferences((lib_1.isReference(lookupExpr.l) ? lookupExpr.l.references : []), (lib_1.isReference(lookupExpr.r) ? lookupExpr.r.references : [])); | ||
const instances = []; | ||
const updateInstance = (time, value, references, caps) => { | ||
if (value) { | ||
instances_1.push({ | ||
instances.push({ | ||
id: lib_1.getId(), | ||
@@ -609,3 +802,3 @@ start: time, | ||
else { | ||
var last = _.last(instances_1); | ||
const last = _.last(instances); | ||
if (last) { | ||
@@ -618,5 +811,5 @@ last.end = time; | ||
updateInstance(0, resultValue, resultReferences, []); | ||
for (var i = 0; i < events_2.length; i++) { | ||
var e = events_2[i]; | ||
var next = events_2[i + 1]; | ||
for (let i = 0; i < events.length; i++) { | ||
const e = events[i]; | ||
const next = events[i + 1]; | ||
if (e.left) { | ||
@@ -631,7 +824,7 @@ leftValue = e.value; | ||
if (!next || next.time !== e.time) { | ||
var newResultValue = calcResult(leftValue, rightValue); | ||
var resultReferences_1 = lib_1.joinReferences(leftInstance ? leftInstance.references : [], rightInstance ? rightInstance.references : []); | ||
var resultCaps = ((leftInstance ? leftInstance.caps || [] : []).concat(rightInstance ? rightInstance.caps || [] : [])); | ||
const newResultValue = calcResult(leftValue, rightValue); | ||
const resultReferences = lib_1.joinReferences(leftInstance ? leftInstance.references : [], rightInstance ? rightInstance.references : []); | ||
const resultCaps = ((leftInstance ? leftInstance.caps || [] : []).concat(rightInstance ? rightInstance.caps || [] : [])); | ||
if (newResultValue !== resultValue) { | ||
updateInstance(e.time, newResultValue, resultReferences_1, resultCaps); | ||
updateInstance(e.time, newResultValue, resultReferences, resultCaps); | ||
resultValue = newResultValue; | ||
@@ -641,23 +834,23 @@ } | ||
} | ||
return instances_1; | ||
return { instances: instances, allReferences: allReferences }; | ||
} | ||
else { | ||
var operateInner_1 = (lookupExpr.o === '+' ? | ||
function (a, b) { return { value: a.value + b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
const operateInner = (lookupExpr.o === '+' ? | ||
(a, b) => { return { value: a.value + b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
lookupExpr.o === '-' ? | ||
function (a, b) { return { value: a.value - b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
(a, b) => { return { value: a.value - b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
lookupExpr.o === '*' ? | ||
function (a, b) { return { value: a.value * b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
(a, b) => { return { value: a.value * b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
lookupExpr.o === '/' ? | ||
function (a, b) { return { value: a.value / b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
(a, b) => { return { value: a.value / b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
lookupExpr.o === '%' ? | ||
function (a, b) { return { value: a.value % b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
function () { return null; }); | ||
var operate = function (a, b) { | ||
(a, b) => { return { value: a.value % b.value, references: lib_1.joinReferences(a.references, b.references) }; } : | ||
() => null); | ||
const operate = (a, b) => { | ||
if (a === null || b === null) | ||
return null; | ||
return operateInner_1(a, b); | ||
return operateInner(a, b); | ||
}; | ||
var result = lib_1.operateOnArrays(lookupExpr.l, lookupExpr.r, operate); | ||
return result; | ||
const result = lib_1.operateOnArrays(lookupExpr.l, lookupExpr.r, operate); | ||
return { instances: result, allReferences: allReferences }; | ||
} | ||
@@ -667,5 +860,5 @@ } | ||
} | ||
return null; | ||
return { instances: null, allReferences: [] }; | ||
} | ||
exports.lookupExpression = lookupExpression; | ||
//# sourceMappingURL=resolver.js.map |
@@ -1,4 +0,4 @@ | ||
import { TimelineState, ResolvedTimeline, Time, Content, ResolvedStates } from '../api/api'; | ||
import { TimelineState, ResolvedTimeline, Time, Content, ResolvedStates, ResolverCache } from '../api/api'; | ||
export declare function getState(resolved: ResolvedTimeline | ResolvedStates, time: Time, eventLimit?: number): TimelineState; | ||
export declare function resolveStates(resolved: ResolvedTimeline, onlyForTime?: Time): ResolvedStates; | ||
export declare function resolveStates(resolved: ResolvedTimeline, onlyForTime?: Time, cache?: ResolverCache): ResolvedStates; | ||
export declare function applyKeyframeContent(parentContent: Content, keyframeContent: Content): void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tslib_1 = require("tslib"); | ||
var _ = require("underscore"); | ||
var common_1 = require("./common"); | ||
var enums_1 = require("../api/enums"); | ||
var lib_1 = require("../lib"); | ||
function getState(resolved, time, eventLimit) { | ||
if (eventLimit === void 0) { eventLimit = 0; } | ||
var resolvedStates = (isResolvedStates(resolved) ? | ||
const _ = require("underscore"); | ||
const common_1 = require("./common"); | ||
const enums_1 = require("../api/enums"); | ||
const lib_1 = require("../lib"); | ||
function getState(resolved, time, eventLimit = 0) { | ||
const resolvedStates = (isResolvedStates(resolved) ? | ||
resolved : | ||
resolveStates(resolved, time)); | ||
var state = { | ||
const state = { | ||
time: time, | ||
layers: {}, | ||
nextEvents: _.filter(resolvedStates.nextEvents, function (e) { return e.time > time; }) | ||
nextEvents: _.filter(resolvedStates.nextEvents, e => e.time > time) | ||
}; | ||
if (eventLimit) | ||
state.nextEvents = state.nextEvents.slice(0, eventLimit); | ||
_.each(_.keys(resolvedStates.layers), function (layer) { | ||
var o = getStateAtTime(resolvedStates.state, layer, time); | ||
_.each(_.keys(resolvedStates.layers), (layer) => { | ||
const o = getStateAtTime(resolvedStates.state, layer, time); | ||
if (o) | ||
@@ -28,4 +26,4 @@ state.layers[layer] = o; | ||
exports.getState = getState; | ||
function resolveStates(resolved, onlyForTime) { | ||
var resolvedStates = { | ||
function resolveStates(resolved, onlyForTime, cache) { | ||
const resolvedStates = { | ||
options: resolved.options, | ||
@@ -40,5 +38,12 @@ statistics: resolved.statistics, | ||
}; | ||
var resolvedObjects = _.values(resolved.objects); | ||
if (cache && | ||
!onlyForTime && | ||
resolved.statistics.resolvingCount === 0 && | ||
cache.resolvedStates) { | ||
// Nothing has changed since last time, just return the states right away: | ||
return cache.resolvedStates; | ||
} | ||
const resolvedObjects = _.values(resolved.objects); | ||
// Sort to make sure parent groups are evaluated before their children: | ||
resolvedObjects.sort(function (a, b) { | ||
resolvedObjects.sort((a, b) => { | ||
if ((a.resolved.levelDeep || 0) > (b.resolved.levelDeep || 0)) | ||
@@ -56,17 +61,17 @@ return 1; | ||
// and which instances that are interesting | ||
var pointsInTime = {}; | ||
var addPointInTime = function (time, enable, obj, instance) { | ||
const pointsInTime = {}; | ||
const addPointInTime = (time, enable, obj, instance) => { | ||
if (!pointsInTime[time + '']) | ||
pointsInTime[time + ''] = []; | ||
pointsInTime[time + ''].push({ obj: obj, instance: instance, enable: enable }); | ||
pointsInTime[time + ''].push({ obj, instance, enable: enable }); | ||
}; | ||
var eventObjectTimes = {}; | ||
_.each(resolvedObjects, function (obj) { | ||
const eventObjectTimes = {}; | ||
_.each(resolvedObjects, (obj) => { | ||
if (!obj.disabled && | ||
obj.resolved.resolved && | ||
!obj.resolved.isKeyframe) { | ||
var parentTimes_1 = getTimesFromParents(resolved, obj); | ||
const parentTimes = getTimesFromParents(resolved, obj); | ||
if (obj.layer) { // if layer is empty, don't put in state | ||
_.each(obj.resolved.instances, function (instance) { | ||
var useInstance = true; | ||
_.each(obj.resolved.instances, (instance) => { | ||
let useInstance = true; | ||
if (onlyForTime) { | ||
@@ -77,15 +82,15 @@ useInstance = ((instance.start || 0) <= onlyForTime && | ||
if (useInstance) { | ||
var timeEvents_1 = []; | ||
timeEvents_1.push({ time: instance.start, enable: true }); | ||
const timeEvents = []; | ||
timeEvents.push({ time: instance.start, enable: true }); | ||
if (instance.end) | ||
timeEvents_1.push({ time: instance.end, enable: false }); | ||
timeEvents.push({ time: instance.end, enable: false }); | ||
// Also include times from parents, as they could affect the state of this instance: | ||
_.each(parentTimes_1, function (parentTime) { | ||
_.each(parentTimes, (parentTime) => { | ||
if (parentTime && (parentTime.time > (instance.start || 0) && | ||
parentTime.time < (instance.end || Infinity))) { | ||
timeEvents_1.push(parentTime); | ||
timeEvents.push(parentTime); | ||
} | ||
}); | ||
// Save a reference to this instance on all points in time that could affect it: | ||
_.each(timeEvents_1, function (timeEvent) { | ||
_.each(timeEvents, (timeEvent) => { | ||
addPointInTime(timeEvent.time, timeEvent.enable, obj, instance); | ||
@@ -99,3 +104,3 @@ }); | ||
// Also add keyframes to pointsInTime: | ||
_.each(resolvedObjects, function (obj) { | ||
_.each(resolvedObjects, (obj) => { | ||
if (!obj.disabled && | ||
@@ -105,3 +110,3 @@ obj.resolved.resolved && | ||
obj.resolved.parentId) { | ||
_.each(obj.resolved.instances, function (instance) { | ||
_.each(obj.resolved.instances, (instance) => { | ||
// Keyframe start time | ||
@@ -120,18 +125,19 @@ addPointInTime(instance.start, true, obj, instance); | ||
// Then sorting it to determine who takes precedence | ||
var currentState = {}; | ||
var activeObjIds = {}; | ||
var activeKeyframes = {}; | ||
var activeKeyframesChecked = {}; | ||
const currentState = {}; | ||
const activeObjIds = {}; | ||
const activeKeyframes = {}; | ||
const activeKeyframesChecked = {}; | ||
/** The objects in aspiringInstances */ | ||
var aspiringInstances = {}; | ||
var keyframeEvents = []; | ||
var times = _.map(_.keys(pointsInTime), function (time) { return parseFloat(time); }); | ||
const aspiringInstances = {}; | ||
const keyframeEvents = []; | ||
const times = _.map(_.keys(pointsInTime), time => parseFloat(time)); | ||
// Sort chronologically: | ||
times.sort(function (a, b) { | ||
times.sort((a, b) => { | ||
return a - b; | ||
}); | ||
_.each(times, function (time) { | ||
var instancesToCheck = pointsInTime[time]; | ||
var checkedObjectsThisTime = {}; | ||
instancesToCheck.sort(function (a, b) { | ||
for (let i = 0; i < times.length; i++) { | ||
const time = times[i]; | ||
const instancesToCheck = pointsInTime[time]; | ||
const checkedObjectsThisTime = {}; | ||
instancesToCheck.sort((a, b) => { | ||
if (a.obj.resolved && b.obj.resolved) { | ||
@@ -156,14 +162,16 @@ // Keyframes comes first: | ||
}); | ||
_.each(instancesToCheck, function (o) { | ||
var obj = o.obj; | ||
var instance = o.instance; | ||
var toBeEnabled = ((instance.start || 0) <= time && | ||
for (let j = 0; j < instancesToCheck.length; j++) { | ||
const o = instancesToCheck[j]; | ||
const obj = o.obj; | ||
const instance = o.instance; | ||
let toBeEnabled = ((instance.start || 0) <= time && | ||
(instance.end || Infinity) > time); | ||
var layer = obj.layer + ''; | ||
if (!checkedObjectsThisTime[obj.id + '_' + instance.id + '_' + o.enable]) { // Only check each object and event-type once for every point in time | ||
checkedObjectsThisTime[obj.id + '_' + instance.id + '_' + o.enable] = true; | ||
const layer = obj.layer + ''; | ||
const identifier = obj.id + '_' + instance.id + '_' + o.enable; | ||
if (!checkedObjectsThisTime[identifier]) { // Only check each object and event-type once for every point in time | ||
checkedObjectsThisTime[identifier] = true; | ||
if (!obj.resolved.isKeyframe) { | ||
// If object has a parent, only set if parent is on a layer (if layer is set for parent) | ||
if (toBeEnabled && obj.resolved.parentId) { | ||
var parentObj = (obj.resolved.parentId ? | ||
const parentObj = (obj.resolved.parentId ? | ||
resolved.objects[obj.resolved.parentId] : | ||
@@ -180,3 +188,3 @@ null); | ||
// Add to aspiringInstances: | ||
aspiringInstances[obj.layer].push({ obj: obj, instance: instance }); | ||
aspiringInstances[obj.layer].push({ obj, instance }); | ||
} | ||
@@ -186,6 +194,6 @@ else { | ||
// Remove from aspiringInstances: | ||
aspiringInstances[layer] = _.reject(aspiringInstances[layer] || [], function (o) { return o.obj.id === obj.id; }); | ||
aspiringInstances[layer] = _.reject(aspiringInstances[layer] || [], o => o.obj.id === obj.id); | ||
} | ||
// Evaluate the layer to determine who has the throne: | ||
aspiringInstances[layer].sort(function (a, b) { | ||
aspiringInstances[layer].sort((a, b) => { | ||
// Determine who takes precedence: | ||
@@ -211,9 +219,9 @@ // First, sort using priority | ||
// Update current state: | ||
var currentOnTopOfLayer = aspiringInstances[layer][0]; | ||
var prevObj = currentState[layer]; | ||
var replaceOldObj = (currentOnTopOfLayer && | ||
const currentOnTopOfLayer = aspiringInstances[layer][0]; | ||
const prevObj = currentState[layer]; | ||
const replaceOldObj = (currentOnTopOfLayer && | ||
(!prevObj || | ||
prevObj.id !== currentOnTopOfLayer.obj.id || | ||
prevObj.instance.id !== currentOnTopOfLayer.instance.id)); | ||
var removeOldObj = (!currentOnTopOfLayer && | ||
const removeOldObj = (!currentOnTopOfLayer && | ||
prevObj); | ||
@@ -241,28 +249,40 @@ if (replaceOldObj || removeOldObj) { | ||
// Construct a new object clone: | ||
var newObj_1; | ||
let newObj; | ||
if (resolvedStates.objects[currentOnTopOfLayer.obj.id]) { | ||
// Use the already existing one | ||
newObj_1 = resolvedStates.objects[currentOnTopOfLayer.obj.id]; | ||
newObj = resolvedStates.objects[currentOnTopOfLayer.obj.id]; | ||
} | ||
else { | ||
newObj_1 = _.clone(currentOnTopOfLayer.obj); | ||
newObj_1.content = JSON.parse(JSON.stringify(newObj_1.content)); | ||
newObj_1.resolved = tslib_1.__assign(tslib_1.__assign({}, newObj_1.resolved || {}), { instances: [] }); | ||
common_1.addObjectToResolvedTimeline(resolvedStates, newObj_1); | ||
newObj = _.clone(currentOnTopOfLayer.obj); | ||
newObj.content = JSON.parse(JSON.stringify(newObj.content)); | ||
newObj.resolved = { | ||
...newObj.resolved || {}, | ||
instances: [] | ||
}; | ||
common_1.addObjectToResolvedTimeline(resolvedStates, newObj); | ||
} | ||
var newInstance_1 = tslib_1.__assign(tslib_1.__assign({}, currentOnTopOfLayer.instance), { | ||
const newInstance = { | ||
...currentOnTopOfLayer.instance, | ||
// We're setting new start & end times so they match up with the state: | ||
start: time, end: null, fromInstanceId: currentOnTopOfLayer.instance.id, originalEnd: (currentOnTopOfLayer.instance.originalEnd !== undefined ? | ||
start: time, | ||
end: null, | ||
fromInstanceId: currentOnTopOfLayer.instance.id, | ||
originalEnd: (currentOnTopOfLayer.instance.originalEnd !== undefined ? | ||
currentOnTopOfLayer.instance.originalEnd : | ||
currentOnTopOfLayer.instance.end), originalStart: (currentOnTopOfLayer.instance.originalStart !== undefined ? | ||
currentOnTopOfLayer.instance.end), | ||
originalStart: (currentOnTopOfLayer.instance.originalStart !== undefined ? | ||
currentOnTopOfLayer.instance.originalStart : | ||
currentOnTopOfLayer.instance.start) }); | ||
currentOnTopOfLayer.instance.start) | ||
}; | ||
// Make the instance id unique: | ||
_.each(newObj_1.resolved.instances, function (instance) { | ||
if (instance.id === newInstance_1.id) { | ||
newInstance_1.id = newInstance_1.id + '_$' + newObj_1.resolved.instances.length; | ||
_.each(newObj.resolved.instances, instance => { | ||
if (instance.id === newInstance.id) { | ||
newInstance.id = newInstance.id + '_$' + newObj.resolved.instances.length; | ||
} | ||
}); | ||
newObj_1.resolved.instances.push(newInstance_1); | ||
var newObjInstance = tslib_1.__assign(tslib_1.__assign({}, newObj_1), { instance: newInstance_1 }); | ||
newObj.resolved.instances.push(newInstance); | ||
const newObjInstance = { | ||
...newObj, | ||
instance: newInstance | ||
}; | ||
// Save to current state: | ||
@@ -275,9 +295,9 @@ currentState[layer] = newObjInstance; | ||
// Add to nextEvents: | ||
if (newInstance_1.start > (onlyForTime || 0)) { | ||
if (newInstance.start > (onlyForTime || 0)) { | ||
resolvedStates.nextEvents.push({ | ||
type: enums_1.EventType.START, | ||
time: newInstance_1.start, | ||
time: newInstance.start, | ||
objId: obj.id | ||
}); | ||
eventObjectTimes[newInstance_1.start + ''] = enums_1.EventType.START; | ||
eventObjectTimes[newInstance.start + ''] = enums_1.EventType.START; | ||
} | ||
@@ -294,9 +314,12 @@ } | ||
// Is a keyframe | ||
var keyframe = obj; | ||
const keyframe = obj; | ||
// Add keyframe to resolvedStates.objects: | ||
resolvedStates.objects[keyframe.id] = keyframe; | ||
var toBeEnabled_1 = ((instance.start || 0) <= time && | ||
const toBeEnabled = ((instance.start || 0) <= time && | ||
(instance.end || Infinity) > time); | ||
if (toBeEnabled_1) { | ||
var newObjInstance = tslib_1.__assign(tslib_1.__assign({}, obj), { instance: instance }); | ||
if (toBeEnabled) { | ||
const newObjInstance = { | ||
...obj, | ||
instance: instance | ||
}; | ||
activeKeyframes[obj.id] = newObjInstance; | ||
@@ -310,16 +333,21 @@ } | ||
} | ||
}); | ||
} | ||
// Go through keyframes: | ||
_.each(activeKeyframes, function (objInstance, objId) { | ||
var keyframe = objInstance; | ||
var instance = objInstance.instance; | ||
_.each(activeKeyframes, (objInstance, objId) => { | ||
const keyframe = objInstance; | ||
const instance = objInstance.instance; | ||
// Check if the keyframe's parent is currently active? | ||
if (keyframe.resolved.parentId) { | ||
var parentObj = activeObjIds[keyframe.resolved.parentId]; | ||
const parentObj = activeObjIds[keyframe.resolved.parentId]; | ||
if (parentObj && parentObj.layer) { // keyframe is on an active object | ||
var parentObjInstance = currentState[parentObj.layer]; | ||
const parentObjInstance = currentState[parentObj.layer]; | ||
if (parentObjInstance) { | ||
if (!activeKeyframesChecked[objId]) { // hasn't started before | ||
activeKeyframesChecked[objId] = true; | ||
var keyframeInstance = tslib_1.__assign(tslib_1.__assign({}, keyframe), { instance: instance, isKeyframe: true, keyframeEndTime: instance.end }); | ||
const keyframeInstance = { | ||
...keyframe, | ||
instance: instance, | ||
isKeyframe: true, | ||
keyframeEndTime: instance.end | ||
}; | ||
// Note: The keyframes are a little bit special, since their contents are applied to their parents. | ||
@@ -352,5 +380,6 @@ // That application is done in the getStateAtTime function. | ||
}); | ||
}); | ||
} | ||
// Go through the keyframe events and add them to nextEvents: | ||
_.each(keyframeEvents, function (keyframeEvent) { | ||
for (let i = 0; i < keyframeEvents.length; i++) { | ||
const keyframeEvent = keyframeEvents[i]; | ||
// tslint:disable-next-line | ||
@@ -361,7 +390,7 @@ if (eventObjectTimes[keyframeEvent.time + ''] === undefined) { // no need to put a keyframe event if there's already another event there | ||
} | ||
}); | ||
} | ||
if (onlyForTime) { | ||
resolvedStates.nextEvents = _.filter(resolvedStates.nextEvents, function (e) { return e.time > onlyForTime; }); | ||
resolvedStates.nextEvents = _.filter(resolvedStates.nextEvents, e => e.time > onlyForTime); | ||
} | ||
resolvedStates.nextEvents.sort(function (a, b) { | ||
resolvedStates.nextEvents.sort((a, b) => { | ||
if (a.time > b.time) | ||
@@ -381,2 +410,5 @@ return 1; | ||
}); | ||
if (cache && !onlyForTime) { | ||
cache.resolvedStates = resolvedStates; | ||
} | ||
return resolvedStates; | ||
@@ -386,3 +418,3 @@ } | ||
function applyKeyframeContent(parentContent, keyframeContent) { | ||
_.each(keyframeContent, function (value, attr) { | ||
_.each(keyframeContent, (value, attr) => { | ||
if (_.isArray(value)) { | ||
@@ -407,8 +439,8 @@ if (!_.isArray(parentContent[attr])) | ||
function getTimesFromParents(resolved, obj) { | ||
var times = []; | ||
var parentObj = (obj.resolved.parentId ? | ||
let times = []; | ||
const parentObj = (obj.resolved.parentId ? | ||
resolved.objects[obj.resolved.parentId] : | ||
null); | ||
if (parentObj && parentObj.resolved.resolved) { | ||
_.each(parentObj.resolved.instances, function (instance) { | ||
_.each(parentObj.resolved.instances, instance => { | ||
times.push({ time: instance.start, enable: true }); | ||
@@ -436,17 +468,17 @@ if (instance.end) | ||
function getStateAtTime(states, layer, requestTime) { | ||
var layerStates = states[layer] || {}; | ||
var times = _.map(_.keys(layerStates), function (time) { return parseFloat(time); }); | ||
times.sort(function (a, b) { | ||
const layerStates = states[layer] || {}; | ||
const times = _.map(_.keys(layerStates), time => parseFloat(time)); | ||
times.sort((a, b) => { | ||
return a - b; | ||
}); | ||
var state = null; | ||
var isCloned = false; | ||
_.find(times, function (time) { | ||
let state = null; | ||
let isCloned = false; | ||
_.find(times, (time) => { | ||
if (time <= requestTime) { | ||
var currentStateInstances = layerStates[time + '']; | ||
const currentStateInstances = layerStates[time + '']; | ||
if (currentStateInstances && currentStateInstances.length) { | ||
_.each(currentStateInstances, function (currentState) { | ||
_.each(currentStateInstances, currentState => { | ||
if (currentState && | ||
currentState.isKeyframe) { | ||
var keyframe = currentState; | ||
const keyframe = currentState; | ||
if (state && keyframe.resolved.parentId === state.id) { | ||
@@ -456,3 +488,6 @@ if ((keyframe.keyframeEndTime || Infinity) > requestTime) { | ||
isCloned = true; | ||
state = tslib_1.__assign(tslib_1.__assign({}, state), { content: JSON.parse(JSON.stringify(state.content)) }); | ||
state = { | ||
...state, | ||
content: JSON.parse(JSON.stringify(state.content)) | ||
}; | ||
} | ||
@@ -459,0 +494,0 @@ // Apply the keyframe on the state: |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _ = require("underscore"); | ||
const _ = require("underscore"); | ||
function validateObject0(obj, strict, uniqueIds) { | ||
@@ -8,37 +8,43 @@ if (!uniqueIds) | ||
if (!obj) | ||
throw new Error("Object is undefined"); | ||
if (!_.isObject(obj)) | ||
throw new Error("Object is not an object"); | ||
throw new Error(`Object is undefined`); | ||
if (typeof obj !== 'object') | ||
throw new Error(`Object is not an object`); | ||
if (!obj.id) | ||
throw new Error("Object missing \"id\" attribute"); | ||
if (!_.isString(obj.id)) | ||
throw new Error("Object \"id\" attribute is not a string: \"" + obj.id + "\""); | ||
throw new Error(`Object missing "id" attribute`); | ||
if (typeof obj.id !== 'string') | ||
throw new Error(`Object "id" attribute is not a string: "${obj.id}"`); | ||
if (uniqueIds[obj.id]) | ||
throw new Error("Object id \"" + obj.id + "\" is not unique"); | ||
throw new Error(`Object id "${obj.id}" is not unique`); | ||
uniqueIds[obj.id] = true; | ||
// @ts-ignore | ||
if (obj.layer === undefined) | ||
throw new Error("Object \"" + obj.id + "\": \"layer\" attribute is undefined"); | ||
throw new Error(`Object "${obj.id}": "layer" attribute is undefined`); | ||
if (!obj.content) | ||
throw new Error("Object \"" + obj.id + "\": \"content\" attribute must be set"); | ||
throw new Error(`Object "${obj.id}": "content" attribute must be set`); | ||
if (!obj.enable) | ||
throw new Error("Object \"" + obj.id + "\": \"enable\" attribute must be set"); | ||
if (obj.enable.start !== undefined) { | ||
if (strict && obj.enable.while !== undefined) | ||
throw new Error("Object \"" + obj.id + "\": \"enable.start\" and \"enable.while\" cannot be combined"); | ||
if (strict && | ||
obj.enable.end !== undefined && | ||
obj.enable.duration !== undefined) | ||
throw new Error("Object \"" + obj.id + "\": \"enable.end\" and \"enable.duration\" cannot be combined"); | ||
} | ||
else if (obj.enable.while !== undefined) { | ||
if (strict && obj.enable.end !== undefined) | ||
throw new Error("Object \"" + obj.id + "\": \"enable.while\" and \"enable.end\" cannot be combined"); | ||
if (strict && obj.enable.duration !== undefined) | ||
throw new Error("Object \"" + obj.id + "\": \"enable.while\" and \"enable.duration\" cannot be combined"); | ||
} | ||
else | ||
throw new Error("Object \"" + obj.id + "\": \"enable.start\" or \"enable.while\" must be set"); | ||
throw new Error(`Object "${obj.id}": "enable" attribute must be set`); | ||
const enables = (_.isArray(obj.enable) ? | ||
obj.enable : | ||
[obj.enable]); | ||
_.each(enables, enable => { | ||
if (enable.start !== undefined) { | ||
if (strict && enable.while !== undefined) | ||
throw new Error(`Object "${obj.id}": "enable.start" and "enable.while" cannot be combined`); | ||
if (strict && | ||
enable.end !== undefined && | ||
enable.duration !== undefined) | ||
throw new Error(`Object "${obj.id}": "enable.end" and "enable.duration" cannot be combined`); | ||
} | ||
else if (enable.while !== undefined) { | ||
if (strict && enable.end !== undefined) | ||
throw new Error(`Object "${obj.id}": "enable.while" and "enable.end" cannot be combined`); | ||
if (strict && enable.duration !== undefined) | ||
throw new Error(`Object "${obj.id}": "enable.while" and "enable.duration" cannot be combined`); | ||
} | ||
else | ||
throw new Error(`Object "${obj.id}": "enable.start" or "enable.while" must be set`); | ||
}); | ||
if (obj.keyframes) { | ||
_.each(obj.keyframes, function (keyframe, i) { | ||
for (let i = 0; i < obj.keyframes.length; i++) { | ||
const keyframe = obj.keyframes[i]; | ||
try { | ||
@@ -48,18 +54,20 @@ validateKeyframe0(keyframe, strict, uniqueIds); | ||
catch (e) { | ||
throw new Error("Object \"" + obj.id + "\" keyframe[" + i + "]: " + e); | ||
throw new Error(`Object "${obj.id}" keyframe[${i}]: ${e}`); | ||
} | ||
}); | ||
} | ||
} | ||
if (obj.classes) { | ||
_.each(obj.classes, function (className, i) { | ||
if (className && !_.isString(className)) | ||
throw new Error("Object \"" + obj.id + "\": \"classes[" + i + "]\" is not a string"); | ||
}); | ||
for (let i = 0; i < obj.classes.length; i++) { | ||
const className = obj.classes[i]; | ||
if (className && typeof className !== 'string') | ||
throw new Error(`Object "${obj.id}": "classes[${i}]" is not a string`); | ||
} | ||
} | ||
if (obj.children && !obj.isGroup) | ||
throw new Error("Object \"" + obj.id + "\": attribute \"children\" is set but \"isGroup\" is not"); | ||
throw new Error(`Object "${obj.id}": attribute "children" is set but "isGroup" is not`); | ||
if (obj.isGroup && !obj.children) | ||
throw new Error("Object \"" + obj.id + "\": attribute \"isGroup\" is set but \"children\" missing"); | ||
throw new Error(`Object "${obj.id}": attribute "isGroup" is set but "children" missing`); | ||
if (obj.children) { | ||
_.each(obj.children, function (child, i) { | ||
for (let i = 0; i < obj.children.length; i++) { | ||
const child = obj.children[i]; | ||
try { | ||
@@ -69,8 +77,8 @@ validateObject0(child, strict, uniqueIds); | ||
catch (e) { | ||
throw new Error("Object \"" + obj.id + "\" child[" + i + "]: " + e); | ||
throw new Error(`Object "${obj.id}" child[${i}]: ${e}`); | ||
} | ||
}); | ||
} | ||
} | ||
if (obj.priority !== undefined && !_.isNumber(obj.priority)) | ||
throw new Error("Object \"" + obj.id + "\": attribute \"priority\" is not a number"); | ||
throw new Error(`Object "${obj.id}": attribute "priority" is not a number`); | ||
} | ||
@@ -81,37 +89,43 @@ function validateKeyframe0(keyframe, strict, uniqueIds) { | ||
if (!keyframe) | ||
throw new Error("Keyframe is undefined"); | ||
if (!_.isObject(keyframe)) | ||
throw new Error("Keyframe is not an object"); | ||
throw new Error(`Keyframe is undefined`); | ||
if (typeof keyframe !== 'object') | ||
throw new Error(`Keyframe is not an object`); | ||
if (!keyframe.id) | ||
throw new Error("Keyframe missing id attribute"); | ||
if (!_.isString(keyframe.id)) | ||
throw new Error("Keyframe id attribute is not a string: \"" + keyframe.id + "\""); | ||
throw new Error(`Keyframe missing id attribute`); | ||
if (typeof keyframe.id !== 'string') | ||
throw new Error(`Keyframe id attribute is not a string: "${keyframe.id}"`); | ||
if (uniqueIds[keyframe.id]) | ||
throw new Error("Keyframe id \"" + keyframe.id + "\" is not unique"); | ||
throw new Error(`Keyframe id "${keyframe.id}" is not unique`); | ||
uniqueIds[keyframe.id] = true; | ||
if (!keyframe.content) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"content\" attribute must be set"); | ||
throw new Error(`Keyframe "${keyframe.id}": "content" attribute must be set`); | ||
if (!keyframe.enable) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable\" attribute must be set"); | ||
if (keyframe.enable.start !== undefined) { | ||
if (strict && keyframe.enable.while !== undefined) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable.start\" and \"enable.while\" cannot be combined"); | ||
if (strict && | ||
keyframe.enable.end !== undefined && | ||
keyframe.enable.duration !== undefined) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable.end\" and \"enable.duration\" cannot be combined"); | ||
} | ||
else if (keyframe.enable.while !== undefined) { | ||
if (strict && keyframe.enable.end !== undefined) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable.while\" and \"enable.end\" cannot be combined"); | ||
if (strict && keyframe.enable.duration !== undefined) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable.while\" and \"enable.duration\" cannot be combined"); | ||
} | ||
else | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"enable.start\" or \"enable.while\" must be set"); | ||
throw new Error(`Keyframe "${keyframe.id}": "enable" attribute must be set`); | ||
const enables = (_.isArray(keyframe.enable) ? | ||
keyframe.enable : | ||
[keyframe.enable]); | ||
_.each(enables, enable => { | ||
if (enable.start !== undefined) { | ||
if (strict && enable.while !== undefined) | ||
throw new Error(`Keyframe "${keyframe.id}": "enable.start" and "enable.while" cannot be combined`); | ||
if (strict && | ||
enable.end !== undefined && | ||
enable.duration !== undefined) | ||
throw new Error(`Keyframe "${keyframe.id}": "enable.end" and "enable.duration" cannot be combined`); | ||
} | ||
else if (enable.while !== undefined) { | ||
if (strict && enable.end !== undefined) | ||
throw new Error(`Keyframe "${keyframe.id}": "enable.while" and "enable.end" cannot be combined`); | ||
if (strict && enable.duration !== undefined) | ||
throw new Error(`Keyframe "${keyframe.id}": "enable.while" and "enable.duration" cannot be combined`); | ||
} | ||
else | ||
throw new Error(`Keyframe "${keyframe.id}": "enable.start" or "enable.while" must be set`); | ||
}); | ||
if (keyframe.classes) { | ||
_.each(keyframe.classes, function (className, i) { | ||
for (let i = 0; i < keyframe.classes.length; i++) { | ||
const className = keyframe.classes[i]; | ||
if (className && !_.isString(className)) | ||
throw new Error("Keyframe \"" + keyframe.id + "\": \"classes[" + i + "]\" is not a string"); | ||
}); | ||
throw new Error(`Keyframe "${keyframe.id}": "classes[${i}]" is not a string`); | ||
} | ||
} | ||
@@ -125,6 +139,7 @@ } | ||
function validateTimeline(timeline, strict) { | ||
var uniqueIds = {}; | ||
_.each(timeline, function (obj) { | ||
const uniqueIds = {}; | ||
for (let i = 0; i < timeline.length; i++) { | ||
const obj = timeline[i]; | ||
validateObject0(obj, strict, uniqueIds); | ||
}); | ||
} | ||
} | ||
@@ -131,0 +146,0 @@ exports.validateTimeline = validateTimeline; |
{ | ||
"name": "superfly-timeline", | ||
"version": "7.3.1", | ||
"version": "8.0.0", | ||
"description": "A collection of rules as well as a resolver for placing objects on a virtual timeline.", | ||
@@ -47,4 +47,5 @@ "license": "MIT", | ||
"reset": "git clean -dfx && git reset --hard && yarn", | ||
"validate:dependencies": "yarn audit && yarn license-validate", | ||
"license-validate": "node-license-validator -p -d --allow-licenses MIT BSD BSD-3-Clause ISC Apache" | ||
"validate:dependencies": "yarn audit --groups dependencies && yarn license-validate", | ||
"validate:dev-dependencies": "yarn audit --groups devDependencies", | ||
"license-validate": "node-license-validator -p -d --allow-licenses MIT BSD 0BSD BSD-3-Clause ISC Apache" | ||
}, | ||
@@ -74,3 +75,3 @@ "scripts-info": { | ||
"engines": { | ||
"node": ">=4.5" | ||
"node": ">=10.10" | ||
}, | ||
@@ -87,13 +88,10 @@ "files": [ | ||
"@types/underscore": "^1.8.9", | ||
"codecov": "^3.5.0", | ||
"codecov": "^3.7.0", | ||
"fast-clone": "^1.5.13", | ||
"gh-pages": "^2.0.1", | ||
"jest": "^24.8.0", | ||
"mkdirp": "^0.5.1", | ||
"node-license-validator": "^1.3.0", | ||
"npm-scripts-info": "^0.3.9", | ||
"nyc": "^14.1.1", | ||
"open-cli": "^5.0.0", | ||
"sleep-ms": "^2.0.1", | ||
"standard-version": "^7.0.0", | ||
"open-cli": "^6.0.1", | ||
"standard-version": "^8.0.0", | ||
"trash-cli": "^3.0.0", | ||
@@ -103,3 +101,3 @@ "ts-jest": "^24.0.2", | ||
"tslint-config-standard": "^8.0.1", | ||
"typedoc": "^0.15.0", | ||
"typedoc": "^0.16.0", | ||
"typescript": "~3.6.4" | ||
@@ -115,3 +113,3 @@ }, | ||
"dependencies": { | ||
"tslib": "^1.10.0", | ||
"tslib": "^1.13.0", | ||
"underscore": "^1.9.1" | ||
@@ -118,0 +116,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
265285
17
34
2674
Updatedtslib@^1.13.0