<commit_before>/*global define*/

/**
 * Define text shapes we can have in the show.
 */
define("krusovice/showobjects/textdefinitions", ["krusovice/showobjects"], function(showobjects) {

    "use strict";

    var textdefinitions = {};

    // Spoof get text for now
    function dummytext(msg) {
        return msg;
    }

    textdefinitions.getDefinitions = function() {

    return [
        {
            id : "clear",

            name : dummytext("Clear"),

            labels : {
                text : {
                    name : dummytext("Text"),
                    x : 0,
                    y : 0,
                    w : 1,
                    h : 1
                }
            },

            // Background image name (if used)
            imageName : null,

            // Allow to move text around
            position : true,

            // No background color
            clear : true,

            // Render frame border
            border : false,

            textBorder : true,

            width : 1,
            height : 1,

            // Transitions for this particular text style
            transitions : {
                transitionIn : {
                    type : "fade",
                    duration : 1
                },
                transitionOut : {
                    type : "fade",
                    duration : 1
                },
                onScreen : {
                    type : "hold"
                }
            }
        },

        {
            id : "notepad",

            imageName :  "notepad.png",

            name : dummytext("Notepad"),

            labels : {

                text : {
                    name : dummytext("Text"),
                    x : 0,
                    y : 0.1,
                    w : 1,
                    h : 0.6,
                    fontSizeAdjust : 1
                },

                secondary : {
                    name : dummytext("Footnote"),
                    x : 0,
                    y : 0.6,
                    w : 1,
                    h : 1,
                    fontSizeAdjust : 0.5
                }
            },

            width : 1,

            height : 1,

            // Transitions for this particular text style
            transitions : {
                transitionIn : {
                    type : "falltop",
                    duration : 3
                },
                transitionOut : {
                    type : "fallbottom",
                    duration : 3
                },
                onScreen : {
                    type : "slightrotatez"
                }
            }

        },

        {
            id : "plain",

            name : dummytext("Plain"),

            labels : {},

            width : 1,

            height : 1

        },

        {
            id : "postcard",

            name : dummytext("Postcard"),

            labels : {},

            width : 0,

            height : 0

        },

        {
            id : "postit",

            imageName :  "postit.png",

            name : dummytext("Post It"),

            labels : {

                text : {
                    name : dummytext("Text"),
                    fontSizeAdjust : 0.9
                }

            },

            width : 1,

            height : 1,

            baseScale : 0.8,

            textBorder : false,

            textStyles : {
                "vertical-align" : "middle",
                "text-align" : "center"
            }

        }
    ];
};

    return textdefinitions;

});
<commit_msg>Some remarks about bg material licenses<commit_after>/*global define*/

/**
 * Define text shapes we can have in the show.
 */
define("krusovice/showobjects/textdefinitions", ["krusovice/showobjects"], function(showobjects) {

    "use strict";

    var textdefinitions = {};

    // Spoof get text for now
    function dummytext(msg) {
        return msg;
    }

    textdefinitions.getDefinitions = function() {

    return [
        {
            id : "clear",

            name : dummytext("Clear"),

            labels : {
                text : {
                    name : dummytext("Text"),
                    x : 0,
                    y : 0,
                    w : 1,
                    h : 1
                }
            },

            // Background image name (if used)
            imageName : null,

            // Allow to move text around
            position : true,

            // No background color
            clear : true,

            // Render frame border
            border : false,

            textBorder : true,

            width : 1,
            height : 1,

            // Transitions for this particular text style
            transitions : {
                transitionIn : {
                    type : "fade",
                    duration : 1
                },
                transitionOut : {
                    type : "fade",
                    duration : 1
                },
                onScreen : {
                    type : "hold"
                }
            }
        },

        // Scanned by Mikko - public domain
        {
            id : "notepad",

            imageName :  "notepad.png",

            name : dummytext("Notepad"),

            labels : {

                text : {
                    name : dummytext("Text"),
                    x : 0,
                    y : 0.1,
                    w : 1,
                    h : 0.6,
                    fontSizeAdjust : 1
                },

                secondary : {
                    name : dummytext("Footnote"),
                    x : 0,
                    y : 0.6,
                    w : 1,
                    h : 1,
                    fontSizeAdjust : 0.5
                }
            },

            width : 1,

            height : 1,

            // Transitions for this particular text style
            transitions : {
                transitionIn : {
                    type : "falltop",
                    duration : 3
                },
                transitionOut : {
                    type : "fallbottom",
                    duration : 3
                },
                onScreen : {
                    type : "slightrotatez"
                }
            }

        },

        {
            id : "plain",

            name : dummytext("Plain"),

            labels : {},

            width : 1,

            height : 1

        },

        {
            id : "postcard",

            name : dummytext("Postcard"),

            labels : {},

            width : 0,

            height : 0

        },

        // CC-BY-SA
        // http://commons.wikimedia.org/wiki/File:Post-it-note-transparent.png
        {
            id : "postit",

            imageName :  "postit.png",

            name : dummytext("Post It"),

            labels : {

                text : {
                    name : dummytext("Text"),
                    fontSizeAdjust : 0.9
                }

            },

            width : 1,

            height : 1,

            baseScale : 0.8,

            textBorder : false,

            textStyles : {
                "vertical-align" : "middle",
                "text-align" : "center"
            }

        }
    ];
};

    return textdefinitions;

});
<|endoftext|>


