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
1274
dist/iwmlib.js
vendored
1274
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 { 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 UITest from './uitest.js'
|
||||||
|
|
||||||
|
import Card from './card/card.js'
|
||||||
import CardWrapper from './card/wrapper.js'
|
import CardWrapper from './card/wrapper.js'
|
||||||
import Highlight from './card/highlight.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'
|
import Theme from './card/theme.js'
|
||||||
|
|
||||||
/* Needed to ensure that rollup.js includes class definitions and the classes
|
/* 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.
|
* The class is used as a namespace and should never called with new.
|
||||||
*/
|
*/
|
||||||
export class Card {
|
export default class Card {
|
||||||
|
|
||||||
static setup(context, modules = []) {
|
static setup(context, modules = []) {
|
||||||
console.log("Setup Card...", modules)
|
console.log("Setup Card...", modules)
|
||||||
@ -431,7 +431,7 @@ export class Card {
|
|||||||
zIndex: this.zIndices.popup
|
zIndex: this.zIndices.popup
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.to(popup.element, this.animation.popup, {
|
TweenLite.to(popup.element, this.animation.popup, {
|
||||||
autoAlpha: 1,
|
autoAlpha: 1,
|
||||||
ease: Power2.easeIn
|
ease: Power2.easeIn
|
||||||
})
|
})
|
||||||
@ -451,7 +451,7 @@ export class Card {
|
|||||||
* TEST if this intereferes with the editor.
|
* TEST if this intereferes with the editor.
|
||||||
*/
|
*/
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
TweenMax.to(overlay, 0.2, {
|
TweenLite.to(overlay, 0.2, {
|
||||||
autoAlpha: 0, onComplete: () => {
|
autoAlpha: 0, onComplete: () => {
|
||||||
popup.remove()
|
popup.remove()
|
||||||
//this._cleanup(context)
|
//this._cleanup(context)
|
||||||
@ -507,9 +507,9 @@ export class Card {
|
|||||||
if (editable) {
|
if (editable) {
|
||||||
if (this.debug) console.log("Append overlay.", context)
|
if (this.debug) console.log("Append overlay.", context)
|
||||||
overlay.classList.add('overlay')
|
overlay.classList.add('overlay')
|
||||||
TweenMax.set(overlay, { autoAlpha: 0 })
|
TweenLite.set(overlay, { autoAlpha: 0 })
|
||||||
context.appendChild(overlay)
|
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.
|
// Extract the body from the Popup site.
|
||||||
@ -949,17 +949,17 @@ export class Card {
|
|||||||
const scaleFactor = 2
|
const scaleFactor = 2
|
||||||
const transformOrigin = 'bottom right'
|
const transformOrigin = 'bottom right'
|
||||||
|
|
||||||
TweenMax.set(zoomedFig, {
|
TweenLite.set(zoomedFig, {
|
||||||
x: current.x,
|
x: current.x,
|
||||||
y: current.y,
|
y: current.y,
|
||||||
width: current.width + borderX,
|
width: current.width + borderX,
|
||||||
height: current.height + borderY,
|
height: current.height + borderY,
|
||||||
transformOrigin
|
transformOrigin
|
||||||
})
|
})
|
||||||
TweenMax.set(zoomable, { opacity: 0 })
|
TweenLite.set(zoomable, { opacity: 0 })
|
||||||
|
|
||||||
let icon = zoomedFig.querySelector(".icon")
|
let icon = zoomedFig.querySelector(".icon")
|
||||||
TweenMax.set(icon, {
|
TweenLite.set(icon, {
|
||||||
transformOrigin
|
transformOrigin
|
||||||
})
|
})
|
||||||
zoomedFig.style.transformOrigin = "calc(100% - " + parseFloat(zoomedFigStyle.borderRightWidth) + "px) calc(100% - " + parseFloat(zoomedFigStyle.borderBottomWidth) + "px)"
|
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)
|
zoomParent.appendChild(zoomedFig)
|
||||||
zoomedFig.style.opacity = 0.5
|
zoomedFig.style.opacity = 0.5
|
||||||
zoomContainer.appendChild(zoomable)
|
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 editor = mainController.topController().ensureEditor(img)
|
||||||
let savedDisplay = zoomIcon.style.display
|
let savedDisplay = zoomIcon.style.display
|
||||||
let iconClone = zoomIcon.cloneNode(true)
|
let iconClone = zoomIcon.cloneNode(true)
|
||||||
@ -1046,14 +1046,14 @@ export class Card {
|
|||||||
zoomedFig.remove()
|
zoomedFig.remove()
|
||||||
zoomContainer.remove()
|
zoomContainer.remove()
|
||||||
zoomParent.appendChild(zoomable)
|
zoomParent.appendChild(zoomable)
|
||||||
TweenMax.set(zoomable, { x: 0, y: 0 })
|
TweenLite.set(zoomable, { x: 0, y: 0 })
|
||||||
zoomable.onmousedown = null
|
zoomable.onmousedown = null
|
||||||
zoomable.onmousemove = null
|
zoomable.onmousemove = null
|
||||||
zoomable.onmouseup = null
|
zoomable.onmouseup = null
|
||||||
zoomable.onmousewheel = null
|
zoomable.onmousewheel = null
|
||||||
}
|
}
|
||||||
wrapper.appendChild(iconClone)
|
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 => {
|
zoomable.onmousedown = event => {
|
||||||
if (this.debug) console.log('mousedown', event.target)
|
if (this.debug) console.log('mousedown', event.target)
|
||||||
@ -1067,7 +1067,7 @@ export class Card {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
let dx = event.pageX - zoomable.dragStartPos.x
|
let dx = event.pageX - zoomable.dragStartPos.x
|
||||||
let dy = event.pageY - zoomable.dragStartPos.y
|
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 }
|
zoomable.dragStartPos = { x: event.pageX, y: event.pageY }
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.showControls()
|
editor.showControls()
|
||||||
@ -1086,7 +1086,7 @@ export class Card {
|
|||||||
let zoom = direction ? zoomFactor : 1 / zoomFactor
|
let zoom = direction ? zoomFactor : 1 / zoomFactor
|
||||||
startZoom *= zoom
|
startZoom *= zoom
|
||||||
|
|
||||||
TweenMax.set(zoomable, { scale: startZoom })
|
TweenLite.set(zoomable, { scale: startZoom })
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.showControls()
|
editor.showControls()
|
||||||
}
|
}
|
||||||
@ -1117,17 +1117,17 @@ export class Card {
|
|||||||
let zoomedCaption = zoomedFig.querySelector("figcaption.zoomcap")
|
let zoomedCaption = zoomedFig.querySelector("figcaption.zoomcap")
|
||||||
|
|
||||||
|
|
||||||
TweenMax.to(zoomedCaption, this.animation.fade, {
|
TweenLite.to(zoomedCaption, this.animation.fade, {
|
||||||
autoAlpha: 0,
|
autoAlpha: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.to(zoomedFig, this.animation.zoomable, {
|
TweenLite.to(zoomedFig, this.animation.zoomable, {
|
||||||
css: {
|
css: {
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1
|
scaleY: 1
|
||||||
},
|
},
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
TweenMax.set(zoomable, {
|
TweenLite.set(zoomable, {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
})
|
})
|
||||||
let div = zoomedFig.parentNode
|
let div = zoomedFig.parentNode
|
||||||
@ -1204,7 +1204,7 @@ export class Card {
|
|||||||
let padding = parseInt(this.css(indexbox, 'padding'))
|
let padding = parseInt(this.css(indexbox, 'padding'))
|
||||||
let maxWidth = this.css(card, 'max-width')
|
let maxWidth = this.css(card, 'max-width')
|
||||||
|
|
||||||
TweenMax.set(clone, {
|
TweenLite.set(clone, {
|
||||||
css: {
|
css: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: globalIndexCardRect.width,
|
width: globalIndexCardRect.width,
|
||||||
@ -1215,12 +1215,12 @@ export class Card {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.set(articleClone, {
|
TweenLite.set(articleClone, {
|
||||||
autoAlpha: 0
|
autoAlpha: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.set(card, { css: { maxWidth: '100%' } })
|
TweenLite.set(card, { css: { maxWidth: '100%' } })
|
||||||
TweenMax.set(clone, {
|
TweenLite.set(clone, {
|
||||||
x: localOrigin.x - padding,
|
x: localOrigin.x - padding,
|
||||||
y: localOrigin.y - padding,
|
y: localOrigin.y - padding,
|
||||||
scaleX,
|
scaleX,
|
||||||
@ -1244,10 +1244,10 @@ export class Card {
|
|||||||
/**
|
/**
|
||||||
* Scale the content from 100% to it's target size.
|
* Scale the content from 100% to it's target size.
|
||||||
*/
|
*/
|
||||||
// TweenMax.set(subcardContent, {
|
// TweenLite.set(subcardContent, {
|
||||||
// height: "100%"
|
// height: "100%"
|
||||||
// })
|
// })
|
||||||
// TweenMax.to(subcardContent, Card.animation.articleTransition, {
|
// TweenLite.to(subcardContent, Card.animation.articleTransition, {
|
||||||
// height: targetHeight + "px"
|
// height: targetHeight + "px"
|
||||||
// })
|
// })
|
||||||
}
|
}
|
||||||
@ -1270,7 +1270,7 @@ export class Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let desiredBorderBottomWidth = parseInt(window.getComputedStyle(titlebar).borderBottomWidth)
|
let desiredBorderBottomWidth = parseInt(window.getComputedStyle(titlebar).borderBottomWidth)
|
||||||
TweenMax.to(clone, Card.animation.articleTransition, {
|
TweenLite.to(clone, Card.animation.articleTransition, {
|
||||||
x: -padding,
|
x: -padding,
|
||||||
y: -padding,
|
y: -padding,
|
||||||
ease: ExpoScaleEase.config(scaleX, 1),
|
ease: ExpoScaleEase.config(scaleX, 1),
|
||||||
@ -1282,10 +1282,10 @@ export class Card {
|
|||||||
onUpdateParams: ['{self}'],
|
onUpdateParams: ['{self}'],
|
||||||
onUpdate: (self) => {
|
onUpdate: (self) => {
|
||||||
let transform = self.target._gsTransform
|
let transform = self.target._gsTransform
|
||||||
TweenMax.set(title, {
|
TweenLite.set(title, {
|
||||||
scale: 1 / transform.scaleX
|
scale: 1 / transform.scaleX
|
||||||
})
|
})
|
||||||
TweenMax.set(titlebar, {
|
TweenLite.set(titlebar, {
|
||||||
height: start.height * 1 / transform.scaleY
|
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,
|
delay: this.animation.articleTransition / 2,
|
||||||
autoAlpha: 1
|
autoAlpha: 1
|
||||||
})
|
})
|
||||||
@ -1327,11 +1327,11 @@ export class Card {
|
|||||||
let titlebarStyle = window.getComputedStyle(previewTitlebar)
|
let titlebarStyle = window.getComputedStyle(previewTitlebar)
|
||||||
let titlebar = clone.querySelector(".titlebar")
|
let titlebar = clone.querySelector(".titlebar")
|
||||||
|
|
||||||
TweenMax.to(titlebar, this.animation.articleTransition, {
|
TweenLite.to(titlebar, this.animation.articleTransition, {
|
||||||
height: parseInt(titlebarStyle.height)
|
height: parseInt(titlebarStyle.height)
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.to(articleClone, this.animation.articleTransition / 2, {
|
TweenLite.to(articleClone, this.animation.articleTransition / 2, {
|
||||||
autoAlpha: 0
|
autoAlpha: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1341,13 +1341,13 @@ export class Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.dynamicHeight) {
|
if (this.dynamicHeight) {
|
||||||
TweenMax.to(subcardContent, this.animation.articleTransition, {
|
TweenLite.to(subcardContent, this.animation.articleTransition, {
|
||||||
height: "100%"
|
height: "100%"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
TweenMax.set(card, { autoAlpha: 1, css: { maxWidth } })
|
TweenLite.set(card, { autoAlpha: 1, css: { maxWidth } })
|
||||||
TweenMax.to(clone, this.animation.articleTransition, {
|
TweenLite.to(clone, this.animation.articleTransition, {
|
||||||
x: localOrigin.x - padding,
|
x: localOrigin.x - padding,
|
||||||
y: localOrigin.y - padding,
|
y: localOrigin.y - padding,
|
||||||
scaleX,
|
scaleX,
|
||||||
@ -1356,7 +1356,7 @@ export class Card {
|
|||||||
rotation: angle,
|
rotation: angle,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
// article.remove()
|
// article.remove()
|
||||||
TweenMax.to(clone, this.animation.fade,
|
TweenLite.to(clone, this.animation.fade,
|
||||||
{
|
{
|
||||||
//delay: 0.2,
|
//delay: 0.2,
|
||||||
autoAlpha: 0,
|
autoAlpha: 0,
|
||||||
@ -1373,11 +1373,11 @@ export class Card {
|
|||||||
onUpdate: function (self) {
|
onUpdate: function (self) {
|
||||||
let transform = self.target._gsTransform
|
let transform = self.target._gsTransform
|
||||||
|
|
||||||
TweenMax.set(title, {
|
TweenLite.set(title, {
|
||||||
scale: 1 / transform.scaleX
|
scale: 1 / transform.scaleX
|
||||||
})
|
})
|
||||||
|
|
||||||
TweenMax.set(titlebar, {
|
TweenLite.set(titlebar, {
|
||||||
height: original.height * 1 / transform.scaleY
|
height: original.height * 1 / transform.scaleY
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1508,7 +1508,7 @@ export class Card {
|
|||||||
// TODO: What is this good for?
|
// TODO: What is this good for?
|
||||||
// let article = parsedHTML.querySelector('article')
|
// let article = parsedHTML.querySelector('article')
|
||||||
// card.insertAdjacentElement('afterbegin', article)
|
// card.insertAdjacentElement('afterbegin', article)
|
||||||
// TweenMax.set(article, { autoAlpha: 0 })
|
// TweenLite.set(article, { autoAlpha: 0 })
|
||||||
|
|
||||||
Card.expandIndexCard(card, parsedHTML, 'article', relativeSource, saveCallback)
|
Card.expandIndexCard(card, parsedHTML, 'article', relativeSource, saveCallback)
|
||||||
}
|
}
|
||||||
@ -1883,680 +1883,3 @@ Card.animation = {
|
|||||||
zoomable: 0.5
|
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…
Reference in New Issue
Block a user