Merge branch 'master' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib
# Bitte geben Sie eine Commit-Beschreibung ein, um zu erklären, warum dieser # Merge erforderlich ist, insbesondere wenn es einen aktualisierten # Upstream-Branch mit einem Thema-Branch zusammenführt. # # Zeilen, die mit '#' beginnen, werden ignoriert, # und eine leere Beschreibung bricht den Commit ab. Merged.
This commit is contained in:
		
						commit
						7f00898767
					
				
							
								
								
									
										1236
									
								
								dist/iwmlib.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1236
									
								
								dist/iwmlib.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -19,9 +19,11 @@ import { ITapDelegate, ResizeEvent, DOMScatterContainer, AbstractScatter, DOMSca
 | 
			
		||||
import { Cycle, Colors, Elements, Angle, Dates, Points, Polygon, Rect, Sets, Strings, isEmpty, getId, lerp, debounce, randomInt, randomFloat, LowPassFilter } from './utils.js'
 | 
			
		||||
import UITest from './uitest.js'
 | 
			
		||||
 | 
			
		||||
import Card from './card/card.js'
 | 
			
		||||
import CardWrapper from './card/wrapper.js'
 | 
			
		||||
import Highlight from './card/highlight.js'
 | 
			
		||||
import {Card, ScatterCard} from './card/card.js'
 | 
			
		||||
import ScatterCard from './card/card.js'
 | 
			
		||||
import { CardPlugin, CardPluginBase } from './card/plugin.js'
 | 
			
		||||
import Theme from './card/theme.js'
 | 
			
		||||
 | 
			
		||||
/* Needed to ensure that rollup.js includes class definitions and the classes
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										747
									
								
								lib/card/card.js
									
									
									
									
									
								
							
							
						
						
									
										747
									
								
								lib/card/card.js
									
									
									
									
									
								
							@ -29,7 +29,7 @@ const enableNearestNeighborTaps = false
 | 
			
		||||
 *
 | 
			
		||||
 * The class is used as a namespace and should never called with new.
 | 
			
		||||
 */
 | 
			
		||||
export class Card {
 | 
			
		||||
export default class Card {
 | 
			
		||||
 | 
			
		||||
    static setup(context, modules = []) {
 | 
			
		||||
        console.log("Setup Card...", modules)
 | 
			
		||||
@ -431,7 +431,7 @@ export class Card {
 | 
			
		||||
                zIndex: this.zIndices.popup
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(popup.element, this.animation.popup, {
 | 
			
		||||
            TweenLite.to(popup.element, this.animation.popup, {
 | 
			
		||||
                autoAlpha: 1,
 | 
			
		||||
                ease: Power2.easeIn
 | 
			
		||||
            })
 | 
			
		||||
@ -451,7 +451,7 @@ export class Card {
 | 
			
		||||
         * TEST if this intereferes with the editor.
 | 
			
		||||
         */
 | 
			
		||||
        if (overlay) {
 | 
			
		||||
            TweenMax.to(overlay, 0.2, {
 | 
			
		||||
            TweenLite.to(overlay, 0.2, {
 | 
			
		||||
                autoAlpha: 0, onComplete: () => {
 | 
			
		||||
                    popup.remove()
 | 
			
		||||
                    //this._cleanup(context)
 | 
			
		||||
@ -507,9 +507,9 @@ export class Card {
 | 
			
		||||
                if (editable) {
 | 
			
		||||
                    if (this.debug) console.log("Append overlay.", context)
 | 
			
		||||
                    overlay.classList.add('overlay')
 | 
			
		||||
                    TweenMax.set(overlay, { autoAlpha: 0 })
 | 
			
		||||
                    TweenLite.set(overlay, { autoAlpha: 0 })
 | 
			
		||||
                    context.appendChild(overlay)
 | 
			
		||||
                    TweenMax.to(overlay, 0.5, { autoAlpha: 0.25 })
 | 
			
		||||
                    TweenLite.to(overlay, 0.5, { autoAlpha: 0.25 })
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Extract the body from the Popup site.
 | 
			
		||||
@ -949,17 +949,17 @@ export class Card {
 | 
			
		||||
            const scaleFactor = 2
 | 
			
		||||
            const transformOrigin = 'bottom right'
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(zoomedFig, {
 | 
			
		||||
            TweenLite.set(zoomedFig, {
 | 
			
		||||
                x: current.x,
 | 
			
		||||
                y: current.y,
 | 
			
		||||
                width: current.width + borderX,
 | 
			
		||||
                height: current.height + borderY,
 | 
			
		||||
                transformOrigin
 | 
			
		||||
            })
 | 
			
		||||
            TweenMax.set(zoomable, { opacity: 0 })
 | 
			
		||||
            TweenLite.set(zoomable, { opacity: 0 })
 | 
			
		||||
 | 
			
		||||
            let icon = zoomedFig.querySelector(".icon")
 | 
			
		||||
            TweenMax.set(icon, {
 | 
			
		||||
            TweenLite.set(icon, {
 | 
			
		||||
                transformOrigin
 | 
			
		||||
            })
 | 
			
		||||
            zoomedFig.style.transformOrigin = "calc(100% - " + parseFloat(zoomedFigStyle.borderRightWidth) + "px) calc(100% - " + parseFloat(zoomedFigStyle.borderBottomWidth) + "px)"
 | 
			
		||||
@ -1029,7 +1029,7 @@ export class Card {
 | 
			
		||||
        zoomParent.appendChild(zoomedFig)
 | 
			
		||||
        zoomedFig.style.opacity = 0.5
 | 
			
		||||
        zoomContainer.appendChild(zoomable)
 | 
			
		||||
        TweenMax.set(zoomable, { x: current.x, y: current.y, width: current.width, height: current.height })
 | 
			
		||||
        TweenLite.set(zoomable, { x: current.x, y: current.y, width: current.width, height: current.height })
 | 
			
		||||
        let editor = mainController.topController().ensureEditor(img)
 | 
			
		||||
        let savedDisplay = zoomIcon.style.display
 | 
			
		||||
        let iconClone = zoomIcon.cloneNode(true)
 | 
			
		||||
@ -1046,14 +1046,14 @@ export class Card {
 | 
			
		||||
            zoomedFig.remove()
 | 
			
		||||
            zoomContainer.remove()
 | 
			
		||||
            zoomParent.appendChild(zoomable)
 | 
			
		||||
            TweenMax.set(zoomable, { x: 0, y: 0 })
 | 
			
		||||
            TweenLite.set(zoomable, { x: 0, y: 0 })
 | 
			
		||||
            zoomable.onmousedown = null
 | 
			
		||||
            zoomable.onmousemove = null
 | 
			
		||||
            zoomable.onmouseup = null
 | 
			
		||||
            zoomable.onmousewheel = null
 | 
			
		||||
        }
 | 
			
		||||
        wrapper.appendChild(iconClone)
 | 
			
		||||
        TweenMax.set(iconClone, { x: current.iconPos.x, y: current.iconPos.y })
 | 
			
		||||
        TweenLite.set(iconClone, { x: current.iconPos.x, y: current.iconPos.y })
 | 
			
		||||
 | 
			
		||||
        zoomable.onmousedown = event => {
 | 
			
		||||
            if (this.debug) console.log('mousedown', event.target)
 | 
			
		||||
@ -1067,7 +1067,7 @@ export class Card {
 | 
			
		||||
                event.preventDefault()
 | 
			
		||||
                let dx = event.pageX - zoomable.dragStartPos.x
 | 
			
		||||
                let dy = event.pageY - zoomable.dragStartPos.y
 | 
			
		||||
                TweenMax.set([zoomable, iconClone], { x: '+=' + dx, y: '+=' + dy })
 | 
			
		||||
                TweenLite.set([zoomable, iconClone], { x: '+=' + dx, y: '+=' + dy })
 | 
			
		||||
                zoomable.dragStartPos = { x: event.pageX, y: event.pageY }
 | 
			
		||||
                if (editor) {
 | 
			
		||||
                    editor.showControls()
 | 
			
		||||
@ -1086,7 +1086,7 @@ export class Card {
 | 
			
		||||
            let zoom = direction ? zoomFactor : 1 / zoomFactor
 | 
			
		||||
            startZoom *= zoom
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(zoomable, { scale: startZoom })
 | 
			
		||||
            TweenLite.set(zoomable, { scale: startZoom })
 | 
			
		||||
            if (editor) {
 | 
			
		||||
                editor.showControls()
 | 
			
		||||
            }
 | 
			
		||||
@ -1117,17 +1117,17 @@ export class Card {
 | 
			
		||||
            let zoomedCaption = zoomedFig.querySelector("figcaption.zoomcap")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(zoomedCaption, this.animation.fade, {
 | 
			
		||||
            TweenLite.to(zoomedCaption, this.animation.fade, {
 | 
			
		||||
                autoAlpha: 0,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(zoomedFig, this.animation.zoomable, {
 | 
			
		||||
            TweenLite.to(zoomedFig, this.animation.zoomable, {
 | 
			
		||||
                css: {
 | 
			
		||||
                    scaleX: 1,
 | 
			
		||||
                    scaleY: 1
 | 
			
		||||
                },
 | 
			
		||||
                onComplete: () => {
 | 
			
		||||
                    TweenMax.set(zoomable, {
 | 
			
		||||
                    TweenLite.set(zoomable, {
 | 
			
		||||
                        opacity: 1
 | 
			
		||||
                    })
 | 
			
		||||
                    let div = zoomedFig.parentNode
 | 
			
		||||
@ -1204,7 +1204,7 @@ export class Card {
 | 
			
		||||
        let padding = parseInt(this.css(indexbox, 'padding'))
 | 
			
		||||
        let maxWidth = this.css(card, 'max-width')
 | 
			
		||||
 | 
			
		||||
        TweenMax.set(clone, {
 | 
			
		||||
        TweenLite.set(clone, {
 | 
			
		||||
            css: {
 | 
			
		||||
                position: 'absolute',
 | 
			
		||||
                width: globalIndexCardRect.width,
 | 
			
		||||
@ -1215,12 +1215,12 @@ export class Card {
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        TweenMax.set(articleClone, {
 | 
			
		||||
        TweenLite.set(articleClone, {
 | 
			
		||||
            autoAlpha: 0
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        TweenMax.set(card, { css: { maxWidth: '100%' } })
 | 
			
		||||
        TweenMax.set(clone, {
 | 
			
		||||
        TweenLite.set(card, { css: { maxWidth: '100%' } })
 | 
			
		||||
        TweenLite.set(clone, {
 | 
			
		||||
            x: localOrigin.x - padding,
 | 
			
		||||
            y: localOrigin.y - padding,
 | 
			
		||||
            scaleX,
 | 
			
		||||
@ -1244,10 +1244,10 @@ export class Card {
 | 
			
		||||
            /**
 | 
			
		||||
             * Scale the content from 100% to it's target size.
 | 
			
		||||
             */
 | 
			
		||||
            // TweenMax.set(subcardContent, {
 | 
			
		||||
            // TweenLite.set(subcardContent, {
 | 
			
		||||
            //     height: "100%"
 | 
			
		||||
            // })
 | 
			
		||||
            // TweenMax.to(subcardContent, Card.animation.articleTransition, {
 | 
			
		||||
            // TweenLite.to(subcardContent, Card.animation.articleTransition, {
 | 
			
		||||
            //     height: targetHeight + "px"
 | 
			
		||||
            // })
 | 
			
		||||
        }
 | 
			
		||||
@ -1270,7 +1270,7 @@ export class Card {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let desiredBorderBottomWidth = parseInt(window.getComputedStyle(titlebar).borderBottomWidth)
 | 
			
		||||
        TweenMax.to(clone, Card.animation.articleTransition, {
 | 
			
		||||
        TweenLite.to(clone, Card.animation.articleTransition, {
 | 
			
		||||
            x: -padding,
 | 
			
		||||
            y: -padding,
 | 
			
		||||
            ease: ExpoScaleEase.config(scaleX, 1),
 | 
			
		||||
@ -1282,10 +1282,10 @@ export class Card {
 | 
			
		||||
            onUpdateParams: ['{self}'],
 | 
			
		||||
            onUpdate: (self) => {
 | 
			
		||||
                let transform = self.target._gsTransform
 | 
			
		||||
                TweenMax.set(title, {
 | 
			
		||||
                TweenLite.set(title, {
 | 
			
		||||
                    scale: 1 / transform.scaleX
 | 
			
		||||
                })
 | 
			
		||||
                TweenMax.set(titlebar, {
 | 
			
		||||
                TweenLite.set(titlebar, {
 | 
			
		||||
                    height: start.height * 1 / transform.scaleY
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
@ -1294,7 +1294,7 @@ export class Card {
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        TweenMax.to([articleClone], this.animation.articleTransition / 2, {
 | 
			
		||||
        TweenLite.to([articleClone], this.animation.articleTransition / 2, {
 | 
			
		||||
            delay: this.animation.articleTransition / 2,
 | 
			
		||||
            autoAlpha: 1
 | 
			
		||||
        })
 | 
			
		||||
@ -1327,11 +1327,11 @@ export class Card {
 | 
			
		||||
            let titlebarStyle = window.getComputedStyle(previewTitlebar)
 | 
			
		||||
            let titlebar = clone.querySelector(".titlebar")
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(titlebar, this.animation.articleTransition, {
 | 
			
		||||
            TweenLite.to(titlebar, this.animation.articleTransition, {
 | 
			
		||||
                height: parseInt(titlebarStyle.height)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(articleClone, this.animation.articleTransition / 2, {
 | 
			
		||||
            TweenLite.to(articleClone, this.animation.articleTransition / 2, {
 | 
			
		||||
                autoAlpha: 0
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
@ -1341,13 +1341,13 @@ export class Card {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.dynamicHeight) {
 | 
			
		||||
                TweenMax.to(subcardContent, this.animation.articleTransition, {
 | 
			
		||||
                TweenLite.to(subcardContent, this.animation.articleTransition, {
 | 
			
		||||
                    height: "100%"
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(card, { autoAlpha: 1, css: { maxWidth } })
 | 
			
		||||
            TweenMax.to(clone, this.animation.articleTransition, {
 | 
			
		||||
            TweenLite.set(card, { autoAlpha: 1, css: { maxWidth } })
 | 
			
		||||
            TweenLite.to(clone, this.animation.articleTransition, {
 | 
			
		||||
                x: localOrigin.x - padding,
 | 
			
		||||
                y: localOrigin.y - padding,
 | 
			
		||||
                scaleX,
 | 
			
		||||
@ -1356,7 +1356,7 @@ export class Card {
 | 
			
		||||
                rotation: angle,
 | 
			
		||||
                onComplete: () => {
 | 
			
		||||
                    // article.remove()
 | 
			
		||||
                    TweenMax.to(clone, this.animation.fade,
 | 
			
		||||
                    TweenLite.to(clone, this.animation.fade,
 | 
			
		||||
                        {
 | 
			
		||||
                            //delay: 0.2,
 | 
			
		||||
                            autoAlpha: 0,
 | 
			
		||||
@ -1373,11 +1373,11 @@ export class Card {
 | 
			
		||||
                onUpdate: function (self) {
 | 
			
		||||
                    let transform = self.target._gsTransform
 | 
			
		||||
 | 
			
		||||
                    TweenMax.set(title, {
 | 
			
		||||
                    TweenLite.set(title, {
 | 
			
		||||
                        scale: 1 / transform.scaleX
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
                    TweenMax.set(titlebar, {
 | 
			
		||||
                    TweenLite.set(titlebar, {
 | 
			
		||||
                        height: original.height * 1 / transform.scaleY
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
@ -1508,7 +1508,7 @@ export class Card {
 | 
			
		||||
                // TODO: What is this good for?
 | 
			
		||||
                // let article = parsedHTML.querySelector('article')
 | 
			
		||||
                // card.insertAdjacentElement('afterbegin', article)
 | 
			
		||||
                // TweenMax.set(article, { autoAlpha: 0 })
 | 
			
		||||
                // TweenLite.set(article, { autoAlpha: 0 })
 | 
			
		||||
 | 
			
		||||
                Card.expandIndexCard(card, parsedHTML, 'article', relativeSource, saveCallback)
 | 
			
		||||
            }
 | 
			
		||||
@ -1883,680 +1883,3 @@ Card.animation = {
 | 
			
		||||
    zoomable: 0.5
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extends the card with scatter functionality.
 | 
			
		||||
 *
 | 
			
		||||
 * @class ScatterCard
 | 
			
		||||
 */
 | 
			
		||||
export class ScatterCard extends Card {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * TODO: Find a more suitable name.
 | 
			
		||||
     * Adjusts the HTML to work in the new context.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} domElement
 | 
			
		||||
     * @param {*} htmlString
 | 
			
		||||
     * @param {*} basePath
 | 
			
		||||
     * @param {*} [opts={}]
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static setup(context, htmlString, {
 | 
			
		||||
        basePath = "./",
 | 
			
		||||
        modules = []
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        context.classList.add("info-card")
 | 
			
		||||
 | 
			
		||||
        this.relativePath = basePath
 | 
			
		||||
        htmlString = this._adjustRelativeLinks(htmlString)
 | 
			
		||||
 | 
			
		||||
        let parser = new DOMParser()
 | 
			
		||||
        let html = parser.parseFromString(htmlString, "text/html")
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Conflicts with the FindTarget method of the Abstract scatter.
 | 
			
		||||
         */
 | 
			
		||||
        this._replaceAttributes(html, "onclick", this._replaceCallback)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let content = html.querySelector(".mainview")
 | 
			
		||||
        context.appendChild(content)
 | 
			
		||||
 | 
			
		||||
        super.setup(context, modules)
 | 
			
		||||
        return context
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Appends a close listener to the scatter element.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} element
 | 
			
		||||
     * @param {*} callback
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static addOnCloseListener(element, callback) {
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            element.onClose = callback
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a scatter for the card and applies the card to it,
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} html
 | 
			
		||||
     * @param {*} scatterContainer
 | 
			
		||||
     * @param {string} [basePath=""]
 | 
			
		||||
     * @param {*} [opts={}]
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static createCardScatter(html, scatterContainer, {
 | 
			
		||||
        basePath = "./",
 | 
			
		||||
        modules = []
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        let element = document.createElement("div")
 | 
			
		||||
 | 
			
		||||
        scatterContainer.element.appendChild(element)
 | 
			
		||||
        new DOMScatter(element, scatterContainer, {
 | 
			
		||||
            width: 1400,
 | 
			
		||||
            height: 1200
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        this.setup(element, html, {
 | 
			
		||||
            basePath,
 | 
			
		||||
            modules
 | 
			
		||||
        })
 | 
			
		||||
        return element
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *Utility function to create a fully functional card scatter.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} scatterContainer
 | 
			
		||||
     * @param {*} path
 | 
			
		||||
     * @param {string} [basePath="."]
 | 
			
		||||
     * @param {*} opts
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof CardScatter
 | 
			
		||||
     */
 | 
			
		||||
    static loadAndCreateScatterCard(scatterContainer, item, {
 | 
			
		||||
        basePath = "../",
 | 
			
		||||
        modules = [],
 | 
			
		||||
        onClose = null
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        console.log(basePath)
 | 
			
		||||
        return new Promise((resolve, reject) => {
 | 
			
		||||
            let url = basePath + "/" + item + "/index.html"
 | 
			
		||||
            console.log("Loading", url)
 | 
			
		||||
            this.loadHTML(url)
 | 
			
		||||
                .then(html => {
 | 
			
		||||
                    console.log("Received", html)
 | 
			
		||||
                    let element = this.createCardScatter(html, scatterContainer, {
 | 
			
		||||
                        basePath,
 | 
			
		||||
                        modules
 | 
			
		||||
                    })
 | 
			
		||||
                    if (onClose)
 | 
			
		||||
                        this.addOnCloseListener(element, onClose)
 | 
			
		||||
                    resolve(element)
 | 
			
		||||
                })
 | 
			
		||||
                .catch(e => reject(e))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static _setLanguage(context, language) {
 | 
			
		||||
        context.language = language
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static _getLanguage(context) {
 | 
			
		||||
        return context.language
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.ScatterCard = ScatterCard
 | 
			
		||||
 | 
			
		||||
ScatterCard.selectedLanguage = 0
 | 
			
		||||
ScatterCard.languages = ["Deutsch", "English"]
 | 
			
		||||
ScatterCard.languageTags = {
 | 
			
		||||
    Deutsch: "de",
 | 
			
		||||
    English: "en"
 | 
			
		||||
}
 | 
			
		||||
ScatterCard.scatterContainer = null
 | 
			
		||||
 | 
			
		||||
var CardPlugin = CardPlugin || {}
 | 
			
		||||
 | 
			
		||||
class CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    apply(context) {
 | 
			
		||||
        if (this.verify(context)) {
 | 
			
		||||
            this.append(context)
 | 
			
		||||
            console.log("Plugin " + this.name + " was verified successfully.")
 | 
			
		||||
            return true
 | 
			
		||||
        } else console.error("Could not verify module " + this.name + ".")
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get name() {
 | 
			
		||||
        return this.constructor.name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    verify(context) {
 | 
			
		||||
        let funcs = this._getVerificationFunctions(context)
 | 
			
		||||
        for (let func of funcs) {
 | 
			
		||||
            if (!func()) return false
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyElementsExist(context, ...selectors) {
 | 
			
		||||
        let missing = []
 | 
			
		||||
 | 
			
		||||
        for (let selector of selectors) {
 | 
			
		||||
            let requiredElement = context.querySelector(selector)
 | 
			
		||||
            if (requiredElement == null) {
 | 
			
		||||
                missing.push(selector)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const valid = (missing.length == 0)
 | 
			
		||||
        if (!valid) console.error("Elements were missing: ", missing.join(", "))
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  Appends the Plugin to the context.
 | 
			
		||||
     *
 | 
			
		||||
     * @memberof CardPlugin
 | 
			
		||||
     */
 | 
			
		||||
    append(context) {
 | 
			
		||||
        console.error("Call of abstract method CardPlugin.prototype.append(context). Plugins need to overwrite the append method!")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        return [
 | 
			
		||||
            this._verifyContext.bind(this, context),
 | 
			
		||||
            this._verifyRequirements.bind(this, context)
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyContext(context) {
 | 
			
		||||
        if (!(context instanceof HTMLElement)) {
 | 
			
		||||
            console.error("Context is not of type HTML Element.", context)
 | 
			
		||||
            return false
 | 
			
		||||
        } else return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyRequirements(context) {
 | 
			
		||||
        let requirements = this._collectAllRequirements()
 | 
			
		||||
        let missing = []
 | 
			
		||||
 | 
			
		||||
        requirements.forEach(module => {
 | 
			
		||||
            if (context.modules.indexOf(module.name) == -1) {
 | 
			
		||||
                missing.push(module.name)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        const valid = (missing.length == 0)
 | 
			
		||||
        if (!valid) console.error("Could not apply module '" + this.name + "'. Following modules are required but were missing: " + missing.join(","))
 | 
			
		||||
        else console.log("All requirements were met! Well done!")
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    _collectAllRequirements() {
 | 
			
		||||
        let requirements = []
 | 
			
		||||
        let klass = this.__proto__
 | 
			
		||||
        while (klass) {
 | 
			
		||||
            if (klass.require != null) {
 | 
			
		||||
                requirements = requirements.concat(klass.require)
 | 
			
		||||
            }
 | 
			
		||||
            klass = klass.__proto__
 | 
			
		||||
        }
 | 
			
		||||
        return requirements
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CardPlugin.LightBox = class LightBox extends CardPluginBase {
 | 
			
		||||
    constructor(className, style = {}) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.className = className
 | 
			
		||||
        this.style = style
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let wrapper = document.createElement("div")
 | 
			
		||||
        wrapper.className = this.className
 | 
			
		||||
 | 
			
		||||
        Object.assign(wrapper.style, {
 | 
			
		||||
            zIndex: 1000,
 | 
			
		||||
            // backgroundColor: "black",
 | 
			
		||||
            top: 0,
 | 
			
		||||
            left: 0,
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "100%"
 | 
			
		||||
        }, this.style, {
 | 
			
		||||
                display: "none",
 | 
			
		||||
                position: "absolute",
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        context.appendChild(wrapper)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The Enlargeable Overlay module allows the user to click on the thumbnail image,
 | 
			
		||||
 * and the images gets enlarged inside the card.
 | 
			
		||||
 *
 | 
			
		||||
 * @class EnlargeableThumbnail
 | 
			
		||||
 * @extends {CardPlugin}
 | 
			
		||||
 */
 | 
			
		||||
CardPlugin.EnlargeableThumbnail = class EnlargeableThumbnail extends CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    constructor(wrapperSelector, overlaySelector = null, {
 | 
			
		||||
        zoomAnimationDuration = 0.4,
 | 
			
		||||
        fadeAnimationDuration = 0.4,
 | 
			
		||||
        interactionType = "tap"
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.wrapperSelector = wrapperSelector
 | 
			
		||||
        this.overlaySelector = overlaySelector
 | 
			
		||||
 | 
			
		||||
        this.zoomAnimationDuration = zoomAnimationDuration
 | 
			
		||||
        this.fadeAnimationDuration = fadeAnimationDuration
 | 
			
		||||
        this.interactionType = interactionType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get require() {
 | 
			
		||||
        return [
 | 
			
		||||
            CardPlugin.LightBox
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        let arr = super._getVerificationFunctions(context)
 | 
			
		||||
        let funcs = [
 | 
			
		||||
            this._verifyElementsExist.bind(this, context, this.wrapperSelector, this.overlaySelector)
 | 
			
		||||
        ]
 | 
			
		||||
        return arr.concat(funcs)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let source = this._retrieveSource(context)
 | 
			
		||||
        this.setupEnlargeableThumbnail(context, source)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the preview image.
 | 
			
		||||
     * 
 | 
			
		||||
     * It depends on the fact, that the thumbnail image is in the same directory
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param {*} context
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof EnlargeableThumbnail
 | 
			
		||||
     */
 | 
			
		||||
    _retrieveSource(context) {
 | 
			
		||||
        let img = context.querySelector(this.wrapperSelector + " img")
 | 
			
		||||
        let src = img.getAttribute("src")
 | 
			
		||||
        let parts = src.split("/")
 | 
			
		||||
        parts.pop()
 | 
			
		||||
        parts.push(parts[parts.length - 1])
 | 
			
		||||
        let imagePath = parts.join("/") + ".jpg"
 | 
			
		||||
        return imagePath
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    setupEnlargeableThumbnail(context, src) {
 | 
			
		||||
        let wrapper = context.querySelector(this.wrapperSelector)
 | 
			
		||||
        let overlay = context.querySelector(this.overlaySelector)
 | 
			
		||||
 | 
			
		||||
        let icon = document.createElement("div")
 | 
			
		||||
        icon.className = "button corner-button bottom-right icon zoom"
 | 
			
		||||
        wrapper.appendChild(icon)
 | 
			
		||||
 | 
			
		||||
        Object.assign(wrapper.style, {
 | 
			
		||||
            cursor: "pointer"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, wrapper, () => {
 | 
			
		||||
            this.openThumbnailDetail(context, src)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, overlay, () => {
 | 
			
		||||
            this.closeThumnailDetail(context)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    openThumbnailDetail(context, src) {
 | 
			
		||||
        let overlay = context.querySelector(".img-overlay")
 | 
			
		||||
        overlay.innerHTML = ""
 | 
			
		||||
        let source = context.querySelector(this.wrapperSelector)
 | 
			
		||||
        let sourceStyle = window.getComputedStyle(source)
 | 
			
		||||
        let imageWrapper = source.cloneNode(true)
 | 
			
		||||
        let image = imageWrapper.querySelector("img")
 | 
			
		||||
 | 
			
		||||
        Object.assign(imageWrapper.style, {
 | 
			
		||||
            maxWidth: "none",
 | 
			
		||||
            maxHeight: "none"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        Object.assign(image.style, {
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "100%",
 | 
			
		||||
            objectFit: "cover"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        this._replaceIcon(imageWrapper)
 | 
			
		||||
 | 
			
		||||
        image.onload = () => {
 | 
			
		||||
            let header = context.querySelector("header")
 | 
			
		||||
            let headerStlye = window.getComputedStyle(header)
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * First the maxFillRatio is considered.
 | 
			
		||||
             * It describes how much the image is allowed to exceed the context element.
 | 
			
		||||
             */
 | 
			
		||||
            const maxFillRatio = 1.5
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The minor side should not exceed the height of the context window. 
 | 
			
		||||
             */
 | 
			
		||||
            const maxMinorSize = context.offsetHeight - 2 * parseInt(headerStlye.paddingTop) - 2 * parseInt(headerStlye.marginTop)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            const max = {
 | 
			
		||||
                width: context.offsetWidth * maxFillRatio,
 | 
			
		||||
                height: context.offsetHeight * maxFillRatio
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let majorSide
 | 
			
		||||
            let minorSide
 | 
			
		||||
            const _width = { name: "width", axis: "x" }
 | 
			
		||||
            const _height = { name: "height", axis: "y" }
 | 
			
		||||
            if (image.naturalHeight > image.naturalWidth) {
 | 
			
		||||
                majorSide = _height
 | 
			
		||||
                minorSide = _width
 | 
			
		||||
            } else {
 | 
			
		||||
                majorSide = _width
 | 
			
		||||
                minorSide = _height
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function capitalize(string) {
 | 
			
		||||
                return string.charAt(0).toUpperCase() + string.slice(1)
 | 
			
		||||
            }
 | 
			
		||||
            function getImageSize(side) {
 | 
			
		||||
                return image["natural" + capitalize(side.name)]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const majorImageSize = getImageSize(majorSide)
 | 
			
		||||
            // const minorImageSize = getImageSize(minorSide)
 | 
			
		||||
 | 
			
		||||
            let ratio = getImageSize(minorSide) / getImageSize(majorSide)
 | 
			
		||||
            let size = (majorImageSize > max[majorSide.name]) ? max[majorSide.name] : majorImageSize
 | 
			
		||||
 | 
			
		||||
            if (size * ratio > maxMinorSize) {
 | 
			
		||||
                size = maxMinorSize / ratio
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let targetDimensions = {
 | 
			
		||||
                width: 0,
 | 
			
		||||
                height: 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let position = Points.fromPageToNode(context, Points.fromNodeToPage(source, { x: 0, y: 0 }))
 | 
			
		||||
 | 
			
		||||
            let targetOffset = {
 | 
			
		||||
                x: 0,
 | 
			
		||||
                y: 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            targetDimensions[majorSide.name] = size
 | 
			
		||||
            targetDimensions[minorSide.name] = size * ratio
 | 
			
		||||
 | 
			
		||||
            targetOffset[majorSide.axis] = (context["offset" + capitalize(majorSide.name)] - targetDimensions[majorSide.name]) / 2
 | 
			
		||||
            targetOffset[minorSide.axis] = (context["offset" + capitalize(minorSide.name)] - targetDimensions[minorSide.name]) / 2
 | 
			
		||||
 | 
			
		||||
            overlay.appendChild(imageWrapper)
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(imageWrapper, {
 | 
			
		||||
                left: 0,
 | 
			
		||||
                top: 0,
 | 
			
		||||
                x: position.x,
 | 
			
		||||
                y: position.y,
 | 
			
		||||
                position: "absolute",
 | 
			
		||||
                width: parseInt(sourceStyle.width),
 | 
			
		||||
                height: parseInt(sourceStyle.height)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(overlay, {
 | 
			
		||||
                display: "flex",
 | 
			
		||||
                autoAlpha: 0
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(imageWrapper, this.zoomAnimationDuration, {
 | 
			
		||||
                x: targetOffset.x,
 | 
			
		||||
                y: targetOffset.y,
 | 
			
		||||
                width: targetDimensions.width,
 | 
			
		||||
                height: targetDimensions.height,
 | 
			
		||||
            })
 | 
			
		||||
            TweenMax.to(overlay, this.fadeAnimationTime, {
 | 
			
		||||
                autoAlpha: 1
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        image.src = src
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _replaceIcon(clone) {
 | 
			
		||||
        let zoomIcon = clone.querySelector(".icon.zoom")
 | 
			
		||||
        zoomIcon.classList.remove("zoom")
 | 
			
		||||
        zoomIcon.classList.add("close")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getBorderHeight(style) {
 | 
			
		||||
        const borderWidth = parseInt(style.borderTopWidth) + parseInt(style.borderBottomWidth)
 | 
			
		||||
        const padding = parseInt(style.paddingTop) + parseInt(style.paddingBottom)
 | 
			
		||||
        return parseInt(style.width) + borderWidth + padding
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getBorderWidth(style) {
 | 
			
		||||
        const borderWidth = parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth)
 | 
			
		||||
        const padding = parseInt(style.paddingLeft) + parseInt(style.paddingRight)
 | 
			
		||||
        return parseInt(style.width) + borderWidth + padding
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeThumnailDetail(context) {
 | 
			
		||||
        let overlay = context.querySelector(".img-overlay")
 | 
			
		||||
 | 
			
		||||
        let timeline = new TimelineLite()
 | 
			
		||||
 | 
			
		||||
        timeline.to(overlay, this.fadeAnimationDuration, {
 | 
			
		||||
            autoAlpha: 0
 | 
			
		||||
        }).set(overlay, {
 | 
			
		||||
            display: "none"
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CardPlugin.Ui = class UiPlugin extends CardPluginBase {
 | 
			
		||||
    constructor(className, parent = null) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.parent = parent
 | 
			
		||||
        this.className = className
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        let arr = super._getVerificationFunctions(context)
 | 
			
		||||
        let func = [
 | 
			
		||||
            this._doesParentExist.bind(this, context, this.parent)
 | 
			
		||||
        ]
 | 
			
		||||
        return arr.concat(func)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _doesParentExist(context, parent) {
 | 
			
		||||
        if (parent == null) return true
 | 
			
		||||
        let valid = (context.querySelector(parent) != null)
 | 
			
		||||
        if (!valid) console.error("Could not find parent on context.", context, parent)
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        parent = (this.parent == null) ? context : context.querySelector(this.parent).appendChild(container)
 | 
			
		||||
        let container = document.createElement("div")
 | 
			
		||||
        container.className = this.className
 | 
			
		||||
        parent.appendChild(container)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    constructor(parentSelector, className, interactionType = "tap") {
 | 
			
		||||
        super()
 | 
			
		||||
        this.className = className
 | 
			
		||||
        this.parentSelector = parentSelector
 | 
			
		||||
        this.interactionType = interactionType
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get require() {
 | 
			
		||||
        return [
 | 
			
		||||
            CardPlugin.Ui
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let container = context.querySelector(this.parentSelector)
 | 
			
		||||
        this.button = document.createElement("div")
 | 
			
		||||
        this.button.className = "icon button " + this.className
 | 
			
		||||
        container.appendChild(this.button)
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, this.button, () => {
 | 
			
		||||
            let subcard = context.querySelector(".mainview > .subcard")
 | 
			
		||||
            let target = (subcard) ? subcard : context
 | 
			
		||||
 | 
			
		||||
            this.speak(target)
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _activate() {
 | 
			
		||||
        this._disableActive()
 | 
			
		||||
        this.active = this
 | 
			
		||||
        this._activateButton()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _activateButton() {
 | 
			
		||||
        if (this.button)
 | 
			
		||||
            this.button.classList.add("active")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _deactivate() {
 | 
			
		||||
        this._deactivateButton()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _deactivateButton() {
 | 
			
		||||
        if (this.button)
 | 
			
		||||
            this.button.classList.remove("active")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isSameNode(node) {
 | 
			
		||||
        //console.log(this.currentText, node.innerText)
 | 
			
		||||
        return (this.currentText == node.innerText)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    speak(node) {
 | 
			
		||||
 | 
			
		||||
        console.log(this._isSameNode(node))
 | 
			
		||||
 | 
			
		||||
        if (!window.speechSynthesis.speaking) {
 | 
			
		||||
            console.log("Noone talking!")
 | 
			
		||||
            this._start(node)
 | 
			
		||||
        } else if (this._isSameNode(node)) {
 | 
			
		||||
            console.log("Requested same!")
 | 
			
		||||
            this._stop()
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("Requested Different!")
 | 
			
		||||
            this._stop()
 | 
			
		||||
            this._start(node)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _disableActive() {
 | 
			
		||||
        console.log("disableActive:", this.active)
 | 
			
		||||
        if (this.active) {
 | 
			
		||||
            this.active._deactivate()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _start(node) {
 | 
			
		||||
        this.currentText = node.innerText
 | 
			
		||||
        let utterance = new SpeechSynthesisUtterance(node.innerText)
 | 
			
		||||
 | 
			
		||||
        let voices = window.speechSynthesis.getVoices()
 | 
			
		||||
        console.log(voices)
 | 
			
		||||
        let voice = voices.filter((val) => {
 | 
			
		||||
            //console.log(val)
 | 
			
		||||
            return val.name == "Microsoft Hedda Desktop - German"
 | 
			
		||||
        })[0]
 | 
			
		||||
 | 
			
		||||
        //console.log(voice)
 | 
			
		||||
 | 
			
		||||
        utterance.voice = voice
 | 
			
		||||
        console.log("TALK: ", utterance)
 | 
			
		||||
        window.speechSynthesis.speak(utterance)
 | 
			
		||||
        this._activate()
 | 
			
		||||
        window.speechSynthesis.resume()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        utterance.onboundary = () => { console.log("onboundary", node.innerText); if (this.currentText.substring(0, 5) != node.innerText.substring(0, 5)) { console.log("text for speech synth changed!", this.currentText, node.innerText); this._stop() } }
 | 
			
		||||
        utterance.onend = () => console.log("onend", node.innerText)
 | 
			
		||||
        utterance.onerror = () => console.log("onerror", node.innerText)
 | 
			
		||||
        utterance.onmark = () => console.log("onmark", node.innerText)
 | 
			
		||||
        utterance.onpause = () => console.log("onpause", node.innerText)
 | 
			
		||||
        utterance.onresume = () => console.log("onresume", node.innerText)
 | 
			
		||||
        utterance.onstart = () => console.log("onstart", node.innerText)
 | 
			
		||||
        utterance.onerror = () => console.log("onerror", node.innerText)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _stop() {
 | 
			
		||||
        window.speechSynthesis.cancel()
 | 
			
		||||
        this.currentText = null
 | 
			
		||||
        this._deactivate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get active() {
 | 
			
		||||
        return this.constructor.active
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set active(val) { this.constructor.active = val }
 | 
			
		||||
 | 
			
		||||
    get currentText() {
 | 
			
		||||
        return this.constructor.text
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set currentText(val) {
 | 
			
		||||
        this.constructor.text = val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}		
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										530
									
								
								lib/card/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										530
									
								
								lib/card/plugin.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,530 @@
 | 
			
		||||
export var CardPlugin = CardPlugin || {}
 | 
			
		||||
 | 
			
		||||
export class CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    apply(context) {
 | 
			
		||||
        if (this.verify(context)) {
 | 
			
		||||
            this.append(context)
 | 
			
		||||
            console.log("Plugin " + this.name + " was verified successfully.")
 | 
			
		||||
            return true
 | 
			
		||||
        } else console.error("Could not verify module " + this.name + ".")
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get name() {
 | 
			
		||||
        return this.constructor.name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    verify(context) {
 | 
			
		||||
        let funcs = this._getVerificationFunctions(context)
 | 
			
		||||
        for (let func of funcs) {
 | 
			
		||||
            if (!func()) return false
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyElementsExist(context, ...selectors) {
 | 
			
		||||
        let missing = []
 | 
			
		||||
 | 
			
		||||
        for (let selector of selectors) {
 | 
			
		||||
            let requiredElement = context.querySelector(selector)
 | 
			
		||||
            if (requiredElement == null) {
 | 
			
		||||
                missing.push(selector)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const valid = (missing.length == 0)
 | 
			
		||||
        if (!valid) console.error("Elements were missing: ", missing.join(", "))
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  Appends the Plugin to the context.
 | 
			
		||||
     *
 | 
			
		||||
     * @memberof CardPlugin
 | 
			
		||||
     */
 | 
			
		||||
    append(context) {
 | 
			
		||||
        console.error("Call of abstract method CardPlugin.prototype.append(context). Plugins need to overwrite the append method!")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        return [
 | 
			
		||||
            this._verifyContext.bind(this, context),
 | 
			
		||||
            this._verifyRequirements.bind(this, context)
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyContext(context) {
 | 
			
		||||
        if (!(context instanceof HTMLElement)) {
 | 
			
		||||
            console.error("Context is not of type HTML Element.", context)
 | 
			
		||||
            return false
 | 
			
		||||
        } else return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _verifyRequirements(context) {
 | 
			
		||||
        let requirements = this._collectAllRequirements()
 | 
			
		||||
        let missing = []
 | 
			
		||||
 | 
			
		||||
        requirements.forEach(module => {
 | 
			
		||||
            if (context.modules.indexOf(module.name) == -1) {
 | 
			
		||||
                missing.push(module.name)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        const valid = (missing.length == 0)
 | 
			
		||||
        if (!valid) console.error("Could not apply module '" + this.name + "'. Following modules are required but were missing: " + missing.join(","))
 | 
			
		||||
        else console.log("All requirements were met! Well done!")
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    _collectAllRequirements() {
 | 
			
		||||
        let requirements = []
 | 
			
		||||
        let klass = this.__proto__
 | 
			
		||||
        while (klass) {
 | 
			
		||||
            if (klass.require != null) {
 | 
			
		||||
                requirements = requirements.concat(klass.require)
 | 
			
		||||
            }
 | 
			
		||||
            klass = klass.__proto__
 | 
			
		||||
        }
 | 
			
		||||
        return requirements
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CardPlugin.LightBox = class LightBox extends CardPluginBase {
 | 
			
		||||
    constructor(className, style = {}) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.className = className
 | 
			
		||||
        this.style = style
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let wrapper = document.createElement("div")
 | 
			
		||||
        wrapper.className = this.className
 | 
			
		||||
 | 
			
		||||
        Object.assign(wrapper.style, {
 | 
			
		||||
            zIndex: 1000,
 | 
			
		||||
            // backgroundColor: "black",
 | 
			
		||||
            top: 0,
 | 
			
		||||
            left: 0,
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "100%"
 | 
			
		||||
        }, this.style, {
 | 
			
		||||
                display: "none",
 | 
			
		||||
                position: "absolute",
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        context.appendChild(wrapper)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The Enlargeable Overlay module allows the user to click on the thumbnail image,
 | 
			
		||||
 * and the images gets enlarged inside the card.
 | 
			
		||||
 *
 | 
			
		||||
 * @class EnlargeableThumbnail
 | 
			
		||||
 * @extends {CardPlugin}
 | 
			
		||||
 */
 | 
			
		||||
CardPlugin.EnlargeableThumbnail = class EnlargeableThumbnail extends CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    constructor(wrapperSelector, overlaySelector = null, {
 | 
			
		||||
        zoomAnimationDuration = 0.4,
 | 
			
		||||
        fadeAnimationDuration = 0.4,
 | 
			
		||||
        interactionType = "tap"
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.wrapperSelector = wrapperSelector
 | 
			
		||||
        this.overlaySelector = overlaySelector
 | 
			
		||||
 | 
			
		||||
        this.zoomAnimationDuration = zoomAnimationDuration
 | 
			
		||||
        this.fadeAnimationDuration = fadeAnimationDuration
 | 
			
		||||
        this.interactionType = interactionType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get require() {
 | 
			
		||||
        return [
 | 
			
		||||
            CardPlugin.LightBox
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        let arr = super._getVerificationFunctions(context)
 | 
			
		||||
        let funcs = [
 | 
			
		||||
            this._verifyElementsExist.bind(this, context, this.wrapperSelector, this.overlaySelector)
 | 
			
		||||
        ]
 | 
			
		||||
        return arr.concat(funcs)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let source = this._retrieveSource(context)
 | 
			
		||||
        this.setupEnlargeableThumbnail(context, source)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the preview image.
 | 
			
		||||
     *
 | 
			
		||||
     * It depends on the fact, that the thumbnail image is in the same directory
 | 
			
		||||
     *
 | 
			
		||||
     *
 | 
			
		||||
     * @param {*} context
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof EnlargeableThumbnail
 | 
			
		||||
     */
 | 
			
		||||
    _retrieveSource(context) {
 | 
			
		||||
        let img = context.querySelector(this.wrapperSelector + " img")
 | 
			
		||||
        let src = img.getAttribute("src")
 | 
			
		||||
        let parts = src.split("/")
 | 
			
		||||
        parts.pop()
 | 
			
		||||
        parts.push(parts[parts.length - 1])
 | 
			
		||||
        let imagePath = parts.join("/") + ".jpg"
 | 
			
		||||
        return imagePath
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    setupEnlargeableThumbnail(context, src) {
 | 
			
		||||
        let wrapper = context.querySelector(this.wrapperSelector)
 | 
			
		||||
        let overlay = context.querySelector(this.overlaySelector)
 | 
			
		||||
 | 
			
		||||
        let icon = document.createElement("div")
 | 
			
		||||
        icon.className = "button corner-button bottom-right icon zoom"
 | 
			
		||||
        wrapper.appendChild(icon)
 | 
			
		||||
 | 
			
		||||
        Object.assign(wrapper.style, {
 | 
			
		||||
            cursor: "pointer"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, wrapper, () => {
 | 
			
		||||
            this.openThumbnailDetail(context, src)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, overlay, () => {
 | 
			
		||||
            this.closeThumnailDetail(context)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    openThumbnailDetail(context, src) {
 | 
			
		||||
        let overlay = context.querySelector(".img-overlay")
 | 
			
		||||
        overlay.innerHTML = ""
 | 
			
		||||
        let source = context.querySelector(this.wrapperSelector)
 | 
			
		||||
        let sourceStyle = window.getComputedStyle(source)
 | 
			
		||||
        let imageWrapper = source.cloneNode(true)
 | 
			
		||||
        let image = imageWrapper.querySelector("img")
 | 
			
		||||
 | 
			
		||||
        Object.assign(imageWrapper.style, {
 | 
			
		||||
            maxWidth: "none",
 | 
			
		||||
            maxHeight: "none"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        Object.assign(image.style, {
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "100%",
 | 
			
		||||
            objectFit: "cover"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        this._replaceIcon(imageWrapper)
 | 
			
		||||
 | 
			
		||||
        image.onload = () => {
 | 
			
		||||
            let header = context.querySelector("header")
 | 
			
		||||
            let headerStlye = window.getComputedStyle(header)
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * First the maxFillRatio is considered.
 | 
			
		||||
             * It describes how much the image is allowed to exceed the context element.
 | 
			
		||||
             */
 | 
			
		||||
            const maxFillRatio = 1.5
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The minor side should not exceed the height of the context window.
 | 
			
		||||
             */
 | 
			
		||||
            const maxMinorSize = context.offsetHeight - 2 * parseInt(headerStlye.paddingTop) - 2 * parseInt(headerStlye.marginTop)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            const max = {
 | 
			
		||||
                width: context.offsetWidth * maxFillRatio,
 | 
			
		||||
                height: context.offsetHeight * maxFillRatio
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let majorSide
 | 
			
		||||
            let minorSide
 | 
			
		||||
            const _width = { name: "width", axis: "x" }
 | 
			
		||||
            const _height = { name: "height", axis: "y" }
 | 
			
		||||
            if (image.naturalHeight > image.naturalWidth) {
 | 
			
		||||
                majorSide = _height
 | 
			
		||||
                minorSide = _width
 | 
			
		||||
            } else {
 | 
			
		||||
                majorSide = _width
 | 
			
		||||
                minorSide = _height
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function capitalize(string) {
 | 
			
		||||
                return string.charAt(0).toUpperCase() + string.slice(1)
 | 
			
		||||
            }
 | 
			
		||||
            function getImageSize(side) {
 | 
			
		||||
                return image["natural" + capitalize(side.name)]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const majorImageSize = getImageSize(majorSide)
 | 
			
		||||
            // const minorImageSize = getImageSize(minorSide)
 | 
			
		||||
 | 
			
		||||
            let ratio = getImageSize(minorSide) / getImageSize(majorSide)
 | 
			
		||||
            let size = (majorImageSize > max[majorSide.name]) ? max[majorSide.name] : majorImageSize
 | 
			
		||||
 | 
			
		||||
            if (size * ratio > maxMinorSize) {
 | 
			
		||||
                size = maxMinorSize / ratio
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let targetDimensions = {
 | 
			
		||||
                width: 0,
 | 
			
		||||
                height: 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let position = Points.fromPageToNode(context, Points.fromNodeToPage(source, { x: 0, y: 0 }))
 | 
			
		||||
 | 
			
		||||
            let targetOffset = {
 | 
			
		||||
                x: 0,
 | 
			
		||||
                y: 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            targetDimensions[majorSide.name] = size
 | 
			
		||||
            targetDimensions[minorSide.name] = size * ratio
 | 
			
		||||
 | 
			
		||||
            targetOffset[majorSide.axis] = (context["offset" + capitalize(majorSide.name)] - targetDimensions[majorSide.name]) / 2
 | 
			
		||||
            targetOffset[minorSide.axis] = (context["offset" + capitalize(minorSide.name)] - targetDimensions[minorSide.name]) / 2
 | 
			
		||||
 | 
			
		||||
            overlay.appendChild(imageWrapper)
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(imageWrapper, {
 | 
			
		||||
                left: 0,
 | 
			
		||||
                top: 0,
 | 
			
		||||
                x: position.x,
 | 
			
		||||
                y: position.y,
 | 
			
		||||
                position: "absolute",
 | 
			
		||||
                width: parseInt(sourceStyle.width),
 | 
			
		||||
                height: parseInt(sourceStyle.height)
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            TweenMax.set(overlay, {
 | 
			
		||||
                display: "flex",
 | 
			
		||||
                autoAlpha: 0
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            TweenMax.to(imageWrapper, this.zoomAnimationDuration, {
 | 
			
		||||
                x: targetOffset.x,
 | 
			
		||||
                y: targetOffset.y,
 | 
			
		||||
                width: targetDimensions.width,
 | 
			
		||||
                height: targetDimensions.height,
 | 
			
		||||
            })
 | 
			
		||||
            TweenMax.to(overlay, this.fadeAnimationTime, {
 | 
			
		||||
                autoAlpha: 1
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        image.src = src
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _replaceIcon(clone) {
 | 
			
		||||
        let zoomIcon = clone.querySelector(".icon.zoom")
 | 
			
		||||
        zoomIcon.classList.remove("zoom")
 | 
			
		||||
        zoomIcon.classList.add("close")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getBorderHeight(style) {
 | 
			
		||||
        const borderWidth = parseInt(style.borderTopWidth) + parseInt(style.borderBottomWidth)
 | 
			
		||||
        const padding = parseInt(style.paddingTop) + parseInt(style.paddingBottom)
 | 
			
		||||
        return parseInt(style.width) + borderWidth + padding
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getBorderWidth(style) {
 | 
			
		||||
        const borderWidth = parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth)
 | 
			
		||||
        const padding = parseInt(style.paddingLeft) + parseInt(style.paddingRight)
 | 
			
		||||
        return parseInt(style.width) + borderWidth + padding
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeThumnailDetail(context) {
 | 
			
		||||
        let overlay = context.querySelector(".img-overlay")
 | 
			
		||||
 | 
			
		||||
        let timeline = new TimelineLite()
 | 
			
		||||
 | 
			
		||||
        timeline.to(overlay, this.fadeAnimationDuration, {
 | 
			
		||||
            autoAlpha: 0
 | 
			
		||||
        }).set(overlay, {
 | 
			
		||||
            display: "none"
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CardPlugin.Ui = class UiPlugin extends CardPluginBase {
 | 
			
		||||
    constructor(className, parent = null) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.parent = parent
 | 
			
		||||
        this.className = className
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVerificationFunctions(context) {
 | 
			
		||||
        let arr = super._getVerificationFunctions(context)
 | 
			
		||||
        let func = [
 | 
			
		||||
            this._doesParentExist.bind(this, context, this.parent)
 | 
			
		||||
        ]
 | 
			
		||||
        return arr.concat(func)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _doesParentExist(context, parent) {
 | 
			
		||||
        if (parent == null) return true
 | 
			
		||||
        let valid = (context.querySelector(parent) != null)
 | 
			
		||||
        if (!valid) console.error("Could not find parent on context.", context, parent)
 | 
			
		||||
        return valid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        parent = (this.parent == null) ? context : context.querySelector(this.parent).appendChild(container)
 | 
			
		||||
        let container = document.createElement("div")
 | 
			
		||||
        container.className = this.className
 | 
			
		||||
        parent.appendChild(container)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
 | 
			
		||||
 | 
			
		||||
    constructor(parentSelector, className, interactionType = "tap") {
 | 
			
		||||
        super()
 | 
			
		||||
        this.className = className
 | 
			
		||||
        this.parentSelector = parentSelector
 | 
			
		||||
        this.interactionType = interactionType
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get require() {
 | 
			
		||||
        return [
 | 
			
		||||
            CardPlugin.Ui
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    append(context) {
 | 
			
		||||
        let container = context.querySelector(this.parentSelector)
 | 
			
		||||
        this.button = document.createElement("div")
 | 
			
		||||
        this.button.className = "icon button " + this.className
 | 
			
		||||
        container.appendChild(this.button)
 | 
			
		||||
 | 
			
		||||
        InteractionMapper.on(this.interactionType, this.button, () => {
 | 
			
		||||
            let subcard = context.querySelector(".mainview > .subcard")
 | 
			
		||||
            let target = (subcard) ? subcard : context
 | 
			
		||||
 | 
			
		||||
            this.speak(target)
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _activate() {
 | 
			
		||||
        this._disableActive()
 | 
			
		||||
        this.active = this
 | 
			
		||||
        this._activateButton()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _activateButton() {
 | 
			
		||||
        if (this.button)
 | 
			
		||||
            this.button.classList.add("active")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _deactivate() {
 | 
			
		||||
        this._deactivateButton()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _deactivateButton() {
 | 
			
		||||
        if (this.button)
 | 
			
		||||
            this.button.classList.remove("active")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isSameNode(node) {
 | 
			
		||||
        //console.log(this.currentText, node.innerText)
 | 
			
		||||
        return (this.currentText == node.innerText)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    speak(node) {
 | 
			
		||||
 | 
			
		||||
        console.log(this._isSameNode(node))
 | 
			
		||||
 | 
			
		||||
        if (!window.speechSynthesis.speaking) {
 | 
			
		||||
            console.log("Noone talking!")
 | 
			
		||||
            this._start(node)
 | 
			
		||||
        } else if (this._isSameNode(node)) {
 | 
			
		||||
            console.log("Requested same!")
 | 
			
		||||
            this._stop()
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("Requested Different!")
 | 
			
		||||
            this._stop()
 | 
			
		||||
            this._start(node)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _disableActive() {
 | 
			
		||||
        console.log("disableActive:", this.active)
 | 
			
		||||
        if (this.active) {
 | 
			
		||||
            this.active._deactivate()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _start(node) {
 | 
			
		||||
        this.currentText = node.innerText
 | 
			
		||||
        let utterance = new SpeechSynthesisUtterance(node.innerText)
 | 
			
		||||
 | 
			
		||||
        let voices = window.speechSynthesis.getVoices()
 | 
			
		||||
        console.log(voices)
 | 
			
		||||
        let voice = voices.filter((val) => {
 | 
			
		||||
            //console.log(val)
 | 
			
		||||
            return val.name == "Microsoft Hedda Desktop - German"
 | 
			
		||||
        })[0]
 | 
			
		||||
 | 
			
		||||
        //console.log(voice)
 | 
			
		||||
 | 
			
		||||
        utterance.voice = voice
 | 
			
		||||
        console.log("TALK: ", utterance)
 | 
			
		||||
        window.speechSynthesis.speak(utterance)
 | 
			
		||||
        this._activate()
 | 
			
		||||
        window.speechSynthesis.resume()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        utterance.onboundary = () => { console.log("onboundary", node.innerText); if (this.currentText.substring(0, 5) != node.innerText.substring(0, 5)) { console.log("text for speech synth changed!", this.currentText, node.innerText); this._stop() } }
 | 
			
		||||
        utterance.onend = () => console.log("onend", node.innerText)
 | 
			
		||||
        utterance.onerror = () => console.log("onerror", node.innerText)
 | 
			
		||||
        utterance.onmark = () => console.log("onmark", node.innerText)
 | 
			
		||||
        utterance.onpause = () => console.log("onpause", node.innerText)
 | 
			
		||||
        utterance.onresume = () => console.log("onresume", node.innerText)
 | 
			
		||||
        utterance.onstart = () => console.log("onstart", node.innerText)
 | 
			
		||||
        utterance.onerror = () => console.log("onerror", node.innerText)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _stop() {
 | 
			
		||||
        window.speechSynthesis.cancel()
 | 
			
		||||
        this.currentText = null
 | 
			
		||||
        this._deactivate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get active() {
 | 
			
		||||
        return this.constructor.active
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set active(val) { this.constructor.active = val }
 | 
			
		||||
 | 
			
		||||
    get currentText() {
 | 
			
		||||
        return this.constructor.text
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set currentText(val) {
 | 
			
		||||
        this.constructor.text = val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								lib/card/scatter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lib/card/scatter.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Extends the card with scatter functionality.
 | 
			
		||||
 *
 | 
			
		||||
 * @class ScatterCard
 | 
			
		||||
 */
 | 
			
		||||
export default class ScatterCard extends Card {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * TODO: Find a more suitable name.
 | 
			
		||||
     * Adjusts the HTML to work in the new context.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} domElement
 | 
			
		||||
     * @param {*} htmlString
 | 
			
		||||
     * @param {*} basePath
 | 
			
		||||
     * @param {*} [opts={}]
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static setup(context, htmlString, {
 | 
			
		||||
        basePath = "./",
 | 
			
		||||
        modules = []
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        context.classList.add("info-card")
 | 
			
		||||
 | 
			
		||||
        this.relativePath = basePath
 | 
			
		||||
        htmlString = this._adjustRelativeLinks(htmlString)
 | 
			
		||||
 | 
			
		||||
        let parser = new DOMParser()
 | 
			
		||||
        let html = parser.parseFromString(htmlString, "text/html")
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Conflicts with the FindTarget method of the Abstract scatter.
 | 
			
		||||
         */
 | 
			
		||||
        this._replaceAttributes(html, "onclick", this._replaceCallback)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let content = html.querySelector(".mainview")
 | 
			
		||||
        context.appendChild(content)
 | 
			
		||||
 | 
			
		||||
        super.setup(context, modules)
 | 
			
		||||
        return context
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Appends a close listener to the scatter element.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} element
 | 
			
		||||
     * @param {*} callback
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static addOnCloseListener(element, callback) {
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            element.onClose = callback
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a scatter for the card and applies the card to it,
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} html
 | 
			
		||||
     * @param {*} scatterContainer
 | 
			
		||||
     * @param {string} [basePath=""]
 | 
			
		||||
     * @param {*} [opts={}]
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof Card
 | 
			
		||||
     */
 | 
			
		||||
    static createCardScatter(html, scatterContainer, {
 | 
			
		||||
        basePath = "./",
 | 
			
		||||
        modules = []
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        let element = document.createElement("div")
 | 
			
		||||
 | 
			
		||||
        scatterContainer.element.appendChild(element)
 | 
			
		||||
        new DOMScatter(element, scatterContainer, {
 | 
			
		||||
            width: 1400,
 | 
			
		||||
            height: 1200
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        this.setup(element, html, {
 | 
			
		||||
            basePath,
 | 
			
		||||
            modules
 | 
			
		||||
        })
 | 
			
		||||
        return element
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *Utility function to create a fully functional card scatter.
 | 
			
		||||
     *
 | 
			
		||||
     * @static
 | 
			
		||||
     * @param {*} scatterContainer
 | 
			
		||||
     * @param {*} path
 | 
			
		||||
     * @param {string} [basePath="."]
 | 
			
		||||
     * @param {*} opts
 | 
			
		||||
     * @returns
 | 
			
		||||
     * @memberof CardScatter
 | 
			
		||||
     */
 | 
			
		||||
    static loadAndCreateScatterCard(scatterContainer, item, {
 | 
			
		||||
        basePath = "../",
 | 
			
		||||
        modules = [],
 | 
			
		||||
        onClose = null
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        console.log(basePath)
 | 
			
		||||
        return new Promise((resolve, reject) => {
 | 
			
		||||
            let url = basePath + "/" + item + "/index.html"
 | 
			
		||||
            console.log("Loading", url)
 | 
			
		||||
            this.loadHTML(url)
 | 
			
		||||
                .then(html => {
 | 
			
		||||
                    console.log("Received", html)
 | 
			
		||||
                    let element = this.createCardScatter(html, scatterContainer, {
 | 
			
		||||
                        basePath,
 | 
			
		||||
                        modules
 | 
			
		||||
                    })
 | 
			
		||||
                    if (onClose)
 | 
			
		||||
                        this.addOnCloseListener(element, onClose)
 | 
			
		||||
                    resolve(element)
 | 
			
		||||
                })
 | 
			
		||||
                .catch(e => reject(e))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static _setLanguage(context, language) {
 | 
			
		||||
        context.language = language
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static _getLanguage(context) {
 | 
			
		||||
        return context.language
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScatterCard.selectedLanguage = 0
 | 
			
		||||
ScatterCard.languages = ["Deutsch", "English"]
 | 
			
		||||
ScatterCard.languageTags = {
 | 
			
		||||
    Deutsch: "de",
 | 
			
		||||
    English: "en"
 | 
			
		||||
}
 | 
			
		||||
ScatterCard.scatterContainer = null
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user