<commit_before>(function(window) {
    'use strict';

    angular.module('openfin.tearout')
        .directive('tearable', ['geometryService', 'hoverService', 'currentWindowService',
            (geometryService, hoverService, currentWindowService) => {
                return {
                    restrict: 'C',
                    link: (scope, element, attrs) => {
                        // Finding the tear element is tightly coupled to the HTML layout.
                        var dragElement = element[0],
                            tearElement = dragElement.parentNode.parentNode,
                            tileWidth = tearElement.clientWidth || 230,
                            tileHeight = tearElement.clientHeight || 100,
                            store;

                        var windowService = window.windowService;
                        var tearoutWindow = windowService.createTearoutWindow(window.name);
                        var me = {};

                        function initialiseTearout() {
                            var myDropTarget = tearElement.parentNode,
                                parent = myDropTarget.parentNode,
                                myHoverArea = parent.getElementsByClassName('hover-area')[0],
                                offset = { x: 0, y: 0 },
                                currentlyDragging = false,
                                insideMainWindow = true,
                                dragService;

                            hoverService.add(myHoverArea, scope.stock.code);

                            // The distance from where the mouse click occurred from the origin of the element that will be torn out.
                            // This is to place the tearout window exactly over the tornout element
                            me.setOffset = (x, y) => {
                                offset.x = x;
                                offset.y = y;

                                return me;
                            };

                            // Sets whether the tearout window is being dragged.
                            // Used to determine whether `mousemove` events should programmatically move the tearout window
                            me.setCurrentlyDragging = (dragging) => {
                                currentlyDragging = dragging;

                                return me;
                            };

                            // A call to the OpenFin API to move the tearout window
                            me.moveTearoutWindow = (x, y) => {
                                var tileTopPadding = 5,
                                    tileRightPadding = 5,
                                    tearElementWidth = 16;

                                tearoutWindow.moveTo(
                                    x - tileWidth + (tearElementWidth - offset.x + tileRightPadding),
                                    y - (tileTopPadding + offset.y));

                                return me;
                            };

                            // A call to the OpenFin API to both show the tearout window and ensure that
                            // it is displayed in the foreground
                            me.displayTearoutWindow = () => {
                                tearoutWindow.show();
                                tearoutWindow.setAsForeground();

                                return me;
                            };

                            // Inject the element being tornout into the new, tearout, window
                            me.appendToOpenfinWindow = (injection, openfinWindow) => {
                                openfinWindow
                                    .contentWindow
                                    .document
                                    .body
                                    .appendChild(injection);

                                return me;
                            };

                            // Grab the DOM element back from the tearout window and append the given container
                            me.returnFromTearout = () => {
                                myDropTarget.appendChild(tearElement);
                                tearoutWindow.hide();
                            };

                            // Clear out all the elements but keep the js context ;)
                            me.clearIncomingTearoutWindow = () => {
                                tearoutWindow
                                    .getNativeWindow()
                                    .document
                                    .body = tearoutWindow
                                    .getNativeWindow()
                                    .document.createElement('body');

                                return me;
                            };

                            // On a mousedown event, we grab our destination tearout window and inject
                            // the DOM element to be torn out.
                            //
                            // `handleMouseDown` is the function assigned to the native `mousedown`
                            // event on the element to be torn out. The param `e` is the native event
                            // passed in by the event listener. The steps taken are as follows:
                            // * Set the X and Y offsets to better position the tearout window
                            // * Move the tearout window into position
                            // * Clear out any DOM elements that may already be in the tearout window
                            // * Move the DOM element to be torn out into the tearout
                            // * Display the tearout window in the foreground
                            me.handleMouseDown = (e) => {
                                if (e.button !== 0) {
                                    // Only process left clicks
                                    return false;
                                }

                                if (dragElement.classList.contains('single')) {
                                    // There is only one favourite card so don't allow tearing out
                                    return false;
                                }

                                dragService = windowService.registerDrag(tearoutWindow);

                                me.setCurrentlyDragging(true)
                                    .setOffset(e.offsetX, e.offsetY)
                                    .moveTearoutWindow(e.screenX, e.screenY)
                                    .clearIncomingTearoutWindow()
                                    .appendToOpenfinWindow(tearElement, tearoutWindow)
                                    .displayTearoutWindow();
                            };

                            // On a mousemove event, if we are in a dragging state, move the torn out window programmatically.
                            //
                            // `handleMouseMove` is the function assigned to the `mousemove` event on the `document`.
                            // The param `e` is the native event passed in by the event listener.
                            // If the `currentlyDragging` flag is true move the tearout window.
                            me.handleMouseMove = (e) => {
                                if (currentlyDragging) {
                                    me.moveTearoutWindow(e.screenX, e.screenY);
                                }
                            };

                            // On a mouseup event we reset the internal state to be ready for the next dragging event
                            //
                            // `handleMouseUp` is the function assigned to the `mouseup` event on the `document`.
                            me.handleMouseUp = (e) => {
                                if (e.button !== 0) {
                                    // Only process left clicks
                                    return false;
                                }

                                if (currentlyDragging) {
                                    me.setCurrentlyDragging(false);
                                    if (insideMainWindow) {
                                        me.returnFromTearout();
                                    } else {
                                        if (!store) {
                                            store = window.storeService.open(window.name);
                                        }

                                        // Remove the stock from the old window
                                        store.remove(scope.stock);

                                        dragService.overAnotherInstance((overAnotherInstance) => {
                                            if (overAnotherInstance) {
                                                dragService.moveToOtherInstance(scope.stock);
                                                dragService = null;
                                            } else {
                                                // Create new window instance
                                                var mainApplicationWindowPosition = geometryService.getWindowPosition(window);

                                                var compact = store.isCompact();
                                                windowService.createMainWindow(null, compact, (newWindow) => {
                                                    newWindow.resizeTo(mainApplicationWindowPosition.width, mainApplicationWindowPosition.height, 'top-left');
                                                    newWindow.moveTo(e.screenX, e.screenY);
                                                    var newStore = window.storeService.open(newWindow.name);
                                                    newStore.add(scope.stock);
                                                    newStore.toggleCompact(compact);
                                                });
                                            }

                                            // Remove drop-target from original instance
                                            parent.removeChild(myHoverArea);
                                            parent.removeChild(myDropTarget);
                                            dispose();

                                            // Destroy myself.
                                            tearoutWindow.close();
                                        });
                                    }
                                }
                            };

                            function reorderFavourites(tearoutRectangle) {
                                var hoverTargets = hoverService.get();

                                for (var i = 0, max = hoverTargets.length; i < max; i++) {
                                    var dropTargetRectangle = geometryService.rectangle(
                                        geometryService.elementScreenPosition(geometryService.getWindowPosition(window), hoverTargets[i].hoverArea)),
                                        overDropTarget = tearoutRectangle.intersects(dropTargetRectangle);

                                    if (overDropTarget) {
                                        if (!store) {
                                            store = window.storeService.open(window.name);
                                        }

                                        store.reorder(scope.stock.code, hoverTargets[i].code);
                                        break;
                                    }
                                }
                            }

                            me.boundsChangingEvent = () => {
                                // Check if you are over a drop target by seeing if the tearout rectangle intersects the drop target
                                var nativeWindow = tearoutWindow.getNativeWindow(),
                                    tearoutRectangle = geometryService.rectangle(geometryService.getWindowPosition(nativeWindow));
                                insideMainWindow = geometryService.windowsIntersect(tearoutWindow, window);

                                if (insideMainWindow) {
                                    reorderFavourites(tearoutRectangle);
                                }
                            };

                            // On the `bounds-changing` event check to see if you are over a potential drop target.
                            // If so update the drop target.
                            tearoutWindow.addEventListener('bounds-changing', me.boundsChangingEvent);

                            dragElement.addEventListener('mousedown', me.handleMouseDown);
                            document.addEventListener('mousemove', me.handleMouseMove, true);
                            document.addEventListener('mouseup', me.handleMouseUp, true);
                        }

                        function dispose() {
                            hoverService.remove(scope.stock.code);
                            tearoutWindow.removeEventListener('bounds-changing', me.boundsChangingEvent);
                            dragElement.removeEventListener('mousedown', me.handleMouseDown);
                            document.removeEventListener('mousemove', me.handleMouseMove);
                            document.removeEventListener('mouseup', me.handleMouseUp);
                        }

                        scope.$on('$destroy', () => {
                            dispose();
                        });

                        initialiseTearout();
                    }
                };
            }]);
}(window));
<commit_msg>Refactor tearout<commit_after>(function(window) {
    'use strict';

    angular.module('openfin.tearout')
        .directive('tearable', ['geometryService', 'hoverService', 'currentWindowService',
            (geometryService, hoverService, currentWindowService) => {
                return {
                    restrict: 'C',
                    link: (scope, element, attrs) => {
                        // Finding the tear element is tightly coupled to the HTML layout.
                        var dragElement = element[0],
                            tearElement = dragElement.parentNode.parentNode,
                            tileWidth = tearElement.clientWidth || 230,
                            tileHeight = tearElement.clientHeight || 100,
                            store;

                        var windowService = window.windowService;
                        var tearoutWindow = windowService.createTearoutWindow(window.name);

                        var myDropTarget = tearElement.parentNode,
                            parent = myDropTarget.parentNode,
                            myHoverArea = parent.getElementsByClassName('hover-area')[0],
                            offset = { x: 0, y: 0 },
                            currentlyDragging = false,
                            insideMainWindow = true,
                            dragService;

                        hoverService.add(myHoverArea, scope.stock.code);

                        // The distance from where the mouse click occurred from the origin of the element that will be torn out.
                        // This is to place the tearout window exactly over the tornout element
                        function setOffset(x, y) {
                            offset.x = x;
                            offset.y = y;
                        }

                        // Sets whether the tearout window is being dragged.
                        // Used to determine whether `mousemove` events should programmatically move the tearout window
                        function setCurrentlyDragging(dragging) {
                            currentlyDragging = dragging;
                        }

                        // A call to the OpenFin API to move the tearout window
                        function moveTearoutWindow(x, y) {
                            var tileTopPadding = 5,
                                tileRightPadding = 5,
                                tearElementWidth = 16;

                            tearoutWindow.moveTo(
                                x - tileWidth + (tearElementWidth - offset.x + tileRightPadding),
                                y - (tileTopPadding + offset.y));
                        }

                        // A call to the OpenFin API to both show the tearout window and ensure that
                        // it is displayed in the foreground
                        function displayTearoutWindow() {
                            tearoutWindow.show();
                            tearoutWindow.setAsForeground();
                        }

                        // Inject the element being tornout into the new, tearout, window
                        function appendToOpenfinWindow(injection, openfinWindow) {
                            openfinWindow
                                .contentWindow
                                .document
                                .body
                                .appendChild(injection);
                        }

                        // Grab the DOM element back from the tearout window and append the given container
                        function returnFromTearout() {
                            myDropTarget.appendChild(tearElement);
                            tearoutWindow.hide();
                        }

                        // Clear out all the elements but keep the js context ;)
                        function clearIncomingTearoutWindow() {
                            tearoutWindow
                                .getNativeWindow()
                                .document
                                .body = tearoutWindow
                                .getNativeWindow()
                                .document.createElement('body');
                        }

                        // On a mousedown event, we grab our destination tearout window and inject
                        // the DOM element to be torn out.
                        //
                        // `handleMouseDown` is the function assigned to the native `mousedown`
                        // event on the element to be torn out. The param `e` is the native event
                        // passed in by the event listener. The steps taken are as follows:
                        // * Set the X and Y offsets to better position the tearout window
                        // * Move the tearout window into position
                        // * Clear out any DOM elements that may already be in the tearout window
                        // * Move the DOM element to be torn out into the tearout
                        // * Display the tearout window in the foreground
                        function handleMouseDown(e) {
                            if (e.button !== 0) {
                                // Only process left clicks
                                return false;
                            }

                            if (dragElement.classList.contains('single')) {
                                // There is only one favourite card so don't allow tearing out
                                return false;
                            }

                            dragService = windowService.registerDrag(tearoutWindow);

                            setCurrentlyDragging(true);
                            setOffset(e.offsetX, e.offsetY);
                            moveTearoutWindow(e.screenX, e.screenY);
                            clearIncomingTearoutWindow();
                            appendToOpenfinWindow(tearElement, tearoutWindow);
                            displayTearoutWindow();
                        }

                        // On a mousemove event, if we are in a dragging state, move the torn out window programmatically.
                        //
                        // `handleMouseMove` is the function assigned to the `mousemove` event on the `document`.
                        // The param `e` is the native event passed in by the event listener.
                        // If the `currentlyDragging` flag is true move the tearout window.
                        function handleMouseMove(e) {
                            if (currentlyDragging) {
                                moveTearoutWindow(e.screenX, e.screenY);
                            }
                        }

                        // On a mouseup event we reset the internal state to be ready for the next dragging event
                        //
                        // `handleMouseUp` is the function assigned to the `mouseup` event on the `document`.
                        function handleMouseUp(e) {
                            if (e.button !== 0) {
                                // Only process left clicks
                                return false;
                            }

                            if (currentlyDragging) {
                                setCurrentlyDragging(false);
                                if (insideMainWindow) {
                                    returnFromTearout();
                                } else {
                                    if (!store) {
                                        store = window.storeService.open(window.name);
                                    }

                                    // Remove the stock from the old window
                                    store.remove(scope.stock);

                                    dragService.overAnotherInstance((overAnotherInstance) => {
                                        if (overAnotherInstance) {
                                            dragService.moveToOtherInstance(scope.stock);
                                            dragService = null;
                                        } else {
                                            // Create new window instance
                                            var mainApplicationWindowPosition = geometryService.getWindowPosition(window);

                                            var compact = store.isCompact();
                                            windowService.createMainWindow(null, compact, (newWindow) => {
                                                newWindow.resizeTo(mainApplicationWindowPosition.width, mainApplicationWindowPosition.height, 'top-left');
                                                newWindow.moveTo(e.screenX, e.screenY);
                                                var newStore = window.storeService.open(newWindow.name);
                                                newStore.add(scope.stock);
                                                newStore.toggleCompact(compact);
                                            });
                                        }

                                        // Remove drop-target from original instance
                                        parent.removeChild(myHoverArea);
                                        parent.removeChild(myDropTarget);
                                        dispose();

                                        // Destroy myself.
                                        tearoutWindow.close();
                                    });
                                }
                            }
                        }

                        function reorderFavourites(tearoutRectangle) {
                            var hoverTargets = hoverService.get();

                            for (var i = 0, max = hoverTargets.length; i < max; i++) {
                                var dropTargetRectangle = geometryService.rectangle(
                                    geometryService.elementScreenPosition(geometryService.getWindowPosition(window), hoverTargets[i].hoverArea)),
                                    overDropTarget = tearoutRectangle.intersects(dropTargetRectangle);

                                if (overDropTarget) {
                                    if (!store) {
                                        store = window.storeService.open(window.name);
                                    }

                                    store.reorder(scope.stock.code, hoverTargets[i].code);
                                    break;
                                }
                            }
                        }

                        function boundsChangingEvent() {
                            // Check if you are over a drop target by seeing if the tearout rectangle intersects the drop target
                            var nativeWindow = tearoutWindow.getNativeWindow(),
                                tearoutRectangle = geometryService.rectangle(geometryService.getWindowPosition(nativeWindow));
                            insideMainWindow = geometryService.windowsIntersect(tearoutWindow, window);

                            if (insideMainWindow) {
                                reorderFavourites(tearoutRectangle);
                            }
                        }

                        // On the `bounds-changing` event check to see if you are over a potential drop target.
                        // If so update the drop target.
                        tearoutWindow.addEventListener('bounds-changing', boundsChangingEvent);

                        dragElement.addEventListener('mousedown', handleMouseDown);
                        document.addEventListener('mousemove', handleMouseMove, true);
                        document.addEventListener('mouseup', handleMouseUp, true);

                        function dispose() {
                            hoverService.remove(scope.stock.code);
                            tearoutWindow.removeEventListener('bounds-changing', boundsChangingEvent);
                            dragElement.removeEventListener('mousedown', handleMouseDown);
                            document.removeEventListener('mousemove', handleMouseMove);
                            document.removeEventListener('mouseup', handleMouseUp);
                        }

                        scope.$on('$destroy', () => {
                            dispose();
                        });
                    }
                };
            }]);
}(window));
<|endoftext|>


