UNPKG

@nent/core

Version:

Functional elements to add routing, data-binding, dynamic HTML, declarative actions, audio, video, and so much more. Supercharge static HTML files into web apps without script or builds.

358 lines (351 loc) 14.3 kB
/*! * NENT 2022 */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const index$1 = require('./index-1829aebc.js'); const index = require('./index-637e8c28.js'); const logging = require('./logging-37c154cf.js'); const state$1 = require('./state-f97ff0e6.js'); const interfaces$3 = require('./interfaces-1da33056.js'); const state = require('./state-246c735d.js'); const elements = require('./elements-e37cb9a5.js'); const interfaces$1 = require('./interfaces-bd37d637.js'); const interfaces = require('./interfaces-564b9114.js'); const interfaces$2 = require('./interfaces-023ea612.js'); require('./index-96f3ab3f.js'); /* istanbul ignore file */ /** * It takes a root element and a default duration, and returns an array of objects that describe the * elements that have `n-in-time` and `n-out-time` attributes * @param {HTMLElement} rootElement - The root element to search for timed nodes. * @param {number} defaultDuration - The default duration of the animation. * @returns An array of TimedNode objects. */ function captureElementChildTimedNodes(rootElement, defaultDuration) { var _a; const timedNodes = []; (_a = rootElement .querySelectorAll('[n-in-time], [n-out-time]')) === null || _a === void 0 ? void 0 : _a.forEach(element => { const startAttribute = element.getAttribute('n-in-time'); const start = startAttribute ? Number.parseFloat(startAttribute) : 0; const endAttribute = element.getAttribute('n-out-time'); const end = endAttribute ? Number.parseFloat(endAttribute) : defaultDuration; timedNodes.push({ start, end, classIn: element.getAttribute('n-in-class'), classOut: element.getAttribute('n-out-class'), element, }); }); return timedNodes; } /** * It resolves the `n-time-to` and `n-percentage-to` attributes, and it resolves the `n-time-in` and * `n-time-out` attributes * @param {HTMLElement} rootElement - The root element of the component. * @param {TimedNode[]} timedNodes - An array of TimedNode objects, which are defined as: * @param {number} elapsedSeconds - The number of seconds that have elapsed since the start of the * video. * @param {number} percentage - The percentage of the video that has elapsed. */ function resolveElementChildTimedNodesByTime(rootElement, timedNodes, elapsedSeconds, percentage) { timedNodes === null || timedNodes === void 0 ? void 0 : timedNodes.forEach(node => { if (node.start > -1 && elapsedSeconds >= node.start && (node.end > -1 ? elapsedSeconds < node.end : true)) { // Time is after start and before end, if it exists if (node.classIn && !node.element.classList.contains(node.classIn)) { node.element.classList.add(node.classIn); } if (node.element.hasAttribute('hidden')) { // Otherwise, if there's a hidden attribute, remove it node.element.removeAttribute('hidden'); } } if (node.end > -1 && elapsedSeconds >= node.end) { // Time is after end, if it exists if (node.classIn && node.element.classList.contains(node.classIn)) { // Remove the in class, if it exists node.element.classList.remove(node.classIn); } if (node.classOut) { // If a class-out was specified and isn't on the element, add it if (!node.element.classList.contains(node.classOut)) { node.element.classList.add(node.classOut); } } else if (!node.element.hasAttribute('hidden')) { // Otherwise, if there's no hidden attribute, add it node.element.setAttribute('hidden', ''); } } }); // Resolve n-time-to const timeValueElements = rootElement.querySelectorAll('[n-time-to]'); timeValueElements === null || timeValueElements === void 0 ? void 0 : timeValueElements.forEach(el => { const seconds = elapsedSeconds; const attributeName = el.getAttribute('n-time-to'); if (attributeName) { el.setAttribute(attributeName, seconds.toString()); } else { el.childNodes.forEach(cn => cn.remove()); el.append(document.createTextNode(seconds.toString())); } }); // Resolve n-percentage-to const timePercentageValueElements = rootElement.querySelectorAll('[n-percentage-to]'); timePercentageValueElements === null || timePercentageValueElements === void 0 ? void 0 : timePercentageValueElements.forEach(element => { const attributeName = element.getAttribute('n-percentage-to'); if (attributeName) { element.setAttribute(attributeName, percentage.toFixed(2)); } else { element.childNodes.forEach(cn => cn.remove()); element.append(document.createTextNode(`${Math.round(percentage * 100)}%`)); } }); } /** * It removes the `classIn` and `classOut` classes from the `timedNodes` and resets the `n-time-to` and * `n-percentage-to` attributes to their initial values * @param {HTMLElement} rootElement - The root element of the component. * @param {TimedNode[]} timedNodes - This is an array of TimedNode objects. */ function restoreElementChildTimedNodes(rootElement, timedNodes) { timedNodes === null || timedNodes === void 0 ? void 0 : timedNodes.forEach(node => { if (node.classIn && node.element.classList.contains(node.classIn)) { node.element.classList.remove(node.classIn); } if (node.classOut && node.element.classList.contains(node.classOut)) { node.element.classList.remove(node.classOut); } }); // Resolve n-time-to const timeValueElements = rootElement.querySelectorAll('[n-time-to]'); timeValueElements === null || timeValueElements === void 0 ? void 0 : timeValueElements.forEach(el => { const attributeName = el.getAttribute('n-time-to'); if (attributeName) { el.setAttribute(attributeName, '0'); } else { el.childNodes.forEach(cn => cn.remove()); el.append(document.createTextNode('0')); } }); // Resolve n-percentage-to const timePercentageValueElements = rootElement.querySelectorAll('[n-percentage-to]'); timePercentageValueElements === null || timePercentageValueElements === void 0 ? void 0 : timePercentageValueElements.forEach(el => { const attributeName = el.getAttribute('n-percentage-to'); if (attributeName) { el.setAttribute(attributeName, '0'); } else { el.childNodes.forEach(cn => cn.remove()); el.append(document.createTextNode('100%')); } }); } /* It subscribes to the timer's `OnInterval` and `OnEnd` events, and when those events are emitted, it activates any `n-action-activator` elements that are configured to activate at that time, and it also sends any `n-presentation-action` elements that are configured to send at that time */ class PresentationService { /** * > This function creates a new instance of the Presentation class * @param {HTMLElement} el - HTMLElement - the element that will be the root of the presentation * @param {ITimer} timeEmitter - ITimer * @param {boolean} [elements=false] - boolean = false * @param {string | null} [analyticsEvent=null] - string | null = null * @param {(() => void) | null} [onEnd=null] - (() => void) | null = null, * @param {boolean} [debug=false] - boolean = false, */ constructor(el, timeEmitter, elements = false, analyticsEvent = null, onEnd = null, debug = false) { this.el = el; this.timeEmitter = timeEmitter; this.elements = elements; this.analyticsEvent = analyticsEvent; this.onEnd = onEnd; this.debug = debug; this.timedNodes = []; this.activatedActions = []; if (this.elements) { this.timedNodes = captureElementChildTimedNodes(this.el, this.timeEmitter.durationSeconds); logging.debugIf(this.debug, `presentation: found ${this.timedNodes.length} timed-child elements`); } logging.debugIf(this.debug, `presentation: service created`); } get actionActivators() { return Array.from(this.el.querySelectorAll('n-action-activator')); } get actions() { return Array.from(this.el.querySelectorAll('n-presentation-action')).map(a => a); } async handleInterval(time) { if (this.elements) { resolveElementChildTimedNodesByTime(this.el, this.timedNodes, time.elapsedSeconds, time.percentage); } if (this.analyticsEvent) { const data = { event: this.analyticsEvent, time, }; index.actionBus.emit(interfaces.ANALYTICS_TOPIC, { topic: interfaces.ANALYTICS_TOPIC, command: interfaces.ANALYTICS_COMMANDS.SendViewTime, data, }); } await elements.activateActionActivators(this.actionActivators, interfaces$1.ActionActivationStrategy.AtTime, activator => { if (this.activatedActions.includes(activator)) return false; if (activator.time && time.elapsedSeconds >= activator.time) { this.activatedActions.push(activator); return true; } return false; }); await elements.sendActions(this.actions, action => { return action.time && time.elapsedSeconds >= action.time; }); } async handleEnded(time) { var _a; if (this.elements) { resolveElementChildTimedNodesByTime(this.el, this.timedNodes, time.elapsedSeconds, time.percentage); } await elements.activateActionActivators(this.actionActivators, interfaces$1.ActionActivationStrategy.AtTimeEnd); await elements.sendActions(this.actions, action => { return action.time == 'end'; }); (_a = this.onEnd) === null || _a === void 0 ? void 0 : _a.call(this); } /** * > The function subscribes to the `timeEmitter` and listens for the `OnInterval` and `OnEnd` events */ subscribe() { this.intervalSubscription = this.timeEmitter.on(interfaces$2.TIMER_EVENTS.OnInterval, (time) => { this.handleInterval(time).catch(e => logging.error(e)); }); this.endSubscription = this.timeEmitter.on(interfaces$2.TIMER_EVENTS.OnEnd, (time) => { logging.debugIf(this.debug, `presentation: ended`); this.handleEnded(time).catch(e => logging.error(e)); }); } /** * It unsubscribes from the interval and end subscriptions. */ unsubscribe() { var _a, _b; if (this.elements) { restoreElementChildTimedNodes(this.el, this.timedNodes); } (_a = this.intervalSubscription) === null || _a === void 0 ? void 0 : _a.call(this); (_b = this.endSubscription) === null || _b === void 0 ? void 0 : _b.call(this); } } const Presentation = class { constructor(hostRef) { index$1.registerInstance(this, hostRef); this.elementWithTimer = null; this.timer = null; /** * The element selector for the timer-element to * bind for interval events. If left blank, it looks * first an n-timer, then for the first n-video. * * If none are found, it creates one manually and starts * it immediately */ this.timerElement = null; /** * To debug timed elements, set this value to true. */ this.debug = false; /** * Go to the next view after the timer ends */ this.nextAfter = false; } get currentRoute() { var _a; const parent = this.el.closest('n-view-prompt') || this.el.closest('n-view'); if (parent) return parent.route; return ((_a = state.state.router) === null || _a === void 0 ? void 0 : _a.exactRoute) || null; } componentWillLoad() { logging.debugIf(this.debug, `n-presentation: loading`); let element = this.timerElement ? this.el.querySelector(this.timerElement) || this.el.ownerDocument.querySelector(this.timerElement) || null : this.el.querySelector('n-presentation-timer') || this.el.ownerDocument.querySelector('n-presentation-timer') || this.el.querySelector('n-video') || this.el.ownerDocument.querySelector('n-video') || null; this.elementWithTimer = element || null; if (this.elementWithTimer == null) { logging.warn(`n-presentation: no timer element found`); return; } else { this.elementWithTimer.addEventListener('ready', () => { var _a, _b, _c, _d; logging.debugIf(this.debug, `n-presentation: element ready`); this.timer = this.elementWithTimer.timer; this.presentation = new PresentationService(this.el, this.timer, state$1.state.elementsEnabled, this.analyticsEvent, () => { var _a; if (this.currentRoute && this.nextAfter) { (_a = this.presentation) === null || _a === void 0 ? void 0 : _a.unsubscribe(); if (typeof this.nextAfter == 'string') { this.currentRoute.router.goToRoute(this.nextAfter); } else { this.currentRoute.router.goNext(); } } }, this.debug); if (this.currentRoute) { logging.debugIf(this.debug, `n-presentation: syncing to route ${this.currentRoute.path}`); if ((_b = (_a = this.currentRoute) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.isExact) { (_c = this.presentation) === null || _c === void 0 ? void 0 : _c.subscribe(); } this.navigationSubscription = index.eventBus.on(interfaces$3.ROUTE_EVENTS.RouteChanged, () => { var _a, _b, _c, _d; if ((_b = (_a = this.currentRoute) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.isExact) { (_c = this.presentation) === null || _c === void 0 ? void 0 : _c.subscribe(); } else { (_d = this.presentation) === null || _d === void 0 ? void 0 : _d.unsubscribe(); } }); } else { (_d = this.presentation) === null || _d === void 0 ? void 0 : _d.subscribe(); } }); } } render() { return index$1.h(index$1.Host, null); } disconnectedCallback() { var _a, _b; (_a = this.presentation) === null || _a === void 0 ? void 0 : _a.unsubscribe(); (_b = this.navigationSubscription) === null || _b === void 0 ? void 0 : _b.call(this); } get el() { return index$1.getElement(this); } }; exports.n_presentation = Presentation;