Fixed closing issue with cards.
This commit is contained in:
+161
-148
@@ -2,6 +2,7 @@ export var CardPlugin = CardPlugin || {}
|
||||
|
||||
export class CardPluginBase {
|
||||
apply(context) {
|
||||
this.context = context
|
||||
if (this.verify(context)) {
|
||||
this.append(context)
|
||||
console.log('Plugin ' + this.name + ' was verified successfully.')
|
||||
@@ -91,6 +92,14 @@ export class CardPluginBase {
|
||||
}
|
||||
return requirements
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the card is removed.
|
||||
* Can be used to cleanup the plugin.
|
||||
*
|
||||
* @memberof CardPluginBase
|
||||
*/
|
||||
remove() {}
|
||||
}
|
||||
|
||||
CardPlugin.LightBox = class LightBox extends CardPluginBase {
|
||||
@@ -392,130 +401,135 @@ CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
|
||||
this.parentSelector = parentSelector
|
||||
this.interactionType = interactionType
|
||||
|
||||
|
||||
// We directly overwriting the function with a version that has a binded
|
||||
// reference to itself. Doing so provides an easy and reliable way to remove
|
||||
// the event listener using this function. - SO
|
||||
this._domWasChanged = this._domWasChanged.bind(this)
|
||||
|
||||
/*
|
||||
/*
|
||||
Speech doesn't stop when page is navigated.
|
||||
Therefore we do it manually here.
|
||||
*/
|
||||
window.addEventListener('beforeunload', () => {
|
||||
window.speechSynthesis.cancel()
|
||||
})
|
||||
window.addEventListener('beforeunload', () => {
|
||||
window.speechSynthesis.cancel()
|
||||
})
|
||||
|
||||
// Binding the function beforehand ensures, that the end function is always the same.
|
||||
this._end = this._end.bind(this)
|
||||
// Binding the function beforehand ensures, that the end function is always the same.
|
||||
this._end = this._end.bind(this)
|
||||
|
||||
this._setupUtterance()
|
||||
this.utterance.addEventListener('end', event => {
|
||||
this._end()
|
||||
})
|
||||
}
|
||||
|
||||
get require() {
|
||||
return [CardPlugin.Ui]
|
||||
}
|
||||
|
||||
subcardChanged(closed) {
|
||||
if (this.cardActive) {
|
||||
this._updateText(closed)
|
||||
this._setupUtterance()
|
||||
this.utterance.addEventListener('end', event => {
|
||||
this._end()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get cardActive() {
|
||||
return this.activeUtterance == this.utterance
|
||||
}
|
||||
get require() {
|
||||
return [CardPlugin.Ui]
|
||||
}
|
||||
|
||||
_updateText(ignoreSubcard = false) {
|
||||
let node = this.context
|
||||
let subcard = this.context.querySelector('.mainview > .subcard')
|
||||
subcardChanged(closed) {
|
||||
if (this.cardActive) {
|
||||
this._updateText(closed)
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreSubcard) {
|
||||
get cardActive() {
|
||||
return this.activeUtterance == this.utterance
|
||||
}
|
||||
|
||||
_updateText(ignoreSubcard = false) {
|
||||
let node = this.context
|
||||
let subcard = node.querySelector('.mainview > .subcard')
|
||||
|
||||
if (ignoreSubcard) {
|
||||
if (subcard != null) {
|
||||
let clone = node.cloneNode(true)
|
||||
let clonedSubcard = clone.querySelector('.mainview > .subcard')
|
||||
clonedSubcard.parentNode.removeChild(clonedSubcard)
|
||||
node = clone
|
||||
}
|
||||
} else {
|
||||
if (subcard) {
|
||||
let clone = subcard.cloneNode(true)
|
||||
clone.querySelectorAll('figure').forEach(figure => {
|
||||
figure.parentNode.removeChild(figure)
|
||||
})
|
||||
|
||||
node = clone
|
||||
}
|
||||
}
|
||||
|
||||
let id = this.context.getAttribute('data-id')
|
||||
let src = this.context.getAttribute('data-source')
|
||||
let subcardSource = null
|
||||
if (subcard != null) {
|
||||
let clone = node.cloneNode(true)
|
||||
let clonedSubcard = clone.querySelector('.mainview > .subcard')
|
||||
clonedSubcard.parentNode.removeChild(clonedSubcard)
|
||||
node = clone
|
||||
subcardSource = subcard.getAttribute('data-source')
|
||||
}
|
||||
} else {
|
||||
if (subcard) {
|
||||
let clone = subcard.cloneNode(true)
|
||||
clone.querySelectorAll('figure').forEach(figure => {
|
||||
figure.parentNode.removeChild(figure)
|
||||
})
|
||||
|
||||
node = clone
|
||||
if (!window.speechSynthesis.speaking) {
|
||||
this._start(node)
|
||||
Logging.log(`Started speech on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
} else if (this.cardActive && this._sameText(node)) {
|
||||
Logging.log(`Stopped speech on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
this._stop()
|
||||
} else {
|
||||
Logging.log(`Updated Text on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
this._stop()
|
||||
.then(() => {
|
||||
this._start(node)
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
let id = this.context.getAttribute('data-id')
|
||||
let src = this.context.getAttribute('data-source')
|
||||
let subcardSource = null
|
||||
if (subcard != null) {
|
||||
subcardSource = subcard.getAttribute('data-source')
|
||||
_sameText(node) {
|
||||
return this.utterance.text == this._cleanupText(node)
|
||||
}
|
||||
|
||||
if (!window.speechSynthesis.speaking) {
|
||||
this._start(node)
|
||||
Logging.log(`Started speech on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
} else if (this.cardActive && this._sameText(node)) {
|
||||
Logging.log(`Stopped speech on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
this._stop()
|
||||
} else {
|
||||
Logging.log(`Updated Text on card: id:${id} - source: ${src} - subcard: ${subcardSource}`)
|
||||
this._stop()
|
||||
.then(() => {
|
||||
this._start(node)
|
||||
})
|
||||
.catch(console.error)
|
||||
_setupUtterance() {
|
||||
this.utterance = new SpeechSynthesisUtterance()
|
||||
this.utterance.lang = 'de-DE'
|
||||
}
|
||||
}
|
||||
|
||||
_sameText(node) {
|
||||
return this.utterance.text == this._cleanupText(node)
|
||||
}
|
||||
get require() {
|
||||
return [CardPlugin.Ui]
|
||||
}
|
||||
|
||||
_setupUtterance() {
|
||||
this.utterance = new SpeechSynthesisUtterance()
|
||||
this.utterance.lang = 'de-DE'
|
||||
}
|
||||
remove() {
|
||||
this.button = null
|
||||
this.context.removeEventListener('DOMNodeRemoved', this._domWasChanged)
|
||||
super.remove()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
remove() {
|
||||
this.button = null
|
||||
super.remove()
|
||||
}
|
||||
InteractionMapper.on(this.interactionType, this.button, () => {
|
||||
this.speak()
|
||||
})
|
||||
|
||||
append(context) {
|
||||
let container = context.querySelector(this.parentSelector)
|
||||
this.button = document.createElement('div')
|
||||
this.button.className = 'icon button ' + this.className
|
||||
container.appendChild(this.button)
|
||||
context.addEventListener('DOMNodeRemoved', this._domWasChanged)
|
||||
}
|
||||
|
||||
InteractionMapper.on(this.interactionType, this.button, () => {
|
||||
this.speak()
|
||||
})
|
||||
|
||||
this.context.addEventListener('DOMNodeRemoved', event => {
|
||||
if (
|
||||
_domWasChanged(event) {
|
||||
if (this.context == null) this._stop()
|
||||
else if (
|
||||
this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode'] &&
|
||||
event.target == this.context
|
||||
) {
|
||||
this._stop()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ScatterCard
|
||||
}
|
||||
_isSameNode(node) {
|
||||
return this.currentText == node.textContent
|
||||
}
|
||||
|
||||
_isSameNode(node) {
|
||||
return this.currentText == node.textContent
|
||||
}
|
||||
|
||||
speak() {
|
||||
/**
|
||||
speak() {
|
||||
/**
|
||||
* This is a little bit ugly, but imho the most elegant of all dirty solutions.
|
||||
*
|
||||
5ht * Within the plugins we have no knowledge of other cards and such. But must differentiate the
|
||||
@@ -529,63 +543,62 @@ speak() {
|
||||
* SO -17.07.19
|
||||
*/
|
||||
|
||||
let activeNode = window.speechSynthesis['speechPluginNode']
|
||||
this._updateText()
|
||||
}
|
||||
let activeNode = window.speechSynthesis['speechPluginNode']
|
||||
this._updateText()
|
||||
}
|
||||
|
||||
async _stop() {
|
||||
return new Promise(resolve => {
|
||||
if (this.activeUtterance) {
|
||||
this.activeUtterance.addEventListener('end', resolve, {
|
||||
once: true
|
||||
})
|
||||
}
|
||||
async _stop() {
|
||||
return new Promise(resolve => {
|
||||
if (this.activeUtterance) {
|
||||
this.activeUtterance.addEventListener('end', resolve, {
|
||||
once: true
|
||||
})
|
||||
}
|
||||
|
||||
window.speechSynthesis.cancel()
|
||||
})
|
||||
}
|
||||
|
||||
get activeUtterance() {
|
||||
return window.speechSynthesis['speechPluginUtterance']
|
||||
}
|
||||
|
||||
_end() {
|
||||
window.speechSynthesis['speechPluginNode'] = null
|
||||
window.speechSynthesis['speechPluginUtterance'] = null
|
||||
this._deactivateButton()
|
||||
this.context.classList.remove('speech-plugin-is-reading')
|
||||
}
|
||||
|
||||
_start(node) {
|
||||
window.speechSynthesis.cancel()
|
||||
})
|
||||
|
||||
window.speechSynthesis['speechPluginUtterance'] = this.utterance
|
||||
window.speechSynthesis['speechPluginNode'] = node
|
||||
this.context['lastSpeechNode'] = node
|
||||
|
||||
let cleanText = this._cleanupText(node)
|
||||
this.utterance.text = cleanText
|
||||
window.speechSynthesis.speak(this.utterance)
|
||||
this._activateButton()
|
||||
|
||||
this.context.classList.add('speech-plugin-is-reading')
|
||||
}
|
||||
|
||||
_cleanupText(node) {
|
||||
let text = node.textContent
|
||||
text = this._removeShy(text)
|
||||
return text
|
||||
}
|
||||
|
||||
_removeShy(text) {
|
||||
return text.replace(/\u00AD/g, '')
|
||||
}
|
||||
|
||||
_activateButton() {
|
||||
if (this.button) this.button.classList.add('active')
|
||||
}
|
||||
_deactivateButton() {
|
||||
if (this.button) this.button.classList.remove('active')
|
||||
}
|
||||
}
|
||||
|
||||
get activeUtterance() {
|
||||
return window.speechSynthesis['speechPluginUtterance']
|
||||
}
|
||||
|
||||
_end() {
|
||||
window.speechSynthesis['speechPluginNode'] = null
|
||||
window.speechSynthesis['speechPluginUtterance'] = null
|
||||
this._deactivateButton()
|
||||
this.context.classList.remove('speech-plugin-is-reading')
|
||||
}
|
||||
|
||||
_start(node) {
|
||||
window.speechSynthesis.cancel()
|
||||
|
||||
window.speechSynthesis['speechPluginUtterance'] = this.utterance
|
||||
window.speechSynthesis['speechPluginNode'] = node
|
||||
this.context['lastSpeechNode'] = node
|
||||
|
||||
let cleanText = this._cleanupText(node)
|
||||
this.utterance.text = cleanText
|
||||
window.speechSynthesis.speak(this.utterance)
|
||||
this._activateButton()
|
||||
|
||||
this.context.classList.add('speech-plugin-is-reading')
|
||||
}
|
||||
|
||||
_cleanupText(node) {
|
||||
let text = node.textContent
|
||||
text = this._removeShy(text)
|
||||
return text
|
||||
}
|
||||
|
||||
_removeShy(text) {
|
||||
return text.replace(/\u00AD/g, '')
|
||||
}
|
||||
|
||||
_activateButton() {
|
||||
if (this.button) this.button.classList.add('active')
|
||||
}
|
||||
_deactivateButton() {
|
||||
if (this.button) this.button.classList.remove('active')
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user