<commit_before>/*
  Render each OSMD sample, grab the generated images, and
  dump them into a local directory as PNG files.

  inspired by Vexflow's generate_png_images and vexflow-tests.js

  This can be used to generate PNGs from OSMD without a browser.
  It's also used with the visual regression test system in
  `tools/visual_regression.sh`.

  Note: this script needs to "fake" quite a few browser elements, like window, document, and a Canvas HTMLElement.
  For that it needs the canvas package installed.
  There are also some hacks needed to set the container size (offsetWidth) correctly.

  Otherwise you'd need to run a headless browser, which is way slower,
  see the semi-obsolete generateDiffImagesPuppeteerLocalhost.js
*/

function sleep (ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms)
    })
}

async function init () {
    console.log('[OSMD.generate] init')

    let [osmdBuildDir, sampleDir, imageDir, pageWidth, pageHeight, filterRegex, debugFlag, debugSleepTimeString] = process.argv.slice(2, 10)
    if (!osmdBuildDir || !sampleDir || !imageDir) {
        console.log('usage: ' +
            'node test/Util/generateImages_browserless.js osmdBuildDir sampleDirectory imageDirectory [width|0] [height|0] [filterRegex|all|allSmall] [--debug] [debugSleepTime]')
        console.log('  (use "all" to skip filterRegex parameter. "allSmall" skips two huge OSMD samples that take forever to render)')
        console.log('example: node test/Util/generateImages_browserless.js ../../build ./test/data/ ./export 210 297 allSmall --debug 5000')
        console.log('Error: need osmdBuildDir, sampleDir and imageDir. Exiting.')
        process.exit(1)
    }

    const DEBUG = debugFlag === '--debug'
    // const debugSleepTime = Number.parseInt(process.env.GENERATE_DEBUG_SLEEP_TIME) || 0; // 5000 works for me [sschmidTU]
    if (DEBUG) {
        console.log(' (note that --debug slows down the script by about 0.3s per file, through logging)')
        const debugSleepTimeMs = Number.parseInt(debugSleepTimeString)
        if (debugSleepTimeMs > 0) {
            console.log('debug sleep time: ' + debugSleepTimeString)
            await sleep(Number.parseInt(debugSleepTimeMs))
            // [VSCode] apparently this is necessary for the debugger to attach itself in time before the program closes.
            // sometimes this is not enough, so you may have to try multiple times or increase the sleep timer. Unfortunately debugging nodejs isn't easy.
        }
    }
    debug('sampleDir: ' + sampleDir, DEBUG)
    debug('imageDir: ' + imageDir, DEBUG)

    let pageFormat = 'Endless'
    pageWidth = Number.parseInt(pageWidth)
    pageHeight = Number.parseInt(pageHeight)
    const endlessPage = !(pageHeight > 0 && pageWidth > 0)
    if (!endlessPage) {
        pageFormat = `${pageWidth}x${pageHeight}`
    }

    // ---- hacks to fake Browser elements OSMD and Vexflow need, like window, document, and a canvas HTMLElement ----
    const { JSDOM } = require('jsdom')
    const dom = new JSDOM('<!DOCTYPE html>')
    // eslint-disable-next-line no-global-assign
    window = dom.window
    // eslint-disable-next-line no-global-assign
    document = dom.window.document

    // eslint-disable-next-line no-global-assign
    global.window = dom.window
    // eslint-disable-next-line no-global-assign
    global.document = window.document
    global.HTMLElement = window.HTMLElement
    global.HTMLAnchorElement = window.HTMLAnchorElement
    global.XMLHttpRequest = window.XMLHttpRequest
    global.DOMParser = window.DOMParser
    global.Node = window.Node
    global.Canvas = window.Canvas

    // fix Blob not found (to support external modules like is-blob)
    global.Blob = require('cross-blob')

    const div = document.createElement('div')
    div.id = 'browserlessDiv'
    document.body.appendChild(div)
    // const canvas = document.createElement('canvas')
    // div.canvas = document.createElement('canvas')

    const zoom = 1.0
    // width of the div / PNG generated
    let width = pageWidth * zoom
    // TODO sometimes the width is way too small for the score, may need to adjust zoom.
    if (endlessPage) {
        width = 1440
    }
    let height = pageHeight
    if (endlessPage) {
        height = 32767
    }
    div.width = width
    div.height = height
    div.offsetWidth = width // doesn't work, offsetWidth is always 0 from this. see below
    div.clientWidth = width
    div.clientHeight = height
    div.scrollHeight = height
    div.scrollWidth = width
    div.setAttribute('width', width)
    div.setAttribute('height', height)
    div.setAttribute('offsetWidth', width)
    debug('div.offsetWidth: ' + div.offsetWidth, DEBUG)
    debug('div.height: ' + div.height, DEBUG)

    // hack: set offsetWidth reliably
    Object.defineProperties(window.HTMLElement.prototype, {
        offsetLeft: {
            get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
        },
        offsetTop: {
            get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
        },
        offsetHeight: {
            get: function () { return height }
        },
        offsetWidth: {
            get: function () { return width }
        }
    })
    debug('div.offsetWidth: ' + div.offsetWidth, DEBUG)
    debug('div.height: ' + div.height, DEBUG)
    if (!DEBUG) {
        // deactivate console logs (mostly)
        console.log = (msg) => {}
    }
    // ---- end browser hacks (hopefully) ----

    const OSMD = require(`${osmdBuildDir}/opensheetmusicdisplay.min.js`)

    const fs = require('fs')
    // Create the image directory if it doesn't exist.
    fs.mkdirSync(imageDir, { recursive: true })

    const sampleDirFilenames = fs.readdirSync(sampleDir)
    let samplesToProcess = [] // samples we want to process/generate pngs of, excluding the filtered out files/filenames
    for (const sampleFilename of sampleDirFilenames) {
        if (filterRegex === 'allSmall') {
            if (sampleFilename.match('^(Actor)|(Gounod)')) { // TODO maybe filter by file size instead
                debug('filtering big file: ' + sampleFilename, DEBUG)
                continue
            }
        }
        // eslint-disable-next-line no-useless-escape
        if (sampleFilename.match('^.*(\.xml)|(\.musicxml)|(\.mxl)$')) {
            // console.log('found musicxml/mxl: ' + sampleFilename)
            samplesToProcess.push(sampleFilename)
        } else {
            debug('discarded file/directory: ' + sampleFilename, DEBUG)
        }
    }

    // filter samples to process by regex if given
    if (filterRegex && filterRegex !== '' && filterRegex !== 'all' && filterRegex !== 'allSmall') {
        debug('filtering samples for regex: ' + filterRegex, DEBUG)
        samplesToProcess = samplesToProcess.filter((filename) => filename.match(filterRegex))
        debug(`found ${samplesToProcess.length} matches: `, DEBUG)
        for (let i = 0; i < samplesToProcess.length; i++) {
            debug(samplesToProcess[i], DEBUG)
        }
    }

    const osmdInstance = new OSMD.OpenSheetMusicDisplay(div, {
        autoResize: false,
        backend: 'canvas',
        pageBackgroundColor: '#FFFFFF',
        pageFormat: pageFormat
        // defaultFontFamily: 'Arial',
        // drawTitle: false
    })
    // for more options check OSMDOptions.ts

    // you can set finer-grained rendering/engraving settings in EngravingRules:
    osmdInstance.EngravingRules.TitleTopDistance = 5.0 // 9.0 is default
    osmdInstance.EngravingRules.PageTopMargin = 5.0 // 5 is default
    osmdInstance.EngravingRules.PageBottomMargin = 5.0 // 5 is default. <5 can cut off scores that extend in the last staffline
    // note that for now the png and canvas will still have the height given in the script argument,
    //   so even with a margin of 0 the image will be filled to the full height.
    osmdInstance.EngravingRules.PageLeftMargin = 5.0 // 5 is default
    osmdInstance.EngravingRules.PageRightMargin = 5.0 // 5 is default
    // osmdInstance.EngravingRules.MetronomeMarkXShift = -8; // -6 is default
    // osmdInstance.EngravingRules.DistanceBetweenVerticalSystemLines = 0.15; // 0.35 is default
    // for more options check EngravingRules.ts (though not all of these are meant and fully supported to be changed at will)

    // await sleep(5000)
    if (DEBUG) {
        osmdInstance.setLogLevel('debug')
        // console.log(`osmd PageFormat: ${osmdInstance.EngravingRules.PageFormat.width}x${osmdInstance.EngravingRules.PageFormat.height}`)
        console.log(`osmd PageFormat idString: ${osmdInstance.EngravingRules.PageFormat.idString}`)
        console.log('PageHeight: ' + osmdInstance.EngravingRules.PageHeight)
    } else {
        osmdInstance.setLogLevel('info') // doesn't seem to work, log.debug still logs
    }

    debug('generateImages', DEBUG)
    for (let i = 0; i < samplesToProcess.length; i++) {
        var sampleFilename = samplesToProcess[i]
        debug('sampleFilename: ' + sampleFilename, DEBUG)

        let loadParameter = fs.readFileSync(sampleDir + '/' + sampleFilename)
        if (sampleFilename.endsWith('.mxl')) {
            loadParameter = await OSMD.MXLHelper.MXLtoXMLstring(loadParameter)
        } else {
            loadParameter = loadParameter.toString()
        }
        // console.log('loadParameter: ' + loadParameter)
        // console.log('typeof loadParameter: ' + typeof loadParameter)

        await osmdInstance.load(loadParameter).then(function () {
            debug('xml loaded', DEBUG)
            try {
                osmdInstance.render()
            } catch (ex) {
                console.log('renderError: ' + ex)
            }
            debug('rendered', DEBUG)

            const dataUrls = []
            let canvasImage

            for (let pageNumber = 1; pageNumber < 999; pageNumber++) {
                canvasImage = document.getElementById('osmdCanvasVexFlowBackendCanvas' + pageNumber)
                if (!canvasImage) {
                    break
                }
                if (!canvasImage.toDataURL) {
                    console.log(`error: could not get canvas image for page ${pageNumber} for file: ${sampleFilename}`)
                    break
                }
                dataUrls.push(canvasImage.toDataURL())
            }
            for (let urlIndex = 0; urlIndex < dataUrls.length; urlIndex++) {
                const pageNumberingString = `_${urlIndex + 1}`
                // pageNumberingString = dataUrls.length > 0 ? pageNumberingString : '' // don't put '_1' at the end if only one page. though that may cause more work
                var pageFilename = `${imageDir}/${sampleFilename}${pageNumberingString}.png`

                const dataUrl = dataUrls[urlIndex]
                if (!dataUrl || !dataUrl.split) {
                    console.log(`error: could not get dataUrl (imageData) for page ${urlIndex + 1} of sample: ${sampleFilename}`)
                    continue
                }
                const imageData = dataUrl.split(';base64,').pop()
                const imageBuffer = Buffer.from(imageData, 'base64')

                debug('got image data, saving to: ' + pageFilename, DEBUG)
                fs.writeFileSync(pageFilename, imageBuffer, { encoding: 'base64' })
            }
        }) // end render then
        //     },
        //     function (e) {
        //         console.log('error while rendering: ' + e)
        //     }) // end load then
        // }) // end read file
    }

    console.log('[OSMD.generate_browserless] exit')
}

