Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@shower/core

Package Overview
Dependencies
Maintainers
3
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shower/core - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

lib/modules/install.js

452

dist/shower.js
/**
* Core for Shower HTML presentation engine
* @shower/core v3.0.0-2, https://github.com/shower/core
* @copyright 2010–2020 Vadim Makeev, https://pepelsbey.net
* @shower/core v3.0.0, https://github.com/shower/core
* @copyright 2010–2021 Vadim Makeev, https://pepelsbey.net
* @license MIT

@@ -10,40 +10,4 @@ */

const EVENT_TARGET = Symbol('EventTarget');
class EventTarget {
constructor() {
this[EVENT_TARGET] = document.createElement('div');
}
addEventListener(...args) {
this[EVENT_TARGET].addEventListener(...args);
}
removeEventListener(...args) {
this[EVENT_TARGET].removeEventListener(...args);
}
dispatchEvent(event) {
Object.defineProperties(event, {
target: { value: this },
currentTarget: { value: this },
});
return this[EVENT_TARGET].dispatchEvent(event);
}
}
const isInteractiveElement = (element) => element.tabIndex !== -1;
const freezeHistory = (callback) => {
history.pushState = () => {};
history.replaceState = () => {};
try {
callback();
} finally {
delete history.pushState;
delete history.replaceState;
}
};
const contentLoaded = (callback) => {

@@ -57,2 +21,15 @@ if (document.currentScript.async) {

const defineReadOnly = (target, props) => {
for (const [key, value] of Object.entries(props)) {
Object.defineProperty(target, key, {
value,
writable: false,
enumerable: true,
configurable: true,
});
}
};
class ShowerError extends Error {}
var defaultOptions = {

@@ -71,18 +48,28 @@ containerSelector: '.shower',

/**
* @param {HTMLElement} element
* @param {object} options
*/
class Slide extends EventTarget {
constructor(element, options) {
/**
* @param {Shower} shower
* @param {HTMLElement} element
*/
constructor(shower, element) {
super();
this.element = element;
this.options = options;
defineReadOnly(this, {
shower,
element,
state: {
visitCount: 0,
innerStepCount: 0,
},
});
this._isActive = false;
this.state = {
visitsCount: 0,
innerStepsCount: 0,
};
this._options = this.shower.options;
this.element.addEventListener('click', (event) => {
if (event.defaultPrevented) return;
this.activate();
this.shower.enterFullMode();
});
}

@@ -95,3 +82,3 @@

get isVisited() {
return this.state.visitsCount > 0;
return this.state.visitCount > 0;
}

@@ -104,23 +91,53 @@

get title() {
const titleElement = this.element.querySelector(this.options.slideTitleSelector);
const titleElement = this.element.querySelector(this._options.slideTitleSelector);
return titleElement ? titleElement.innerText : '';
}
/**
* Deactivates currently active slide (if any) and activates itself.
* @emits Slide#deactivate
* @emits Slide#activate
* @emits Shower#slidechange
*/
activate() {
if (this._isActive) return;
const prev = this.shower.activeSlide;
if (prev) {
prev._deactivate();
}
this.state.visitCount++;
this.element.classList.add(this._options.activeSlideClass);
this._isActive = true;
this.state.visitsCount++;
this.element.classList.add(this.options.activeSlideClass);
this.dispatchEvent(new Event('activate'));
this.shower.dispatchEvent(
new CustomEvent('slidechange', {
detail: { prev },
}),
);
}
/**
* @throws {ShowerError}
* @emits Slide#deactivate
*/
deactivate() {
if (!this._isActive) return;
if (this.shower.isFullMode) {
throw new ShowerError('In full mode, another slide should be activated instead.');
}
this._isActive = false;
if (this._isActive) {
this._deactivate();
}
}
_deactivate() {
this.element.classList.replace(
this.options.activeSlideClass,
this.options.visitedSlideClass,
this._options.activeSlideClass,
this._options.visitedSlideClass,
);
this._isActive = false;
this.dispatchEvent(new Event('deactivate'));

@@ -160,2 +177,7 @@ }

shower.addEventListener('start', () => {
updateDocumentRole();
updateLiveRegion();
});
shower.addEventListener('modechange', updateDocumentRole);

@@ -188,2 +210,3 @@ shower.addEventListener('slidechange', updateLiveRegion);

case 'BACKSPACE':
case 'PAGEUP':

@@ -287,11 +310,8 @@ case 'ARROWUP':

const applyURLMode = () => {
const isFull = new URL(location).searchParams.has('full');
freezeHistory(() => {
if (isFull) {
shower.enterFullMode();
} else {
shower.exitFullMode();
}
});
const isFull = new URLSearchParams(location.search).has('full');
if (isFull) {
shower.enterFullMode();
} else {
shower.exitFullMode();
}
};

@@ -305,5 +325,3 @@

if (target) {
freezeHistory(() => {
target.activate();
});
target.activate();
} else if (!shower.activeSlide) {

@@ -319,2 +337,9 @@ shower.first(); // invalid hash

applyURL();
window.addEventListener('popstate', applyURL);
shower.addEventListener('start', () => {
history.replaceState(null, document.title, composeURL());
});
shower.addEventListener('modechange', () => {

@@ -325,56 +350,61 @@ history.replaceState(null, document.title, composeURL());

shower.addEventListener('slidechange', () => {
history.pushState(null, document.title, composeURL());
const url = composeURL();
if (!location.href.endsWith(url)) {
history.pushState(null, document.title, url);
}
});
shower.addEventListener('start', applyURL);
window.addEventListener('popstate', applyURL);
};
var next = (shower) => {
const { stepSelector, activeSlideClass } = shower.options;
const { stepSelector, activeSlideClass, visitedSlideClass } = shower.options;
let innerSteps;
let innerAt;
let activeIndex;
const getInnerSteps = () => {
const { element } = shower.activeSlide;
return [...element.querySelectorAll(stepSelector)];
};
const isActive = (step) => step.classList.contains(activeSlideClass);
const isVisited = (step) => step.classList.contains(visitedSlideClass);
const getInnerAt = () => {
return innerSteps.filter((step) => {
return step.classList.contains(activeSlideClass);
}).length;
};
const setInnerStepsState = () => {
if (shower.isListMode) return;
const toggleActive = () => {
innerSteps.forEach((step, index) => {
step.classList.toggle(activeSlideClass, index < innerAt);
});
const slide = shower.activeSlide;
innerSteps = [...slide.element.querySelectorAll(stepSelector)];
activeIndex =
innerSteps.length && innerSteps.every(isVisited)
? innerSteps.length
: innerSteps.filter(isActive).length - 1;
slide.state.innerStepCount = innerSteps.length;
};
shower.addEventListener('slidechange', () => {
innerSteps = getInnerSteps();
innerAt = getInnerAt();
shower.addEventListener('start', setInnerStepsState);
shower.addEventListener('modechange', setInnerStepsState);
shower.addEventListener('slidechange', setInnerStepsState);
const slide = shower.activeSlide;
slide.state.innerStepsCount = innerSteps.length;
});
shower.addEventListener('next', (event) => {
if (event.defaultPrevented || !event.cancelable) return;
if (shower.isListMode || innerAt === innerSteps.length) return;
if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
event.preventDefault();
innerAt++;
toggleActive();
activeIndex++;
innerSteps.forEach((step, index) => {
step.classList.toggle(visitedSlideClass, index < activeIndex);
step.classList.toggle(activeSlideClass, index === activeIndex);
});
if (activeIndex < innerSteps.length) {
event.preventDefault();
}
});
shower.addEventListener('prev', (event) => {
if (event.defaultPrevented || !event.cancelable) return;
if (shower.isListMode || innerAt === innerSteps.length || !innerAt) return;
if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
if (activeIndex === -1 || activeIndex === innerSteps.length) return;
activeIndex--;
innerSteps.forEach((step, index) => {
step.classList.toggle(visitedSlideClass, index < activeIndex + 1);
step.classList.toggle(activeSlideClass, index === activeIndex);
});
event.preventDefault();
innerAt--;
toggleActive();
});

@@ -402,25 +432,6 @@ };

shower.addEventListener('start', updateProgress);
shower.addEventListener('slidechange', updateProgress);
};
var scale = (shower) => {
const { container } = shower;
const getScale = () => {
const maxRatio = Math.max(
container.offsetWidth / window.innerWidth,
container.offsetHeight / window.innerHeight,
);
return `scale(${1 / maxRatio})`;
};
const updateStyle = () => {
container.style.transform = shower.isFullMode ? getScale() : '';
};
shower.addEventListener('modechange', updateStyle);
window.addEventListener('resize', updateStyle);
window.addEventListener('load', updateStyle);
};
const units = ['s', 'm', 'h'];

@@ -458,3 +469,3 @@ const hasUnits = (timing) => {

const setTimer = () => {
const resetTimer = () => {
clearTimeout(id);

@@ -464,4 +475,4 @@ if (shower.isListMode) return;

const slide = shower.activeSlide;
const { visitsCount, innerStepsCount } = slide.state;
if (visitsCount > 1) return;
const { visitCount, innerStepCount } = slide.state;
if (visitCount > 1) return;

@@ -471,4 +482,4 @@ const timing = parseTiming(slide.element.dataset.timing);

if (innerStepsCount) {
const stepTiming = timing / (innerStepsCount + 1);
if (innerStepCount) {
const stepTiming = timing / (innerStepCount + 1);
id = setInterval(() => shower.next(), stepTiming);

@@ -480,4 +491,5 @@ } else {

shower.addEventListener('modechange', setTimer);
shower.addEventListener('slidechange', setTimer);
shower.addEventListener('start', resetTimer);
shower.addEventListener('modechange', resetTimer);
shower.addEventListener('slidechange', resetTimer);

@@ -508,2 +520,3 @@ shower.container.addEventListener('keydown', (event) => {

shower.addEventListener('start', updateTitle);
shower.addEventListener('modechange', updateTitle);

@@ -517,12 +530,35 @@ shower.addEventListener('slidechange', updateTitle);

shower.addEventListener('modechange', () => {
if (container.classList.contains(fullModeClass)) {
shower.enterFullMode();
} else {
container.classList.add(listModeClass);
}
const updateScale = () => {
const firstSlide = shower.slides[0];
if (!firstSlide) return;
const { innerWidth, innerHeight } = window;
const { offsetWidth, offsetHeight } = firstSlide.element;
const listScale = 1 / (offsetWidth / innerWidth);
const fullScale = 1 / Math.max(offsetWidth / innerWidth, offsetHeight / innerHeight);
container.style.setProperty('--shower-list-scale', listScale);
container.style.setProperty('--shower-full-scale', fullScale);
};
const updateModeView = () => {
if (shower.isFullMode) {
container.classList.remove(listModeClass);
container.classList.add(fullModeClass);
return;
} else {
container.classList.remove(fullModeClass);
container.classList.add(listModeClass);
}
container.classList.remove(fullModeClass);
container.classList.add(listModeClass);
updateScale();
if (shower.isFullMode) return;
const slide = shower.activeSlide;

@@ -532,5 +568,9 @@ if (slide) {

}
});
};
shower.addEventListener('start', updateModeView);
shower.addEventListener('modechange', updateModeView);
shower.addEventListener('slidechange', () => {
if (shower.isFullMode) return;
const slide = shower.activeSlide;

@@ -540,9 +580,3 @@ slide.element.scrollIntoView({ block: 'nearest' });

shower.addEventListener('start', () => {
if (container.classList.contains(fullModeClass)) {
shower.enterFullMode();
} else {
container.classList.add(listModeClass);
}
});
window.addEventListener('resize', updateScale);
};

@@ -552,15 +586,13 @@

a11y(shower);
keys(shower); // should come before `timer`
progress(shower);
next(shower); // should come before `timer`
timer(shower);
title(shower); // should come before `location`
location$1(shower);
keys(shower);
next(shower);
timer(shower); // should come after `keys` and `next`
title(shower);
location$1(shower); // should come after `title`
view(shower);
scale(shower);
};
const ensureSlideId = (slideElement, index) => {
if (!slideElement.id) {
slideElement.id = index + 1;
// maintains invariant: active slide always exists in `full` mode
if (shower.isFullMode && !shower.activeSlide) {
shower.first();
}

@@ -570,17 +602,33 @@ };

class Shower extends EventTarget {
/**
* @param {object=} options
*/
constructor(options) {
super();
defineReadOnly(this, {
options: { ...defaultOptions, ...options },
});
this._mode = 'list';
this._isStarted = false;
this.options = { ...defaultOptions, ...options };
this._container = null;
}
/**
* @param {object} options
* @param {object=} options
* @throws {ShowerError}
*/
configure(options) {
if (this._isStarted) {
throw new ShowerError('Shower should be configured before it is started.');
}
Object.assign(this.options, options);
}
/**
* @throws {ShowerError}
* @emits Shower#start
*/
start() {

@@ -590,18 +638,13 @@ if (this._isStarted) return;

const { containerSelector } = this.options;
this.container = document.querySelector(containerSelector);
if (!this.container) {
throw new Error(`Shower container with selector '${containerSelector}' not found.`);
this._container = document.querySelector(containerSelector);
if (!this._container) {
throw new ShowerError(
`Shower container with selector '${containerSelector}' was not found.`,
);
}
this._isStarted = true;
this._initSlides();
installModules(this);
// maintains invariant: active slide always exists in `full` mode
this.addEventListener('modechange', () => {
if (this.isFullMode && !this.activeSlide) {
this.first();
}
});
installModules(this);
this._isStarted = true;
this.dispatchEvent(new Event('start'));

@@ -611,41 +654,34 @@ }

_initSlides() {
const slideElements = [
...this.container.querySelectorAll(this.options.slideSelector),
].filter((slideElement) => !slideElement.hidden);
const visibleSlideSelector = `${this.options.slideSelector}:not([hidden])`;
const visibleSlideElements = this._container.querySelectorAll(visibleSlideSelector);
slideElements.forEach(ensureSlideId);
this.slides = slideElements.map((slideElement) => {
const slide = new Slide(slideElement, this.options);
this.slides = Array.from(visibleSlideElements, (slideElement, index) => {
if (!slideElement.id) {
slideElement.id = index + 1;
}
slide.addEventListener('activate', () => {
this._changeActiveSlide(slide);
});
slide.element.addEventListener('click', () => {
if (this.isListMode) {
this.enterFullMode();
slide.activate();
}
});
return slide;
return new Slide(this, slideElement);
});
}
_changeActiveSlide(next) {
const prev = this.slides.find((slide) => {
return slide.isActive && slide !== next;
});
_setMode(mode) {
if (mode === this._mode) return;
if (prev) {
prev.deactivate();
}
this._mode = mode;
this.dispatchEvent(new Event('modechange'));
}
const event = new CustomEvent('slidechange', {
detail: { prev },
});
/**
* @param {Event} event
*/
dispatchEvent(event) {
if (!this._isStarted) return false;
this.dispatchEvent(event);
return super.dispatchEvent(event);
}
get container() {
return this._container;
}
get isFullMode() {

@@ -669,8 +705,6 @@ return this._mode === 'full';

* Slide fills the maximum area.
* @emits Shower#modechange
*/
enterFullMode() {
if (!this.isFullMode) {
this._mode = 'full';
this.dispatchEvent(new Event('modechange'));
}
this._setMode('full');
}

@@ -680,8 +714,6 @@

* Shower returns into list mode.
* @emits Shower#modechange
*/
exitFullMode() {
if (!this.isListMode) {
this._mode = 'list';
this.dispatchEvent(new Event('modechange'));
}
this._setMode('list');
}

@@ -702,3 +734,3 @@

*/
go(delta) {
goBy(delta) {
this.goTo(this.activeSlideIndex + delta);

@@ -708,3 +740,4 @@ }

/**
* @param {boolean=} isForce
* @param {boolean} [isForce=false]
* @emits Shower#prev
*/

@@ -714,3 +747,3 @@ prev(isForce) {

if (this.dispatchEvent(prev)) {
this.go(-1);
this.goBy(-1);
}

@@ -720,3 +753,4 @@ }

/**
* @param {boolean=} isForce
* @param {boolean} [isForce=false]
* @emits Shower#next
*/

@@ -726,3 +760,3 @@ next(isForce) {

if (this.dispatchEvent(next)) {
this.go(1);
this.goBy(1);
}

@@ -729,0 +763,0 @@ }

@@ -31,4 +31,9 @@ const createLiveRegion = () => {

shower.addEventListener('start', () => {
updateDocumentRole();
updateLiveRegion();
});
shower.addEventListener('modechange', updateDocumentRole);
shower.addEventListener('slidechange', updateLiveRegion);
};

@@ -26,2 +26,3 @@ import { isInteractiveElement } from '../utils';

case 'BACKSPACE':
case 'PAGEUP':

@@ -28,0 +29,0 @@ case 'ARROWUP':

@@ -1,3 +0,1 @@

import { freezeHistory } from '../utils';
export default (shower) => {

@@ -13,11 +11,8 @@ const composeURL = () => {

const applyURLMode = () => {
const isFull = new URL(location).searchParams.has('full');
freezeHistory(() => {
if (isFull) {
shower.enterFullMode();
} else {
shower.exitFullMode();
}
});
const isFull = new URLSearchParams(location.search).has('full');
if (isFull) {
shower.enterFullMode();
} else {
shower.exitFullMode();
}
};

@@ -31,5 +26,3 @@

if (target) {
freezeHistory(() => {
target.activate();
});
target.activate();
} else if (!shower.activeSlide) {

@@ -45,2 +38,9 @@ shower.first(); // invalid hash

applyURL();
window.addEventListener('popstate', applyURL);
shower.addEventListener('start', () => {
history.replaceState(null, document.title, composeURL());
});
shower.addEventListener('modechange', () => {

@@ -51,7 +51,7 @@ history.replaceState(null, document.title, composeURL());

shower.addEventListener('slidechange', () => {
history.pushState(null, document.title, composeURL());
const url = composeURL();
if (!location.href.endsWith(url)) {
history.pushState(null, document.title, url);
}
});
shower.addEventListener('start', applyURL);
window.addEventListener('popstate', applyURL);
};
export default (shower) => {
const { stepSelector, activeSlideClass } = shower.options;
const { stepSelector, activeSlideClass, visitedSlideClass } = shower.options;
let innerSteps;
let innerAt;
let activeIndex;
const getInnerSteps = () => {
const { element } = shower.activeSlide;
return [...element.querySelectorAll(stepSelector)];
};
const isActive = (step) => step.classList.contains(activeSlideClass);
const isVisited = (step) => step.classList.contains(visitedSlideClass);
const getInnerAt = () => {
return innerSteps.filter((step) => {
return step.classList.contains(activeSlideClass);
}).length;
};
const setInnerStepsState = () => {
if (shower.isListMode) return;
const toggleActive = () => {
innerSteps.forEach((step, index) => {
step.classList.toggle(activeSlideClass, index < innerAt);
});
const slide = shower.activeSlide;
innerSteps = [...slide.element.querySelectorAll(stepSelector)];
activeIndex =
innerSteps.length && innerSteps.every(isVisited)
? innerSteps.length
: innerSteps.filter(isActive).length - 1;
slide.state.innerStepCount = innerSteps.length;
};
shower.addEventListener('slidechange', () => {
innerSteps = getInnerSteps();
innerAt = getInnerAt();
shower.addEventListener('start', setInnerStepsState);
shower.addEventListener('modechange', setInnerStepsState);
shower.addEventListener('slidechange', setInnerStepsState);
const slide = shower.activeSlide;
slide.state.innerStepsCount = innerSteps.length;
});
shower.addEventListener('next', (event) => {
if (event.defaultPrevented || !event.cancelable) return;
if (shower.isListMode || innerAt === innerSteps.length) return;
if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
event.preventDefault();
innerAt++;
toggleActive();
activeIndex++;
innerSteps.forEach((step, index) => {
step.classList.toggle(visitedSlideClass, index < activeIndex);
step.classList.toggle(activeSlideClass, index === activeIndex);
});
if (activeIndex < innerSteps.length) {
event.preventDefault();
}
});
shower.addEventListener('prev', (event) => {
if (event.defaultPrevented || !event.cancelable) return;
if (shower.isListMode || innerAt === innerSteps.length || !innerAt) return;
if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
if (activeIndex === -1 || activeIndex === innerSteps.length) return;
activeIndex--;
innerSteps.forEach((step, index) => {
step.classList.toggle(visitedSlideClass, index < activeIndex + 1);
step.classList.toggle(activeSlideClass, index === activeIndex);
});
event.preventDefault();
innerAt--;
toggleActive();
});
};

@@ -20,3 +20,4 @@ export default (shower) => {

shower.addEventListener('start', updateProgress);
shower.addEventListener('slidechange', updateProgress);
};

@@ -18,4 +18,5 @@ const mdash = '\u2014';

shower.addEventListener('start', updateTitle);
shower.addEventListener('modechange', updateTitle);
shower.addEventListener('slidechange', updateTitle);
};

@@ -5,12 +5,35 @@ export default (shower) => {

shower.addEventListener('modechange', () => {
if (container.classList.contains(fullModeClass)) {
shower.enterFullMode();
} else {
container.classList.add(listModeClass);
}
const updateScale = () => {
const firstSlide = shower.slides[0];
if (!firstSlide) return;
const { innerWidth, innerHeight } = window;
const { offsetWidth, offsetHeight } = firstSlide.element;
const listScale = 1 / (offsetWidth / innerWidth);
const fullScale = 1 / Math.max(offsetWidth / innerWidth, offsetHeight / innerHeight);
container.style.setProperty('--shower-list-scale', listScale);
container.style.setProperty('--shower-full-scale', fullScale);
};
const updateModeView = () => {
if (shower.isFullMode) {
container.classList.remove(listModeClass);
container.classList.add(fullModeClass);
return;
} else {
container.classList.remove(fullModeClass);
container.classList.add(listModeClass);
}
container.classList.remove(fullModeClass);
container.classList.add(listModeClass);
updateScale();
if (shower.isFullMode) return;
const slide = shower.activeSlide;

@@ -20,5 +43,9 @@ if (slide) {

}
});
};
shower.addEventListener('start', updateModeView);
shower.addEventListener('modechange', updateModeView);
shower.addEventListener('slidechange', () => {
if (shower.isFullMode) return;
const slide = shower.activeSlide;

@@ -28,9 +55,3 @@ slide.element.scrollIntoView({ block: 'nearest' });

shower.addEventListener('start', () => {
if (container.classList.contains(fullModeClass)) {
shower.enterFullMode();
} else {
container.classList.add(listModeClass);
}
});
window.addEventListener('resize', updateScale);
};
import defaultOptions from './default-options';
import Slide from './slide';
import { EventTarget } from './utils';
import installModules from './modules';
import { defineReadOnly, ShowerError } from './utils';
import installModules from './modules/install';
const ensureSlideId = (slideElement, index) => {
if (!slideElement.id) {
slideElement.id = index + 1;
}
};
class Shower extends EventTarget {
/**
* @param {object=} options
*/
constructor(options) {
super();
defineReadOnly(this, {
options: { ...defaultOptions, ...options },
});
this._mode = 'list';
this._isStarted = false;
this.options = { ...defaultOptions, ...options };
this._container = null;
}
/**
* @param {object} options
* @param {object=} options
* @throws {ShowerError}
*/
configure(options) {
if (this._isStarted) {
throw new ShowerError('Shower should be configured before it is started.');
}
Object.assign(this.options, options);
}
/**
* @throws {ShowerError}
* @emits Shower#start
*/
start() {

@@ -32,18 +42,13 @@ if (this._isStarted) return;

const { containerSelector } = this.options;
this.container = document.querySelector(containerSelector);
if (!this.container) {
throw new Error(`Shower container with selector '${containerSelector}' not found.`);
this._container = document.querySelector(containerSelector);
if (!this._container) {
throw new ShowerError(
`Shower container with selector '${containerSelector}' was not found.`,
);
}
this._isStarted = true;
this._initSlides();
installModules(this);
// maintains invariant: active slide always exists in `full` mode
this.addEventListener('modechange', () => {
if (this.isFullMode && !this.activeSlide) {
this.first();
}
});
installModules(this);
this._isStarted = true;
this.dispatchEvent(new Event('start'));

@@ -53,41 +58,34 @@ }

_initSlides() {
const slideElements = [
...this.container.querySelectorAll(this.options.slideSelector),
].filter((slideElement) => !slideElement.hidden);
const visibleSlideSelector = `${this.options.slideSelector}:not([hidden])`;
const visibleSlideElements = this._container.querySelectorAll(visibleSlideSelector);
slideElements.forEach(ensureSlideId);
this.slides = slideElements.map((slideElement) => {
const slide = new Slide(slideElement, this.options);
this.slides = Array.from(visibleSlideElements, (slideElement, index) => {
if (!slideElement.id) {
slideElement.id = index + 1;
}
slide.addEventListener('activate', () => {
this._changeActiveSlide(slide);
});
slide.element.addEventListener('click', () => {
if (this.isListMode) {
this.enterFullMode();
slide.activate();
}
});
return slide;
return new Slide(this, slideElement);
});
}
_changeActiveSlide(next) {
const prev = this.slides.find((slide) => {
return slide.isActive && slide !== next;
});
_setMode(mode) {
if (mode === this._mode) return;
if (prev) {
prev.deactivate();
}
this._mode = mode;
this.dispatchEvent(new Event('modechange'));
}
const event = new CustomEvent('slidechange', {
detail: { prev },
});
/**
* @param {Event} event
*/
dispatchEvent(event) {
if (!this._isStarted) return false;
this.dispatchEvent(event);
return super.dispatchEvent(event);
}
get container() {
return this._container;
}
get isFullMode() {

@@ -111,8 +109,6 @@ return this._mode === 'full';

* Slide fills the maximum area.
* @emits Shower#modechange
*/
enterFullMode() {
if (!this.isFullMode) {
this._mode = 'full';
this.dispatchEvent(new Event('modechange'));
}
this._setMode('full');
}

@@ -122,8 +118,6 @@

* Shower returns into list mode.
* @emits Shower#modechange
*/
exitFullMode() {
if (!this.isListMode) {
this._mode = 'list';
this.dispatchEvent(new Event('modechange'));
}
this._setMode('list');
}

@@ -144,3 +138,3 @@

*/
go(delta) {
goBy(delta) {
this.goTo(this.activeSlideIndex + delta);

@@ -150,3 +144,4 @@ }

/**
* @param {boolean=} isForce
* @param {boolean} [isForce=false]
* @emits Shower#prev
*/

@@ -156,3 +151,3 @@ prev(isForce) {

if (this.dispatchEvent(prev)) {
this.go(-1);
this.goBy(-1);
}

@@ -162,3 +157,4 @@ }

/**
* @param {boolean=} isForce
* @param {boolean} [isForce=false]
* @emits Shower#next
*/

@@ -168,3 +164,3 @@ next(isForce) {

if (this.dispatchEvent(next)) {
this.go(1);
this.goBy(1);
}

@@ -171,0 +167,0 @@ }

@@ -1,19 +0,29 @@

import { EventTarget } from './utils';
import { defineReadOnly, ShowerError } from './utils';
/**
* @param {HTMLElement} element
* @param {object} options
*/
class Slide extends EventTarget {
constructor(element, options) {
/**
* @param {Shower} shower
* @param {HTMLElement} element
*/
constructor(shower, element) {
super();
this.element = element;
this.options = options;
defineReadOnly(this, {
shower,
element,
state: {
visitCount: 0,
innerStepCount: 0,
},
});
this._isActive = false;
this.state = {
visitsCount: 0,
innerStepsCount: 0,
};
this._options = this.shower.options;
this.element.addEventListener('click', (event) => {
if (event.defaultPrevented) return;
this.activate();
this.shower.enterFullMode();
});
}

@@ -26,3 +36,3 @@

get isVisited() {
return this.state.visitsCount > 0;
return this.state.visitCount > 0;
}

@@ -35,23 +45,53 @@

get title() {
const titleElement = this.element.querySelector(this.options.slideTitleSelector);
const titleElement = this.element.querySelector(this._options.slideTitleSelector);
return titleElement ? titleElement.innerText : '';
}
/**
* Deactivates currently active slide (if any) and activates itself.
* @emits Slide#deactivate
* @emits Slide#activate
* @emits Shower#slidechange
*/
activate() {
if (this._isActive) return;
const prev = this.shower.activeSlide;
if (prev) {
prev._deactivate();
}
this.state.visitCount++;
this.element.classList.add(this._options.activeSlideClass);
this._isActive = true;
this.state.visitsCount++;
this.element.classList.add(this.options.activeSlideClass);
this.dispatchEvent(new Event('activate'));
this.shower.dispatchEvent(
new CustomEvent('slidechange', {
detail: { prev },
}),
);
}
/**
* @throws {ShowerError}
* @emits Slide#deactivate
*/
deactivate() {
if (!this._isActive) return;
if (this.shower.isFullMode) {
throw new ShowerError('In full mode, another slide should be activated instead.');
}
this._isActive = false;
if (this._isActive) {
this._deactivate();
}
}
_deactivate() {
this.element.classList.replace(
this.options.activeSlideClass,
this.options.visitedSlideClass,
this._options.activeSlideClass,
this._options.visitedSlideClass,
);
this._isActive = false;
this.dispatchEvent(new Event('deactivate'));

@@ -58,0 +98,0 @@ }

{
"name": "@shower/core",
"description": "Core for Shower HTML presentation engine",
"version": "3.0.0",
"version": "3.1.0",
"publishConfig": {

@@ -28,38 +28,37 @@ "access": "public"

"devDependencies": {
"chai": "^4.2.0",
"chai-dom": "^1.8.1",
"chromedriver": "^81.0.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-prettier": "^3.1.0",
"esm": "^3.2.25",
"husky": "^4.2.5",
"lint-staged": "^10.1.3",
"mocha": "^7.1.1",
"nightwatch": "^1.3.4",
"prettier": "^2.0.4",
"puppeteer": "^3.0.0",
"rollup": "^2.6.1",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-node-resolve": "^5.0.3",
"sauce-connect-launcher": "^1.2.7",
"serve": "^11.0.2",
"serve-handler": "^6.0.2",
"yn": "^4.0.0"
"chai": "^4.3.0",
"chromedriver": "^83.0.1",
"eslint": "^7.21.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.3.1",
"husky": "^4.3.8",
"lint-staged": "^10.5.4",
"mocha": "^7.2.0",
"nightwatch": "^1.5.1",
"prettier": "^2.2.1",
"puppeteer": "^3.3.0",
"rollup": "^2.40.0",
"sauce-connect-launcher": "^1.3.2",
"serve": "^11.3.2",
"serve-handler": "^6.1.3"
},
"scripts": {
"build": "rollup --config",
"build:watch": "rollup --config --watch",
"serve": "serve dist",
"lint": "eslint '**/*.js'",
"lint:fix": "eslint '**/*.js' --fix",
"lint": "eslint '**/*.{js,mjs}'",
"lint:fix": "eslint '**/*.{js,mjs}' --fix",
"format": "npm run format:js && npm run format:etc",
"format:js": "prettier '**/*.js' --write",
"format:js": "prettier '**/*.{js,mjs}' --write",
"format:etc": "prettier '**/*.{json,md,yml}' --write",
"test": "npm run lint && npm run test:unit && npm run test:local",
"test:unit": "mocha test/unit --require esm --require chai/register-should",
"test:local": "nightwatch --env chrome-local",
"test:sauce": "nightwatch --env chrome,firefox,safari,edge"
"test:unit": "mocha test/unit --require chai/register-should",
"test:local": "nightwatch --env chrome-local --timeout 500",
"test:sauce": "nightwatch --env chrome,firefox,safari"
},
"config": {
"test_port": 8031
}
}

@@ -9,3 +9,3 @@ # Core of Shower [![Build Status](https://travis-ci.org/shower/core.svg?branch=master)](https://travis-ci.org/shower/core)

npm install shower
npm install @shower/shower

@@ -12,0 +12,0 @@ You can also install core as a separate package:

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc