UNPKG

superfly-timeline

Version:

Resolver for defining objects with temporal boolean logic relationships on a timeline

217 lines 9.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimelineValidator = void 0; const ExpressionHandler_1 = require("./ExpressionHandler"); const lib_1 = require("./lib/lib"); const performance_1 = require("./lib/performance"); /** These characters are reserved and cannot be used in ids, etc */ const RESERVED_CHARACTERS = /[#.$]/g; /** These characters are reserved for possible future use and cannot be used in ids, etc */ const FUTURE_RESERVED_CHARACTERS = /[=?@{}[\]^§]/g; /** * Note: A TimelineValidator instance is short-lived and used to validate a timeline. * Intended usage: * 1. const validator = new TimelineValidator() * 2. validator.validateTimeline(timeline) * or: * 1. const validator = new TimelineValidator() * 2. validator.validateObject(obj) * or: * 1. const validator = new TimelineValidator() * 2. validator.validateKeyframe(obj) */ class TimelineValidator { constructor() { this.uniqueIds = {}; } /** Validates all objects in the timeline. Throws an error if something's wrong. */ validateTimeline( /** The timeline to validate */ timeline, /** Set to true to enable some optional strict rules. Set this to true to increase future compatibility. */ strict) { const toc = (0, performance_1.tic)(' validateTimeline'); for (let i = 0; i < timeline.length; i++) { const obj = timeline[i]; this.validateObject(obj, strict); } toc(); } /** Validates a simgle Timeline-object. Throws an error if something's wrong. */ validateObject( /** The object to validate */ obj, /** Set to true to enable some optional strict rules. Set this to true to increase future compatibility. */ strict) { if (!obj) throw new Error(`Object is undefined`); if (typeof obj !== 'object') throw new Error(`Object is not an object`); try { this.validateId(obj, strict); this.validateLayer(obj, strict); this.validateContent(obj); this.validateEnable(obj, strict); if (obj.keyframes) { for (let i = 0; i < obj.keyframes.length; i++) { const keyframe = obj.keyframes[i]; try { this.validateKeyframe(keyframe, strict); } catch (e) { throw new Error(`Keyframe[${i}]: ${e}`); } } } this.validateClasses(obj, strict); if (obj.children && !obj.isGroup) throw new Error(`Attribute "children" is set but "isGroup" is not`); if (obj.isGroup && !obj.children) throw new Error(`Attribute "isGroup" is set but "children" missing`); if (obj.children) { for (let i = 0; i < obj.children.length; i++) { const child = obj.children[i]; try { this.validateObject(child, strict); } catch (e) { throw new Error(`Child[${i}]: ${e}`); } } } if (obj.priority !== undefined && typeof obj.priority !== 'number') throw new Error(`Attribute "priority" is not a number`); } catch (err) { if (err instanceof Error) { const err2 = new Error(`Object "${obj.id}": ${err.message}`); err2.stack = err.stack; throw err2; } else throw err; } } /** Validates a simgle Timeline-object. Throws an error if something's wrong. */ validateKeyframe( /** The object to validate */ keyframe, /** Set to true to enable some optional strict rules. Set this to true to increase future compatibility */ strict) { if (!keyframe) throw new Error(`Keyframe is undefined`); if (typeof keyframe !== 'object') throw new Error(`Keyframe is not an object`); try { this.validateId(keyframe, strict); this.validateContent(keyframe); this.validateEnable(keyframe, strict); this.validateClasses(keyframe, strict); } catch (err) { if (err instanceof Error) { const err2 = new Error(`Keyframe "${keyframe.id}": ${err.message}`); err2.stack = err.stack; throw err; } else throw err; } } validateId(obj, strict) { if (!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}"`); try { TimelineValidator.validateReferenceString(obj.id, strict); } catch (err) { throw new Error(`Object "id" attribute: ${err}`); } if (this.uniqueIds[obj.id]) throw new Error(`id "${obj.id}" is not unique`); this.uniqueIds[obj.id] = true; } validateLayer(obj, strict) { if (obj.layer === undefined) throw new Error(`"layer" attribute is undefined. (If an object is to have no layer, set this to an empty string.)`); try { TimelineValidator.validateReferenceString(`${obj.layer}`, strict); } catch (err) { throw new Error(`"layer" attribute: ${err}`); } } validateContent(obj) { if (!obj.content) throw new Error(`"content" attribute must be set`); } validateEnable(obj, strict) { if (!obj.enable) throw new Error(`"enable" attribute must be set`); const enables = (0, lib_1.ensureArray)(obj.enable); for (let i = 0; i < enables.length; i++) { const enable = enables[i]; if (enable.start !== undefined) { if (strict && enable.while !== undefined) throw new Error(`"enable.start" and "enable.while" cannot be combined`); if (strict && enable.end !== undefined && enable.duration !== undefined) throw new Error(`"enable.end" and "enable.duration" cannot be combined`); } else if (enable.while !== undefined) { if (strict && enable.end !== undefined) throw new Error(`"enable.while" and "enable.end" cannot be combined`); if (strict && enable.duration !== undefined) throw new Error(`"enable.while" and "enable.duration" cannot be combined`); } else throw new Error(`"enable.start" or "enable.while" must be set`); } } validateClasses(obj, strict) { if (obj.classes) { for (let i = 0; i < obj.classes.length; i++) { const className = obj.classes[i]; if (className && typeof className !== 'string') throw new Error(`"classes[${i}]" is not a string`); try { TimelineValidator.validateReferenceString(className, strict); } catch (err) { throw new Error(` "classes[${i}]": ${err}`); } } } } /** * Validates a string that is used in Timeline as a reference (an id, a class or layer) * @param str The string to validate * @param strict Set to true to enable some strict rules (rules that can possibly be ignored) */ static validateReferenceString(str, strict) { if (!str) return; const matchesOperators = ExpressionHandler_1.REGEXP_OPERATORS.test(str); const matchesReserved = RESERVED_CHARACTERS.test(str); const matchesFutureReserved = strict && FUTURE_RESERVED_CHARACTERS.test(str); if (matchesOperators || matchesReserved || matchesFutureReserved) { const matchOperators = str.match(ExpressionHandler_1.REGEXP_OPERATORS) ?? []; const matchReserved = str.match(RESERVED_CHARACTERS) ?? []; const matchFutureReserved = (strict && str.match(FUTURE_RESERVED_CHARACTERS)) || []; throw new Error(`The string "${str}" contains characters which aren't allowed in Timeline: ${[ matchOperators.length > 0 && `${matchOperators.map((o) => `"${o}"`).join(', ')} (is an operator)`, matchReserved.length > 0 && `${matchReserved.map((o) => `"${o}"`).join(', ')} (is a reserved character)`, matchFutureReserved.length > 0 && `${matchFutureReserved .map((o) => `"${o}"`) .join(', ')} (is a strict reserved character and might be used in the future)`, ] .filter(Boolean) .join(', ')}`); } } } exports.TimelineValidator = TimelineValidator; //# sourceMappingURL=TimelineValidator.js.map