function debug (msg, debugEnabled) {
    if (debugEnabled) {
        console.log(msg)
    }
}

init()
<commit_msg>visual testing: set sample-specific options. breaks current blessed images. #676<commit_after>/*
  Render each OSMD sample, grab the generated images, and
  dump them into a local directory as PNG files.

  inspired by Vexflow's generate_png_images and vexflow-tests.js

  This can be used to generate PNGs from OSMD without a browser.
  It's also used with the visual regression test system in
  `tools/visual_regression.sh`.

  Note: this script needs to "fake" quite a few browser elements, like window, document, and a Canvas HTMLElement.
  For that it needs the canvas package installed.
  There are also some hacks needed to set the container size (offsetWidth) correctly.

  Otherwise you'd need to run a headless browser, which is way slower,
  see the semi-obsolete generateDiffImagesPuppeteerLocalhost.js
*/

function sleep (ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms)
    })
}

async function init () {
    console.log('[OSMD.generate] init')

    let [osmdBuildDir, sampleDir, imageDir, pageWidth, pageHeight, filterRegex, debugFlag, debugSleepTimeString] = process.argv.slice(2, 10)
    if (!osmdBuildDir || !sampleDir || !imageDir) {
        console.log('usage: ' +
            'node test/Util/generateImages_browserless.js osmdBuildDir sampleDirectory imageDirectory [width|0] [height|0] [filterRegex|all|allSmall] [--debug] [debugSleepTime]')
        console.log('  (use "all" to skip filterRegex parameter. "allSmall" skips two huge OSMD samples that take forever to render)')
        console.log('example: node test/Util/generateImages_browserless.js ../../build ./test/data/ ./export 210 297 allSmall --debug 5000')
        console.log('Error: need osmdBuildDir, sampleDir and imageDir. Exiting.')
        process.exit(1)
    }

    const DEBUG = debugFlag === '--debug'
    // const debugSleepTime = Number.parseInt(process.env.GENERATE_DEBUG_SLEEP_TIME) || 0; // 5000 works for me [sschmidTU]
    if (DEBUG) {
        console.log(' (note that --debug slows down the script by about 0.3s per file, through logging)')
        const debugSleepTimeMs = Number.parseInt(debugSleepTimeString)
        if (debugSleepTimeMs > 0) {
            console.log('debug sleep time: ' + debugSleepTimeString)
            await sleep(Number.parseInt(debugSleepTimeMs))
            // [VSCode] apparently this is necessary for the debugger to attach itself in time before the program closes.
            // sometimes this is not enough, so you may have to try multiple times or increase the sleep timer. Unfortunately debugging nodejs isn't easy.
        }
    }
    debug('sampleDir: ' + sampleDir, DEBUG)
    debug('imageDir: ' + imageDir, DEBUG)

    let pageFormat = 'Endless'
    pageWidth = Number.parseInt(pageWidth)
    pageHeight = Number.parseInt(pageHeight)
    const endlessPage = !(pageHeight > 0 && pageWidth > 0)
    if (!endlessPage) {
        pageFormat = `${pageWidth}x${pageHeight}`
    }

    // ---- hacks to fake Browser elements OSMD and Vexflow need, like window, document, and a canvas HTMLElement ----
    const { JSDOM } = require('jsdom')
    const dom = new JSDOM('<!DOCTYPE html>')
    // eslint-disable-next-line no-global-assign
    window = dom.window
    // eslint-disable-next-line no-global-assign
    document = dom.window.document

    // eslint-disable-next-line no-global-assign
    global.window = dom.window
    // eslint-disable-next-line no-global-assign
    global.document = window.document
    global.HTMLElement = window.HTMLElement
    global.HTMLAnchorElement = window.HTMLAnchorElement
    global.XMLHttpRequest = window.XMLHttpRequest
    global.DOMParser = window.DOMParser
    global.Node = window.Node
    global.Canvas = window.Canvas

    // fix Blob not found (to support external modules like is-blob)
    global.Blob = require('cross-blob')

    const div = document.createElement('div')
    div.id = 'browserlessDiv'
    document.body.appendChild(div)
    // const canvas = document.createElement('canvas')
    // div.canvas = document.createElement('canvas')

    const zoom = 1.0
    // width of the div / PNG generated
    let width = pageWidth * zoom
    // TODO sometimes the width is way too small for the score, may need to adjust zoom.
    if (endlessPage) {
        width = 1440
    }
    let height = pageHeight
    if (endlessPage) {
        height = 32767
    }
    div.width = width
    div.height = height
    div.offsetWidth = width // doesn't work, offsetWidth is always 0 from this. see below
    div.clientWidth = width
    div.clientHeight = height
    div.scrollHeight = height
    div.scrollWidth = width
    div.setAttribute('width', width)
    div.setAttribute('height', height)
    div.setAttribute('offsetWidth', width)
    debug('div.offsetWidth: ' + div.offsetWidth, DEBUG)
    debug('div.height: ' + div.height, DEBUG)

    // hack: set offsetWidth reliably
    Object.defineProperties(window.HTMLElement.prototype, {
        offsetLeft: {
            get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
        },
        offsetTop: {
            get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
        },
        offsetHeight: {
            get: function () { return height }
        },
        offsetWidth: {
            get: function () { return width }
        }
    })
    debug('div.offsetWidth: ' + div.offsetWidth, DEBUG)
    debug('div.height: ' + div.height, DEBUG)
    if (!DEBUG) {
        // deactivate console logs (mostly)
        console.log = (msg) => {}
    }
    // ---- end browser hacks (hopefully) ----

    const OSMD = require(`${osmdBuildDir}/opensheetmusicdisplay.min.js`)

    const fs = require('fs')
    // Create the image directory if it doesn't exist.
    fs.mkdirSync(imageDir, { recursive: true })

    const sampleDirFilenames = fs.readdirSync(sampleDir)
    let samplesToProcess = [] // samples we want to process/generate pngs of, excluding the filtered out files/filenames
    for (const sampleFilename of sampleDirFilenames) {
        if (filterRegex === 'allSmall') {
            if (sampleFilename.match('^(Actor)|(Gounod)')) { // TODO maybe filter by file size instead
                debug('filtering big file: ' + sampleFilename, DEBUG)
                continue
            }
        }
        // eslint-disable-next-line no-useless-escape
        if (sampleFilename.match('^.*(\.xml)|(\.musicxml)|(\.mxl)$')) {
            // console.log('found musicxml/mxl: ' + sampleFilename)
            samplesToProcess.push(sampleFilename)
        } else {
            debug('discarded file/directory: ' + sampleFilename, DEBUG)
        }
    }

    // filter samples to process by regex if given
    if (filterRegex && filterRegex !== '' && filterRegex !== 'all' && filterRegex !== 'allSmall') {
        debug('filtering samples for regex: ' + filterRegex, DEBUG)
        samplesToProcess = samplesToProcess.filter((filename) => filename.match(filterRegex))
        debug(`found ${samplesToProcess.length} matches: `, DEBUG)
        for (let i = 0; i < samplesToProcess.length; i++) {
            debug(samplesToProcess[i], DEBUG)
        }
    }

    const osmdInstance = new OSMD.OpenSheetMusicDisplay(div, {
        autoResize: false,
        backend: 'canvas',
        pageBackgroundColor: '#FFFFFF',
        pageFormat: pageFormat
        // defaultFontFamily: 'Arial',
        // drawTitle: false
    })
    // for more options check OSMDOptions.ts

    // you can set finer-grained rendering/engraving settings in EngravingRules:
    osmdInstance.EngravingRules.TitleTopDistance = 5.0 // 9.0 is default
    osmdInstance.EngravingRules.PageTopMargin = 5.0 // 5 is default
    osmdInstance.EngravingRules.PageBottomMargin = 5.0 // 5 is default. <5 can cut off scores that extend in the last staffline
    // note that for now the png and canvas will still have the height given in the script argument,
    //   so even with a margin of 0 the image will be filled to the full height.
    osmdInstance.EngravingRules.PageLeftMargin = 5.0 // 5 is default
    osmdInstance.EngravingRules.PageRightMargin = 5.0 // 5 is default
    // osmdInstance.EngravingRules.MetronomeMarkXShift = -8; // -6 is default
    // osmdInstance.EngravingRules.DistanceBetweenVerticalSystemLines = 0.15; // 0.35 is default
    // for more options check EngravingRules.ts (though not all of these are meant and fully supported to be changed at will)

    // await sleep(5000)
    if (DEBUG) {
        osmdInstance.setLogLevel('debug')
        // console.log(`osmd PageFormat: ${osmdInstance.EngravingRules.PageFormat.width}x${osmdInstance.EngravingRules.PageFormat.height}`)
        console.log(`osmd PageFormat idString: ${osmdInstance.EngravingRules.PageFormat.idString}`)
        console.log('PageHeight: ' + osmdInstance.EngravingRules.PageHeight)
    } else {
        osmdInstance.setLogLevel('info') // doesn't seem to work, log.debug still logs
    }

    debug('generateImages', DEBUG)
    for (let i = 0; i < samplesToProcess.length; i++) {
        var sampleFilename = samplesToProcess[i]
        debug('sampleFilename: ' + sampleFilename, DEBUG)

        let loadParameter = fs.readFileSync(sampleDir + '/' + sampleFilename)
        if (sampleFilename.endsWith('.mxl')) {
            loadParameter = await OSMD.MXLHelper.MXLtoXMLstring(loadParameter)
        } else {
            loadParameter = loadParameter.toString()
        }
        // console.log('loadParameter: ' + loadParameter)
        // console.log('typeof loadParameter: ' + typeof loadParameter)

        // set sample-specific options for OSMD visual regression testing
        // TODO separate OSMD-testing-specific code from a generic image export script for other purposes
        const isFunctionTestAutobeam = sampleFilename.startsWith('OSMD_function_test_autobeam')
        const isFunctionTestAutoColoring = sampleFilename.startsWith('OSMD_function_test_auto-custom-coloring')
        const isFunctionTestSystemAndPageBreaks = sampleFilename.startsWith('OSMD_Function_Test_System_and_Page_Breaks')
        const isFunctionTestDrawingRange = sampleFilename.startsWith('OSMD_function_test_measuresToDraw_')
        osmdInstance.setOptions({
            autoBeam: isFunctionTestAutobeam, // only set to true for function test autobeam
            coloringMode: isFunctionTestAutoColoring ? 2 : 0,
            coloringSetCustom: isFunctionTestAutoColoring ? ['#d82c6b', '#F89D15', '#FFE21A', '#4dbd5c', '#009D96', '#43469d', '#76429c', '#ff0000'] : undefined,
            colorStemsLikeNoteheads: isFunctionTestAutoColoring,
            drawFromMeasureNumber: isFunctionTestDrawingRange ? 9 : 1,
            drawUpToMeasureNumber: isFunctionTestDrawingRange ? 12 : Number.MAX_SAFE_INTEGER,
            newSystemFromXML: isFunctionTestSystemAndPageBreaks,
            newPageFromXML: isFunctionTestSystemAndPageBreaks
        })

        await osmdInstance.load(loadParameter).then(function () {
            debug('xml loaded', DEBUG)
            try {
                osmdInstance.render()
            } catch (ex) {
                console.log('renderError: ' + ex)
            }
            debug('rendered', DEBUG)

            const dataUrls = []
            let canvasImage

            for (let pageNumber = 1; pageNumber < 999; pageNumber++) {
                canvasImage = document.getElementById('osmdCanvasVexFlowBackendCanvas' + pageNumber)
                if (!canvasImage) {
                    break
                }
                if (!canvasImage.toDataURL) {
                    console.log(`error: could not get canvas image for page ${pageNumber} for file: ${sampleFilename}`)
                    break
                }
                dataUrls.push(canvasImage.toDataURL())
            }
            for (let urlIndex = 0; urlIndex < dataUrls.length; urlIndex++) {
                const pageNumberingString = `_${urlIndex + 1}`
                // pageNumberingString = dataUrls.length > 0 ? pageNumberingString : '' // don't put '_1' at the end if only one page. though that may cause more work
                var pageFilename = `${imageDir}/${sampleFilename}${pageNumberingString}.png`

                const dataUrl = dataUrls[urlIndex]
                if (!dataUrl || !dataUrl.split) {
                    console.log(`error: could not get dataUrl (imageData) for page ${urlIndex + 1} of sample: ${sampleFilename}`)
                    continue
                }
                const imageData = dataUrl.split(';base64,').pop()
                const imageBuffer = Buffer.from(imageData, 'base64')

                debug('got image data, saving to: ' + pageFilename, DEBUG)
                fs.writeFileSync(pageFilename, imageBuffer, { encoding: 'base64' })
            }
        }) // end render then
        //     },
        //     function (e) {
        //         console.log('error while rendering: ' + e)
        //     }) // end load then
        // }) // end read file
    }

    console.log('[OSMD.generate_browserless] exit')
}

