diff --git a/lib/plugins/CetraPlugin.js b/lib/plugins/CetraPlugin.js new file mode 100644 index 0000000..a28a93c --- /dev/null +++ b/lib/plugins/CetraPlugin.js @@ -0,0 +1,11 @@ +export default class CetraPlugin { + constructor() {} + + register(name, id) { + console.log('register method not implemented', this.constructor.name); + } + + initComponent(el) { + console.log('there is no init method for this element', el); + } +} diff --git a/lib/plugins/Interact.js b/lib/plugins/Interact.js new file mode 100644 index 0000000..7e2b509 --- /dev/null +++ b/lib/plugins/Interact.js @@ -0,0 +1,88 @@ +import CetraPlugin from './CetraPlugin'; +import Registry from '../core/Registry'; +import Logger from '../core/Logger'; + +export default class Interact extends CetraPlugin { + static #actions = [ + 'on', + 'off', + 'click', + 'dblclick', + 'mousedown', + 'mouseup', + 'keydown', + 'keyup', + ]; + #cetraIdentifier; + #pluginName = 'interact'; + constructor(cetraId) { + super(cetraId); + this.#cetraIdentifier = cetraId; + } + + register(cetraId) { + this.#cetraIdentifier = cetraId; + const cetra = Registry.getParent(this.#cetraIdentifier); + if (!cetra) + throw new Error( + `no cetra instance found: ${this.#cetraIdentifier}` + ); + + cetra.registerCommands(Interact.#actions, this.#pluginName); + + return this; + } + + #actionSelectors(action = null) { + const cetra = Registry.getParent(this.#cetraIdentifier); + if (!cetra) + throw new Error( + `no cetra instance found: ${this.#cetraIdentifier}` + ); + return (action ? [action] : Interact.#actions) + .map((action) => + [ + `[data-${cetra.getOption('selector')}-${action}]`, + `[${cetra.getOption('selector')}-${action}]`, + ] + .map( + (v) => + `${v}:not([data-${cetra.getOption('selector')}-id])` + ) + .join(',') + ) + .join(','); + } + + initComponent(comp, compData) { + const els = comp.querySelectorAll(this.#actionSelectors()); + + for (const el of els) { + this.#initElement(el, compData.get('id')); + } + } + + #initElement(el, componentId) { + const cetra = Registry.getParent(this.#cetraIdentifier); + if (!cetra) { + throw new TypeError( + `no cetra instance found: ${this.#cetraIdentifier}` + ); + } + + let [action, method, varName] = ( + el.getAttribute(`data-${cetra.getOption('selector')}-on`) || + el.getAttribute(`${cetra.getOption('selector')}-on`) || + '' + ).split(':'); + + // adding an eventlistener to the element and triggering a method on the cetra object which is the executioner of the action + el.addEventListener(action, (e) => { + // exec is the action performer it checks + // if there is a command named like the action + // and if so it gives the action to the appropriate + // component + cetra.exec(method, varName, componentId, e); + }); + } +} diff --git a/lib/plugins/Timing.js b/lib/plugins/Timing.js new file mode 100644 index 0000000..1b7c97f --- /dev/null +++ b/lib/plugins/Timing.js @@ -0,0 +1,113 @@ +import CetraPlugin from './CetraPlugin'; + +export default class Timing extends CetraPlugin { + static timers = new Map(); + static animationFrameId; + static previousTimestamp; + + #pluginName = 'timing'; + + #uniqueId; + + constructor() { + super(); + this.#uniqueId = Symbol('timer'); + } + + /** + * Clears all active timers. + * + * @static + * @memberof Cetra + * @returns {void} + */ + static #clearAllTimers() { + Cetra.timers.clear(); + cancelAnimationFrame(Cetra.animationFrameId); + Cetra.animationFrameId = null; + } + + /** + * gets called each tick the animationframe runs + * @memberof Cetra + * @static + * @method + * @param {Number} timestamp - the current timestamp of the animationframe + */ + static #update(timestamp) { + const elapsed = timestamp - Cetra.previousTimestamp; + Cetra.previousTimestamp = timestamp; + + Cetra.timers.forEach((timer, timerId) => { + timer.timing -= elapsed; + + if (timer.timing <= 0) { + timer.callback(elapsed / 1_000); + + if (timer.loop) { + timer.timing = timer.interval; + } else { + Cetra.timers.delete(timerId); + } + } + }); + + if (Cetra.timers.size > 0) { + Cetra.animationFrameId = requestAnimationFrame(Cetra.update); + } else { + Cetra.animationFrameId = null; + } + } + + register() { + Cetra.#clearAllTimers(); + } + + initComponent(el) { + //
+ } + + /** + * Starts a timer with the specified callback function, interval, and loop flag. + * + * @static + * @memberof Cetra + * @param {Function} callback - The callback function to be executed on each timer tick. + * @param {number} interval - The interval (in milliseconds) between each timer tick. + * @param {boolean} [loop=false] - A flag indicating whether the timer should loop indefinitely. + * @returns {Symbol} The unique timer identifier. + */ + static startTimer(callback, interval, loop = false) { + const timerId = Symbol('timer'); + Cetra.timers.set(timerId, { + callback, + interval, + loop, + timing: interval, + startTime: performance.now(), + }); + + if (!Cetra.animationFrameId) { + Cetra.previousTimestamp = performance.now(); + Cetra.animationFrameId = requestAnimationFrame(Cetra.#update); + } + + return timerId; + } + + /** + * Clears the timer with the specified identifier. + * + * @static + * @memberof Cetra + * @param {Symbol} timerId - The unique identifier of the timer to be cleared. + * @returns {void} + */ + static clearTimer(timerId) { + Cetra.timers.delete(timerId); + if (Cetra.timers.size === 0) { + cancelAnimationFrame(Cetra.animationFrameId); + Cetra.animationFrameId = null; + } + } +} diff --git a/lib/plugins/Utils.js b/lib/plugins/Utils.js new file mode 100644 index 0000000..21087b7 --- /dev/null +++ b/lib/plugins/Utils.js @@ -0,0 +1,26 @@ +import CetraPlugin from './CetraPlugin'; +import Registry from '../core/Registry'; +import Logger from '../core/Logger'; + +export default class Utils extends CetraPlugin { + #cetraIdentifier; + #pluginName = 'utils'; + constructor(cetraId) { + super(cetraId); + + this.#cetraIdentifier = cetraId; + } + + register(cetraId) {} + initComponent(component) {} + + increment(val, options = { amount: 1 }) { + console.log('increment', val, options); + const { amount } = options; + return val + amount; + } + + increase(val, options = { amount: 1 }) { + return this.increment(val, options); + } +}