Fixed closing issue with cards.
This commit is contained in:
parent
30c7113713
commit
28a7a0b6a2
43
dist/iwmlib.js
vendored
43
dist/iwmlib.js
vendored
@ -2772,7 +2772,9 @@
|
||||
for (let j = 0; j < elements.length; j++) {
|
||||
// if(elements[j].tagName == "svg") return false;
|
||||
|
||||
let hammer = new Hammer(elements[j], opts);
|
||||
const target = elements[j];
|
||||
|
||||
let hammer = new Hammer(target, opts);
|
||||
|
||||
if (window.propagating !== 'undefined') {
|
||||
hammer = propagating(hammer);
|
||||
@ -2793,16 +2795,23 @@
|
||||
hammer.get('tap').set(opts);
|
||||
}
|
||||
|
||||
console.log('APPLY HAMMER ON', type);
|
||||
|
||||
target.addEventListener("click", ()=>{
|
||||
console.log("Hello");
|
||||
});
|
||||
|
||||
hammer.on(type, event => {
|
||||
console.log('FIRED');
|
||||
cb(event);
|
||||
});
|
||||
|
||||
if (Hammer.__hammers.has(elements[j])) {
|
||||
const elementHammers = Hammer.__hammers.get(elements[j]);
|
||||
if (Hammer.__hammers.has(target)) {
|
||||
const elementHammers = Hammer.__hammers.get(target);
|
||||
elementHammers.push(hammer);
|
||||
Hammer.__hammers.set(elements[j], elementHammers);
|
||||
Hammer.__hammers.set(target, elementHammers);
|
||||
} else {
|
||||
Hammer.__hammers.set(elements[j], [hammer]);
|
||||
Hammer.__hammers.set(target, [hammer]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -7709,8 +7718,9 @@
|
||||
* @memberof Card
|
||||
*/
|
||||
static _replaceAttributes(context, html, attribute, replaceFunc) {
|
||||
let clickables = html.querySelectorAll(`[${attribute}]`);
|
||||
clickables.forEach(element => {
|
||||
let attributeCarrier = html.querySelectorAll(`[${attribute}]`);
|
||||
attributeCarrier.forEach(element => {
|
||||
console.log(element);
|
||||
let attributeVal = element.getAttribute(attribute);
|
||||
element.removeAttribute(attribute);
|
||||
replaceFunc.call(this, context, element, attributeVal);
|
||||
@ -7759,10 +7769,13 @@
|
||||
// These are 'hardcoded' inside the convert.js.
|
||||
if (element.tagName == 'circle') return false
|
||||
|
||||
console.log("Replace" , context, element, attributeVal);
|
||||
|
||||
this.registerEvent(context, interactionType, element, event => {
|
||||
/**
|
||||
* Replaces the strings from the listener with the cooresponding variables.
|
||||
*/
|
||||
console.log("EVENT");
|
||||
let args = [];
|
||||
argsStrings.forEach(arg => {
|
||||
arg = arg.trim();
|
||||
@ -7811,13 +7824,6 @@
|
||||
/<\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
|
||||
@ -9152,7 +9158,7 @@
|
||||
* @memberof Card
|
||||
*/
|
||||
static openIndexCard(event, src) {
|
||||
//console.log("openIndexCard", src)
|
||||
console.log("openIndexCard", src);
|
||||
/*
|
||||
* Called by the expandIndexCard(...)
|
||||
*/
|
||||
@ -9324,6 +9330,7 @@
|
||||
let parser = new DOMParser();
|
||||
let html = parser.parseFromString(htmlString, 'text/html');
|
||||
if (!editable) {
|
||||
console.log("REPLACE ONCLICK");
|
||||
this._replaceAttributes(context, html, 'onclick', this._replaceCallback);
|
||||
}
|
||||
let zoomableWrappers = html.querySelectorAll('.svg-wrapper');
|
||||
@ -9562,7 +9569,10 @@
|
||||
* @memberof Card
|
||||
*/
|
||||
static registerEvent(context, types, element, callback) {
|
||||
InteractionMapper.on(types, element, callback);
|
||||
console.log("REGISTER INTERACTION EVENT",context, types, element, callback);
|
||||
InteractionMapper.on(types, element, ()=>{
|
||||
console.log("HELLO");
|
||||
});
|
||||
if (context._registeredEvents == null) context._registeredEvents = [];
|
||||
if (context._registeredEvents.indexOf(element) == -1) context._registeredEvents.push(element);
|
||||
}
|
||||
@ -10202,6 +10212,7 @@
|
||||
context.classList.add('info-card');
|
||||
|
||||
this.relativePath = basePath;
|
||||
console.log(htmlString);
|
||||
htmlString = this._adjustRelativeLinks(htmlString);
|
||||
|
||||
let parser = new DOMParser();
|
||||
|
19
dist/iwmlib.pixi.js
vendored
19
dist/iwmlib.pixi.js
vendored
@ -6396,7 +6396,9 @@
|
||||
for (let j = 0; j < elements.length; j++) {
|
||||
// if(elements[j].tagName == "svg") return false;
|
||||
|
||||
let hammer = new Hammer(elements[j], opts);
|
||||
const target = elements[j];
|
||||
|
||||
let hammer = new Hammer(target, opts);
|
||||
|
||||
if (window.propagating !== 'undefined') {
|
||||
hammer = propagating(hammer);
|
||||
@ -6417,16 +6419,23 @@
|
||||
hammer.get('tap').set(opts);
|
||||
}
|
||||
|
||||
console.log('APPLY HAMMER ON', type);
|
||||
|
||||
target.addEventListener("click", ()=>{
|
||||
console.log("Hello");
|
||||
});
|
||||
|
||||
hammer.on(type, event => {
|
||||
console.log('FIRED');
|
||||
cb(event);
|
||||
});
|
||||
|
||||
if (Hammer.__hammers.has(elements[j])) {
|
||||
const elementHammers = Hammer.__hammers.get(elements[j]);
|
||||
if (Hammer.__hammers.has(target)) {
|
||||
const elementHammers = Hammer.__hammers.get(target);
|
||||
elementHammers.push(hammer);
|
||||
Hammer.__hammers.set(elements[j], elementHammers);
|
||||
Hammer.__hammers.set(target, elementHammers);
|
||||
} else {
|
||||
Hammer.__hammers.set(elements[j], [hammer]);
|
||||
Hammer.__hammers.set(target, [hammer]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
206
lib/card/card.js
206
lib/card/card.js
@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
'use strict';
|
||||
import Highlight from './highlight.js'
|
||||
|
||||
/** To avoid problems with relative URL paths, we use inline data URI to load svg icons. */
|
||||
const closeIconDataURI = `data:image/svg+xml;utf8,
|
||||
@ -33,10 +34,17 @@ const enableNearestNeighborTaps = false
|
||||
export default class Card {
|
||||
static setup(context, modules = []) {
|
||||
console.log('Setup Card...', modules)
|
||||
context.modules = []
|
||||
|
||||
/**
|
||||
* This is required for the callback functions to work properly.
|
||||
*/
|
||||
window.Card = Card
|
||||
|
||||
context.modules = []
|
||||
context.module = {}
|
||||
|
||||
context.onClose = null
|
||||
|
||||
context.classList.add('info-card')
|
||||
context.setAttribute('data-id', Card.id++)
|
||||
|
||||
@ -51,16 +59,57 @@ export default class Card {
|
||||
|
||||
static remove(context) {
|
||||
for (let module of Object.values(context.module)) {
|
||||
module.remove()
|
||||
const moduleHasRemoveFunction = typeof module.remove === 'function'
|
||||
if (moduleHasRemoveFunction) module.remove()
|
||||
}
|
||||
}
|
||||
|
||||
static eventClose(event) {
|
||||
static close(event) {
|
||||
let context = this.getContext(event.target)
|
||||
|
||||
if (context) {
|
||||
this.constructor.close(context)
|
||||
} else console.error('Could not find context!', event.target)
|
||||
if (context.onClose != null) {
|
||||
context.onClose()
|
||||
} else {
|
||||
this.remove(context)
|
||||
}
|
||||
|
||||
// console.error("Remove")
|
||||
// let context = this.getContext(event.target)
|
||||
|
||||
// if (context) {
|
||||
// this.remove(context)
|
||||
// } else console.error('Could not find context!', event.target)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an on close method to the provided context.
|
||||
* This will overwrite the default closing behaviour.
|
||||
* Removing the
|
||||
*
|
||||
* @static
|
||||
* @param {DOMElement} context - Context on which the onClose will be set.
|
||||
* @param {Function} callback - Callback function of the onClose.
|
||||
* @memberof Card
|
||||
*/
|
||||
static setOnClose(context, callback) {
|
||||
if (context.onClose != null) console.error('OnClose was already set. It was overwritten by the new method.')
|
||||
context.onClose = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the onClose.
|
||||
*
|
||||
* Note: This may be used in conjunction with the setOnClose method.
|
||||
* Using the setOnClose method to adjust behaviour before closing the card.
|
||||
* Then unsetting the onClose to close the Card appropriately by calling the
|
||||
* Card.Close again.
|
||||
*
|
||||
* @static
|
||||
* @param {DOMElement} context - Context on which the remove will be executed.
|
||||
* @memberof Card
|
||||
*/
|
||||
static removeOnClose(context) {
|
||||
context.onClose = null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,12 +119,12 @@ export default class Card {
|
||||
* @param {*} event
|
||||
* @memberof Card
|
||||
*/
|
||||
static close(context) {
|
||||
console.log('CLOSE CARD!!!')
|
||||
this.unregisterAllEvents(context)
|
||||
if (context.onClose) {
|
||||
context.onClose(event)
|
||||
} else context.parentNode.removeChild(context)
|
||||
static remove(context) {
|
||||
if (context.parentNode != null) {
|
||||
context.parentNode.removeChild(context)
|
||||
} else {
|
||||
console.error('Tried removing card but it was already removed.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,8 +137,8 @@ export default class Card {
|
||||
* @memberof Card
|
||||
*/
|
||||
static _replaceAttributes(context, html, attribute, replaceFunc) {
|
||||
let clickables = html.querySelectorAll(`[${attribute}]`)
|
||||
clickables.forEach(element => {
|
||||
let attributeCarrier = html.querySelectorAll(`[${attribute}]`)
|
||||
attributeCarrier.forEach(element => {
|
||||
let attributeVal = element.getAttribute(attribute)
|
||||
element.removeAttribute(attribute)
|
||||
replaceFunc.call(this, context, element, attributeVal)
|
||||
@ -124,11 +173,23 @@ export default class Card {
|
||||
let argsStrings = trimmedArgs.split(',').filter(entry => {
|
||||
return entry.trim() != ''
|
||||
})
|
||||
|
||||
/**
|
||||
* As we determine a function by a string we must traverse from the window object to
|
||||
* get the associated javascript function.
|
||||
*/
|
||||
let callStack = window
|
||||
let last = 'window'
|
||||
do {
|
||||
callStack = callStack[callParts.shift().trim()]
|
||||
let func = callParts.shift().trim()
|
||||
if (callStack[func] == null) {
|
||||
callStack = null
|
||||
console.error(
|
||||
`Could not access callback function: ${attributeVal}. Member ${func} of ${last} could not be found.`
|
||||
)
|
||||
break
|
||||
} else callStack = callStack[func]
|
||||
} while (callParts.length > 0)
|
||||
let targetFunc = callStack
|
||||
|
||||
let that = this
|
||||
|
||||
@ -161,8 +222,9 @@ export default class Card {
|
||||
}
|
||||
})
|
||||
event.stopPropagation()
|
||||
if (callStack) callStack.call(that, ...args)
|
||||
else {
|
||||
if (targetFunc) {
|
||||
targetFunc.call(that, ...args)
|
||||
} else {
|
||||
console.error('Could not call callback function ' + attributeVal, ...args)
|
||||
}
|
||||
})
|
||||
@ -190,13 +252,6 @@ export default class Card {
|
||||
/<\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
|
||||
@ -751,7 +806,7 @@ export default class Card {
|
||||
|
||||
let point = svgPoint.matrixTransform(matrix)
|
||||
let closestDiv = node.closest('div')
|
||||
console.log('closestDiv', closestDiv, point)
|
||||
// console.log('closestDiv', closestDiv, point)
|
||||
let global = Points.fromNodeToPage(closestDiv, point)
|
||||
let local = Points.fromPageToNode(context, global)
|
||||
|
||||
@ -763,7 +818,7 @@ export default class Card {
|
||||
// we could load the data while the circle is animating.
|
||||
// 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)
|
||||
// console.log('loadHighlightPopup', src, position, local)
|
||||
this._loadPopupContent(context, src)
|
||||
.then(content => {
|
||||
this._openPopup(context, src, local, content, {
|
||||
@ -1214,7 +1269,7 @@ export default class Card {
|
||||
*
|
||||
* @param {*} card - The card to expand
|
||||
* @param {string} html - The original HTML of the card
|
||||
* @param {*} tagName - The tagname of the element that is used as exanded element
|
||||
* @param {*} tagName - The tagname of the element that is used as expanded element
|
||||
* @param {*} src - The src of the expanded element
|
||||
* @param {*} callback - A callback that is called when the expanded element is closed
|
||||
*/
|
||||
@ -1257,7 +1312,6 @@ export default class Card {
|
||||
let scaleY = globalPreviewRect.height / globalIndexCardRect.height
|
||||
|
||||
let padding = parseInt(this.css(indexbox, 'padding'))
|
||||
let maxWidth = this.css(card, 'max-width')
|
||||
|
||||
TweenLite.set(clone, {
|
||||
css: {
|
||||
@ -1295,7 +1349,6 @@ export default class Card {
|
||||
|
||||
if (this.dynamicHeight) {
|
||||
let targetHeight = subcardContent.offsetHeight
|
||||
console.log(targetHeight)
|
||||
subcardContent.classList.add('dynamic-height')
|
||||
/**
|
||||
* Scale the content from 100% to it's target size.
|
||||
@ -1309,7 +1362,7 @@ export default class Card {
|
||||
}
|
||||
|
||||
//jquery hyphenate below
|
||||
if (this.constructor._jQueryIsPresent()) {
|
||||
if (this._isJQueryPresent()) {
|
||||
$('.column')
|
||||
.not('.overview')
|
||||
.children('p')
|
||||
@ -1389,7 +1442,7 @@ export default class Card {
|
||||
if (enableNearestNeighborTaps) {
|
||||
//look for nearby popups on tap
|
||||
InteractionMapper.on('tap', indexbox, () => {
|
||||
console.log('Tap handler called', editable)
|
||||
// console.log('Tap handler called', editable)
|
||||
if (!editable) {
|
||||
this.findNearbyPopups(event, card)
|
||||
}
|
||||
@ -1407,24 +1460,55 @@ export default class Card {
|
||||
if (isDirty) {
|
||||
mainController.saveNode(html.innerHTML, url => {
|
||||
callback(url)
|
||||
this._closeIndexCard(context, card,{
|
||||
this._closeIndexCard(context, card, clone, articleClone, {
|
||||
eventElements,
|
||||
src
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this._closeIndexCard(context, card)
|
||||
this._closeIndexCard(context, card, clone, articleClone)
|
||||
}
|
||||
} else {
|
||||
this._closeIndexCard(context, card)
|
||||
this._closeIndexCard(context, card, clone, articleClone)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static _closeIndexCard(context, card, {
|
||||
eventElements = [],
|
||||
src = null
|
||||
} = []) {
|
||||
/**
|
||||
* Closes the index card again.
|
||||
*
|
||||
* @static
|
||||
* @param {DOMElement} context - The Card element.
|
||||
* @param {DOMElement} subcard - The original subcard element visible on the main card.
|
||||
* @param {DOMElement} clonedSubcard - The cloned subcard that's going to be expanded.
|
||||
* @param {DOMElement} clonedArticle - The article part of the ClonedSubcard.
|
||||
* @param {Object} [{ eventElements = [], src = null }=[]]
|
||||
* @memberof Card
|
||||
*/
|
||||
static _closeIndexCard(
|
||||
context,
|
||||
subcard,
|
||||
clonedSubcard,
|
||||
clonedArticle,
|
||||
|
||||
{ eventElements = [], src = null } = []
|
||||
) {
|
||||
let indexbox = context.querySelector('.mainview')
|
||||
let padding = parseInt(this.css(indexbox, 'padding'))
|
||||
|
||||
let globalPreviewRect = Card._getGlobalRect(subcard)
|
||||
let globalIndexCardRect = Card._getGlobalRect(indexbox)
|
||||
|
||||
let scale = {
|
||||
x: globalPreviewRect.width / globalIndexCardRect.width,
|
||||
y: globalPreviewRect.height / globalIndexCardRect.height
|
||||
}
|
||||
|
||||
let titlebar = clonedSubcard.querySelector('.titlebar')
|
||||
|
||||
let desiredBorderBottomWidth = parseInt(window.getComputedStyle(titlebar).borderBottomWidth)
|
||||
let localOrigin = Points.fromPageToNode(indexbox, Rect.getPosition(globalPreviewRect))
|
||||
|
||||
//logging
|
||||
if (src) {
|
||||
let strparts = src.split('/')
|
||||
@ -1442,15 +1526,14 @@ export default class Card {
|
||||
this._subcardChanged(context, true)
|
||||
this._enableCardCloseButton(context)
|
||||
|
||||
let previewTitlebar = card.querySelector('.titlebar')
|
||||
let previewTitlebar = subcard.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, {
|
||||
TweenLite.to(clonedArticle, this.animation.articleTransition / 2, {
|
||||
autoAlpha: 0
|
||||
})
|
||||
|
||||
@ -1465,24 +1548,25 @@ export default class Card {
|
||||
})
|
||||
}
|
||||
|
||||
TweenLite.set(card, { autoAlpha: 1, css: { maxWidth } })
|
||||
TweenLite.to(clone, this.animation.articleTransition, {
|
||||
let maxWidth = this.css(subcard, 'max-width')
|
||||
TweenLite.set(subcard, { autoAlpha: 1, css: { maxWidth } })
|
||||
TweenLite.to(clonedSubcard, this.animation.articleTransition, {
|
||||
x: localOrigin.x - padding,
|
||||
y: localOrigin.y - padding,
|
||||
scaleX,
|
||||
scaleY,
|
||||
ease: ExpoScaleEase.config(1, scaleX),
|
||||
rotation: angle,
|
||||
scaleX: scale.x,
|
||||
scaleY: scale.y,
|
||||
ease: ExpoScaleEase.config(1, scale.x),
|
||||
// rotation: angle,
|
||||
onComplete: () => {
|
||||
// article.remove()
|
||||
TweenLite.to(clone, this.animation.fade, {
|
||||
TweenLite.to(clonedSubcard, this.animation.fade, {
|
||||
//delay: 0.2,
|
||||
autoAlpha: 0,
|
||||
onComplete: () => {
|
||||
if (editable) {
|
||||
if (Card.isEditable()) {
|
||||
mainController.popController()
|
||||
}
|
||||
clone.remove()
|
||||
clonedSubcard.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -1508,7 +1592,7 @@ export default class Card {
|
||||
* Tests if jQuery is properly included in the project.
|
||||
* Otherwise specific features may not work correctly (e.g. hyphenation)
|
||||
*/
|
||||
_jQueryIsPresent() {
|
||||
static _isJQueryPresent() {
|
||||
let jQueryInitialized = typeof $ != 'undefined'
|
||||
if (!jQueryInitialized) console.error('No jQuery is provided. Specific features may fail.')
|
||||
return jQueryInitialized
|
||||
@ -1542,7 +1626,7 @@ export default class Card {
|
||||
* @memberof Card
|
||||
*/
|
||||
static openIndexCard(event, src) {
|
||||
//console.log("openIndexCard", src)
|
||||
console.log('openIndexCard', src)
|
||||
/*
|
||||
* Called by the expandIndexCard(...)
|
||||
*/
|
||||
@ -1593,9 +1677,7 @@ export default class Card {
|
||||
}
|
||||
|
||||
static _enableCardCloseButton(context) {
|
||||
//console.log("ENABLE")
|
||||
let btn = this._selectCardCloseButton(context)
|
||||
//console.log(btn)
|
||||
btn.classList.remove('disabled')
|
||||
}
|
||||
|
||||
@ -1706,11 +1788,9 @@ export default class Card {
|
||||
* @memberof Card
|
||||
*/
|
||||
static postProcessResponseText(context, htmlString) {
|
||||
console.error('RUN POSTPROCESS')
|
||||
let editable = this.isEditable()
|
||||
|
||||
htmlString = this._adjustRelativeLinks(htmlString)
|
||||
//console.log(htmlString)
|
||||
let parser = new DOMParser()
|
||||
let html = parser.parseFromString(htmlString, 'text/html')
|
||||
if (!editable) {
|
||||
@ -1835,7 +1915,7 @@ export default class Card {
|
||||
this._setPopupSource(popup, source)
|
||||
context.popup = popup
|
||||
|
||||
if (this.constructor._jQueryIsPresent()) {
|
||||
if (this._isJQueryPresent()) {
|
||||
//jquery hyphenate below
|
||||
console.log('hyphenated popup:', $('span').hyphenate('de'))
|
||||
}
|
||||
@ -1908,6 +1988,14 @@ export default class Card {
|
||||
context.subcard = null
|
||||
}
|
||||
|
||||
static _subcardChanged(context, closed = false) {
|
||||
for (let [key, module] of Object.entries(context.module)) {
|
||||
if (module.subcardChanged) {
|
||||
module.subcardChanged(closed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static incrementZIndex(context) {
|
||||
if (!context.zIndex) context.zIndex = 0
|
||||
context.zIndex++
|
||||
|
@ -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')
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,17 @@ export default class ScatterCard extends Card {
|
||||
* @memberof ScatterCard
|
||||
*/
|
||||
static setup(context, htmlString, { basePath = './', modules = [] } = {}) {
|
||||
if (typeof context.scatter == 'undefined') {
|
||||
console.error(
|
||||
"You need to wrap the context inside a DOMScatter before executing the ScatterCard's setup function."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is required for the callback functions to work properly
|
||||
*/
|
||||
window.ScatterCard = ScatterCard
|
||||
|
||||
context.classList.add('info-card')
|
||||
|
||||
this.relativePath = basePath
|
||||
@ -38,29 +49,6 @@ export default class ScatterCard extends Card {
|
||||
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
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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,
|
||||
*
|
||||
@ -95,15 +83,19 @@ export default class ScatterCard extends Card {
|
||||
* of the scatter.
|
||||
*/
|
||||
static close(context) {
|
||||
|
||||
Card.close(context)
|
||||
|
||||
if (context['scatter']) {
|
||||
console.error('CLOSED CARD')
|
||||
context.scatter.close()
|
||||
} else {
|
||||
if (typeof context.scatter != 'undefined') context.scatter.close()
|
||||
else {
|
||||
console.error('Expected a scatter element to close!', this)
|
||||
}
|
||||
|
||||
// Card.close(context)
|
||||
|
||||
// if (context['scatter']) {
|
||||
// console.error('CLOSED CARD')
|
||||
// context.scatter.close()
|
||||
// } else {
|
||||
// console.error('Expected a scatter element to close!', this)
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,14 +108,13 @@ export default class ScatterCard extends Card {
|
||||
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.
|
||||
*
|
||||
@ -135,7 +126,7 @@ export default class ScatterCard extends Card {
|
||||
* @returns
|
||||
* @memberof CardScatter
|
||||
*/
|
||||
static loadAndCreateScatterCard(scatterContainer, item, { basePath = '../', modules = [], onClose = null } = {}) {
|
||||
static loadAndCreateScatterCard(scatterContainer, item, { basePath = '../', modules = [] } = {}) {
|
||||
console.log(basePath)
|
||||
return new Promise((resolve, reject) => {
|
||||
let url = basePath + '/' + item + '/index.html'
|
||||
@ -147,7 +138,6 @@ export default class ScatterCard extends Card {
|
||||
basePath,
|
||||
modules
|
||||
})
|
||||
if (onClose) this.addOnCloseListener(element, onClose)
|
||||
resolve(element)
|
||||
})
|
||||
.catch(e => reject(e))
|
||||
|
@ -64,6 +64,8 @@
|
||||
})
|
||||
})
|
||||
|
||||
createCard()
|
||||
|
||||
function createCard() {
|
||||
const path = './example/01/index.html'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user