Updated cards from tuesch changes. Incorporated InteractionMapper memory leak fix.

This commit is contained in:
2019-08-13 14:31:03 +02:00
parent 03be6673c5
commit 30c7113713
193 changed files with 21151 additions and 474 deletions
+451 -239
View File
@@ -3364,7 +3364,9 @@
}
close() {
console.log('SCATTER WAS CLOSED!');
this._callCloseCallbacks();
this._removeCallbacks();
this._removeSelfFromScatterContainer();
}
@@ -3374,6 +3376,11 @@
}
}
_removeCallbacks() {
this.onClose = [];
this.onTransform = [];
}
_removeSelfFromScatterContainer() {
// Removes self from container when it's closed.
if (this.container) {
@@ -7638,8 +7645,6 @@
}
}
/** To avoid problems with relative URL paths, we use inline data URI to load svg icons. */
/**
* A class that collects static methods to maintain the states and parts of
* EyeVisit like cards.
@@ -7650,11 +7655,35 @@
static setup(context, modules = []) {
console.log('Setup Card...', modules);
context.modules = [];
context.module = {};
context.classList.add('info-card');
context.setAttribute('data-id', Card.id++);
modules.forEach(module => {
if (module.apply(context)) context.modules.push(module.constructor.name);
if (module.apply(context)) {
const moduleName = module.constructor.name;
context.modules.push(moduleName);
context.module[moduleName] = module;
}
});
}
static remove(context) {
for (let module of Object.values(context.module)) {
module.remove();
}
}
static eventClose(event) {
let context = this.getContext(event.target);
if (context) {
this.constructor.close(context);
} else console.error('Could not find context!', event.target);
}
/**
*
*
@@ -7662,13 +7691,12 @@
* @param {*} event
* @memberof Card
*/
static close(event) {
let context = this.getContext(event.target);
if (context) {
if (context.onClose) {
context.onClose(event);
} else context.parentNode.removeChild(context);
} else console.error('Could not find context!', event.target);
static close(context) {
console.log('CLOSE CARD!!!');
this.unregisterAllEvents(context);
if (context.onClose) {
context.onClose(event);
} else context.parentNode.removeChild(context);
}
/**
@@ -7680,12 +7708,12 @@
* @param {*} replaceFunc
* @memberof Card
*/
static _replaceAttributes(html, attribute, replaceFunc) {
static _replaceAttributes(context, html, attribute, replaceFunc) {
let clickables = html.querySelectorAll(`[${attribute}]`);
clickables.forEach(element => {
let attributeVal = element.getAttribute(attribute);
element.removeAttribute(attribute);
replaceFunc.call(this, element, attributeVal);
replaceFunc.call(this, context, element, attributeVal);
});
}
@@ -7699,7 +7727,7 @@
* @returns
* @memberof Card
*/
static _replaceCallback(element, attributeVal) {
static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') {
element.addEventListener('click', event => {
event.preventDefault();
@@ -7731,7 +7759,7 @@
// These are 'hardcoded' inside the convert.js.
if (element.tagName == 'circle') return false
InteractionMapper.on(interactionType, element, event => {
this.registerEvent(context, interactionType, element, event => {
/**
* Replaces the strings from the listener with the cooresponding variables.
*/
@@ -7783,6 +7811,13 @@
/<\s*(a|video|img|image|circle)\s(.*?)(xlink:href|href|src)\s*=\s*["'](\..*?)["']\s*(.*?)>/g,
function(data) {
let path = that._getRelativePath(arguments[4]);
console.log('REPLACE ', arguments[1]);
if (arguments[1] == 'a') {
console.error('NOT REPLACING LINKS');
return ''
}
const tag = `<${arguments[1]} ${arguments[2]} ${arguments[3]}="${path}" ${arguments[5]}>`;
/* if (that.debug) */ console.log('Adjusted: ', tag);
return tag
@@ -7979,6 +8014,7 @@
if (this.debug) console.log('Close Popup.', context, popup);
window.popup = popup;
popup.close();
InteractionMapper.off(popup.element);
this._unsetPopup(context);
} else {
console.error('Requested to close popup, but popup was not found.');
@@ -8348,7 +8384,7 @@
// but for simplicity it's just done here for now.
// TODO: Adjust to load while animating (Problem: Unload when cancelled).
console.log('loadHighlightPopup', src, position, local);
this._loadPopupContent(src)
this._loadPopupContent(context, src)
.then(content => {
this._openPopup(context, src, local, content, {
highlight: node,
@@ -8375,14 +8411,14 @@
* @returns {Promise} - Returns a promise, that's resolved when the data is loaded.
* @memberof Card
*/
static _loadPopupContent(source) {
static _loadPopupContent(context, source) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('get', source, true);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
let html = this.postProcessResponseText(xhr.responseText);
let html = this.postProcessResponseText(context, xhr.responseText);
let selector = Card.popupHtmlSelector;
let content = { html: html.body.innerHTML, selector };
resolve(content);
@@ -8477,6 +8513,9 @@
let wrapper = this.getContext(node);
let zoomable = node.closest('figure');
if (zoomable == null) {
return
}
// load mainimg - if none exists, there is nothing to open
let img = zoomable.querySelector('.mainimg');
@@ -8735,18 +8774,18 @@
}
/**
* Closes a zoomable object with animation
* Closes an zoomable object with an animation.
*
* @static
* @param {any} wrapper - the wrapper containing the index card
* @param {any} div - the figure containing the relevant elements
* @param {any} zoomable - the zoomable element, from which the zoomed figure originates
* @param {any} rect - the target rect for the tween (typically the top left width height of the zoomable)
* @param {DOMElement} context - Context of the zoomable.
* @param {*} zoomable
* @param {*} zoomedFig
* @memberof Card
*/
static closeZoomable(context, zoomable, zoomedFig) {
if (this.debug) console.log('Close Zoomable', context, zoomable, zoomedFig);
//TODO: Why do I need this check. Shouldn't it be always present?! - SO
if (zoomable) {
this._unsetZoomable(context);
let caption = zoomable.querySelector('figcaption.cap');
@@ -8767,12 +8806,15 @@
TweenLite.set(zoomable, {
opacity: 1
});
let div = zoomedFig.parentNode;
let videoElement = div.querySelector('video');
if (videoElement) videoElement.pause();
div.parentNode.removeChild(div);
}
});
InteractionMapper.off(zoomedFig);
}
}
@@ -8862,6 +8904,7 @@
rotation: angle
});
indexbox.prepend(clone);
clone.setAttribute('data-source', src);
let titlebar = clone.querySelector('.titlebar');
let title = titlebar.querySelector('h2');
@@ -8886,7 +8929,7 @@
}
//jquery hyphenate below
if (typeof $ != 'undefined') {
if (this.constructor._jQueryIsPresent()) {
$('.column')
.not('.overview')
.children('p')
@@ -8941,86 +8984,6 @@
Card._disableCardCloseButton(context);
const closeAnimation = () => {
//logging
if (src) {
let strparts = src.split('/');
let cardID = strparts[strparts.length - 2];
let cardName = strparts[strparts.length - 1];
strparts = card.className.split(' ');
let cardType = strparts[1];
let msg = 'Card: ' + cardID + ': closeTopic: ' + cardType + ', ' + cardName;
console.log('Logging:', msg);
Logging.log(msg);
}
Card._cleanup(context);
Card._unsetSubcard(context);
this._enableCardCloseButton(context);
let previewTitlebar = card.querySelector('.titlebar');
let titlebarStyle = window.getComputedStyle(previewTitlebar);
let titlebar = clone.querySelector('.titlebar');
TweenLite.to(titlebar, this.animation.articleTransition, {
height: parseInt(titlebarStyle.height)
});
TweenLite.to(articleClone, this.animation.articleTransition / 2, {
autoAlpha: 0
});
let title = titlebar.querySelector('h2');
let original = {
height: parseInt(titlebarStyle.height)
};
if (this.dynamicHeight) {
TweenLite.to(subcardContent, this.animation.articleTransition, {
height: '100%'
});
}
TweenLite.set(card, { autoAlpha: 1, css: { maxWidth } });
TweenLite.to(clone, this.animation.articleTransition, {
x: localOrigin.x - padding,
y: localOrigin.y - padding,
scaleX,
scaleY,
ease: ExpoScaleEase.config(1, scaleX),
rotation: angle,
onComplete: () => {
// article.remove()
TweenLite.to(clone, this.animation.fade, {
//delay: 0.2,
autoAlpha: 0,
onComplete: () => {
if (editable) {
mainController.popController();
}
clone.remove();
}
});
},
onUpdateParams: ['{self}'],
onUpdate: function(self) {
let transform = self.target._gsTransform;
TweenLite.set(title, {
scale: 1 / transform.scaleX
});
TweenLite.set(titlebar, {
height: (original.height * 1) / transform.scaleY
});
// Retain the border at same visual thickness.
titlebar.style.borderBottomWidth = desiredBorderBottomWidth / transform.scaleY + 'px';
}
});
};
//TODO consider renaming it to something more intuitive.
let iconClone = clone.querySelector('.card-icon');
@@ -9042,6 +9005,9 @@
if (this.dynamicHeight) {
article.appendChild(iconClone);
}
const eventElements = [indexbox, iconClone, clone];
// Use the 'tap' event for closing.
// Otherwise the subcard cannot be closed,
// when another subcard is touched.
@@ -9051,17 +9017,113 @@
if (isDirty) {
mainController.saveNode(html.innerHTML, url => {
callback(url);
closeAnimation();
this._closeIndexCard(context, card,{
eventElements,
src
});
});
} else {
closeAnimation();
this._closeIndexCard(context, card);
}
} else {
closeAnimation();
this._closeIndexCard(context, card);
}
});
}
static _closeIndexCard(context, card, {
eventElements = [],
src = null
} = []) {
//logging
if (src) {
let strparts = src.split('/');
let cardID = strparts[strparts.length - 2];
let cardName = strparts[strparts.length - 1];
strparts = card.className.split(' ');
let cardType = strparts[1];
let msg = 'Card: ' + cardID + ': closeTopic: ' + cardType + ', ' + cardName;
console.log('Logging:', msg);
Logging.log(msg);
}
Card._cleanup(context);
Card._unsetSubcard(context);
this._subcardChanged(context, true);
this._enableCardCloseButton(context);
let previewTitlebar = card.querySelector('.titlebar');
let titlebarStyle = window.getComputedStyle(previewTitlebar);
let titlebar = clone.querySelector('.titlebar');
TweenLite.to(titlebar, this.animation.articleTransition, {
height: parseInt(titlebarStyle.height)
});
TweenLite.to(articleClone, this.animation.articleTransition / 2, {
autoAlpha: 0
});
let title = titlebar.querySelector('h2');
let original = {
height: parseInt(titlebarStyle.height)
};
if (this.dynamicHeight) {
TweenLite.to(subcardContent, this.animation.articleTransition, {
height: '100%'
});
}
TweenLite.set(card, { autoAlpha: 1, css: { maxWidth } });
TweenLite.to(clone, this.animation.articleTransition, {
x: localOrigin.x - padding,
y: localOrigin.y - padding,
scaleX,
scaleY,
ease: ExpoScaleEase.config(1, scaleX),
rotation: angle,
onComplete: () => {
// article.remove()
TweenLite.to(clone, this.animation.fade, {
//delay: 0.2,
autoAlpha: 0,
onComplete: () => {
if (editable) {
mainController.popController();
}
clone.remove();
}
});
},
onUpdateParams: ['{self}'],
onUpdate: function(self) {
let transform = self.target._gsTransform;
TweenLite.set(title, {
scale: 1 / transform.scaleX
});
TweenLite.set(titlebar, {
height: (original.height * 1) / transform.scaleY
});
// Retain the border at same visual thickness.
titlebar.style.borderBottomWidth = desiredBorderBottomWidth / transform.scaleY + 'px';
}
});
}
/**
* Tests if jQuery is properly included in the project.
* Otherwise specific features may not work correctly (e.g. hyphenation)
*/
_jQueryIsPresent() {
let jQueryInitialized = typeof $ != 'undefined';
if (!jQueryInitialized) console.error('No jQuery is provided. Specific features may fail.');
return jQueryInitialized
}
/**
* Opens the index card. Called by the zoom icon click handler.
* The assumed card structure is as follows:
@@ -9124,14 +9186,10 @@
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
let html = xhr.responseText;
let parsedHTML = this.postProcessResponseText(html);
// TODO: What is this good for?
// let article = parsedHTML.querySelector('article')
// card.insertAdjacentElement('afterbegin', article)
// TweenLite.set(article, { autoAlpha: 0 })
let parsedHTML = this.postProcessResponseText(context, html);
Card.expandIndexCard(card, parsedHTML, 'article', relativeSource, saveCallback);
this._subcardChanged(context);
}
};
xhr.onerror = () => {
@@ -9257,7 +9315,8 @@
* @returns
* @memberof Card
*/
static postProcessResponseText(htmlString) {
static postProcessResponseText(context, htmlString) {
console.error('RUN POSTPROCESS');
let editable = this.isEditable();
htmlString = this._adjustRelativeLinks(htmlString);
@@ -9265,7 +9324,7 @@
let parser = new DOMParser();
let html = parser.parseFromString(htmlString, 'text/html');
if (!editable) {
this._replaceAttributes(html, 'onclick', this._replaceCallback);
this._replaceAttributes(context, html, 'onclick', this._replaceCallback);
}
let zoomableWrappers = html.querySelectorAll('.svg-wrapper');
zoomableWrappers.forEach(wrapper => {
@@ -9386,7 +9445,7 @@
this._setPopupSource(popup, source);
context.popup = popup;
if (typeof $ != 'undefined') {
if (this.constructor._jQueryIsPresent()) {
//jquery hyphenate below
console.log('hyphenated popup:', $('span').hyphenate('de'));
}
@@ -9485,8 +9544,44 @@
static get relativePath() {
return Card._relativePath
}
static getRegisteredEvents(context) {
return context._registeredEvents == null ? [] : context._registeredEvents
}
/**
* Helper method that registers InteractionMapper events on the info card.
* Those events are saved on the context element. An they are unregistered when the
* card is closed again.
*
* @static
* @param {DOMElement} context - Context of the Card.
* @param {string} types
* @param {DOMElement} element -Element on which the event is registered.
* @param {Function} callback - Function thats called when the event occurs.
* @memberof Card
*/
static registerEvent(context, types, element, callback) {
InteractionMapper.on(types, element, callback);
if (context._registeredEvents == null) context._registeredEvents = [];
if (context._registeredEvents.indexOf(element) == -1) context._registeredEvents.push(element);
}
/**
* Unregisters all events on the infocard.
*
*
* @static
* @param {DomElement} context - Context of the card.
* @memberof Card
*/
static unregisterAllEvents(context) {
let eventElements = this.getRegisteredEvents(context);
InteractionMapper.off(eventElements);
}
}
Card.id = 0;
Card.debug = true;
Card._relativePath = '';
Card.scatterContainer = null;
@@ -10091,7 +10186,7 @@
*
* @class ScatterCard
*/
class ScatterCard extends Card {
class ScatterCard$1 extends Card {
/**
* TODO: Find a more suitable name.
* Adjusts the HTML to work in the new context.
@@ -10101,7 +10196,7 @@
* @param {*} htmlString
* @param {*} basePath
* @param {*} [opts={}]
* @memberof Card
* @memberof ScatterCard
*/
static setup(context, htmlString, { basePath = './', modules = [] } = {}) {
context.classList.add('info-card');
@@ -10115,7 +10210,7 @@
/**
* Conflicts with the FindTarget method of the Abstract scatter.
*/
this._replaceAttributes(html, 'onclick', this._replaceCallback);
this._replaceAttributes(context, html, 'onclick', this._replaceCallback);
let content = html.querySelector('.mainview');
context.appendChild(content);
@@ -10137,7 +10232,16 @@
element.onClose = callback;
}
}
/**
* Removes the close listener from a card element.
*
* @static
* @param {HTMLElement} element - Context of the Card.
* @memberof ScatterCard
*/
static removeOnCloseListener(element) {
element.onClose = null;
}
/**
* Creates a scatter for the card and applies the card to it,
*
@@ -10147,7 +10251,7 @@
* @param {string} [basePath=""]
* @param {*} [opts={}]
* @returns
* @memberof Card
* @memberof ScatterCard
*/
static createCardScatter(html, scatterContainer, { basePath = './', modules = [] } = {}) {
let element = document.createElement('div');
@@ -10165,6 +10269,42 @@
return element
}
/**
* Closes but NOT removes the scatter element.
*
* The remove must be called separately, it may be used in the close callback
* of the scatter.
*/
static close(context) {
Card.close(context);
if (context['scatter']) {
console.error('CLOSED CARD');
context.scatter.close();
} else {
console.error('Expected a scatter element to close!', this);
}
}
/**
* Cleans up the card.
*
* @static
* @override
* @memberof ScatterCard
*/
static remove(context) {
if (context['scatter']) {
context.scatter = null;
console.error('REMOVED CARD');
} else {
console.error('Expected a scatter element to remove!', this);
}
Card.remove(context);
}
/**
*Utility function to create a fully functional card scatter.
*
@@ -10204,13 +10344,13 @@
}
}
ScatterCard.selectedLanguage = 0;
ScatterCard.languages = ['Deutsch', 'English'];
ScatterCard.languageTags = {
ScatterCard$1.selectedLanguage = 0;
ScatterCard$1.languages = ['Deutsch', 'English'];
ScatterCard$1.languageTags = {
Deutsch: 'de',
English: 'en'
};
ScatterCard.scatterContainer = null;
ScatterCard$1.scatterContainer = null;
var CardPlugin = CardPlugin || {};
@@ -10360,7 +10500,6 @@
this.fadeAnimationDuration = fadeAnimationDuration;
this.interactionType = interactionType;
}
get require() {
return [CardPlugin.LightBox]
}
@@ -10606,128 +10745,201 @@
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() {
/*
Speech doesn't stop when page is navigated.
Therefore we do it manually here.
*/
window.addEventListener('beforeunload', () => {
window.speechSynthesis.cancel();
this.currentText = null;
this._deactivate();
});
// 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);
}
}
get cardActive() {
return this.activeUtterance == this.utterance
}
_updateText(ignoreSubcard = false) {
let node = this.context;
let subcard = this.context.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;
}
}
get active() {
return this.constructor.active
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');
}
set active(val) {
this.constructor.active = val;
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);
}
}
get currentText() {
return this.constructor.text
}
_sameText(node) {
return this.utterance.text == this._cleanupText(node)
}
_setupUtterance() {
this.utterance = new SpeechSynthesisUtterance();
this.utterance.lang = 'de-DE';
}
get require() {
return [CardPlugin.Ui]
}
remove() {
this.button = null;
super.remove();
}
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, () => {
this.speak();
});
this.context.addEventListener('DOMNodeRemoved', event => {
if (
this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode'] &&
event.target == this.context
) {
this._stop();
}
});
}
_isSameNode(node) {
return this.currentText == node.textContent
}
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
* clicks by their corresponding owner. The SpeechUtterance just takes a text and has no knowledge
* about the node that is currently read to the user.
*
* This means, that we can identify same text, but not differentiate same text on different nodes.
* To account for that, we add the node to the speechSynthesis object (#benefitsOfJavaScript) and
* have access to the node, by - let's say - expanding the functionality of the SpeechSynthesis object.
*
* SO -17.07.19
*/
let activeNode = window.speechSynthesis['speechPluginNode'];
this._updateText();
}
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');
}
set currentText(val) {
this.constructor.text = val;
}
};
/* eslint-disable no-unused-vars */
@@ -10842,7 +11054,7 @@
window.Card = Card;
window.CardPlugin = CardPlugin;
window.CardPluginBase = CardPluginBase;
window.ScatterCard = ScatterCard;
window.ScatterCard = ScatterCard$1;
window.Highlight = Highlight$1;
window.Theme = Theme;
+7
View File
@@ -6979,7 +6979,9 @@
}
close() {
console.log('SCATTER WAS CLOSED!');
this._callCloseCallbacks();
this._removeCallbacks();
this._removeSelfFromScatterContainer();
}
@@ -6989,6 +6991,11 @@
}
}
_removeCallbacks() {
this.onClose = [];
this.onTransform = [];
}
_removeSelfFromScatterContainer() {
// Removes self from container when it's closed.
if (this.container) {