function debug (msg, debugEnabled) {
    if (debugEnabled) {
        console.log(msg)
    }
}

init()
<|endoftext|>


<commit_before>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * An array containing all HTML5 Audio tags that could be used for individual
         * sound's playback. Number of instances depends on the config value passed
         * to the Loader#audio method call, default is 1.
         *
         * @private
         * @property {HTMLAudioElement[]} tags
         */
        this.tags = manager.game.cache.audio.get(key);
        if (!this.tags) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * Reference to an HTML5 Audio tag used for playing sound.
         *
         * @private
         * @property {HTMLAudioElement} audio
         * @default null
         */
        this.audio = null;
        /**
         * Timestamp as generated by the Request Animation Frame or SetTimeout
         * representing the time at which the delayed sound playback should start.
         * Set to 0 if sound playback is not delayed.
         *
         * @private
         * @property {number} startTime
         * @default 0
         */
        this.startTime = 0;
        /**
         * Audio tag's playback position recorded on previous
         * update method call. Set to 0 if sound is not playing.
         *
         * @private
         * @property {number} previousTime
         * @default 0
         */
        this.previousTime = 0;
        /**
         * A queue of all actions performed on a sound object while audio was locked.
         * Once the audio gets unlocked, after an explicit user interaction,
         * all actions will be performed in chronological order.
         *
         * @private
         * @property {{
         *   sound: Phaser.Sound.HTML5AudioSound,
         *   type: string,
         *   name: string,
         *   value?: any,
         *   time: number,
         * }[]} touchLockedActionQueue
         */
        this.touchLockedActionQueue = manager.touchLocked ? [] : null;
        this.duration = this.tags[0].duration;
        this.totalDuration = this.tags[0].duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (this.checkTouchLocked('method', 'play', [markerName, config])) {
            return false;
        }
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.emit('play', this);
        return true;
    },
    pause: function () {
        if (this.checkTouchLocked('method', 'pause')) {
            return false;
        }
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.audio.currentTime
            - (this.currentMarker ? this.currentMarker.start : 0);
        this.stopAndReleaseAudioTag();
        this.emit('pause', this);
        return true;
    },
    resume: function () {
        if (this.checkTouchLocked('method', 'resume')) {
            return false;
        }
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.emit('resume', this);
        return true;
    },
    stop: function () {
        if (this.checkTouchLocked('method', 'stop')) {
            return false;
        }
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndReleaseAudioTag();
        this.emit('stop', this);
        return true;
    },
    pickAndPlayAudioTag: function () {
        if (!this.pickAudioTag()) {
            this.reset();
            return false;
        }
        var seek = this.currentConfig.seek;
        var delay = this.currentConfig.delay;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        this.previousTime = offset;
        this.audio.currentTime = offset;
        this.applyConfig();
        if (delay === 0) {
            this.startTime = 0;
            if (this.audio.paused) {
                this.audio.play();
            }
        }
        else {
            this.startTime = window.performance.now() + delay * 1000;
            if (!this.audio.paused) {
                this.audio.pause();
            }
        }
        this.resetConfig();
        return true;
    },
    pickAudioTag: function () {
        if (!this.audio) {
            for (var i = 0; i < this.tags.length; i++) {
                var audio = this.tags[i];
                if (audio.dataset.used === 'false') {
                    audio.dataset.used = 'true';
                    this.audio = audio;
                    return true;
                }
            }
            if (!this.manager.override) {
                return false;
            }
            var otherSounds_1 = [];
            this.manager.forEachActiveSound(function (sound) {
                if (sound.key === this.key && sound.audio) {
                    otherSounds_1.push(sound);
                }
            }, this);
            otherSounds_1.sort(function (a1, a2) {
                if (a1.loop === a2.loop) {
                    // sort by progress
                    return (a2.seek / a2.duration) - (a1.seek / a1.duration);
                }
                return a1.loop ? 1 : -1;
            });
            var selectedSound = otherSounds_1[0];
            this.audio = selectedSound.audio;
            selectedSound.reset();
            selectedSound.audio = null;
            selectedSound.startTime = 0;
            selectedSound.previousTime = 0;
        }
        return true;
    },
    stopAndReleaseAudioTag: function () {
        this.audio.pause();
        this.audio.dataset.used = 'false';
        this.audio = null;
        this.startTime = 0;
        this.previousTime = 0;
    },
    reset: function () {
        BaseSound.prototype.stop.call(this);
    },
    onBlur: function () {
        this.isPlaying = false;
        this.isPaused = true;
        this.currentConfig.seek = this.audio.currentTime -
            (this.currentMarker ? this.currentMarker.start : 0);
        this.currentConfig.delay = Math.max(0, (this.startTime - window.performance.now()) / 1000);
        this.stopAndReleaseAudioTag();
    },
    onFocus: function () {
        this.isPlaying = true;
        this.isPaused = false;
        this.pickAndPlayAudioTag();
    },
    update: function (time, delta) {
        if (this.isPlaying) {
            // handling delayed playback
            if (this.startTime > 0) {
                if (this.startTime < time - this.manager.audioPlayDelay) {
                    this.audio.currentTime += Math.max(0, time - this.startTime) / 1000;
                    this.startTime = 0;
                    this.previousTime = this.audio.currentTime;
                    this.audio.play();
                }
                return;
            }
            // handle looping and ending
            var startTime = this.currentMarker ? this.currentMarker.start : 0;
            var endTime = startTime + this.duration;
            var currentTime = this.audio.currentTime;
            if (this.currentConfig.loop) {
                if (currentTime >= endTime - this.manager.loopEndOffset) {
                    this.audio.currentTime = startTime + Math.max(0, currentTime - endTime);
                    currentTime = this.audio.currentTime;
                }
                else if (currentTime < startTime) {
                    this.audio.currentTime += startTime;
                    currentTime = this.audio.currentTime;
                }
                if (currentTime < this.previousTime) {
                    this.emit('looped', this);
                }
            }
            else if (currentTime >= endTime) {
                this.reset();
                this.stopAndReleaseAudioTag();
                this.emit('ended', this);
                return;
            }
            this.previousTime = currentTime;
        }
    },
    destroy: function () {
        BaseSound.prototype.destroy.call(this);
        this.tags = null;
        if (this.audio) {
            this.stopAndReleaseAudioTag();
        }
    },
    setMute: function () {
        if (this.audio) {
            this.audio.muted = this.currentConfig.mute || this.manager.mute;
        }
    },
    setVolume: function () {
        if (this.audio) {
            this.audio.volume = this.currentConfig.volume * this.manager.volume;
        }
    },
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.audio) {
            this.audio.playbackRate = this.totalRate;
        }
    },
    checkTouchLocked: function (type, name, value) {
        if (this.manager.touchLocked) {
            this.touchLockedActionQueue.push({
                sound: this,
                type: type,
                name: name,
                value: value,
                time: window.performance.now()
            });
            return true;
        }
        return false;
    }
});
/**
 * Mute setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#mute
 * @property {boolean} mute
 */
