672 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			672 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* eslint-disable no-unused-vars */
 | 
						|
/* global PDFJS Power1 */
 | 
						|
import { getId } from './utils.js'
 | 
						|
import { DOMScatter } from './scatter.js'
 | 
						|
import CardWrapper from './card/wrapper.js'
 | 
						|
 | 
						|
export class CardLoader {
 | 
						|
    constructor(
 | 
						|
        src,
 | 
						|
        {
 | 
						|
            x = 0,
 | 
						|
            y = 0,
 | 
						|
            width = 1000,
 | 
						|
            height = 800,
 | 
						|
            maxWidth = null,
 | 
						|
            maxHeight = null,
 | 
						|
            scale = 1,
 | 
						|
            minScale = 0.5,
 | 
						|
            maxScale = 1.5,
 | 
						|
            rotation = 0
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        this.src = src
 | 
						|
        this.x = x
 | 
						|
        this.y = y
 | 
						|
        this.scale = scale
 | 
						|
        this.rotation = rotation
 | 
						|
        this.maxScale = maxScale
 | 
						|
        this.minScale = minScale
 | 
						|
        this.wantedWidth = width
 | 
						|
        this.wantedHeight = height
 | 
						|
        this.maxWidth = maxWidth != null ? maxWidth : window.innerWidth
 | 
						|
        this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight
 | 
						|
        this.addedNode = null
 | 
						|
    }
 | 
						|
 | 
						|
    unload() {
 | 
						|
        if (this.addedNode) {
 | 
						|
            this.addedNode.remove()
 | 
						|
            this.addedNode = null
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class PDFLoader extends CardLoader {
 | 
						|
    constructor(src, { width = 1640, height = 800, scale = 1 } = {}) {
 | 
						|
        super(src, { width, height, scale })
 | 
						|
        if (typeof PDFJS == 'undefined') {
 | 
						|
            alert('PDF.js needed')
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    load(domNode) {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            PDFJS.getDocument(this.src).then((pdf) => {
 | 
						|
                pdf.getPage(1).then((page) => {
 | 
						|
                    let scale = this.scale * app.renderer.resolution
 | 
						|
                    let invScale = 1 / scale
 | 
						|
                    let viewport = page.getViewport(scale)
 | 
						|
 | 
						|
                    // Prepare canvas using PDF page dimensions.
 | 
						|
                    let canvas = document.createElement('canvas')
 | 
						|
                    let context = canvas.getContext('2d')
 | 
						|
                    canvas.height = viewport.height
 | 
						|
                    canvas.width = viewport.width
 | 
						|
 | 
						|
                    // Render PDF page into canvas context.
 | 
						|
                    let renderContext = {
 | 
						|
                        canvasContext: context,
 | 
						|
                        viewport: viewport
 | 
						|
                    }
 | 
						|
                    page.render(renderContext)
 | 
						|
                    domNode.appendChild(canvas)
 | 
						|
                    this.wantedWidth = canvas.width
 | 
						|
                    this.wantedHeight = canvas.height
 | 
						|
                    this.scale = invScale
 | 
						|
                    this.addedNode = canvas
 | 
						|
                    resolve(this)
 | 
						|
                })
 | 
						|
            })
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class ImageLoader extends CardLoader {
 | 
						|
    load(domNode) {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            let isImage = domNode instanceof HTMLImageElement
 | 
						|
            let image = isImage ? domNode : document.createElement('img')
 | 
						|
            image.onload = (e) => {
 | 
						|
                if (!isImage) {
 | 
						|
                    domNode.appendChild(image)
 | 
						|
                    this.addedNode = image
 | 
						|
                }
 | 
						|
                this.wantedWidth = image.naturalWidth
 | 
						|
                this.wantedHeight = image.naturalHeight
 | 
						|
 | 
						|
                let scaleW = this.maxWidth / image.naturalWidth
 | 
						|
                let scaleH = this.maxHeight / image.naturalHeight
 | 
						|
                this.scale = Math.min(this.maxScale, Math.min(scaleW, scaleH))
 | 
						|
                image.setAttribute('draggable', false)
 | 
						|
                image.width = image.naturalWidth
 | 
						|
                image.height = image.naturalHeight
 | 
						|
                resolve(this)
 | 
						|
            }
 | 
						|
            image.onerror = (e) => {
 | 
						|
                reject(this)
 | 
						|
            }
 | 
						|
            image.src = this.src
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class FrameLoader extends CardLoader {
 | 
						|
    load(domNode) {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            let isFrame = domNode instanceof HTMLIFrameElement
 | 
						|
            let iframe = isFrame ? domNode : document.createElement('iframe')
 | 
						|
            iframe.frameBorder = 0
 | 
						|
            iframe.style.scrolling = false
 | 
						|
            iframe.width = this.wantedWidth
 | 
						|
            iframe.height = this.wantedHeight
 | 
						|
            if (!isFrame) {
 | 
						|
                // Unlike img onload is only triggered if the iframe is part of the DOM tree
 | 
						|
                domNode.appendChild(iframe)
 | 
						|
                this.addedNode = iframe
 | 
						|
            }
 | 
						|
            iframe.onload = (e) => {
 | 
						|
                resolve(this)
 | 
						|
            }
 | 
						|
            iframe.onerror = (e) => {
 | 
						|
                reject(this)
 | 
						|
            }
 | 
						|
            iframe.src = this.src
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class HTMLLoader extends CardLoader {
 | 
						|
    load(domNode) {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            let xhr = new XMLHttpRequest()
 | 
						|
            xhr.open('GET', this.src, false)
 | 
						|
            xhr.onreadystatechange = (e) => {
 | 
						|
                if (xhr.readyState == 4) {
 | 
						|
                    domNode.innerHTML = this.prepare(xhr.response)
 | 
						|
                    this.addedNode = domNode.firstElementChild
 | 
						|
                    let { width, height } = this.size(this.addedNode)
 | 
						|
                    if (width) this.wantedWidth = width || this.wantedWidth
 | 
						|
                    if (height) this.wantedHeight = height || this.wantedHeight
 | 
						|
                    resolve(this)
 | 
						|
                }
 | 
						|
            }
 | 
						|
            xhr.onerror = (e) => {
 | 
						|
                reject(this)
 | 
						|
            }
 | 
						|
            xhr.send()
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Preoares the html before it is assigned with innerHTML.
 | 
						|
     * Can be overwritten in subclasses.
 | 
						|
     *
 | 
						|
     * @param {*} html
 | 
						|
     * @returns
 | 
						|
     * @memberof HTMLLoader
 | 
						|
     */
 | 
						|
    prepare(html) {
 | 
						|
        return html
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Tries to determine the size of the addedNode.
 | 
						|
     * Checks for explicit width and height style attributes.
 | 
						|
     *
 | 
						|
     * Overwrite this method if you want to extract values from other infos.
 | 
						|
     *
 | 
						|
     * @returns { width: int, height: int }
 | 
						|
     * @memberof HTMLLoader
 | 
						|
     */
 | 
						|
    size(node) {
 | 
						|
        let width = parseInt(node.style.width) || null
 | 
						|
        let height = parseInt(node.style.height) || null
 | 
						|
        return { width, height }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class DOMFlip {
 | 
						|
    constructor(
 | 
						|
        domScatterContainer,
 | 
						|
        flipTemplate,
 | 
						|
        frontLoader,
 | 
						|
        backLoader,
 | 
						|
        {
 | 
						|
            closeOnMinScale = false,
 | 
						|
            flipDuration = 1,
 | 
						|
            fadeDuration = 0.2,
 | 
						|
            overdoScaling = 1,
 | 
						|
            autoLoad = false,
 | 
						|
            center = null,
 | 
						|
            preloadBack = false,
 | 
						|
            translatable = true,
 | 
						|
            scalable = true,
 | 
						|
            rotatable = true,
 | 
						|
            tapDelegateFactory = null,
 | 
						|
            onFront = null,
 | 
						|
            onBack = null,
 | 
						|
            onClose = null,
 | 
						|
            onUpdate = null,
 | 
						|
            onRemoved = null,
 | 
						|
            onLoaded = null
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        this.domScatterContainer = domScatterContainer
 | 
						|
        this.id = getId()
 | 
						|
        this.flipDuration = flipDuration
 | 
						|
        this.fadeDuration = fadeDuration
 | 
						|
        this.closeOnMinScale = closeOnMinScale
 | 
						|
        this.flipTemplate = flipTemplate
 | 
						|
        this.frontLoader = frontLoader
 | 
						|
        this.backLoader = backLoader
 | 
						|
        this.translatable = translatable
 | 
						|
        this.scalable = scalable
 | 
						|
        this.rotatable = rotatable
 | 
						|
        this.tapDelegateFactory = tapDelegateFactory
 | 
						|
        this.onFrontFlipped = onFront
 | 
						|
        this.onBackFlipped = onBack
 | 
						|
        this.onClose = onClose
 | 
						|
        this.onRemoved = onRemoved
 | 
						|
        this.onUpdate = onUpdate
 | 
						|
        this.onLoaded = onLoaded
 | 
						|
        this.center = center
 | 
						|
        this.preloadBack = preloadBack
 | 
						|
        this.overdoScaling = overdoScaling
 | 
						|
        if (autoLoad) {
 | 
						|
            this.load()
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    load() {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            let t = this.flipTemplate
 | 
						|
            let dom = this.domScatterContainer.element
 | 
						|
            let wrapper = t.content.querySelector('.flipWrapper')
 | 
						|
            wrapper.id = this.id
 | 
						|
            let clone = document.importNode(t.content, true)
 | 
						|
            dom.appendChild(clone)
 | 
						|
            // We cannot use the document fragment itself because it
 | 
						|
            // is not part of the main dom tree. After the appendChild
 | 
						|
            // call we can access the new dom element by id
 | 
						|
            this.cardWrapper = dom.querySelector('#' + this.id)
 | 
						|
            let front = this.cardWrapper.querySelector('.front')
 | 
						|
            this.frontLoader.load(front).then((loader) => {
 | 
						|
                this.frontLoaded(loader).then((obj) => {
 | 
						|
                    if (this.onLoaded) this.onLoaded()
 | 
						|
                    resolve(this)
 | 
						|
                })
 | 
						|
            })
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    frontLoaded(loader) {
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            let scatter = new DOMScatter(this.cardWrapper, this.domScatterContainer, {
 | 
						|
                x: loader.x,
 | 
						|
                y: loader.y,
 | 
						|
                startScale: loader.scale,
 | 
						|
                scale: loader.scale,
 | 
						|
                maxScale: loader.maxScale,
 | 
						|
                minScale: loader.minScale,
 | 
						|
                width: loader.wantedWidth,
 | 
						|
                height: loader.wantedHeight,
 | 
						|
                rotation: loader.rotation,
 | 
						|
                translatable: this.translatable,
 | 
						|
                scalable: this.scalable,
 | 
						|
                rotatable: this.rotatable,
 | 
						|
                overdoScaling: this.overdoScaling,
 | 
						|
                tapDelegate: this.tapDelegateFactory ? new this.tapDelegateFactory(this.cardWrapper) : null
 | 
						|
            })
 | 
						|
 | 
						|
            if (this.center) {
 | 
						|
                scatter.centerAt(this.center)
 | 
						|
            }
 | 
						|
 | 
						|
            if (this.closeOnMinScale) {
 | 
						|
                const removeOnMinScale = function () {
 | 
						|
                    if (scatter.scale <= scatter.minScale) {
 | 
						|
                        this.flippable.close()
 | 
						|
 | 
						|
                        // 'Disable' overdoscaling to avoid weird jumps on close.
 | 
						|
                        scatter.minScale /= scatter.overdoScaling
 | 
						|
                        scatter.overdoScaling = 1
 | 
						|
 | 
						|
                        //Remove callback
 | 
						|
                        if (scatter.onTransform) {
 | 
						|
                            let callbackIdx = scatter.onTransform.indexOf(removeOnMinScale)
 | 
						|
                            scatter.onTransform.splice(callbackIdx, 1)
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }.bind(this)
 | 
						|
                scatter.addTransformEventCallback(removeOnMinScale)
 | 
						|
            }
 | 
						|
 | 
						|
            let flippable = new DOMFlippable(this.cardWrapper, scatter, this)
 | 
						|
            let back = this.cardWrapper.querySelector('.back')
 | 
						|
 | 
						|
            if (this.preloadBack) {
 | 
						|
                this.backLoader.load(back).then((loader) => {
 | 
						|
                    this.setupFlippable(flippable, loader)
 | 
						|
                })
 | 
						|
            }
 | 
						|
            this.flippable = flippable
 | 
						|
            resolve(this)
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    centerAt(p) {
 | 
						|
        this.center = p
 | 
						|
        this.flippable.centerAt(p)
 | 
						|
    }
 | 
						|
 | 
						|
    zoom(scale) {
 | 
						|
        this.flippable.zoom(scale)
 | 
						|
    }
 | 
						|
 | 
						|
    setupFlippable(flippable, loader) {
 | 
						|
        flippable.wantedWidth = loader.wantedWidth
 | 
						|
        flippable.wantedHeight = loader.wantedHeight
 | 
						|
        flippable.wantedScale = loader.scale
 | 
						|
        flippable.minScale = loader.minScale
 | 
						|
        flippable.maxScale = loader.maxScale
 | 
						|
        flippable.scaleButtons()
 | 
						|
    }
 | 
						|
 | 
						|
    start({ targetCenter = null } = {}) {
 | 
						|
        this.flippable.showFront()
 | 
						|
        if (this.preloadBack) {
 | 
						|
            this.flippable.start({ duration: this.flipDuration, targetCenter })
 | 
						|
        } else {
 | 
						|
            let back = this.cardWrapper.querySelector('.back')
 | 
						|
            let flippable = this.flippable
 | 
						|
            this.backLoader.load(back).then((loader) => {
 | 
						|
                this.setupFlippable(flippable, loader)
 | 
						|
                flippable.start({ duration: this.flipDuration, targetCenter })
 | 
						|
            })
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    fadeOutAndRemove() {
 | 
						|
        TweenLite.to(this.cardWrapper, this.fadeDuration, {
 | 
						|
            opacity: 0,
 | 
						|
            onComplete: () => {
 | 
						|
                this.cardWrapper.remove()
 | 
						|
            }
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    closed() {
 | 
						|
        this.unload()
 | 
						|
    }
 | 
						|
 | 
						|
    unload() {
 | 
						|
        if (!this.preloadBack) {
 | 
						|
            this.backLoader.unload()
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export class DOMFlippable {
 | 
						|
    constructor(element, scatter, flip) {
 | 
						|
        // Set log to console.log or a custom log function
 | 
						|
        // define data structures to store our touchpoints in
 | 
						|
 | 
						|
        this.element = element
 | 
						|
        this.flip = flip
 | 
						|
        this.card = element.querySelector('.flipCard')
 | 
						|
        this.front = element.querySelector('.front')
 | 
						|
        this.back = element.querySelector('.back')
 | 
						|
        this.flipped = false
 | 
						|
        this.scatter = scatter
 | 
						|
        this.onFrontFlipped = flip.onFrontFlipped
 | 
						|
        this.onBackFlipped = flip.onBackFlipped
 | 
						|
        this.onClose = flip.onClose
 | 
						|
        this.onRemoved = flip.onRemoved
 | 
						|
        this.onUpdate = flip.onUpdate
 | 
						|
 | 
						|
        this.wantedWidth = scatter.width
 | 
						|
        this.wantedHeight = scatter.height
 | 
						|
        this.wantedScale = scatter.scale
 | 
						|
        this.minScale = scatter.minScale
 | 
						|
        this.maxScale = scatter.maxScale
 | 
						|
 | 
						|
        this.flipDuration = flip.flipDuration
 | 
						|
        this.fadeDuration = flip.fadeDuration
 | 
						|
        scatter.addTransformEventCallback(this.scatterTransformed.bind(this))
 | 
						|
 | 
						|
        TweenLite.set(this.element, { perspective: 5000 })
 | 
						|
        TweenLite.set(this.card, { transformStyle: 'preserve-3d' })
 | 
						|
        TweenLite.set(this.back, { rotationY: -180 })
 | 
						|
        TweenLite.set([this.back, this.front], {
 | 
						|
            backfaceVisibility: 'hidden',
 | 
						|
            perspective: 5000
 | 
						|
        })
 | 
						|
        TweenLite.set(this.front, { visibility: 'visible' })
 | 
						|
        this.infoBtn = element.querySelector('.infoBtn')
 | 
						|
        this.backBtn = element.querySelector('.backBtn')
 | 
						|
        this.closeBtn = element.querySelector('.closeBtn')
 | 
						|
        /* Buttons are not guaranteed to exist. */
 | 
						|
        if (scatter.tapDelegate == null) {
 | 
						|
            let tapDelegate = new CardWrapper(element)
 | 
						|
            scatter.tapDelegate = tapDelegate
 | 
						|
        }
 | 
						|
        if (this.infoBtn) {
 | 
						|
            scatter.tapDelegate.onTap(this.infoBtn, (event) => {
 | 
						|
                this.flip.start()
 | 
						|
            })
 | 
						|
            this.enable(this.infoBtn)
 | 
						|
        }
 | 
						|
        if (this.backBtn) {
 | 
						|
            scatter.tapDelegate.onTap(this.backBtn, (event) => {
 | 
						|
                this.start()
 | 
						|
            })
 | 
						|
        }
 | 
						|
        if (this.closeBtn) {
 | 
						|
            scatter.tapDelegate.onTap(this.closeBtn, (event) => {
 | 
						|
                this.close()
 | 
						|
            })
 | 
						|
            this.enable(this.closeBtn)
 | 
						|
        }
 | 
						|
        this.scaleButtons()
 | 
						|
        this.bringToFront()
 | 
						|
    }
 | 
						|
 | 
						|
    close() {
 | 
						|
        this.disable(this.infoBtn)
 | 
						|
        this.disable(this.closeBtn)
 | 
						|
        if (this.onClose) {
 | 
						|
            this.onClose(this)
 | 
						|
            this.flip.closed()
 | 
						|
        } else {
 | 
						|
            this.scatter.zoom(0.1, {
 | 
						|
                animate: this.fadeDuration,
 | 
						|
                onComplete: () => {
 | 
						|
                    this.element.remove()
 | 
						|
                    this.flip.closed()
 | 
						|
                    if (this.onRemoved) {
 | 
						|
                        this.onRemoved.call(this)
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            })
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    showFront() {
 | 
						|
        TweenLite.set(this.front, { visibility: 'visible' })
 | 
						|
    }
 | 
						|
 | 
						|
    centerAt(p) {
 | 
						|
        this.scatter.centerAt(p)
 | 
						|
    }
 | 
						|
 | 
						|
    zoom(scale) {
 | 
						|
        this.scatter.zoom(scale)
 | 
						|
    }
 | 
						|
 | 
						|
    get buttonScale() {
 | 
						|
        let iscale = 1.0
 | 
						|
 | 
						|
        if (this.scatter != null) {
 | 
						|
            let scale = this.scatter.scale || 1
 | 
						|
            iscale = 1.0 / scale
 | 
						|
        }
 | 
						|
        return iscale
 | 
						|
    }
 | 
						|
 | 
						|
    scaleButtons() {
 | 
						|
        TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], {
 | 
						|
            scale: this.buttonScale
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    bringToFront() {
 | 
						|
        this.scatter.bringToFront()
 | 
						|
        TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ })
 | 
						|
    }
 | 
						|
 | 
						|
    clickInfo() {
 | 
						|
        this.bringToFront()
 | 
						|
        this.infoBtn.click()
 | 
						|
    }
 | 
						|
 | 
						|
    scatterTransformed(event) {
 | 
						|
        this.scaleButtons()
 | 
						|
    }
 | 
						|
 | 
						|
    targetRotation(alpha) {
 | 
						|
        let ortho = 90
 | 
						|
        let rest = alpha % ortho
 | 
						|
        let delta = 0.0
 | 
						|
        if (rest > ortho / 2.0) {
 | 
						|
            delta = ortho - rest
 | 
						|
        } else {
 | 
						|
            delta = -rest
 | 
						|
        }
 | 
						|
        return delta
 | 
						|
    }
 | 
						|
 | 
						|
    infoValues(info) {
 | 
						|
        let startX = this.element._gsTransform.x
 | 
						|
        let startY = this.element._gsTransform.y
 | 
						|
        let startAngle = this.element._gsTransform.rotation
 | 
						|
        let startScale = this.element._gsTransform.scaleX
 | 
						|
        let w = this.element.style.width
 | 
						|
        let h = this.element.style.height
 | 
						|
        // eslint-disable-next-line no-console
 | 
						|
        console.log(info, startX, startY, startAngle, startScale, w, h)
 | 
						|
    }
 | 
						|
 | 
						|
    show(element, duration = 0, alpha = 1) {
 | 
						|
        if (element) {
 | 
						|
            TweenLite.to(element, duration, { autoAlpha: alpha }) // visibility: 'visible', display: 'initial'})
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    hide(element, duration = 0, alpha = 0) {
 | 
						|
        if (element) {
 | 
						|
            TweenLite.to(element, duration, { autoAlpha: alpha }) // {visibility: 'hidden', display: 'none'})
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    enable(button) {
 | 
						|
        this.show(button, this.fadeDuration)
 | 
						|
        if (button) {
 | 
						|
            TweenLite.set(button, { pointerEvents: 'auto' })
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    disable(button) {
 | 
						|
        this.hide(button, this.fadeDuration)
 | 
						|
        if (button) {
 | 
						|
            TweenLite.set(button, { pointerEvents: 'none' })
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    start({ targetCenter = null } = {}) {
 | 
						|
        this.bringToFront()
 | 
						|
        console.log('flippable start', this.flipped)
 | 
						|
        if (!this.flipped) {
 | 
						|
            this.startX = this.element._gsTransform.x
 | 
						|
            this.startY = this.element._gsTransform.y
 | 
						|
            this.startAngle = this.element._gsTransform.rotation
 | 
						|
            this.startScale = this.element._gsTransform.scaleX
 | 
						|
            this.startWidth = this.element.style.width
 | 
						|
            this.startHeight = this.element.style.height
 | 
						|
            this.scatterStartWidth = this.scatter.width
 | 
						|
            this.scatterStartHeight = this.scatter.height
 | 
						|
            this.show(this.back)
 | 
						|
            this.disable(this.infoBtn)
 | 
						|
            this.disable(this.closeBtn)
 | 
						|
        } else {
 | 
						|
            this.show(this.front, this.fadeDuration)
 | 
						|
            this.disable(this.backBtn)
 | 
						|
        }
 | 
						|
        let { scalable, translatable, rotatable } = this.scatter
 | 
						|
        this.saved = { scalable, translatable, rotatable }
 | 
						|
        this.scatter.scalable = false
 | 
						|
        this.scatter.translatable = false
 | 
						|
        this.scatter.rotatable = false
 | 
						|
        this.scatter.killAnimation()
 | 
						|
 | 
						|
        this.flipped = !this.flipped
 | 
						|
        let targetY = this.flipped ? 180 : 0
 | 
						|
        let targetZ = this.flipped ? this.startAngle + this.targetRotation(this.startAngle) : this.startAngle
 | 
						|
        let targetScale = this.flipped ? this.wantedScale : this.startScale
 | 
						|
        let w = this.flipped ? this.wantedWidth : this.startWidth
 | 
						|
        let h = this.flipped ? this.wantedHeight : this.startHeight
 | 
						|
        let dw = this.wantedWidth - this.scatter.width
 | 
						|
        let dh = this.wantedHeight - this.scatter.height
 | 
						|
        let tc = targetCenter
 | 
						|
        let xx = tc != null ? tc.x - w / 2 : this.startX - dw / 2
 | 
						|
        let yy = tc != null ? tc.y - h / 2 : this.startY - dh / 2
 | 
						|
        let x = this.flipped ? xx : this.startX
 | 
						|
        let y = this.flipped ? yy : this.startY
 | 
						|
 | 
						|
        let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null
 | 
						|
        TweenLite.to(this.card, this.flipDuration, {
 | 
						|
            rotationY: targetY,
 | 
						|
            ease: Power1.easeOut,
 | 
						|
            transformOrigin: '50% 50%',
 | 
						|
            onUpdate,
 | 
						|
            onComplete: (e) => {
 | 
						|
                if (this.flipped) {
 | 
						|
                    //this.hide(this.front)
 | 
						|
                    this.enable(this.backBtn)
 | 
						|
                    this.show(this.backBtn)
 | 
						|
 | 
						|
                    if (this.onFrontFlipped) {
 | 
						|
                        this.onFrontFlipped(this)
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    if (this.onBackFlipped == null) {
 | 
						|
                        this.enable(this.infoBtn, this.fadeDuration)
 | 
						|
                        this.enable(this.closeBtn, this.fadeDuration)
 | 
						|
                    } else {
 | 
						|
                        this.onBackFlipped(this)
 | 
						|
                    }
 | 
						|
                    this.flip.unload()
 | 
						|
                }
 | 
						|
                this.scatter.scale = targetScale
 | 
						|
                this.scaleButtons()
 | 
						|
                this.scatter.rotationDegrees = targetZ
 | 
						|
                this.scatter.width = this.flipped ? w : this.scatterStartWidth
 | 
						|
                this.scatter.height = this.flipped ? h : this.scatterStartHeight
 | 
						|
 | 
						|
                let { scalable, translatable, rotatable } = this.saved
 | 
						|
                this.scatter.scalable = scalable
 | 
						|
                this.scatter.translatable = translatable
 | 
						|
                this.scatter.rotatable = rotatable
 | 
						|
            },
 | 
						|
            force3D: true
 | 
						|
        })
 | 
						|
        // See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
 | 
						|
        const duration = this.flipDuration / 2
 | 
						|
        TweenLite.to(this.element, duration, {
 | 
						|
            scale: targetScale,
 | 
						|
            ease: Power1.easeOut,
 | 
						|
            rotationZ: targetZ + '_short',
 | 
						|
            transformOrigin: '50% 50%',
 | 
						|
            width: w,
 | 
						|
            height: h,
 | 
						|
            x: x,
 | 
						|
            y: y,
 | 
						|
            onComplete: (e) => {
 | 
						|
                console.log('onComplete', e)
 | 
						|
                if (this.flipped) {
 | 
						|
                    this.hide(this.front)
 | 
						|
                    // this.hide(this.infoBtn)
 | 
						|
                } else {
 | 
						|
                    this.hide(this.back)
 | 
						|
                    // this.show(this.infoBtn)
 | 
						|
                }
 | 
						|
            }
 | 
						|
        })
 | 
						|
 | 
						|
        //uo: check for special case that the front image must be adapted to the back size
 | 
						|
        let frontImage = this.front.querySelector('img')
 | 
						|
        if (frontImage) {
 | 
						|
            // Assumes that startWidth and startHeight are encoded in px
 | 
						|
            let sh = parseInt(this.startHeight)
 | 
						|
            let sw = parseInt(this.startWidth)
 | 
						|
            if (this.flipped) {
 | 
						|
                let scaleY = h / sh
 | 
						|
                let scaleX = w / sw
 | 
						|
                TweenLite.to(frontImage, duration, {
 | 
						|
                    ease: Power1.easeOut,
 | 
						|
                    transformOrigin: '0% 0%',
 | 
						|
                    scaleY,
 | 
						|
                    scaleX
 | 
						|
                })
 | 
						|
            } else {
 | 
						|
                TweenLite.to(frontImage, duration, {
 | 
						|
                    ease: Power1.easeOut,
 | 
						|
                    transformOrigin: '0% 0%',
 | 
						|
                    scaleY: 1.0,
 | 
						|
                    scaleX: 1.0
 | 
						|
                })
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |