lacymorrow/crossover

View on GitHub
src/renderer/renderer.js

Summary

Maintainability
B
4 hrs
Test Coverage
/* global feather, inlineSVG, randomColor */

( () => {

    // Todo: better error handling for img onError
    // Renderer error handling
    window.addEventListener( 'error', event => {

        console.log( 'Error', event )
        window.crossover.send( 'error', `Renderer - ${event.message} @ ${event.filename}:${event.lineno}` )

    } )

    // DOM elements
    const background = document.querySelector( '.background' )
    const closeBtn = document.querySelector( '.close-button' )
    const infoBtn = document.querySelector( '.info-button' )
    const centerBtn = document.querySelector( '.center-button' )
    const settingsBtn = document.querySelector( '.settings-button' )
    const container = document.querySelector( '.container' )
    const crosshairElement = document.querySelector( '#crosshair' )
    let crosshairImg = document.querySelector( '#crosshairImg' )

    const img = document.createElement( 'IMG' )
    img.id = 'crosshairImg'
    img.draggable = false
    img.addEventListener( 'error', event => console.log( event ) )
    img.src = '../static/crosshairs/Actual/leupold-dot.png'

    // OS Specific
    if ( window.crossover?.isMacOs ) {

        // Set class
        document.body.classList.add( 'mac' )

    } else if ( window.crossover?.isWindows ) {

        // Set class
        document.body.classList.add( 'windows' )

    }

    // Render icons
    if ( feather ) {

        feather.replace()

    }

    window.crossover.receive( 'set_properties', arg => {

        for ( const [ key, value ] of Object.entries( arg ) ) {

            // console.log( 'Setting:', key, value )
            document.documentElement.style.setProperty( key, value )

        }

    } )

    // Add/Remove classes
    window.crossover.receive( 'add_class', arg => {

        // Trigger things
        if ( arg === 'shadow' ) {

            // This is a child window being instantiated
            background.style.background = randomColor( {
                luminosiy: 'light',
                format: 'rgba',
                alpha: window.crossover.config.APP_BACKGROUND_OPACITY || 0.9, // E.g. 'rgba(9, 1, 107, 0.5)',
            } )

        }

        document.body.classList.add( arg )

    } )

    window.crossover.receive( 'remove_class', arg => {

        document.body.classList.remove( arg )

    } )

    // Sounds
    window.crossover.receive( 'preload_sounds', arg => {

        window.crossover.preloadSounds( arg )

    } )

    window.crossover.receive( 'play_sound', arg => {

        window.crossover.playSound( arg )

    } )

    // Notifications
    window.crossover.receive( 'notify', arg => {

        if ( !arg.title || !arg.body ) {

            console.error( 'Invalid Notification, title and body are required.' )

            return

        }

        const notification = new window.Notification( arg.title, {
            body: arg.body,
            // silent: arg.silent, // We'll play our own sound
        } )

        // If the user clicks in the Notifications Center, show the app
        notification.addEventListener( 'click', () => {

            window.crossover.send( 'focusWindow' )

        } )

    } )

    // Auto Update info icon
    window.crossover.receive( 'set_info_icon', arg => {

        // Hide all icons
        infoBtn.querySelectorAll( '.feather' ).forEach( elem => {

            elem.classList.add( 'd-none' )

        } )

        // Pick one icon: move, info, resize
        let iconEl = infoBtn.querySelector( `.${arg}-icon` )
        if ( !iconEl ) {

            iconEl = infoBtn.querySelector( '.move-icon' )

        }

        // Show
        iconEl.classList.remove( 'd-none' )

        // Windows needs -webkit-app-region: no-drag to be resizable
        infoBtn.classList.remove( 'info', 'move', 'resize' )
        infoBtn.classList.add( arg )

    } )

    // Crosshair
    const setCrosshair = crosshair => {

        // Don't set if same image already set
        if ( document.querySelector( '#crosshairImg' ).dataset.src === crosshair ) {

            return

        }

        // Reset to IMG element if SVG
        if ( document.querySelector( 'svg#crosshairImg' ) ) {

            document.querySelector( '#crosshairImg' ).remove()
            crosshairElement.prepend( img.cloneNode( true ) )

        }

        crosshairImg = document.querySelector( '#crosshairImg' )

        // Hide if no crosshair selected
        if ( crosshair === 'none' ) {

            crosshairImg.style.display = 'none'

        }

        // Set image
        crosshairImg.src = crosshair
        crosshairImg.dataset.src = crosshair
        crosshairImg.style.display = 'block'

        // Inline if SVG
        if ( crosshair.split( '.' ).pop() === 'svg' ) {

            inlineSVG.init( {
                svgSelector: '#crosshairImg', // the class attached to all images that should be inlined
            }, () => {

                console.log( 'SVG inlined' )

            } )

        }

    }

    window.crossover.receive( 'set_crosshair', arg => {

        setCrosshair( arg )

    } )

    // Sight
    const setReticle = reticle => {

        document.querySelector( '.reticle' ).classList.remove( 'dot', 'cross', 'off' )
        document.querySelector( '.reticle' ).classList.add( reticle )

    }

    window.crossover.receive( 'set_reticle', arg => {

        setReticle( arg )

    } )

    // Lock
    window.crossover.receive( 'lock_window', lock => {

        if ( lock ) {

            document.body.classList.remove( 'draggable' )

        } else {

            document.body.classList.add( 'draggable' )

        }

    } )

    /* Event Listeners */
    // Close window
    closeBtn.addEventListener( 'click', () => {

        if ( document.body.classList.contains( 'shadow' ) ) {

            window.crossover.send( 'close_window' )

        } else {

            // This is the main window
            window.crossover.send( 'quit' )

        }

    } )

    // Open settings window
    settingsBtn.addEventListener( 'click', () => {

        window.crossover.send( 'open_settings' )

    } )

    // Open Chooser
    const dOpenChooser = window.crossover.debounce( () => {

        window.crossover.send( 'open_chooser', crosshairImg.src )

    }, window.crossover.config.DEBOUNCE_DELAY )

    centerBtn.addEventListener( 'click', () => {

        dOpenChooser()

    } )

    // Center window on double click
    centerBtn.addEventListener( 'dblclick', () => {

        dOpenChooser( { abort: true } )
        window.crossover.send( 'center_window' )

    } )

    crosshairElement.addEventListener( 'dblclick', () => {

        window.crossover.send( 'center_window' )

    } )

    // Drag and drop Custom Image
    // for drop events to fire, must cancel dragover and dragleave events

    let eventCounter = 0 // I kind of hate this but it works
    document.addEventListener( 'dragenter', event => {

        event.preventDefault()

        // Highlight potential drop target when the draggable element enters it
        eventCounter++
        container.classList.add( 'dropping' )

    }, false )

    document.addEventListener( 'dragover', event => {

        event.preventDefault()
        container.classList.add( 'dropping' )

    }, false )

    document.addEventListener( 'dragleave', event => {

        event.preventDefault()
        eventCounter--
        if ( eventCounter === 0 ) {

            container.classList.remove( 'dropping' )

        }

    }, false )

    document.addEventListener( 'dragend', event => {

        event.preventDefault()
        eventCounter = 0
        container.classList.remove( 'dropping' )

    }, false )

    document.addEventListener( 'drop', event => {

        event.preventDefault()
        eventCounter = 0
        container.classList.remove( 'dropping' )

        // Send file path to main
        window.crossover.send( 'save_custom_image', event.dataTransfer.files[0].path )

    }, true )

} )()