Object.defineProperty(HTML5AudioSound.prototype, 'mute', {
    get: function () {
        return this.currentConfig.mute;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        if (this.checkTouchLocked('property', 'mute', value)) {
            return;
        }
        this.setMute();
        this.emit('mute', this, value);
    }
});
/**
 * Volume setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#volume
 * @property {number} volume
 */
Object.defineProperty(HTML5AudioSound.prototype, 'volume', {
    get: function () {
        return this.currentConfig.volume;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        if (this.checkTouchLocked('property', 'volume', value)) {
            return;
        }
        this.setVolume();
        this.emit('volume', this, value);
    }
});
/**
 * Playback rate.
 *
 * @name Phaser.Sound.HTML5AudioSound#rate
 * @property {number} rate
 */
Object.defineProperty(HTML5AudioSound.prototype, 'rate', {
    get: function () {
        return Object.getOwnPropertyDescriptor(BaseSound.prototype, 'rate').get.call(this);
    },
    set: function (value) {
        this.currentConfig.rate = value;
        if (this.checkTouchLocked('property', 'rate', value)) {
            return;
        }
        Object.getOwnPropertyDescriptor(BaseSound.prototype, 'rate').set.call(this, value);
    }
});
/**
 * Detuning of sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#detune
 * @property {number} detune
 */
