UNPKG

@uiloos/core

Version:

The core of the uiloos headless UI

1,490 lines (1,468 loc) 108 kB
'use strict'; class _LicenseChecker { constructor() { this._licenseKey = ''; this._logOnSuccess = false; this._success = false; } activateLicense(licenseKey, options = { logLicenseActivated: true }) { this._licenseKey = licenseKey; this._logOnSuccess = options.logLicenseActivated; } _checkLicense() { if (this._success) { return; } if (this._licenseKey) { const parts = this._licenseKey.split('-'); if (parts.length !== 2) { console.warn(`uiloos > license > invalid license key detected: ${this._licenseKey}, ${buy}`); } else { const [_, type] = parts; if (this._logOnSuccess) { console.log(`uiloos > license > license activated, this license is for use with ${type} developers. We thank you for your support, you can disable this message if you want to. ${owner}`); } this._success = true; } } else { console.warn(`uiloos > license > you are using commercial software, ${buy}`); } } } let licenseChecker = new _LicenseChecker(); const owner = 'If you are not the owner of this website please ignore this message.'; const buy = `please purchase a license at https://www.uiloos.dev. ${owner}`; const common$3 = `uiloos > ActiveList >`; class ActiveListAutoPlayDurationError extends Error { constructor() { super(`${common$3} autoPlay > duration cannot be negative or zero`); this.name = 'ActiveListAutoPlayDurationError'; } } class _AutoPlay { constructor(activeList, config) { this._autoPlayTimeoutId = null; this._autoPlayStarted = new Date(); this._pauseStarted = null; this._autoPlayCurrentDuration = 0; this._config = null; this._activeList = activeList; this._config = config; } _setConfig(config) { this._config = config; } _play(inform) { this._cancelTimer(); if (!this._config || this._activeList.lastActivatedContent === null) { return; } const duration = this._getDuration(this._config, this._activeList.lastActivatedContent); if (duration <= 0) { throw new ActiveListAutoPlayDurationError(); } this._autoPlayCurrentDuration = duration; if (this._pauseStarted === null) { this._activeList.autoPlay.duration = duration; } else { this._pauseStarted = null; } this._autoPlayStarted = new Date(); this._autoPlayTimeoutId = window.setTimeout(() => { this._autoPlayTimeoutId = null; this._activeList.activateNext({ isUserInteraction: false }); }, duration); this._activeList.autoPlay.isPlaying = true; if (inform) { const event = { type: 'AUTO_PLAY_PLAYING', time: new Date(), }; this._activeList._inform(event); } } _pause() { if (!this._activeList.autoPlay.isPlaying) { return; } this._pauseStarted = new Date(); this._cancelTimer(); this._activeList.autoPlay.isPlaying = false; const event = { type: 'AUTO_PLAY_PAUSED', time: new Date(), }; this._activeList._inform(event); } _stop() { if (!this._activeList.autoPlay.isPlaying && !this._pauseStarted) { return; } this._cancelTimer(); this._pauseStarted = null; this._activeList.autoPlay.isPlaying = false; this._activeList.autoPlay.duration = 0; this._activeList.autoPlay.hasBeenStoppedBefore = true; const event = { type: 'AUTO_PLAY_STOPPED', time: new Date(), }; this._activeList._inform(event); } _cancelTimer() { if (this._autoPlayTimeoutId !== null) { window.clearTimeout(this._autoPlayTimeoutId); this._autoPlayTimeoutId = null; } } _onDeactivation(activationOptions) { if (this._activeList.lastActivatedContent === null) { this._stop(); return; } if (!this._config) { return; } if (this._shouldStopOnUserInteraction(activationOptions, this._config)) { this._stop(); return; } } onActiveIndexChanged(index, activationOptions) { if (!this._config) { return; } if (this._shouldStopOnUserInteraction(activationOptions, this._config)) { this._stop(); } else if (this._activeList.isCircular === false && index === this._activeList.getLastIndex()) { this._stop(); } else { this._play(false); } } _shouldStopOnUserInteraction(activationOptions, config) { return !!(activationOptions && activationOptions.isUserInteraction !== false && config.stopsOnUserInteraction); } _getDuration(config, lastActivatedContent) { if (this._pauseStarted) { return (this._autoPlayCurrentDuration - (this._pauseStarted.getTime() - this._autoPlayStarted.getTime())); } if (typeof config.duration === 'number') { return config.duration; } else { return config.duration({ index: lastActivatedContent.index, content: lastActivatedContent, value: lastActivatedContent.value, activeList: this._activeList, }); } } } class ActiveListContent { constructor(activeList, index, value) { this.isActive = false; this.hasBeenActiveBefore = false; this.isFirst = false; this.isLast = false; this.hasNext = false; this.hasPrevious = false; this.isNext = false; this.isPrevious = false; this.activeList = activeList; this.index = index; this.value = value; } activate(activationOptions) { this.activeList.activateByIndex(this.index, activationOptions); } deactivate(activationOptions) { this.activeList.deactivateByIndex(this.index, activationOptions); } toggle(activationOptions) { this.activeList.toggleByIndex(this.index, activationOptions); } remove() { return this.activeList.removeByIndex(this.index); } swapWith(item) { const itemIndex = this.activeList.getIndex(item); this.swapWithByIndex(itemIndex); } swapWithByIndex(index) { this.activeList.swapByIndex(this.index, index); } swapWithNext() { const nextIndex = this.activeList._getBoundedNextIndex(this.index); this.swapWithByIndex(nextIndex); } swapWithPrevious() { const previousIndex = this.activeList._getBoundedPreviousIndex(this.index); this.swapWithByIndex(previousIndex); } moveToIndex(to) { this.activeList.moveByIndex(this.index, to); } moveToPredicate(predicate, options) { this.activeList.moveByIndexByPredicate(this.index, predicate, options); } moveToFirst() { this.activeList.moveByIndex(this.index, 0); } moveToLast() { this.activeList.moveByIndex(this.index, this.activeList.getLastIndex()); } } class ActiveListCooldownDurationError extends Error { constructor() { super(`${common$3} cooldown > duration cannot be negative or zero`); this.name = "ActiveListCooldownDurationError"; } } class _CooldownTimer { constructor(activeList, cooldown) { this._cooldownTimeoutId = null; this._cooldown = undefined; if (typeof cooldown === 'number') { this._assertDuration(cooldown); } this._activeList = activeList; this._cooldown = cooldown; } _isActive(activationOptions) { if (activationOptions.isUserInteraction === false) { return false; } return this._activeList.cooldown.isActive; } _setCooldown(activationOptions, content) { if (activationOptions.isUserInteraction === false) { return; } const duration = this._getDuration(activationOptions, content); if (duration === -1) { this._stopCooldown(); return; } this._activeList.cooldown.isActive = true; this._activeList.cooldown.duration = duration; this._cooldownTimeoutId = window.setTimeout(() => { this._stopCooldown(); }, duration); const event = { type: 'COOLDOWN_STARTED', time: new Date(), }; this._activeList._inform(event); } _getDuration(activationOptions, content) { let duration = -1; if (activationOptions.cooldown !== undefined) { duration = this._getDurationFromConfig(activationOptions.cooldown, content); } else if (this._cooldown !== undefined) { duration = this._getDurationFromConfig(this._cooldown, content); } else { return -1; } this._assertDuration(duration); return duration; } _getDurationFromConfig(cooldownConfig, content) { if (typeof cooldownConfig === 'number') { return cooldownConfig; } else { return cooldownConfig({ index: content.index, content: content, value: content.value, activeList: this._activeList, }); } } _assertDuration(cooldownValue) { if (cooldownValue <= 0) { throw new ActiveListCooldownDurationError(); } } _stopCooldown() { if (this._cooldownTimeoutId) { window.clearTimeout(this._cooldownTimeoutId); } if (!this._activeList.cooldown.isActive) { return; } this._activeList.cooldown.isActive = false; this._activeList.cooldown.duration = 0; const event = { type: 'COOLDOWN_ENDED', time: new Date(), }; this._activeList._inform(event); } } class ActiveListActivationLimitReachedError extends Error { constructor() { super(`${common$3} activateByIndex > activation limit reached`); this.name = 'ActiveListActivationLimitReachedError'; } } class ActiveListIndexOutOfBoundsError extends Error { constructor(message) { super(message); this.name = "ActiveListIndexOutOfBoundsError"; } } function throwIndexOutOfBoundsError(method, indexName) { throw new ActiveListIndexOutOfBoundsError(`${common$3} ${method} > "${indexName}" is out of bounds`); } class ActiveListItemNotFoundError extends Error { constructor() { super(`${common$3} getIndex > index cannot be found, item is not in contents array`); this.name = 'ActiveListItemNotFoundError'; } } class _History { constructor() { this._events = []; this._keepHistoryFor = 0; } _push(event) { if (this._keepHistoryFor > 0) { this._events.push(event); if (this._events.length - 1 === this._keepHistoryFor) { this._events.shift(); } } } _setKeepHistoryFor(_keepHistoryFor = 0) { this._keepHistoryFor = _keepHistoryFor; } } class _Observer { constructor() { this._subscribers = []; } _subscribe(subscriber) { this._subscribers.push(subscriber); return () => { this._unsubscribe(subscriber); }; } _unsubscribe(subscriber) { this._subscribers = this._subscribers.filter((s) => subscriber !== s); } _clear() { this._subscribers.length = 0; } _inform(observable, event) { this._subscribers.forEach((subscriber) => subscriber(observable, event)); } } class ActiveList { constructor(config = {}, subscriber) { this._isInitializing = false; this.contents = []; this.maxActivationLimit = 1; this.maxActivationLimitBehavior = 'circular'; this.active = []; this.activeContents = []; this.activeIndexes = []; this.lastActivated = null; this.lastActivatedContent = null; this.lastActivatedIndex = -1; this.lastDeactivated = null; this.lastDeactivatedContent = null; this.lastDeactivatedIndex = -1; this.isCircular = false; this.direction = 'right'; this.oppositeDirection = 'left'; this._history = new _History(); this.history = this._history._events; this._observer = new _Observer(); this.hasActiveChangedAtLeastOnce = false; this.cooldown = { isActive: false, duration: 0, }; this.autoPlay = { isPlaying: false, duration: 0, hasBeenStoppedBefore: false, }; licenseChecker._checkLicense(); if (subscriber) { this.subscribe(subscriber); } this.initialize(config); } subscribe(subscriber) { return this._observer._subscribe(subscriber); } unsubscribe(subscriber) { this._observer._unsubscribe(subscriber); } unsubscribeAll() { this._observer._clear(); } initialize(config) { this._isInitializing = true; this.maxActivationLimit = config.maxActivationLimit !== undefined ? config.maxActivationLimit : 1; this.maxActivationLimitBehavior = config.maxActivationLimitBehavior ? config.maxActivationLimitBehavior : 'circular'; this.isCircular = !!config.isCircular; const contents = config.contents ? config.contents : []; this.contents.length = 0; contents.forEach((c, index) => { this.contents[index] = this._initializeABrokenContent(c, index, contents); }); this._directions = config.directions ? config.directions : { next: 'right', previous: 'left' }; this._history._events.length = 0; this._history._setKeepHistoryFor(config.keepHistoryFor); this._becameEmpty(); this._activationCooldownTimer = new _CooldownTimer(this, config.cooldown); this.cooldown.isActive = false; this.cooldown.duration = 0; if (config.active !== undefined) { if (Array.isArray(config.active)) { config.active.forEach((active) => this.activate(active, { isUserInteraction: false })); } else { this.activate(config.active, { isUserInteraction: false }); } } else if (config.activeIndexes !== undefined) { if (Array.isArray(config.activeIndexes)) { config.activeIndexes.forEach((index) => this.activateByIndex(index, { isUserInteraction: false, })); } else { this.activateByIndex(config.activeIndexes, { isUserInteraction: false, }); } } this._emptyLastDeactivated(); this.hasActiveChangedAtLeastOnce = false; this.direction = this._directions.next; this.oppositeDirection = this._directions.previous; this.autoPlay.isPlaying = false; this.autoPlay.duration = 0; this.autoPlay.hasBeenStoppedBefore = false; this._autoPlay = new _AutoPlay(this, config.autoPlay ? config.autoPlay : null); this._autoPlay._play(false); this._isInitializing = false; const event = { type: 'INITIALIZED', values: [...this.active], indexes: [...this.activeIndexes], time: new Date(), }; this._inform(event); } _initializeABrokenContent(value, index, contents) { const content = new ActiveListContent(this, index, value); this._repairContent(content, index, contents); return content; } activateByIndex(index, activationOptions = { isUserInteraction: true, cooldown: undefined, }) { const previousDeactivatedIndex = this.lastDeactivatedIndex; const activatedContent = this._doActivateByIndex(index, activationOptions); if (!activatedContent) { return; } let deactivatedIndex = -1; let deactivatedValue = null; if (previousDeactivatedIndex !== this.lastDeactivatedIndex) { deactivatedIndex = this.lastDeactivatedIndex; deactivatedValue = this.lastDeactivated; } const event = { type: 'ACTIVATED', value: activatedContent.value, index, deactivatedIndex, deactivatedValue, time: new Date(), }; this._inform(event); this._activationCooldownTimer._setCooldown(activationOptions, this.lastActivatedContent); } _doActivateByIndex(index, activationOptions) { if (this._checkIndex(index)) { throwIndexOutOfBoundsError('activateByIndex', 'index'); } if (this.activeIndexes.includes(index)) { return null; } if (this._activationCooldownTimer._isActive(activationOptions)) { return null; } const limitReached = this.maxActivationLimit === false ? false : this.maxActivationLimit === this.activeIndexes.length; if (limitReached) { if (this.maxActivationLimitBehavior === 'error') { throw new ActiveListActivationLimitReachedError(); } else if (this.maxActivationLimitBehavior === 'ignore') { return null; } } const nextIndex = this._getUnboundedNextIndex(index); const previousIndex = this._getUnboundedPreviousIndex(index); this.contents.forEach((content, i) => { content.isActive = content.isActive ? content.isActive : index === i; content.isNext = nextIndex === i; content.isPrevious = previousIndex === i; if (index === i) { this.activeIndexes.push(i); this.activeContents.push(content); this.active.push(content.value); if (limitReached) { this.activeIndexes.shift(); this.active.shift(); const content = this.activeContents.shift(); if (content) { this._deactivateContent(content); } } this.direction = this._getDirectionWhenMovingToIndex(i); this.oppositeDirection = this.direction === this._directions.next ? this._directions.previous : this._directions.next; this.lastActivated = content.value; this.lastActivatedContent = content; this.lastActivatedIndex = i; content.hasBeenActiveBefore = true; } }); if (this._autoPlay) { this._autoPlay.onActiveIndexChanged(index, activationOptions); } this.hasActiveChangedAtLeastOnce = true; return this.lastActivatedContent; } activate(item, activationOptions) { const index = this.getIndex(item); this.activateByIndex(index, activationOptions); } activateByPredicate(predicate, activationOptions = { isUserInteraction: true, cooldown: undefined, }) { if (this._activationCooldownTimer._isActive(activationOptions)) { return undefined; } const previousActiveIndexes = [...this.activeIndexes]; let lastActivated = null; let error = null; this._execPred(predicate, (index) => { try { const content = this._doActivateByIndex(index, activationOptions); if (content) { lastActivated = content; } } catch (e) { if (e instanceof ActiveListActivationLimitReachedError) { error = e; return true; } } }); if (lastActivated) { const values = []; const indexes = []; this.activeIndexes.forEach((current) => { const isNewlyActivated = previousActiveIndexes.every((prev) => prev !== current); if (isNewlyActivated) { indexes.push(current); values.push(this.contents[current].value); } }); const deactivatedValues = []; const deactivatedIndexes = []; if (this.maxActivationLimit !== false && this.maxActivationLimitBehavior === 'circular') { previousActiveIndexes.forEach((prev) => { const isNewlyDeactivated = this.activeIndexes.every((current) => prev !== current); if (isNewlyDeactivated) { deactivatedIndexes.push(prev); deactivatedValues.push(this.contents[prev].value); } }); } const event = { type: 'ACTIVATED_MULTIPLE', values, indexes, deactivatedIndexes, deactivatedValues, time: new Date(), }; this._inform(event); if (lastActivated) { this._activationCooldownTimer._setCooldown(activationOptions, lastActivated); } } if (error) { throw error; } } activateNext(activationOptions) { if (this.isEmpty()) { return; } const index = this._getBoundedNextIndex(this.lastActivatedIndex); this.activateByIndex(index, activationOptions); } activatePrevious(activationOptions) { if (this.isEmpty()) { return; } const index = this._getBoundedPreviousIndex(this.lastActivatedIndex); this.activateByIndex(index, activationOptions); } activateFirst(activationOptions) { if (this.isEmpty()) { return; } this.activateByIndex(0, activationOptions); } activateLast(activationOptions) { if (this.isEmpty()) { return; } this.activateByIndex(this.getLastIndex(), activationOptions); } deactivateByIndex(index, activationOptions = { isUserInteraction: true, cooldown: undefined, }) { const deactivatedContent = this._doDeactivateByIndex(index, activationOptions); if (!deactivatedContent) { return; } const event = { type: 'DEACTIVATED', value: this.contents[index].value, index, time: new Date(), }; this._inform(event); this._activationCooldownTimer._setCooldown(activationOptions, deactivatedContent); } _doDeactivateByIndex(index, activationOptions) { if (this._checkIndex(index)) { throwIndexOutOfBoundsError('deactivateByIndex', 'index'); } const indexOfIndex = this.activeIndexes.indexOf(index); if (indexOfIndex === -1) { return null; } if (this._activationCooldownTimer._isActive(activationOptions)) { return null; } const deactivatedContent = this.activeContents[indexOfIndex]; this._deactivateContent(deactivatedContent); this.activeIndexes.splice(indexOfIndex, 1); this.active.splice(indexOfIndex, 1); this.activeContents.splice(indexOfIndex, 1); if (this.activeIndexes.length === 0) { this._emptyLastActives(); this.direction = this._directions.next; } else { this._setLastActives(); this.direction = this._getDirectionWhenMovingToIndex(deactivatedContent.index); this.oppositeDirection = this.direction; this.direction = this.direction === this._directions.next ? this._directions.previous : this._directions.next; } this._repairContents(); this.hasActiveChangedAtLeastOnce = true; if (this._autoPlay) { this._autoPlay._onDeactivation(activationOptions); } return deactivatedContent; } deactivate(item, activationOptions) { const index = this.getIndex(item); this.deactivateByIndex(index, activationOptions); } deactivateByPredicate(predicate, activationOptions = { isUserInteraction: true, cooldown: undefined, }) { if (this._activationCooldownTimer._isActive(activationOptions)) { return undefined; } const deactivatedIndexes = []; const deactivatedValues = []; let lastRemoved = null; this._execPred(predicate, (index) => { const content = this._doDeactivateByIndex(index, activationOptions); if (content) { deactivatedIndexes.push(content.index); deactivatedValues.push(content.value); lastRemoved = content; } }); if (deactivatedIndexes.length === 0) { return; } const event = { type: 'DEACTIVATED_MULTIPLE', values: deactivatedValues, indexes: deactivatedIndexes, time: new Date(), }; this._inform(event); if (lastRemoved) { this._activationCooldownTimer._setCooldown(activationOptions, lastRemoved); } } toggleByIndex(index, activationOptions) { if (this._checkIndex(index)) { throwIndexOutOfBoundsError('toggleByIndex', 'index'); } if (this.contents[index].isActive) { this.deactivateByIndex(index, activationOptions); } else { this.activateByIndex(index, activationOptions); } } toggle(item, activationOptions) { const index = this.getIndex(item); this.toggleByIndex(index, activationOptions); } play() { this._autoPlay._play(true); } pause() { this._autoPlay._pause(); } stop() { this._autoPlay._stop(); } configureAutoPlay(autoPlayConfig) { this._autoPlay._setConfig(autoPlayConfig); if (autoPlayConfig) { this._autoPlay._play(true); } else { this._autoPlay._stop(); } } insertAtIndex(item, index) { if (index < 0 || index > this.contents.length) { throwIndexOutOfBoundsError('insertAtIndex', 'index'); } const content = this._initializeABrokenContent(item, index, this.contents); this.activeIndexes.forEach((i, aiIndex) => { this.activeIndexes[aiIndex] = i >= index ? i + 1 : i; }); this.contents.splice(index, 0, content); if (index <= this.lastActivatedIndex) { this.lastActivatedIndex += 1; } this._repairContents(); const event = { type: 'INSERTED', value: item, index, time: new Date(), }; this._inform(event); return content; } push(item) { return this.insertAtIndex(item, this.contents.length); } unshift(item) { return this.insertAtIndex(item, 0); } insertByPredicate(item, predicate, options = { mode: 'at' }) { const mod = this._modeToMod(options.mode); return this._execPred(predicate, (index) => { const atIndex = Math.max(0, index + mod); return this.insertAtIndex(item, atIndex); }); } removeByIndex(index) { const value = this._doRemoveAtIndex(index); const indexOfIndex = this.activeIndexes.indexOf(index); if (indexOfIndex !== -1) { this.activeIndexes.splice(indexOfIndex, 1); this.active.splice(indexOfIndex, 1); this.activeContents.splice(indexOfIndex, 1); this.hasActiveChangedAtLeastOnce = true; } this.activeIndexes.map((i, aiIndex) => { this.activeIndexes[aiIndex] = i >= index ? i - 1 : i; }); if (this.isEmpty()) { this._becameEmpty(); this._autoPlay._stop(); } else { this._setLastActives(); } this._repairContents(); const event = { type: 'REMOVED', value, index, time: new Date(), }; this._inform(event); return value; } _doRemoveAtIndex(index) { if (this._checkIndex(index)) { throwIndexOutOfBoundsError('removeByIndex', 'index'); } if (this.lastDeactivated && this.lastDeactivatedIndex === index) { this._emptyLastDeactivated(); } const value = this.contents[index].value; this.contents.splice(index, 1); return value; } remove(item) { const index = this.getIndex(item); return this.removeByIndex(index); } pop() { if (this.isEmpty()) { return undefined; } return this.removeByIndex(this.getLastIndex()); } shift() { if (this.isEmpty()) { return undefined; } return this.removeByIndex(0); } removeByPredicate(predicate) { if (this.isEmpty()) { return []; } const removed = []; this._execPred(predicate, (index) => { const content = this.contents[index]; removed.push(content); }); const removedIndexes = []; removed.forEach((content, index) => { const actualIndex = content.index - index; this._doRemoveAtIndex(actualIndex); removedIndexes.push(content.index); }); if (this.isEmpty()) { this._becameEmpty(); this._autoPlay._stop(); } else { removedIndexes.forEach((index) => { const indexOfIndex = this.activeIndexes.indexOf(index); if (indexOfIndex !== -1) { this.activeIndexes.splice(indexOfIndex, 1); this.active.splice(indexOfIndex, 1); this.activeContents.splice(indexOfIndex, 1); this.hasActiveChangedAtLeastOnce = true; } }); removedIndexes.forEach((removed) => { this.activeIndexes.forEach((index, aiIndex) => { this.activeIndexes[aiIndex] = index >= removed ? index - 1 : index; }); }); this._setLastActives(); } const removedValues = removed.map((r) => r.value); if (removedIndexes.length > 0) { this._repairContents(); const event = { type: 'REMOVED_MULTIPLE', indexes: [...removedIndexes], values: [...removedValues], time: new Date(), }; this._inform(event); } return removedValues; } swapByIndex(a, b) { if (this._checkIndex(a)) { throwIndexOutOfBoundsError('swapByIndex', 'a'); } if (this._checkIndex(b)) { throwIndexOutOfBoundsError('swapByIndex', 'b'); } if (a === b) { return; } const itemA = this.contents[a]; const itemB = this.contents[b]; if (this.lastActivatedIndex === itemA.index) { this.lastActivatedIndex = itemB.index; } else if (this.lastActivatedIndex === itemB.index) { this.lastActivatedIndex = itemA.index; } const indexOfA = this.activeIndexes.indexOf(itemA.index); const indexOfB = this.activeIndexes.indexOf(itemB.index); if (indexOfA !== -1) { this.activeIndexes[indexOfA] = itemB.index; } if (indexOfB !== -1) { this.activeIndexes[indexOfB] = itemA.index; } itemA.index = b; itemB.index = a; this.contents[a] = itemB; this.contents[b] = itemA; this._repairContents(); const event = { type: 'SWAPPED', value: { a: itemA.value, b: itemB.value, }, index: { a, b, }, time: new Date(), }; this._inform(event); } swap(a, b) { const indexA = this.getIndex(a); const indexB = this.getIndex(b); this.swapByIndex(indexA, indexB); } moveByIndex(from, to) { if (this._checkIndex(from)) { throwIndexOutOfBoundsError('moveByIndex', 'from'); } if (this._checkIndex(to)) { throwIndexOutOfBoundsError('moveByIndex', 'to'); } if (from === to) { return; } const lastActivatedIndex = this.lastActivatedIndex; if (lastActivatedIndex === from) { this.lastActivatedIndex = to; } else if (to === lastActivatedIndex && from > lastActivatedIndex) { this.lastActivatedIndex += 1; } else if (to === lastActivatedIndex && from < lastActivatedIndex) { this.lastActivatedIndex -= 1; } else if (to > lastActivatedIndex && from < lastActivatedIndex) { this.lastActivatedIndex -= 1; } else if (to < lastActivatedIndex && from > lastActivatedIndex) { this.lastActivatedIndex += 1; } this.activeIndexes.forEach((index, aiIndex) => { if (index === from) { this.activeIndexes[aiIndex] = to; return; } if (index > from && index > to) { this.activeIndexes[aiIndex] = index; return; } if (index < from && index < to) { this.activeIndexes[aiIndex] = index; return; } this.activeIndexes[aiIndex] = from > to ? index + 1 : index - 1; }); const fromItem = this.contents[from]; this.contents.splice(from, 1); this.contents.splice(to, 0, fromItem); this._repairContents(); const event = { type: 'MOVED', value: fromItem.value, index: { from, to, }, time: new Date(), }; this._inform(event); } move(item, to) { const from = this.getIndex(item); this.moveByIndex(from, to); } moveByIndexByPredicate(index, predicate, options = { mode: 'at' }) { const mod = this._modeToMod(options.mode); this._execPred(predicate, (i, length) => { const atIndex = Math.min(Math.max(0, i + mod), length - 1); this.moveByIndex(index, atIndex); return true; }); } moveByPredicate(item, predicate, options) { const index = this.getIndex(item); this.moveByIndexByPredicate(index, predicate, options); } getIndex(item) { const contents = this.contents; const length = contents.length; for (let i = 0; i < length; i++) { if (contents[i].value === item) { return i; } } throw new ActiveListItemNotFoundError(); } getLastIndex() { return this.contents.length - 1; } _getBoundedNextIndex(index) { let nextIndex = index + 1; if (nextIndex >= this.contents.length) { nextIndex = this.isCircular ? 0 : this.getLastIndex(); } return nextIndex; } _getBoundedPreviousIndex(index) { let previousIndex = index - 1; if (previousIndex < 0) { previousIndex = this.isCircular ? this.getLastIndex() : 0; } return previousIndex; } _getUnboundedNextIndex(index) { const nextIndex = index + 1; if (this.isCircular && nextIndex === this.contents.length) { return 0; } return nextIndex; } _getUnboundedPreviousIndex(index) { const previousIndex = index - 1; if (this.isCircular && previousIndex < 0) { return this.getLastIndex(); } return previousIndex; } isEmpty() { return this.contents.length === 0; } _getDirectionWhenMovingToIndex(next) { const lastActivatedIndex = this.lastActivatedIndex; if (this.isCircular) { if (this.lastActivatedIndex === -1) { return this._directions.next; } const lastActivatedLargerThanNext = this.lastActivatedIndex > next; const lastIndex = this.getLastIndex(); const leftDistance = lastActivatedLargerThanNext ? this.lastActivatedIndex - next : lastIndex - next + this.lastActivatedIndex + 1; const rightDistance = lastActivatedLargerThanNext ? 1 + next + (lastIndex - this.lastActivatedIndex) : next - this.lastActivatedIndex; return leftDistance >= rightDistance ? this._directions.next : this._directions.previous; } else { return next >= lastActivatedIndex ? this._directions.next : this._directions.previous; } } _repairContents() { let nextIndex = null; let previousIndex = null; if (this.lastActivatedIndex !== -1) { nextIndex = this._getUnboundedNextIndex(this.lastActivatedIndex); previousIndex = this._getUnboundedPreviousIndex(this.lastActivatedIndex); } this.contents.forEach((content, index) => { content.index = index; content.isNext = nextIndex === index; content.isPrevious = previousIndex === index; this._repairContent(content, index, this.contents); }); } _repairContent(content, index, contents) { content.isFirst = index === 0; content.isLast = index === contents.length - 1; if (this.isCircular) { content.hasNext = true; content.hasPrevious = true; } else { content.hasNext = index + 1 < contents.length; content.hasPrevious = index - 1 >= 0; } } _emptyLastActives() { this.lastActivatedIndex = -1; this.lastActivated = null; this.lastActivatedContent = null; } _emptyLastDeactivated() { this.lastDeactivatedIndex = -1; this.lastDeactivated = null; this.lastDeactivatedContent = null; } _becameEmpty() { this._emptyLastDeactivated(); this._emptyLastActives(); this.activeContents.length = 0; this.activeIndexes.length = 0; this.active.length = 0; this.hasActiveChangedAtLeastOnce = true; } _setLastActives() { if (this.activeIndexes.length === 0) { this._emptyLastActives(); return; } const newLastActiveIndex = this.activeIndexes[this.activeIndexes.length - 1]; const newLastActiveList = this.contents[newLastActiveIndex]; this.lastActivated = newLastActiveList.value; this.lastActivatedContent = newLastActiveList; this.lastActivatedIndex = newLastActiveIndex; } _deactivateContent(content) { content.isActive = false; this.lastDeactivated = content.value; this.lastDeactivatedContent = content; this.lastDeactivatedIndex = content.index; } _execPred(predicate, action) { const contents = this.contents; const length = contents.length; for (let index = 0; index < length; index++) { const content = this.contents[index]; const data = { index, content, value: content.value, activeList: this, }; if (predicate(data)) { const result = action(index, length); if (result !== undefined) { return result; } } } return null; } _inform(event) { if (this._isInitializing) { return; } this._history._push(event); this._observer._inform(this, event); } _checkIndex(index) { return index < 0 || index >= this.contents.length; } _modeToMod(mode) { return mode === 'at' ? 0 : mode === 'after' ? 1 : -1; } } function _callSubscriber(subscriberName, event, component, config) { const methodName = 'on' + event.type .toLowerCase() .split('_') .reduce((acc, word) => { const letters = word.split(''); letters[0] = letters[0].toUpperCase(); return acc + letters.join(''); }, ''); const method = config[methodName]; if (!method) { if (config.debug) { console.warn(`uiloos > ${subscriberName} event '${event.type}' was fired but '${methodName}' method is not implemented, this might not be correct.`); } return; } method(event, component); } function createActiveListSubscriber(config) { return (activeList, event) => { _callSubscriber('createActiveListSubscriber', event, activeList, config); }; } const common$2 = `uiloos > ViewChannel >`; class ViewChannelAutoDismissDurationError extends Error { constructor() { super(`${common$2} autoDismiss > duration cannot be negative or zero`); this.name = 'ViewChannelAutoDismissDurationError'; } } class _AutoDismiss { constructor(view, config) { this._autoDismissTimeoutId = null; this._autoDismissStarted = new Date(); this._pauseStarted = null; this._autoDismissCurrentDuration = 0; this._config = null; this._view = view; this._config = config; } _play(inform) { if (this._view.autoDismiss.isPlaying) { return; } this._cancelTimer(); if (!this._config) { return; } const duration = this._getDuration(this._config); if (duration <= 0) { throw new ViewChannelAutoDismissDurationError(); } this._autoDismissCurrentDuration = duration; this._autoDismissStarted = new Date(); if (this._pauseStarted === null) { this._view.autoDismiss.duration = duration; } const result = this._config.result; this._autoDismissTimeoutId = window.setTimeout(() => { if (this._view.isPresented) { this._view.autoDismiss.isPlaying = false; this._view.autoDismiss.duration = 0; this._view.viewChannel._doRemoveByIndex(this._view.index, result, 'AUTO_DISMISS'); } this._autoDismissTimeoutId = null; }, duration); this._view.result.then(() => { this._cancelTimer(); }); this._view.autoDismiss.isPlaying = true; if (inform) { const event = { type: 'AUTO_DISMISS_PLAYING', view: this._view, index: this._view.index, time: new Date(), }; this._view.viewChannel._inform(event); } } _pause() { if (!this._view.autoDismiss.isPlaying) { return; } this._pauseStarted = new Date(); this._cancelTimer(); this._view.autoDismiss.isPlaying = false; const event = { type: 'AUTO_DISMISS_PAUSED', view: this._view, index: this._view.index, time: new Date(), }; this._view.viewChannel._inform(event); } _stop() { if (!this._view.autoDismiss.isPlaying && !this._pauseStarted) { return; } this._cancelTimer(); this._pauseStarted = null; this._view.autoDismiss.isPlaying = false; this._view.autoDismiss.duration = 0; const event = { type: 'AUTO_DISMISS_STOPPED', view: this._view, index: this._view.index, time: new Date(), }; this._view.viewChannel._inform(event); } _cancelTimer() { if (this._autoDismissTimeoutId !== null) { window.clearTimeout(this._autoDismissTimeoutId); this._autoDismissTimeoutId = null; } } _getDuration(config) { if (this._pauseStarted) { return (this._autoDismissCurrentDuration - (this._pauseStarted.getTime() - this._autoDismissStarted.getTime())); } return config.duration; } } class ViewChannelView { constructor(viewChannel, index, data, priority, autoDismissConfig) { var _a; this.autoDismiss = { isPlaying: false, duration: 0, }; this._resolve = null; this.isPresented = true; this.viewChannel = viewChannel; this.index = index; this.data = data; this.priority = priority; this.result = new Promise((resolve) => { this._resolve = resolve; }); this._autoDismiss = new _AutoDismiss(this, autoDismissConfig); this._autoDismiss._play(false); this.autoDismiss.duration = (_a = autoDismissConfig === null || autoDismissConfig === void 0 ? void 0 : autoDismissConfig.duration) !== null && _a !== void 0 ? _a : 0; } dismiss(result) { this.viewChannel.dismiss(this, result); } play() { this._autoDismiss._play(true); } pause() { this._autoDismiss._pause(); } stop() { this._autoDismiss._stop(); } changeData(data) { this.viewChannel.changeData(this, data); } } class ViewChannelIndexOutOfBoundsError extends Error { constructor(method) { super(`${common$2} ${method} > "index" is out of bounds`); this.name = 'ViewChannelIndexOutOfBoundsError'; } } class ViewChannelViewNotFoundError extends Error { constructor(method) { super(`${common$2} ${method} > "ViewChannelView" not found in views array`); this.name = 'ViewChannelViewNotFoundError'; } } class ViewChannel { constructor(config = {}, subscriber) { this.views = []; this._history = new _History(); this.history = this._history._events; this._observer = new _Observer(); licenseChecker._checkLicense(); if (subscriber) { this.subscribe(subscriber); } this.initialize(config); } initialize(config) { this._history._events.length = 0; this._history._setKeepHistoryFor(config.keepHistoryFor); this._clearViews(); const event = { type: 'INITIALIZED', time: new Date(), }; this._inform(event); } subscribe(subscriber) { return this._observer._subscribe(subscriber); } unsubscribe(subscriber) { this._observer._unsubscribe(subscriber); } unsubscribeAll() { this._observer._clear(); } present(viewConfig) { const priority = viewConfig.priority ? viewConfig.priority : 0; const priorityArray = Array.isArray(priority) ? priority : [priority]; const index = this._getIndexForPriority(priorityArray); const view = new ViewChannelView(this, index, viewConfig.data, priorityArray, viewConfig.autoDismiss); this.views.splice(index, 0, view); this._repairIndexes(); const event = { type: 'PRESENTED', view, index, time: new Date(), }; this._inform(event); return view; } _doRemoveByIndex(index, result, reason) { if (index < 0 || index >= this.views.length) { throw new ViewChannelIndexOutOfBoundsError('dismissByIndex'); } const view = this.views[index]; this.views.splice(index, 1); this._repairIndexes(); view.isPresented = false; view.autoDismiss.duration = 0; view.autoDismiss.isPlaying = false; view._resolve(result); const event = { type: 'DISMISSED', view, index, reason, time: new Date(), }; this._inform(event); } dismissByIndex(index, result) { this._doRemoveByIndex(index, result, 'USER_INTERACTION'); } dismiss(view, result) { if (!view.isPresented) { return; } const index = this.views.indexOf(view); if (index === -1) { throw new ViewChannelViewNotFoundError('dismiss'); } this.dismissByIndex(index, result); } dismissAll(result) { if (this.views.length === 0) { return; } const indexes = []; const dismissedViews = [...this.views]; this._clearViews(); dismissedViews.forEach((view)