diff --git a/src/scripts/app.js b/src/scripts/app.js index ae48b34..a582df3 100644 --- a/src/scripts/app.js +++ b/src/scripts/app.js @@ -21,6 +21,16 @@ import '@styles/h5p-portfolio.scss'; * positioning and heights set in CSS. Grid is a hot candidate, but this will * still involve a lot of work. */ + +/** @constant {number} DOM_OFFSET some DOM offset that I am not paid to find. */ +const DOM_OFFSET_PX = 19; + +/** @constant {number} RESIZE_TIMEOUT_MS Resize timeout in ms. */ +const RESIZE_TIMEOUT_MS = 150; + +/** @constant {number} FULLSCREEN_TIMEOUT_MS Resize timeout in ms. */ +const FULLSCREEN_TIMEOUT_MS = 250; + export default class Portfolio extends H5P.EventDispatcher { /** * @class @@ -484,11 +494,10 @@ export default class Portfolio extends H5P.EventDispatcher { extraContentHeight - this.statusBarFooter.getHeight(); - // Yes, 19 is a magic number, some DOM offset that I am not paid to find - if (minHeight - 19 > 0) { + if (minHeight - DOM_OFFSET_PX > 0) { minHeight = Math.min(minHeight, extraContentHeight); - currentChapter.setChapterContentMinHeight(minHeight - 19); + currentChapter.setChapterContentMinHeight(minHeight - DOM_OFFSET_PX); } } } @@ -645,7 +654,7 @@ export default class Portfolio extends H5P.EventDispatcher { setTimeout(() => { this.trigger('resize'); }, 0); // Content may need to be resized after re-drawn - }, 150); + }, RESIZE_TIMEOUT_MS); return isMenuOpen; } @@ -665,7 +674,7 @@ export default class Portfolio extends H5P.EventDispatcher { window.setTimeout(() => { this.trigger('resize'); - }, 250); // Browser may need time to exit full screen + }, FULLSCREEN_TIMEOUT_MS); // Browser may need time to exit full screen } /** diff --git a/src/scripts/components/pagecontent/hotspotnavigation/hotspot.js b/src/scripts/components/pagecontent/hotspotnavigation/hotspot.js index 5c0873d..25ca5b6 100644 --- a/src/scripts/components/pagecontent/hotspotnavigation/hotspot.js +++ b/src/scripts/components/pagecontent/hotspotnavigation/hotspot.js @@ -3,6 +3,9 @@ import Label from './label.js'; import he from 'he'; import './hotspot.scss'; +/** @constant {number} Center_PERCENTPOINTS 50 as 100 / 2, obviously */ +const CENTER_PERCENTPOINTS = 50; + export default class Hotspot { /** * @class @@ -11,7 +14,7 @@ export default class Hotspot { */ constructor(params = {}, callbacks = {}) { this.params = Util.extend({ - position: { x: 50, y: 50 } + position: { x: CENTER_PERCENTPOINTS, y: CENTER_PERCENTPOINTS } }, params); this.callbacks = Util.extend({ onClicked: (() => {}) @@ -33,8 +36,8 @@ export default class Hotspot { } if (this.params.title) { - const vertical = this.params.position.y < 50 ? 'bottom' : 'top'; - const horizontal = this.params.position.x < 50 ? 'right' : 'left'; + const vertical = this.params.position.y < CENTER_PERCENTPOINTS ? 'bottom' : 'top'; + const horizontal = this.params.position.x < CENTER_PERCENTPOINTS ? 'right' : 'left'; this.label = new Label({ text: this.params.title, diff --git a/src/scripts/components/pagecontent/hotspotnavigation/label.js b/src/scripts/components/pagecontent/hotspotnavigation/label.js index e9fd9e3..6fe1665 100644 --- a/src/scripts/components/pagecontent/hotspotnavigation/label.js +++ b/src/scripts/components/pagecontent/hotspotnavigation/label.js @@ -1,6 +1,12 @@ import Util from '@services/util.js'; import './label.scss'; +/** @constant {number} LABEL_SIZE_FACTOR Label size factor compared to font size. */ +const LABEL_SIZE_FACTOR = 1.5; + +/** @constant {number} VISIBILITY_TIMEOUT_MS Visibility timeout in milliseconds. */ +const VISIBILITY_TIMEOUT_MS = 10; + export default class Label { /** @@ -63,7 +69,7 @@ export default class Label { window.getComputedStyle(this.labelInner).getPropertyValue('font-size') ); const labelSize = Math.floor(this.labelInner.getBoundingClientRect().height); - this.dom.classList.toggle('multiline', fontSize * 1.5 < labelSize); + this.dom.classList.toggle('multiline', fontSize * LABEL_SIZE_FACTOR < labelSize); this.dom.classList.toggle('touch-device', params.isTouch || false); @@ -73,7 +79,7 @@ export default class Label { else { window.setTimeout(() => { this.dom.classList.remove('visibility-hidden'); - }, 10); + }, VISIBILITY_TIMEOUT_MS); } this.dom.classList.remove('display-none'); diff --git a/src/scripts/components/pagecontent/pagecontent.js b/src/scripts/components/pagecontent/pagecontent.js index 4b74443..b0ddea3 100644 --- a/src/scripts/components/pagecontent/pagecontent.js +++ b/src/scripts/components/pagecontent/pagecontent.js @@ -2,6 +2,15 @@ import Util from '@services/util.js'; import HotspotNavigation from './hotspotnavigation/navigation.js'; import './pagecontent.scss'; +/** @constant {number} SCROLL_INTO_VIEW_TIMEOUT_MS Timeout for scroll into view in ms. */ +const SCROLL_INTO_VIEW_TIMEOUT_MS = 100; + +/** @constant {number} ANIMATION_DURATION_TIMEOUT_MS Timeout for animation workaround in ms. */ +const ANIMATION_DURATION_TIMEOUT_MS = 250; + +/** @constant {number} ANIMATION_DELAY_MS Delay for animation in ms. */ +const ANIMATION_DELAY_MS = 50; + export default class PageContent { /** * @class @@ -229,7 +238,7 @@ export default class PageContent { setTimeout(() => { dom.scrollIntoView(true); - }, 100); + }, SCROLL_INTO_VIEW_TIMEOUT_MS); } } @@ -336,9 +345,9 @@ export default class PageContent { // Ensure all animation has ended window.setTimeout(() => { this.callbacks.onResized(); - }, 250); // Animation duration - }, 250); // Animation duration - }, 50); + }, ANIMATION_DURATION_TIMEOUT_MS); + }, ANIMATION_DURATION_TIMEOUT_MS); + }, ANIMATION_DELAY_MS); } /** diff --git a/src/scripts/components/statusbar.js b/src/scripts/components/statusbar.js index 90ebd1f..a1f73fe 100644 --- a/src/scripts/components/statusbar.js +++ b/src/scripts/components/statusbar.js @@ -45,8 +45,8 @@ export default class StatusBar { // Navigation buttons this.navigationButtons = this.buildNavigationButtons(); - wrapperInfo.appendChild(this.navigationButtons['next']); - wrapperInfo.appendChild(this.navigationButtons['previous']); + wrapperInfo.appendChild(this.navigationButtons.next); + wrapperInfo.appendChild(this.navigationButtons.previous); // Menu toggle button if (this.params.displayMenuToggleButton) { @@ -204,7 +204,7 @@ export default class StatusBar { buildNavigationButtons() { const buttons = {}; - buttons['previous'] = this.buildNavigationButton({ + buttons.previous = this.buildNavigationButton({ icon: 'icon-previous', class: 'previous', label: this.params.dictionary.get('l10n.previousPage'), @@ -216,7 +216,7 @@ export default class StatusBar { }) }); - buttons['next'] = this.buildNavigationButton({ + buttons.next = this.buildNavigationButton({ icon: 'icon-next', class: 'next', label: this.params.dictionary.get('l10n.nextPage'), @@ -328,6 +328,7 @@ export default class StatusBar { */ updateProgressBar(chapterId) { this.progressBar.progress.style.width = + // eslint-disable-next-line no-magic-numbers `${chapterId / this.params.chapters.get().length * 100}%`; const title = this.params.dictionary.get('a11y.progress') diff --git a/src/scripts/services/colors.js b/src/scripts/services/colors.js index 2b0ce4b..e072dc3 100644 --- a/src/scripts/services/colors.js +++ b/src/scripts/services/colors.js @@ -1,6 +1,9 @@ import Color from 'color'; import '@styles/_color_overrides.scss'; +/** @constant {number} CONTRAST_STEP_PERCENTAGE Steps in between contrasts as percent */ +const CONTRAST_STEP_PERCENTAGE = 0.05; + /** * Color class. * @class @@ -52,6 +55,7 @@ export default class Colors { typeof params.opacity === 'string' && /^([0-9]|[1-8][0-9]|9[0-9]|100)(\.\d+)?\s?%$/.test(params.opacity) ) { + // eslint-disable-next-line no-magic-numbers params.opacity = parseInt(params.opacity) / 100; } @@ -95,9 +99,10 @@ export default class Colors { const luminance = comparisonColor.luminosity(); let contrastColor; - for (let diff = 0; diff <= 1; diff = diff + 0.05) { + for (let diff = 0; diff <= 1; diff = diff + CONTRAST_STEP_PERCENTAGE) { contrastColor = Color.rgb(baseColor.rgb().array().map((value) => { - return value * ((luminance > .5) ? (1 - diff) : (1 + diff)); + // eslint-disable-next-line no-magic-numbers + return value * ((luminance > 0.5) ? (1 - diff) : (1 + diff)); })); const contrast = contrastColor.contrast(comparisonColor); @@ -167,7 +172,10 @@ export default class Colors { Colors.DEFAULT_COLOR_BASE = Color('#1768c4'); Colors.DEFAULT_COLOR_BG = Color('#ffffff'); -/** @constant {number} Minimum acceptable contrast for normal font size, cmp. https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure */ +/** + * Minimum acceptable contrast for normal font size, cmp. https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure + * @constant {number} MINIMUM_ACCEPTABLE_CONTRAST + */ Colors.MINIMUM_ACCEPTABLE_CONTRAST = 4.5; diff --git a/src/scripts/services/urltools.js b/src/scripts/services/urltools.js index 241aee2..42452d6 100644 --- a/src/scripts/services/urltools.js +++ b/src/scripts/services/urltools.js @@ -74,10 +74,12 @@ export default class URLTools { return ''; } + const QUERY_LENGTH = 2; + prefix = prefix ?? ''; const queryString = Object.entries(urlQueries) - .filter((entry) => entry.length === 2 && !!entry[1]) + .filter((entry) => entry.length === QUERY_LENGTH && !!entry[1]) .map((entry) => `${entry[0]}=${entry[1]}`) .join('&'); diff --git a/src/scripts/services/urltools.test.js b/src/scripts/services/urltools.test.js index 6244f96..f34432c 100644 --- a/src/scripts/services/urltools.test.js +++ b/src/scripts/services/urltools.test.js @@ -68,7 +68,8 @@ describe('URLTools', () => { } }, { - name: 'invalid hash (InteractiveBook format) values: 2, extra search', hash: '#foo=1&bar=2', search: '?batz=3', + name: + 'invalid hash (InteractiveBook format) values: 2, extra search', hash: '#foo=1&bar=2', search: '?batz=3', result: { foo: '1', bar: '2', @@ -76,7 +77,8 @@ describe('URLTools', () => { } }, { - name: 'invalid hash (InteractiveBook format) values: 2, duplicate search', hash: '#foo=1&bar=2', search: '?foo=3', + name: + 'invalid hash (InteractiveBook format) values: 2, duplicate search', hash: '#foo=1&bar=2', search: '?foo=3', result: { foo: '3', bar: '2' diff --git a/src/scripts/services/util.js b/src/scripts/services/util.js index 9bc963c..a3f72e0 100644 --- a/src/scripts/services/util.js +++ b/src/scripts/services/util.js @@ -112,7 +112,7 @@ export default class Util { // Check for (temporary) exceptions const exceptions = [ - 'H5P.Blanks', // Exception required for original V1.12.11 and before, can be removed when later is out or changes merged in + 'H5P.Blanks', // Exception required for original V1.12.11 and before 'H5P.MemoryGame', // Doesn't implement getMaxScore yet 'H5P.SpeakTheWordsSet' // Doesn't implement getMaxScore yet ];