Object.defineProperty(HTML5AudioSound.prototype, 'detune', {
    get: function () {
        return Object.getOwnPropertyDescriptor(BaseSound.prototype, 'detune').get.call(this);
    },
    set: function (value) {
        this.currentConfig.detune = value;
        if (this.checkTouchLocked('property', 'detune', value)) {
            return;
        }
        Object.getOwnPropertyDescriptor(BaseSound.prototype, 'detune').set.call(this, value);
    }
});
/**
 * Current position of playing sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#seek
 * @property {number} seek
 */
Object.defineProperty(HTML5AudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.audio.currentTime -
                (this.currentMarker ? this.currentMarker.start : 0);
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.startTime > 0) {
            return;
        }
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            if (this.isPlaying) {
                this.previousTime = value;
                this.audio.currentTime = value;
            }
            else if (this.isPaused) {
                this.currentConfig.seek = value;
            }
            this.emit('seek', this, value);
        }
    }
});
/**
 * Property indicating whether or not
 * the sound or current sound marker will loop.
 *
 * @name Phaser.Sound.HTML5AudioSound#loop
 * @property {boolean} loop
 */
Object.defineProperty(HTML5AudioSound.prototype, 'loop', {
    get: function () {
        return this.currentConfig.loop;
    },
    set: function (value) {
        this.currentConfig.loop = value;
        if (this.audio) {
            this.audio.loop = value;
        }
        this.emit('loop', this, value);
    }
});
module.exports = HTML5AudioSound;
<commit_msg>Using checkTouchLocked with seek property<commit_after>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * An array containing all HTML5 Audio tags that could be used for individual
         * sound's playback. Number of instances depends on the config value passed
         * to the Loader#audio method call, default is 1.
         *
         * @private
         * @property {HTMLAudioElement[]} tags
         */
        this.tags = manager.game.cache.audio.get(key);
        if (!this.tags) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * Reference to an HTML5 Audio tag used for playing sound.
         *
         * @private
         * @property {HTMLAudioElement} audio
         * @default null
         */
        this.audio = null;
        /**
         * Timestamp as generated by the Request Animation Frame or SetTimeout
         * representing the time at which the delayed sound playback should start.
         * Set to 0 if sound playback is not delayed.
         *
         * @private
         * @property {number} startTime
         * @default 0
         */
        this.startTime = 0;
        /**
         * Audio tag's playback position recorded on previous
         * update method call. Set to 0 if sound is not playing.
         *
         * @private
         * @property {number} previousTime
         * @default 0
         */
        this.previousTime = 0;
        /**
         * A queue of all actions performed on a sound object while audio was locked.
         * Once the audio gets unlocked, after an explicit user interaction,
         * all actions will be performed in chronological order.
         *
         * @private
         * @property {{
         *   sound: Phaser.Sound.HTML5AudioSound,
         *   type: string,
         *   name: string,
         *   value?: any,
         *   time: number,
         * }[]} touchLockedActionQueue
         */
        this.touchLockedActionQueue = manager.touchLocked ? [] : null;
        this.duration = this.tags[0].duration;
        this.totalDuration = this.tags[0].duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (this.checkTouchLocked('method', 'play', [markerName, config])) {
            return false;
        }
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.emit('play', this);
        return true;
    },
    pause: function () {
        if (this.checkTouchLocked('method', 'pause')) {
            return false;
        }
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.audio.currentTime
            - (this.currentMarker ? this.currentMarker.start : 0);
        this.stopAndReleaseAudioTag();
        this.emit('pause', this);
        return true;
    },
    resume: function () {
        if (this.checkTouchLocked('method', 'resume')) {
            return false;
        }
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.emit('resume', this);
        return true;
    },
    stop: function () {
        if (this.checkTouchLocked('method', 'stop')) {
            return false;
        }
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndReleaseAudioTag();
        this.emit('stop', this);
        return true;
    },
    pickAndPlayAudioTag: function () {
        if (!this.pickAudioTag()) {
            this.reset();
            return false;
        }
        var seek = this.currentConfig.seek;
        var delay = this.currentConfig.delay;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        this.previousTime = offset;
        this.audio.currentTime = offset;
        this.applyConfig();
        if (delay === 0) {
            this.startTime = 0;
            if (this.audio.paused) {
                this.audio.play();
            }
        }
        else {
            this.startTime = window.performance.now() + delay * 1000;
            if (!this.audio.paused) {
                this.audio.pause();
            }
        }
        this.resetConfig();
        return true;
    },
    pickAudioTag: function () {
        if (!this.audio) {
            for (var i = 0; i < this.tags.length; i++) {
                var audio = this.tags[i];
                if (audio.dataset.used === 'false') {
                    audio.dataset.used = 'true';
                    this.audio = audio;
                    return true;
                }
            }
            if (!this.manager.override) {
                return false;
            }
            var otherSounds_1 = [];
            this.manager.forEachActiveSound(function (sound) {
                if (sound.key === this.key && sound.audio) {
                    otherSounds_1.push(sound);
                }
            }, this);
            otherSounds_1.sort(function (a1, a2) {
                if (a1.loop === a2.loop) {
                    // sort by progress
                    return (a2.seek / a2.duration) - (a1.seek / a1.duration);
                }
                return a1.loop ? 1 : -1;
            });
            var selectedSound = otherSounds_1[0];
            this.audio = selectedSound.audio;
            selectedSound.reset();
            selectedSound.audio = null;
            selectedSound.startTime = 0;
            selectedSound.previousTime = 0;
        }
        return true;
    },
    stopAndReleaseAudioTag: function () {
        this.audio.pause();
        this.audio.dataset.used = 'false';
        this.audio = null;
        this.startTime = 0;
        this.previousTime = 0;
    },
    reset: function () {
        BaseSound.prototype.stop.call(this);
    },
    onBlur: function () {
        this.isPlaying = false;
        this.isPaused = true;
        this.currentConfig.seek = this.audio.currentTime -
            (this.currentMarker ? this.currentMarker.start : 0);
        this.currentConfig.delay = Math.max(0, (this.startTime - window.performance.now()) / 1000);
        this.stopAndReleaseAudioTag();
    },
    onFocus: function () {
        this.isPlaying = true;
        this.isPaused = false;
        this.pickAndPlayAudioTag();
    },
    update: function (time, delta) {
        if (this.isPlaying) {
            // handling delayed playback
            if (this.startTime > 0) {
                if (this.startTime < time - this.manager.audioPlayDelay) {
                    this.audio.currentTime += Math.max(0, time - this.startTime) / 1000;
                    this.startTime = 0;
                    this.previousTime = this.audio.currentTime;
                    this.audio.play();
                }
                return;
            }
            // handle looping and ending
            var startTime = this.currentMarker ? this.currentMarker.start : 0;
            var endTime = startTime + this.duration;
            var currentTime = this.audio.currentTime;
            if (this.currentConfig.loop) {
                if (currentTime >= endTime - this.manager.loopEndOffset) {
                    this.audio.currentTime = startTime + Math.max(0, currentTime - endTime);
                    currentTime = this.audio.currentTime;
                }
                else if (currentTime < startTime) {
                    this.audio.currentTime += startTime;
                    currentTime = this.audio.currentTime;
                }
                if (currentTime < this.previousTime) {
                    this.emit('looped', this);
                }
            }
            else if (currentTime >= endTime) {
                this.reset();
                this.stopAndReleaseAudioTag();
                this.emit('ended', this);
                return;
            }
            this.previousTime = currentTime;
        }
    },
    destroy: function () {
        BaseSound.prototype.destroy.call(this);
        this.tags = null;
        if (this.audio) {
            this.stopAndReleaseAudioTag();
        }
    },
    setMute: function () {
        if (this.audio) {
            this.audio.muted = this.currentConfig.mute || this.manager.mute;
        }
    },
    setVolume: function () {
        if (this.audio) {
            this.audio.volume = this.currentConfig.volume * this.manager.volume;
        }
    },
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.audio) {
            this.audio.playbackRate = this.totalRate;
        }
    },
    checkTouchLocked: function (type, name, value) {
        if (this.manager.touchLocked) {
            this.touchLockedActionQueue.push({
                sound: this,
                type: type,
                name: name,
                value: value,
                time: window.performance.now()
            });
            return true;
        }
        return false;
    }
});
/**
 * Mute setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#mute
 * @property {boolean} mute
 */
