Added files to make classes better accessible.
This commit is contained in:
parent
78108c4090
commit
17bbe1a90e
3870
dist/iwmlib.js
vendored
3870
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
|
||||
|
743
lib/card/card.js
743
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)
|
||||
@ -113,7 +113,7 @@ export class Card {
|
||||
|
||||
let interactionType = this.interactionType
|
||||
|
||||
//Remove the events on the circle.
|
||||
//Remove the events on the circle.
|
||||
// These are 'hardcoded' inside the convert.js.
|
||||
if (element.tagName == "circle") return false
|
||||
|
||||
@ -155,11 +155,11 @@ export class Card {
|
||||
|
||||
/**
|
||||
* Transform the relative links to absolute ones.
|
||||
*
|
||||
*
|
||||
* Currently covers:
|
||||
* Tags: a,img, image, circle
|
||||
* Attributes: xlink:href,href,src (first occurence only)
|
||||
*
|
||||
*
|
||||
* @static
|
||||
* @param {DomElement} element - The children of the element are inspected, if there are relative paths, that has to be adjusted to absolute ones.
|
||||
* @returns
|
||||
@ -167,7 +167,7 @@ export class Card {
|
||||
*/
|
||||
static _adjustRelativeLinks(html) {
|
||||
const that = this
|
||||
/*
|
||||
/*
|
||||
This RegEx finds all requested tags[1], and all requested attributes[3] and replaces the relative path [4] with the absolute one.
|
||||
while all other attributes [2],[5] are preserved.
|
||||
*/
|
||||
@ -383,7 +383,7 @@ export class Card {
|
||||
* @param {Point} position - The position, where the popup will be shown.
|
||||
* @param {object} content - The content of the popup as required by the Popup class.
|
||||
* @param {object} [options={}] - Additional options for the popup (optional).
|
||||
* @returns {Promise} - Returns a Promise, which is resolved, when the Popup is fully loaded.
|
||||
* @returns {Promise} - Returns a Promise, which is resolved, when the Popup is fully loaded.
|
||||
* @memberof Card
|
||||
*/
|
||||
static _createPopup(context, position, content, options = {}) {
|
||||
@ -399,7 +399,7 @@ export class Card {
|
||||
posOffset: 10
|
||||
}, options)))
|
||||
|
||||
// Placing the popup when it required loading,
|
||||
// Placing the popup when it required loading,
|
||||
// it resulted in flahing up at the default position.
|
||||
// We manually prevent this here.
|
||||
popup.element.style.display = "none"
|
||||
@ -447,7 +447,7 @@ export class Card {
|
||||
/**
|
||||
* The cleanup functionality is now covered by the _cleanup function.
|
||||
* It cleans up zoomables, popups and open image highlights.
|
||||
*
|
||||
*
|
||||
* TEST if this intereferes with the editor.
|
||||
*/
|
||||
if (overlay) {
|
||||
@ -522,14 +522,14 @@ export class Card {
|
||||
})
|
||||
let html = popupPage.body.innerHTML
|
||||
/**
|
||||
* We do not want to use the popup class again.
|
||||
* We do not want to use the popup class again.
|
||||
* This results in problems when styling the popup.
|
||||
* (You could style the .unselectable.popup, but that
|
||||
* wouldn't be clean).
|
||||
*
|
||||
* (You could style the .unselectable.popup, but that
|
||||
* wouldn't be clean).
|
||||
*
|
||||
* Therefore the selector was changed:
|
||||
* '.popup' => '.popupHtml'
|
||||
*
|
||||
*
|
||||
* Which describes the construct even better than the
|
||||
* '.popup' class.
|
||||
*/
|
||||
@ -678,8 +678,8 @@ export class Card {
|
||||
let context = this.getContext(node)
|
||||
event.stopPropagation()
|
||||
|
||||
/**
|
||||
* This node is the documents body, as events wont work
|
||||
/**
|
||||
* This node is the documents body, as events wont work
|
||||
* on svg elements properly. We need a workaround for that.
|
||||
*/
|
||||
let src = node.getAttribute("xlink:href")
|
||||
@ -746,7 +746,7 @@ export class Card {
|
||||
*
|
||||
* @static
|
||||
* @private
|
||||
* @param {string} source - Url to a popup file.
|
||||
* @param {string} source - Url to a popup file.
|
||||
* @returns {Promise} - Returns a promise, that's resolved when the data is loaded.
|
||||
* @memberof Card
|
||||
*/
|
||||
@ -799,9 +799,9 @@ export class Card {
|
||||
static zoomableCurrentGeometry(zoomable, wrapper) {
|
||||
|
||||
|
||||
/*
|
||||
I don't think it's wise, that the zoomable calculation relies on
|
||||
some icon that may or may not be present. When the same calculation can be
|
||||
/*
|
||||
I don't think it's wise, that the zoomable calculation relies on
|
||||
some icon that may or may not be present. When the same calculation can be
|
||||
done using the bounding box of the desired element.
|
||||
- SO
|
||||
*/
|
||||
@ -990,7 +990,7 @@ export class Card {
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects and transforms the zoomicon from a zoomicon to a closeicon
|
||||
* Selects and transforms the zoomicon from a zoomicon to a closeicon
|
||||
* or the other way around.
|
||||
*
|
||||
* @static
|
||||
@ -1174,8 +1174,8 @@ export class Card {
|
||||
|
||||
|
||||
/**
|
||||
* We have to reorder the clone, as it still contains the
|
||||
* preview text image. And the new html is
|
||||
* We have to reorder the clone, as it still contains the
|
||||
* preview text image. And the new html is
|
||||
* inserted before everything else.
|
||||
*/
|
||||
let cloneWrapper = clone.querySelector(".wrapper")
|
||||
@ -1418,8 +1418,8 @@ export class Card {
|
||||
}
|
||||
})
|
||||
}
|
||||
// Use the 'tap' event for closing.
|
||||
// Otherwise the subcard cannot be closed,
|
||||
// Use the 'tap' event for closing.
|
||||
// Otherwise the subcard cannot be closed,
|
||||
// when another subcard is touched.
|
||||
InteractionMapper.on("tap", iconClone, () => {
|
||||
if (editable) {
|
||||
@ -1505,7 +1505,7 @@ export class Card {
|
||||
let html = xhr.responseText
|
||||
let parsedHTML = this.postProcessResponseText(html)
|
||||
|
||||
// TODO: What is this good for?
|
||||
// TODO: What is this good for?
|
||||
// let article = parsedHTML.querySelector('article')
|
||||
// card.insertAdjacentElement('afterbegin', article)
|
||||
// TweenMax.set(article, { autoAlpha: 0 })
|
||||
@ -1542,7 +1542,7 @@ export class Card {
|
||||
*
|
||||
* @static
|
||||
* @param {DomElement} child - A dom element for which the context shall be retrieved.
|
||||
* @returns {DomElement} - The containing context / info-card.
|
||||
* @returns {DomElement} - The containing context / info-card.
|
||||
* @memberof Card
|
||||
*/
|
||||
static getContext(child) {
|
||||
@ -1585,7 +1585,7 @@ export class Card {
|
||||
|
||||
/**
|
||||
* Retrieves an Rectangle for an element in the local space of a provided context.
|
||||
*
|
||||
*
|
||||
* Note: This works also for rotated DomElements unlike the Element.getBoundingClientRectangle method.
|
||||
*
|
||||
* @static
|
||||
@ -1610,7 +1610,7 @@ export class Card {
|
||||
* Gets a rectangle in global space for a provided element.
|
||||
*
|
||||
* Note: This works also for rotated DomElements unlike the Element.getBoundingClientRectangle method.
|
||||
*
|
||||
*
|
||||
* @static
|
||||
* @param {DomElement} element
|
||||
* @returns {DomRect} - Returns a rectangle that specifies the location in global space.
|
||||
@ -1630,7 +1630,7 @@ export class Card {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts all links
|
||||
* Adjusts all links
|
||||
*
|
||||
* @static
|
||||
* @param {*} htmlString
|
||||
@ -1693,7 +1693,7 @@ export class Card {
|
||||
if (event.target) {
|
||||
//let column = event.target.closest(".column")
|
||||
let indexbox = this.closestWithClass(card, 'mainview')
|
||||
if (indexbox != null) { // column != null ||
|
||||
if (indexbox != null) { // column != null ||
|
||||
let links = Array.from(indexbox.getElementsByTagName("a"))
|
||||
let globalClick = (event.center) ? event.center : { x: event.x, y: event.y }
|
||||
let localClick = Points.fromPageToNode(indexbox, globalClick)
|
||||
@ -1842,9 +1842,9 @@ export class Card {
|
||||
/**
|
||||
* This getter and setter pair solves the problem,
|
||||
* when we assign 'relativePath' to a child class, the assign is routed
|
||||
* to this base class. And we ensure, that we always set the same
|
||||
* private variable.
|
||||
*
|
||||
* to this base class. And we ensure, that we always set the same
|
||||
* private variable.
|
||||
*
|
||||
* Not doing this lead to some cases, when Card-/ScatterCard.func
|
||||
* was called and depending on context their were different values
|
||||
* inside the relativePath variable.
|
||||
@ -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…
Reference in New Issue
Block a user