Object.defineProperty(HTML5AudioSound.prototype, 'mute', {
    get: function () {
        return this.currentConfig.mute;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        if (this.checkTouchLocked('property', 'mute', value)) {
            return;
        }
        this.setMute();
        this.emit('mute', this, value);
    }
});
/**
 * Volume setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#volume
 * @property {number} volume
 */
Object.defineProperty(HTML5AudioSound.prototype, 'volume', {
    get: function () {
        return this.currentConfig.volume;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        if (this.checkTouchLocked('property', 'volume', value)) {
            return;
        }
        this.setVolume();
        this.emit('volume', this, value);
    }
});
/**
 * Playback rate.
 *
 * @name Phaser.Sound.HTML5AudioSound#rate
 * @property {number} rate
 */
Object.defineProperty(HTML5AudioSound.prototype, 'rate', {
    get: function () {
        return Object.getOwnPropertyDescriptor(BaseSound.prototype, 'rate').get.call(this);
    },
    set: function (value) {
        this.currentConfig.rate = value;
        if (this.checkTouchLocked('property', 'rate', value)) {
            return;
        }
        Object.getOwnPropertyDescriptor(BaseSound.prototype, 'rate').set.call(this, value);
    }
});
/**
 * Detuning of sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#detune
 * @property {number} detune
 */
Object.defineProperty(HTML5AudioSound.prototype, 'detune', {
    get: function () {
        return Object.getOwnPropertyDescriptor(BaseSound.prototype, 'detune').get.call(this);
    },
    set: function (value) {
        this.currentConfig.detune = value;
        if (this.checkTouchLocked('property', 'detune', value)) {
            return;
        }
        Object.getOwnPropertyDescriptor(BaseSound.prototype, 'detune').set.call(this, value);
    }
});
/**
 * Current position of playing sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#seek
 * @property {number} seek
 */
Object.defineProperty(HTML5AudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.audio.currentTime -
                (this.currentMarker ? this.currentMarker.start : 0);
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.checkTouchLocked('property', 'seek', value)) {
            return;
        }
        if (this.startTime > 0) {
            return;
        }
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            if (this.isPlaying) {
                this.previousTime = value;
                this.audio.currentTime = value;
            }
            else if (this.isPaused) {
                this.currentConfig.seek = value;
            }
            this.emit('seek', this, value);
        }
    }
});
/**
 * Property indicating whether or not
 * the sound or current sound marker will loop.
 *
 * @name Phaser.Sound.HTML5AudioSound#loop
 * @property {boolean} loop
 */
Object.defineProperty(HTML5AudioSound.prototype, 'loop', {
    get: function () {
        return this.currentConfig.loop;
    },
    set: function (value) {
        this.currentConfig.loop = value;
        if (this.audio) {
            this.audio.loop = value;
        }
        this.emit('loop', this, value);
    }
});
module.exports = HTML5AudioSound;
<|endoftext|>