<commit_before>import Tile from './tile';
import {TileID} from './tile_id';
import TilePyramid from './tile_pyramid';
import Geo from '../utils/geo';
import mainThreadLabelCollisionPass from '../labels/main_pass';
import log from '../utils/log';
import WorkerBroker from '../utils/worker_broker';
import Task from '../utils/task';

export default class TileManager {

    constructor({ scene, view }) {
        this.scene = scene;
        this.view = view;
        this.tiles = {};
        this.pyramid = new TilePyramid();
        this.visible_coords = {};
        this.queued_coords = [];
        this.building_tiles = null;
        this.renderable_tiles = [];
        this.active_styles = [];
        this.collision = {
            tile_keys: null,
            mesh_set: null,
            zoom: null,
            zoom_steps: 3 // divisions per zoom at which labels are re-collided (e.g. 0, 0.33, 0.66)
        };

        // Provide a hook for this object to be called from worker threads
        this.main_thread_target = ['TileManager', this.scene.id].join('_');
        WorkerBroker.addTarget(this.main_thread_target, this);
    }

    destroy() {
        this.forEachTile(tile => tile.destroy());
        this.tiles = {};
        this.pyramid = null;
        this.visible_coords = {};
        this.queued_coords = [];
        this.scene = null;
        this.view = null;
    }

    keepTile(tile) {
        this.tiles[tile.key] = tile;
        this.pyramid.addTile(tile);
    }

    hasTile(key) {
        return this.tiles[key] !== undefined;
    }

    forgetTile(key) {
        if (this.hasTile(key)) {
            let tile = this.tiles[key];
            this.pyramid.removeTile(tile);
        }

        delete this.tiles[key];
        this.tileBuildStop(key);
    }

    // Remove a single tile
    removeTile(key) {
        log('trace', `tile unload for ${key}`);

        var tile = this.tiles[key];

        if (tile != null) {
            tile.destroy();
        }

        this.forgetTile(tile.key);
        this.scene.requestRedraw();
    }

    // Run a function on each tile
    forEachTile(func) {
        for (let t in this.tiles) {
            func(this.tiles[t]);
        }
    }

    // Remove tiles that pass a filter condition
    removeTiles(filter) {
        let remove_tiles = [];
        for (let t in this.tiles) {
            let tile = this.tiles[t];
            if (filter(tile)) {
                remove_tiles.push(t);
            }
        }
        for (let r=0; r < remove_tiles.length; r++) {
            let key = remove_tiles[r];
            this.removeTile(key);
        }
    }

    updateTilesForView() {
        // Find visible tiles and load new ones
        this.visible_coords = {};
        let tile_coords = this.view.findVisibleTileCoordinates();
        for (let c=0; c < tile_coords.length; c++) {
            const coords = tile_coords[c];
            this.queueCoordinate(coords);
            this.visible_coords[coords.key] = coords;
        }

        this.updateTileStates();
    }

    updateTileStates () {
        this.forEachTile(tile => {
            this.updateVisibility(tile);
        });

        this.loadQueuedCoordinates();
        this.updateProxyTiles();
        this.view.pruneTilesForView();
        this.updateRenderableTiles();
        this.updateActiveStyles();
        return this.updateLabels();
    }

    updateLabels () {
        if (this.scene.building && !this.scene.building.initial) {
            // log('debug', `Skip label layout due to on-going scene rebuild`);
            return Promise.resolve({});
        }

        // get current visible tiles and sort by key for consistency collision order
        const tiles = this.renderable_tiles
            .filter(t => t.valid)
            .filter(t => t.built);

        if (tiles.length === 0) {
            return Promise.resolve({});
        }

        // Evaluate labels in order of tile build, to prevent previously visible labels
        // from disappearing, e.g. due to a newly loaded repeat label nearby
        tiles.sort((a, b) => a.build_id < b.build_id ? -1 : (a.build_id > b.build_id ? 1 : 0));

        // check if tile set has changed (in ways that affect collision)
        // if not, bail so that the existing collision task can carry on
        // if so, carry on and start a new collision task
        if (// 1st: check if same zoom level (rounded to a configurable precision)
            this.collision.zoom === roundPrecision(this.view.zoom, this.collision.zoom_steps) &&
            // 2nd: check if same set of tiles
            this.collision.tile_keys === JSON.stringify(tiles.map(t => t.key)) &&
            // 3rd: check if same set of meshes
            this.collision.mesh_set === meshSetString(tiles)) {
            // log('debug', `Skip label layout due to same tile/meshes (zoom ${this.view.zoom.toFixed(2)}, tiles ${this.collision.tile_keys})`);
            return Promise.resolve({});
        }

        // update collision if not already updating
        if (!this.collision.task) {
            this.collision.zoom = roundPrecision(this.view.zoom, this.collision.zoom_steps);
            this.collision.tile_keys = JSON.stringify(tiles.map(t => t.key));
            this.collision.mesh_set = meshSetString(tiles);
            // log('debug', `Update label collisions (zoom ${this.collision.zoom}, ${this.collision.tile_keys})`);

            // make a new collision task
            this.collision.task = {
                type: 'tileManagerUpdateLabels',
                run: (task) => {
                    return mainThreadLabelCollisionPass(tiles, this.collision.zoom, this.isLoadingVisibleTiles()).then(results => {
                        this.collision.task = null;
                        Task.finish(task, results);
                        this.updateTileStates().then(() => this.scene.immediateRedraw());
                    });
                },
                user_moving_view: false // don't run task when user is moving view
            };
            Task.add(this.collision.task);
        }
        // else {
        //     log('debug', `Skip label layout due to on-going layout (zoom ${this.view.zoom.toFixed(2)}, tiles ${this.collision.tile_keys})`);
        // }
        return this.collision.task.promise;
    }

    updateProxyTiles () {
        if (this.view.zoom_direction === 0) {
            return;
        }

        // Clear previous proxies
        this.forEachTile(tile => tile.setProxyFor(null));

        let proxy = false;
        this.forEachTile(tile => {
            if (this.view.zoom_direction === 1) {
                if (tile.visible && !tile.labeled) {
                    const parent = this.pyramid.getAncestor(tile);
                    if (parent) {
                        parent.setProxyFor(tile);
                        proxy = true;
                    }
                }
            }
            else if (this.view.zoom_direction === -1) {
                if (tile.visible && !tile.labeled) {
                    const descendants = this.pyramid.getDescendants(tile);
                    for (let i=0; i < descendants.length; i++) {
                        descendants[i].setProxyFor(tile);
                        proxy = true;
                    }
                }
            }
        });

        if (!proxy) {
            this.view.zoom_direction = 0;
        }
    }

    updateVisibility(tile) {
        tile.visible = false;
        if (tile.style_zoom === this.view.tile_zoom) {
            if (this.visible_coords[tile.coords.key]) {
                tile.visible = true;
            }
            else {
                // brute force
                for (let key in this.visible_coords) {
                    if (TileID.isDescendant(tile.coords, this.visible_coords[key])) {
                        tile.visible = true;
                        break;
                    }
                }
            }
        }
    }

    // Remove tiles that aren't visible, and flag remaining visible ones to be updated (for loading, proxy, etc.)
    pruneToVisibleTiles () {
        this.removeTiles(tile => !tile.visible);
    }

    getRenderableTiles () {
        return this.renderable_tiles;
    }

    updateRenderableTiles() {
        this.renderable_tiles = [];
        for (let t in this.tiles) {
            let tile = this.tiles[t];
            if (tile.visible && tile.loaded) {
                this.renderable_tiles.push(tile);
            }
        }
        return this.renderable_tiles;
    }

    // Assign tile to worker thread based on coordinates and data source
    getWorkerForTile(coords, source) {
        let worker;

        if (source.tiled) {
            // Pin tile to a worker thread based on its coordinates
            worker = this.scene.workers[Math.abs(coords.x + coords.y + coords.z) % this.scene.workers.length];
        }
        else {
            // Pin all tiles from each non-tiled source to a single worker
            // Prevents data for these sources from being loaded more than once
            worker = this.scene.workers[source.id % this.scene.workers.length];
        }

        return worker;
    }

    getActiveStyles () {
        return this.active_styles;
    }

    updateActiveStyles () {
        let tiles = this.renderable_tiles;
        let active = {};
        for (let t=0; t < tiles.length; t++) {
            let tile = tiles[t];
            Object.keys(tile.meshes).forEach(s => active[s] = true);
        }
        this.active_styles = Object.keys(active);
        return this.active_styles;
    }

    isLoadingVisibleTiles () {
        return Object.keys(this.tiles).some(k => this.tiles[k].visible && !this.tiles[k].built);
    }

    allVisibleTilesLabeled () {
        return this.renderable_tiles.every(t => t.labeled);
    }

    // Queue a tile for load
    queueCoordinate(coords) {
        this.queued_coords[this.queued_coords.length] = coords;
    }

    // Load all queued tiles
    loadQueuedCoordinates() {
        if (this.queued_coords.length === 0) {
            return;
        }

        // Sort queued tiles from center tile
        this.queued_coords.sort((a, b) => {
            let center = this.view.center.meters;
            let half_span = Geo.metersPerTile(a.z) / 2;

            let ac = Geo.metersForTile(a);
            ac.x += half_span;
            ac.y -= half_span;

            let bc = Geo.metersForTile(b);
            bc.x += half_span;
            bc.y -= half_span;

            let ad = Math.abs(center.x - ac.x) + Math.abs(center.y - ac.y);
            let bd = Math.abs(center.x - bc.x) + Math.abs(center.y - bc.y);

            a.center_dist = ad;
            b.center_dist = bd;

            return (bd > ad ? -1 : (bd === ad ? 0 : 1));
        });
        this.queued_coords.forEach(coords => this.loadCoordinate(coords));
        this.queued_coords = [];
    }

    // Load all tiles to cover a given logical tile coordinate
    loadCoordinate(coords) {
        // Skip if not at current scene zoom
        if (coords.z !== this.view.center.tile.z) {
            return;
        }

        // Determine necessary tiles for each source
        for (let s in this.scene.sources) {
            let source = this.scene.sources[s];
            // Check if data source should build this tile
            if (!source.builds_geometry_tiles || !source.includesTile(coords, this.view.tile_zoom)) {
                continue;
            }

            let key = TileID.normalizedKey(coords, source, this.view.tile_zoom);
            if (key && !this.hasTile(key)) {
                log('trace', `load tile ${key}, distance from view center: ${coords.center_dist}`);
                let tile = new Tile({
                    source,
                    coords,
                    worker: this.getWorkerForTile(coords, source),
                    style_zoom: this.view.baseZoom(coords.z),
                    view: this.view
                });

                this.keepTile(tile);
                this.buildTile(tile);
            }
        }
    }

    // Start tile build process
    buildTile(tile, options) {
        this.tileBuildStart(tile.key);
        this.updateVisibility(tile);
        tile.build(this.scene.generation, options);
    }

    // Called on main thread when a web worker completes processing for a single tile (initial load, or rebuild)
    buildTileStylesCompleted({ tile, progress }) {
        // Removed this tile during load?
        if (this.tiles[tile.key] == null) {
            log('trace', `discarded tile ${tile.key} in TileManager.buildTileStylesCompleted because previously removed`);
            Tile.abortBuild(tile);
            this.updateTileStates();
        }
        // Built with an outdated scene configuration?
        else if (tile.generation !== this.scene.generation) {
            log('trace', `discarded tile ${tile.key} in TileManager.buildTileStylesCompleted because built with ` +
                `scene config gen ${tile.generation}, current ${this.scene.generation}`);
            Tile.abortBuild(tile);
            this.updateTileStates();
        }
        else {
            // Update tile with properties from worker
            if (this.tiles[tile.key]) {
                // Ignore if from a previously discarded tile
                if (tile.id < this.tiles[tile.key].id) {
                    log('trace', `discarded tile ${tile.key} for id ${tile.id} in TileManager.buildTileStylesCompleted because built for discarded tile id`);
                    Tile.abortBuild(tile);
                    return;
                }

                tile = this.tiles[tile.key].merge(tile);
            }

            if (progress.done) {
                tile.built = true;
            }

            tile.buildMeshes(this.scene.styles, progress);
            this.updateTileStates();
            this.scene.requestRedraw();
        }

        if (progress.done) {
            this.tileBuildStop(tile.key);
        }
    }

    // Called on main thread when web worker encounters an error building a tile
    buildTileError(tile) {
        log('error', `Error building tile ${tile.key}:`, tile.error);
        this.forgetTile(tile.key);
        Tile.abortBuild(tile);
    }

    // Track tile build state
    tileBuildStart(key) {
        this.building_tiles = this.building_tiles || {};
        this.building_tiles[key] = true;
        log('trace', `tileBuildStart for ${key}: ${Object.keys(this.building_tiles).length}`);
    }

    tileBuildStop(key) {
        // Done building?
        if (this.building_tiles) {
            log('trace', `tileBuildStop for ${key}: ${Object.keys(this.building_tiles).length}`);
            delete this.building_tiles[key];
            this.checkBuildQueue();
        }
    }

    // Check status of tile building queue and notify scene when we're done
    checkBuildQueue() {
        if (!this.building_tiles || Object.keys(this.building_tiles).length === 0) {
            this.building_tiles = null;
            this.scene.tileManagerBuildDone();
        }
    }

    // Get a debug property across tiles
    getDebugProp(prop, filter) {
        var vals = [];
        for (var t in this.tiles) {
            if (this.tiles[t].debug[prop] != null && (typeof filter !== 'function' || filter(this.tiles[t]) === true)) {
                vals.push(this.tiles[t].debug[prop]);
            }
        }
        return vals;
    }

    // Sum of a debug property across tiles
    getDebugSum(prop, filter) {
        var sum = 0;
        for (var t in this.tiles) {
            if (this.tiles[t].debug[prop] != null && (typeof filter !== 'function' || filter(this.tiles[t]) === true)) {
                sum += this.tiles[t].debug[prop];
            }
        }
        return sum;
    }

    // Average of a debug property across tiles
    getDebugAverage(prop, filter) {
        return this.getDebugSum(prop, filter) / Object.keys(this.tiles).length;
    }

}

// Round a number to given number of decimal divisions
// e.g. roundPrecision(x, 4) rounds a number to increments of 0.25
function roundPrecision (x, d, places = 2) {
    return (Math.floor(x * d) / d).toFixed(places);
}

// Create a string representing the current set of meshes for a given set of tiles,
// based on their created timestamp. Used to determine when tiles should be re-collided.
function meshSetString (tiles) {
    return JSON.stringify(
        Object.entries(tiles).map(([,t]) => {
            return Object.entries(t.meshes).map(([,s]) => {
                return s.map(m => m.created_at);
            });
        })
    );
}
<commit_msg>tile manager: remove worker broker target on destroy<commit_after>import Tile from './tile';
import {TileID} from './tile_id';
import TilePyramid from './tile_pyramid';
import Geo from '../utils/geo';
import mainThreadLabelCollisionPass from '../labels/main_pass';
import log from '../utils/log';
import WorkerBroker from '../utils/worker_broker';
import Task from '../utils/task';

export default class TileManager {

    constructor({ scene, view }) {
        this.scene = scene;
        this.view = view;
        this.tiles = {};
        this.pyramid = new TilePyramid();
        this.visible_coords = {};
        this.queued_coords = [];
        this.building_tiles = null;
        this.renderable_tiles = [];
        this.active_styles = [];
        this.collision = {
            tile_keys: null,
            mesh_set: null,
            zoom: null,
            zoom_steps: 3 // divisions per zoom at which labels are re-collided (e.g. 0, 0.33, 0.66)
        };

        // Provide a hook for this object to be called from worker threads
        this.main_thread_target = ['TileManager', this.scene.id].join('_');
        WorkerBroker.addTarget(this.main_thread_target, this);
    }

    destroy() {
        this.forEachTile(tile => tile.destroy());
        this.tiles = {};
        this.pyramid = null;
        this.visible_coords = {};
        this.queued_coords = [];
        this.scene = null;
        this.view = null;
        WorkerBroker.removeTarget(this.main_thread_target);
    }

    keepTile(tile) {
        this.tiles[tile.key] = tile;
        this.pyramid.addTile(tile);
    }

    hasTile(key) {
        return this.tiles[key] !== undefined;
    }

    forgetTile(key) {
        if (this.hasTile(key)) {
            let tile = this.tiles[key];
            this.pyramid.removeTile(tile);
        }

        delete this.tiles[key];
        this.tileBuildStop(key);
    }

    // Remove a single tile
    removeTile(key) {
        log('trace', `tile unload for ${key}`);

        var tile = this.tiles[key];

        if (tile != null) {
            tile.destroy();
        }

        this.forgetTile(tile.key);
        this.scene.requestRedraw();
    }

    // Run a function on each tile
    forEachTile(func) {
        for (let t in this.tiles) {
            func(this.tiles[t]);
        }
    }

    // Remove tiles that pass a filter condition
    removeTiles(filter) {
        let remove_tiles = [];
        for (let t in this.tiles) {
            let tile = this.tiles[t];
            if (filter(tile)) {
                remove_tiles.push(t);
            }
        }
        for (let r=0; r < remove_tiles.length; r++) {
            let key = remove_tiles[r];
            this.removeTile(key);
        }
    }

    updateTilesForView() {
        // Find visible tiles and load new ones
        this.visible_coords = {};
        let tile_coords = this.view.findVisibleTileCoordinates();
        for (let c=0; c < tile_coords.length; c++) {
            const coords = tile_coords[c];
            this.queueCoordinate(coords);
            this.visible_coords[coords.key] = coords;
        }

        this.updateTileStates();
    }

    updateTileStates () {
        this.forEachTile(tile => {
            this.updateVisibility(tile);
        });

        this.loadQueuedCoordinates();
        this.updateProxyTiles();
        this.view.pruneTilesForView();
        this.updateRenderableTiles();
        this.updateActiveStyles();
        return this.updateLabels();
    }

    updateLabels () {
        if (this.scene.building && !this.scene.building.initial) {
            // log('debug', `Skip label layout due to on-going scene rebuild`);
            return Promise.resolve({});
        }

        // get current visible tiles and sort by key for consistency collision order
        const tiles = this.renderable_tiles
            .filter(t => t.valid)
            .filter(t => t.built);

        if (tiles.length === 0) {
            return Promise.resolve({});
        }

        // Evaluate labels in order of tile build, to prevent previously visible labels
        // from disappearing, e.g. due to a newly loaded repeat label nearby
        tiles.sort((a, b) => a.build_id < b.build_id ? -1 : (a.build_id > b.build_id ? 1 : 0));

        // check if tile set has changed (in ways that affect collision)
        // if not, bail so that the existing collision task can carry on
        // if so, carry on and start a new collision task
        if (// 1st: check if same zoom level (rounded to a configurable precision)
            this.collision.zoom === roundPrecision(this.view.zoom, this.collision.zoom_steps) &&
            // 2nd: check if same set of tiles
            this.collision.tile_keys === JSON.stringify(tiles.map(t => t.key)) &&
            // 3rd: check if same set of meshes
            this.collision.mesh_set === meshSetString(tiles)) {
            // log('debug', `Skip label layout due to same tile/meshes (zoom ${this.view.zoom.toFixed(2)}, tiles ${this.collision.tile_keys})`);
            return Promise.resolve({});
        }

        // update collision if not already updating
        if (!this.collision.task) {
            this.collision.zoom = roundPrecision(this.view.zoom, this.collision.zoom_steps);
            this.collision.tile_keys = JSON.stringify(tiles.map(t => t.key));
            this.collision.mesh_set = meshSetString(tiles);
            // log('debug', `Update label collisions (zoom ${this.collision.zoom}, ${this.collision.tile_keys})`);

            // make a new collision task
            this.collision.task = {
                type: 'tileManagerUpdateLabels',
                run: (task) => {
                    return mainThreadLabelCollisionPass(tiles, this.collision.zoom, this.isLoadingVisibleTiles()).then(results => {
                        this.collision.task = null;
                        Task.finish(task, results);
                        this.updateTileStates().then(() => this.scene.immediateRedraw());
                    });
                },
                user_moving_view: false // don't run task when user is moving view
            };
            Task.add(this.collision.task);
        }
        // else {
        //     log('debug', `Skip label layout due to on-going layout (zoom ${this.view.zoom.toFixed(2)}, tiles ${this.collision.tile_keys})`);
        // }
        return this.collision.task.promise;
    }

    updateProxyTiles () {
        if (this.view.zoom_direction === 0) {
            return;
        }

        // Clear previous proxies
        this.forEachTile(tile => tile.setProxyFor(null));

        let proxy = false;
        this.forEachTile(tile => {
            if (this.view.zoom_direction === 1) {
                if (tile.visible && !tile.labeled) {
                    const parent = this.pyramid.getAncestor(tile);
                    if (parent) {
                        parent.setProxyFor(tile);
                        proxy = true;
                    }
                }
            }
            else if (this.view.zoom_direction === -1) {
                if (tile.visible && !tile.labeled) {
                    const descendants = this.pyramid.getDescendants(tile);
                    for (let i=0; i < descendants.length; i++) {
                        descendants[i].setProxyFor(tile);
                        proxy = true;
                    }
                }
            }
        });

        if (!proxy) {
            this.view.zoom_direction = 0;
        }
    }

    updateVisibility(tile) {
        tile.visible = false;
        if (tile.style_zoom === this.view.tile_zoom) {
            if (this.visible_coords[tile.coords.key]) {
                tile.visible = true;
            }
            else {
                // brute force
                for (let key in this.visible_coords) {
                    if (TileID.isDescendant(tile.coords, this.visible_coords[key])) {
                        tile.visible = true;
                        break;
                    }
                }
            }
        }
    }

    // Remove tiles that aren't visible, and flag remaining visible ones to be updated (for loading, proxy, etc.)
    pruneToVisibleTiles () {
        this.removeTiles(tile => !tile.visible);
    }

    getRenderableTiles () {
        return this.renderable_tiles;
    }

    updateRenderableTiles() {
        this.renderable_tiles = [];
        for (let t in this.tiles) {
            let tile = this.tiles[t];
            if (tile.visible && tile.loaded) {
                this.renderable_tiles.push(tile);
            }
        }
        return this.renderable_tiles;
    }

    // Assign tile to worker thread based on coordinates and data source
    getWorkerForTile(coords, source) {
        let worker;

        if (source.tiled) {
            // Pin tile to a worker thread based on its coordinates
            worker = this.scene.workers[Math.abs(coords.x + coords.y + coords.z) % this.scene.workers.length];
        }
        else {
            // Pin all tiles from each non-tiled source to a single worker
            // Prevents data for these sources from being loaded more than once
            worker = this.scene.workers[source.id % this.scene.workers.length];
        }

        return worker;
    }

    getActiveStyles () {
        return this.active_styles;
    }

    updateActiveStyles () {
        let tiles = this.renderable_tiles;
        let active = {};
        for (let t=0; t < tiles.length; t++) {
            let tile = tiles[t];
            Object.keys(tile.meshes).forEach(s => active[s] = true);
        }
        this.active_styles = Object.keys(active);
        return this.active_styles;
    }

    isLoadingVisibleTiles () {
        return Object.keys(this.tiles).some(k => this.tiles[k].visible && !this.tiles[k].built);
    }

    allVisibleTilesLabeled () {
        return this.renderable_tiles.every(t => t.labeled);
    }

    // Queue a tile for load
    queueCoordinate(coords) {
        this.queued_coords[this.queued_coords.length] = coords;
    }

    // Load all queued tiles
    loadQueuedCoordinates() {
        if (this.queued_coords.length === 0) {
            return;
        }

        // Sort queued tiles from center tile
        this.queued_coords.sort((a, b) => {
            let center = this.view.center.meters;
            let half_span = Geo.metersPerTile(a.z) / 2;

            let ac = Geo.metersForTile(a);
            ac.x += half_span;
            ac.y -= half_span;

            let bc = Geo.metersForTile(b);
            bc.x += half_span;
            bc.y -= half_span;

            let ad = Math.abs(center.x - ac.x) + Math.abs(center.y - ac.y);
            let bd = Math.abs(center.x - bc.x) + Math.abs(center.y - bc.y);

            a.center_dist = ad;
            b.center_dist = bd;

            return (bd > ad ? -1 : (bd === ad ? 0 : 1));
        });
        this.queued_coords.forEach(coords => this.loadCoordinate(coords));
        this.queued_coords = [];
    }

    // Load all tiles to cover a given logical tile coordinate
    loadCoordinate(coords) {
        // Skip if not at current scene zoom
        if (coords.z !== this.view.center.tile.z) {
            return;
        }

        // Determine necessary tiles for each source
        for (let s in this.scene.sources) {
            let source = this.scene.sources[s];
            // Check if data source should build this tile
            if (!source.builds_geometry_tiles || !source.includesTile(coords, this.view.tile_zoom)) {
                continue;
            }

            let key = TileID.normalizedKey(coords, source, this.view.tile_zoom);
            if (key && !this.hasTile(key)) {
                log('trace', `load tile ${key}, distance from view center: ${coords.center_dist}`);
                let tile = new Tile({
                    source,
                    coords,
                    worker: this.getWorkerForTile(coords, source),
                    style_zoom: this.view.baseZoom(coords.z),
                    view: this.view
                });

                this.keepTile(tile);
                this.buildTile(tile);
            }
        }
    }

    // Start tile build process
    buildTile(tile, options) {
        this.tileBuildStart(tile.key);
        this.updateVisibility(tile);
        tile.build(this.scene.generation, options);
    }

    // Called on main thread when a web worker completes processing for a single tile (initial load, or rebuild)
    buildTileStylesCompleted({ tile, progress }) {
        // Removed this tile during load?
        if (this.tiles[tile.key] == null) {
            log('trace', `discarded tile ${tile.key} in TileManager.buildTileStylesCompleted because previously removed`);
            Tile.abortBuild(tile);
            this.updateTileStates();
        }
        // Built with an outdated scene configuration?
        else if (tile.generation !== this.scene.generation) {
            log('trace', `discarded tile ${tile.key} in TileManager.buildTileStylesCompleted because built with ` +
                `scene config gen ${tile.generation}, current ${this.scene.generation}`);
            Tile.abortBuild(tile);
            this.updateTileStates();
        }
        else {
            // Update tile with properties from worker
            if (this.tiles[tile.key]) {
                // Ignore if from a previously discarded tile
                if (tile.id < this.tiles[tile.key].id) {
                    log('trace', `discarded tile ${tile.key} for id ${tile.id} in TileManager.buildTileStylesCompleted because built for discarded tile id`);
                    Tile.abortBuild(tile);
                    return;
                }

                tile = this.tiles[tile.key].merge(tile);
            }

            if (progress.done) {
                tile.built = true;
            }

            tile.buildMeshes(this.scene.styles, progress);
            this.updateTileStates();
            this.scene.requestRedraw();
        }

        if (progress.done) {
            this.tileBuildStop(tile.key);
        }
    }

    // Called on main thread when web worker encounters an error building a tile
    buildTileError(tile) {
        log('error', `Error building tile ${tile.key}:`, tile.error);
        this.forgetTile(tile.key);
        Tile.abortBuild(tile);
    }

    // Track tile build state
    tileBuildStart(key) {
        this.building_tiles = this.building_tiles || {};
        this.building_tiles[key] = true;
        log('trace', `tileBuildStart for ${key}: ${Object.keys(this.building_tiles).length}`);
    }

    tileBuildStop(key) {
        // Done building?
        if (this.building_tiles) {
            log('trace', `tileBuildStop for ${key}: ${Object.keys(this.building_tiles).length}`);
            delete this.building_tiles[key];
            this.checkBuildQueue();
        }
    }

    // Check status of tile building queue and notify scene when we're done
    checkBuildQueue() {
        if (!this.building_tiles || Object.keys(this.building_tiles).length === 0) {
            this.building_tiles = null;
            this.scene.tileManagerBuildDone();
        }
    }

    // Get a debug property across tiles
    getDebugProp(prop, filter) {
        var vals = [];
        for (var t in this.tiles) {
            if (this.tiles[t].debug[prop] != null && (typeof filter !== 'function' || filter(this.tiles[t]) === true)) {
                vals.push(this.tiles[t].debug[prop]);
            }
        }
        return vals;
    }

    // Sum of a debug property across tiles
    getDebugSum(prop, filter) {
        var sum = 0;
        for (var t in this.tiles) {
            if (this.tiles[t].debug[prop] != null && (typeof filter !== 'function' || filter(this.tiles[t]) === true)) {
                sum += this.tiles[t].debug[prop];
            }
        }
        return sum;
    }

    // Average of a debug property across tiles
    getDebugAverage(prop, filter) {
        return this.getDebugSum(prop, filter) / Object.keys(this.tiles).length;
    }

}

// Round a number to given number of decimal divisions
// e.g. roundPrecision(x, 4) rounds a number to increments of 0.25
function roundPrecision (x, d, places = 2) {
    return (Math.floor(x * d) / d).toFixed(places);
}

// Create a string representing the current set of meshes for a given set of tiles,
// based on their created timestamp. Used to determine when tiles should be re-collided.
function meshSetString (tiles) {
    return JSON.stringify(
        Object.entries(tiles).map(([,t]) => {
            return Object.entries(t.meshes).map(([,s]) => {
                return s.map(m => m.created_at);
            });
        })
    );
}
<|endoftext|>


<commit_before>
require('./browser-polyfills');

function _isType (type) {
    return function (o) {
        return (typeof o === type);
    };
}

function _instanceOf (_constructor) {
    return function (o) {
        return ( o instanceof _constructor );
    };
}

var _isObject = _isType('object'),
    _isFunction = _isType('function'),
    _isString = _isType('string'),
    _isNumber = _isType('number'),
    _isArray = Array.isArray || _instanceOf(Array),
    _isDate = _instanceOf(Date),
    _isRegExp = _instanceOf(RegExp),
    _isElement = function(o) {
      return o && o.nodeType === 1;
    };

function listen (element, eventName, listener) {
  if( element instanceof Array ) {
    for( var i = 0, n = element.length ; i < n ; i++ ) {
      element[i].addEventListener(eventName, listener, false);
    }
    return;
  }
  element.addEventListener(eventName, listener, false);
}

function _ready (callback) {
  if (/loaded|complete/.test(document.readyState)) {
    callback();
  } else {
    window.addEventListener('load', callback);
  }
}

function once (fn) {
  var done;
  return function () {
    if( !done ) {
      done = true;
      return fn.apply(this, arguments);
    }
  };
}

function docReady (_callback, delay) {
  var callback = delay ? function () { setTimeout(_callback, delay); } : _callback;

  if( document.readyState === 'complete' ) {
    callback();
  } else {
    listen(window, 'load', callback);
  }
}

function replaceKeys (tmpl, keys) {
  return keys ? tmpl.replace(/\{\{([^\}]+)\}\}/g, function (match, key) {
    return keys[key];
  }) : function (ks) {
    return replaceKeys(tmpl, ks);
  };
}

var arrayShift = [].shift;
function extend () {
    var dest = arrayShift.call(arguments),
      src = arrayShift.call(arguments),
      key;

  while( src ) {
    for( key in src) {
      dest[key] = src[key];
    }
    src = arrayShift.call(arguments);
  }

  return dest;
}

function merge () {
    var dest = arrayShift.call(arguments),
        src = arrayShift.call(arguments),
        key;

    while( src ) {

        if( typeof dest !== typeof src ) {
            dest = _isArray(src) ? [] : ( _isObject(src) ? {} : src );
        }

        if( _isObject(src) ) {

            for( key in src ) {
                if( src[key] !== undefined ) {
                    if( typeof dest[key] !== typeof src[key] ) {
                        dest[key] = merge(undefined, src[key]);
                    } else if( _isArray(dest[key]) ) {
                        [].push.apply(dest[key], src[key]);
                    } else if( _isObject(dest[key]) ) {
                        dest[key] = merge(dest[key], src[key]);
                    } else {
                        dest[key] = src[key];
                    }
                }
            }
        }
        src = arrayShift.call(arguments);
    }

    return dest;
}

function joinPath () {
  return [].reduce.call(arguments, function (prev, path, index, list) {

    path = index ? path.replace(/^\//, '') : path;
    path = ( index === (list.length - 1) ) ? path : path.replace(/\/$/, '');

    return prev + ( index ? '/' : '' ) + path;

  }, '');
}

function writeIframe (iframe, content) {
  var iframeDoc = iframe.contentWindow.document;
  try { iframeDoc.charset = 'UTF-8'; } catch(err) {}
  iframeDoc.open();
  iframeDoc.write(content);
  iframeDoc.close();
}

function getIFrame (iframeStyles) {
  var iframe = document.createElement('iframe');
  extend(iframe.style, iframeStyles || {});

  iframe.frameBorder = '0';
  return iframe;
}

function template (name, data){
  return template.cache[name](data || {});
}

template.cache = {};

template.compile = function (tmpl) {
  // John Resig micro-template
  return new Function('obj', // jshint ignore:line
    'var p=[],print=function(){p.push.apply(p,arguments);};' +

    // Introduce the data as local variables using with(){}
    'with(obj){p.push(\'' +

    // Convert the template into pure JavaScript
    tmpl.trim()
      .replace(/[\r\t\n]/g, ' ')
      .split('<%').join('\t')
      .replace(/((^|%>)[^\t]*)'/g, '$1\r')
      .replace(/\t=(.*?)%>/g, '\',$1,\'')
      .split('\t').join('\');')
      .split('%>').join('p.push(\'')
      .split('\r').join('\\\'') + '\');}return p.join(\'\');');
};

template.put = function (name, tmpl) {
  template.cache[name] = template.compile(tmpl);
};

template.lookup = function () {
  [].forEach.call(document.querySelectorAll('script[type="application/x-template"][data-template]'), function (tmpl) {
    template.put(tmpl.getAttribute('data-template'), tmpl.text);
  });
};

function findBubbleClose (str) {
  var level = 0;

  for( var i = 0, len = str.length; i < len ; i++ ) {
    if( str[i] === '(' ) {
      level++;
    } else if( str[i] === ')' ) {
      if( level === 0 ) {
        return i;
      } else {
        level--;
      }
    }
  }

  return -1;
}

function hasSelector (selector, rootElement) {
  var splitHas = selector.split(':has(');

  return splitHas.reduce(function (matches, partial) {

    var closePosition = findBubbleClose(partial),
        hasFilter = partial.substr(0, closePosition),
        partialQuery = partial.substr(closePosition + 1).trim();

    if( closePosition === -1 ) {
      throw new Error('malformed selector');
    }

    matches = matches.filter(function (element) {
      return element.querySelector(hasFilter);
    });

    if( partialQuery ) {
      var submatches = [];

      matches.forEach(function (element) {
        [].push.apply(submatches, element.querySelectorAll(partialQuery) );
      });

      return submatches;
    }

    return matches;

  }, [].slice.call( (rootElement || document).querySelectorAll( splitHas.shift() ) ) );
}

function querySelector (selector, rootElement) {
  // 'tr:has(> .row) div:has(span) img'.split(':has(');
  if( !selector ) {
    return [];
  }

  if( !/\:has\(/.test(selector) ) {
    return [].slice.call( (rootElement || document).querySelectorAll( selector ) );
  }

  return hasSelector(selector);
}

function cssQuery (_selector, rootElement) {
  var selectors = _selector.split(/\s*,\s*/);

  return selectors.reduce(function (list, selector) {
    return list.concat( querySelector(selector, rootElement) );
  }, []);
}

function getAmount (amount) {
  var prefix = '';

  if( amount < 0 ) {
    prefix = '-';
    amount = 0 - amount;
  }

  if( !amount ) {
    return '0,00';
  } else if( amount < 10 ) {
    return '0,0' + amount;
  } else if( amount < 100 ) {
    return '0,' + amount;
  }
  return prefix + ('' + amount).replace(/..$/, ',$&');
}

var cssHack = (function () {
  var cache = {},
      hacks = {
        overlay: '.aplazame-overlay { font-family: \'Montserrat\', sans-serif; position: fixed; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; width: 100vw; height: 100vh; background: rgba(53, 64, 71, 0.9); text-align:center;} .aplazame-overlay::before{content:\'\';display:block;height:50vh;}.aplazame-logo-wrapper{display:inline-block;margin-top:-50%;} .aplazame-checkout-loading-text{color:#95A6B1;margin-top:14px;font-size:14px;}' +
                '@keyframes aplazame-overlay{0%{opacity:0}0%,to{transform:translateZ(0)}to{opacity:1}}.aplazame-overlay{animation-duration:.4s;animation-name:aplazame-overlay}' +
                  '@keyframes aplazame-logo-rotate360{0%{transform:rotate(0deg)}60%,90%,to{transform:rotate(1turn)}}@keyframes aplazame-logo-rotate360-delay{0%,30%{transform:rotate(0deg)}90%,to{transform:rotate(1turn)}}@keyframes aplazame-logo-rotate720{0%{transform:rotate(0deg)}90%,to{transform:rotate(2turn)}}.logo-aplazame{position:relative;display:inline-block;transform:translateZ(0);width:150px;height:150px}.logo-aplazame .line-large,.logo-aplazame .line-short,.logo-aplazame .smile{stroke:#ddd}.logo-aplazame .smile-outline{stroke:#485259}@media (min-width:480px){.logo-aplazame{width:200px;height:200px}}.logo-aplazame svg{position:absolute;top:0;left:0;width:100%;height:100%}.logo-aplazame.animate .line-large,.logo-aplazame.animate .line-short,.logo-aplazame.animate .smile{animation-timing-function:cubic-beizer(0,.35,.65,1);animation-iteration-count:infinite}.logo-aplazame.animate .smile{animation-name:aplazame-logo-rotate720;animation-duration:1.5s}.logo-aplazame.animate .line-large{animation-name:aplazame-logo-rotate360;animation-duration:1.5s}.logo-aplazame.animate .line-short{animation-name:aplazame-logo-rotate360-delay;animation-duration:1.5s}',
        // blur: 'body > *:not(.aplazame-modal):not(.aplazame-overlay) { -webkit-filter: blur(0px); filter: blur(0px); transition: all 0.4s linear; } body.aplazame-blur > *:not(.aplazame-modal):not(.aplazame-overlay) { -webkit-filter: blur(3px); filter: blur(3px); }',
        blur: '@keyframes aplazame-blur{0%{opacity:0;filter:blur(0);-webkit-filter:blur(0)}to{opacity:1;-webkit-filter:blur(3px)}}body.aplazame-blur>:not(.aplazame-modal):not(.aplazame-overlay){animation-duration:.4s;animation-name:aplazame-blur;filter:blur(3px);-webkit-filter:blur(3px)}body.aplazame-unblur>:not(.aplazame-modal):not(.aplazame-overlay){animation-duration:.4s;animation-name:aplazame-blur;animation-direction:reverse}',
        // modal: '.aplazame-modal { height: 100%; } html, body { margin: 0; padding: 0; } @media (max-width: 767px) { body > *:not(.aplazame-modal) { display: none; } }'
        modal: '.aplazame-modal { height: 100%; } body { overflow: hidden; } .aplazame-modal.hide {display:none;}' +
               '@media (max-width: 600px) { html { background-color: #333A3E; } html, body { height: 100%; margin: 0; padding: 0; } body > *:not(.aplazame-modal) { display: none; } iframe.aplazame-modal { position: absolute; } }' +
               '@media (min-width: 601px) { .aplazame-modal { position: fixed; } }'
        // overflow: '/* html { height: 100%; } body { overflow: hidden; } */',
        // inputFocus: 'html, body { height: 100vh; overflow: hidden; }'
      };

  return function hack (hackName) {
    if( !cache[hackName] ) {
      var style = document.createElement('style');
      style.setAttribute('rel', 'stylesheet');
      style.setAttribute('type', 'text/css');
      style.textContent = hacks[hackName].replace(/;/g, ' !important;');

      var enabled = false;

      style.hack = function (enable) {
        enable = enable === undefined || enable;

        if( enable ) {
          if( enabled ) { return; }
          enabled = true;
          document.head.appendChild(style);
        } else {
          if( !enabled ) { return; }
          enabled = false;
          document.head.removeChild(style);
        }
      };

      cache[hackName] = style;
    }
    return cache[hackName];
  };
})();

function scrollTop (value) {
  if( value !== undefined ) {
    document.documentElement.scrollTop = value;
    document.body.scrollTop = value;
  }
  return document.documentElement.scrollTop || document.body.scrollTop;
}

var _classActions = {
  add: document.documentElement.classList ? function (element, className) {
    element.classList.add(className);
  } : function (element, className) {
    var RE_CLEANCLASS = new RegExp('\\b' + (className || '') + '\\b','');
    _classActions.remove(element, className);
    element.className += ' ' + className;
  },
  remove: document.documentElement.classList ? function (element, className) {
    element.classList.remove(className);
  } : function (element, className) {
    var RE_CLEANCLASS = new RegExp('\\b' + (className || '') + '\\b','');
    element.className = element.className.replace(RE_CLEANCLASS,'');
  },
  action: function (action, tools) {
    return function (element, className) {
      if( className.indexOf(' ') >= 0 ) {
        className.split(' ').forEach(function (cn) {
          _classActions[action](element, cn);
        });
      } else {
        _classActions[action](element, className);
      }
      return tools;
    };
  }
};

var tools = {
  isObject: _isObject,
  isFunction: _isFunction,
  isString: _isString,
  isNumber: _isNumber,
  isArray: _isArray,
  isDate: _isDate,
  isRegExp: _isRegExp,
  isElement: _isElement,
  listen: listen,
  once: once,
  ready: docReady,
  replaceKeys: replaceKeys,
  merge: merge,
  extend: extend,
  joinPath: joinPath,
  writeIframe: writeIframe,
  getIFrame: getIFrame,
  template: template,
  cssQuery: cssQuery,
  getAmount: getAmount,
  cssHack: cssHack,
  scrollTop: scrollTop,
  addClass: _classActions.action('add', tools),
  removeClass: _classActions.action('remove', tools)
};

module.exports = tools;
<commit_msg>better autoprefixed checkout overlay styles<commit_after>
require('./browser-polyfills');

function _isType (type) {
    return function (o) {
        return (typeof o === type);
    };
}

function _instanceOf (_constructor) {
    return function (o) {
        return ( o instanceof _constructor );
    };
}

var _isObject = _isType('object'),
    _isFunction = _isType('function'),
    _isString = _isType('string'),
    _isNumber = _isType('number'),
    _isArray = Array.isArray || _instanceOf(Array),
    _isDate = _instanceOf(Date),
    _isRegExp = _instanceOf(RegExp),
    _isElement = function(o) {
      return o && o.nodeType === 1;
    };

function listen (element, eventName, listener) {
  if( element instanceof Array ) {
    for( var i = 0, n = element.length ; i < n ; i++ ) {
      element[i].addEventListener(eventName, listener, false);
    }
    return;
  }
  element.addEventListener(eventName, listener, false);
}

function _ready (callback) {
  if (/loaded|complete/.test(document.readyState)) {
    callback();
  } else {
    window.addEventListener('load', callback);
  }
}

function once (fn) {
  var done;
  return function () {
    if( !done ) {
      done = true;
      return fn.apply(this, arguments);
    }
  };
}

function docReady (_callback, delay) {
  var callback = delay ? function () { setTimeout(_callback, delay); } : _callback;

  if( document.readyState === 'complete' ) {
    callback();
  } else {
    listen(window, 'load', callback);
  }
}

function replaceKeys (tmpl, keys) {
  return keys ? tmpl.replace(/\{\{([^\}]+)\}\}/g, function (match, key) {
    return keys[key];
  }) : function (ks) {
    return replaceKeys(tmpl, ks);
  };
}

var arrayShift = [].shift;
function extend () {
    var dest = arrayShift.call(arguments),
      src = arrayShift.call(arguments),
      key;

  while( src ) {
    for( key in src) {
      dest[key] = src[key];
    }
    src = arrayShift.call(arguments);
  }

  return dest;
}

function merge () {
    var dest = arrayShift.call(arguments),
        src = arrayShift.call(arguments),
        key;

    while( src ) {

        if( typeof dest !== typeof src ) {
            dest = _isArray(src) ? [] : ( _isObject(src) ? {} : src );
        }

        if( _isObject(src) ) {

            for( key in src ) {
                if( src[key] !== undefined ) {
                    if( typeof dest[key] !== typeof src[key] ) {
                        dest[key] = merge(undefined, src[key]);
                    } else if( _isArray(dest[key]) ) {
                        [].push.apply(dest[key], src[key]);
                    } else if( _isObject(dest[key]) ) {
                        dest[key] = merge(dest[key], src[key]);
                    } else {
                        dest[key] = src[key];
                    }
                }
            }
        }
        src = arrayShift.call(arguments);
    }

    return dest;
}

function joinPath () {
  return [].reduce.call(arguments, function (prev, path, index, list) {

    path = index ? path.replace(/^\//, '') : path;
    path = ( index === (list.length - 1) ) ? path : path.replace(/\/$/, '');

    return prev + ( index ? '/' : '' ) + path;

  }, '');
}

function writeIframe (iframe, content) {
  var iframeDoc = iframe.contentWindow.document;
  try { iframeDoc.charset = 'UTF-8'; } catch(err) {}
  iframeDoc.open();
  iframeDoc.write(content);
  iframeDoc.close();
}

function getIFrame (iframeStyles) {
  var iframe = document.createElement('iframe');
  extend(iframe.style, iframeStyles || {});

  iframe.frameBorder = '0';
  return iframe;
}

function template (name, data){
  return template.cache[name](data || {});
}

template.cache = {};

template.compile = function (tmpl) {
  // John Resig micro-template
  return new Function('obj', // jshint ignore:line
    'var p=[],print=function(){p.push.apply(p,arguments);};' +

    // Introduce the data as local variables using with(){}
    'with(obj){p.push(\'' +

    // Convert the template into pure JavaScript
    tmpl.trim()
      .replace(/[\r\t\n]/g, ' ')
      .split('<%').join('\t')
      .replace(/((^|%>)[^\t]*)'/g, '$1\r')
      .replace(/\t=(.*?)%>/g, '\',$1,\'')
      .split('\t').join('\');')
      .split('%>').join('p.push(\'')
      .split('\r').join('\\\'') + '\');}return p.join(\'\');');
};

template.put = function (name, tmpl) {
  template.cache[name] = template.compile(tmpl);
};

template.lookup = function () {
  [].forEach.call(document.querySelectorAll('script[type="application/x-template"][data-template]'), function (tmpl) {
    template.put(tmpl.getAttribute('data-template'), tmpl.text);
  });
};

function findBubbleClose (str) {
  var level = 0;

  for( var i = 0, len = str.length; i < len ; i++ ) {
    if( str[i] === '(' ) {
      level++;
    } else if( str[i] === ')' ) {
      if( level === 0 ) {
        return i;
      } else {
        level--;
      }
    }
  }

  return -1;
}

function hasSelector (selector, rootElement) {
  var splitHas = selector.split(':has(');

  return splitHas.reduce(function (matches, partial) {

    var closePosition = findBubbleClose(partial),
        hasFilter = partial.substr(0, closePosition),
        partialQuery = partial.substr(closePosition + 1).trim();

    if( closePosition === -1 ) {
      throw new Error('malformed selector');
    }

    matches = matches.filter(function (element) {
      return element.querySelector(hasFilter);
    });

    if( partialQuery ) {
      var submatches = [];

      matches.forEach(function (element) {
        [].push.apply(submatches, element.querySelectorAll(partialQuery) );
      });

      return submatches;
    }

    return matches;

  }, [].slice.call( (rootElement || document).querySelectorAll( splitHas.shift() ) ) );
}

function querySelector (selector, rootElement) {
  // 'tr:has(> .row) div:has(span) img'.split(':has(');
  if( !selector ) {
    return [];
  }

  if( !/\:has\(/.test(selector) ) {
    return [].slice.call( (rootElement || document).querySelectorAll( selector ) );
  }

  return hasSelector(selector);
}

function cssQuery (_selector, rootElement) {
  var selectors = _selector.split(/\s*,\s*/);

  return selectors.reduce(function (list, selector) {
    return list.concat( querySelector(selector, rootElement) );
  }, []);
}

function getAmount (amount) {
  var prefix = '';

  if( amount < 0 ) {
    prefix = '-';
    amount = 0 - amount;
  }

  if( !amount ) {
    return '0,00';
  } else if( amount < 10 ) {
    return '0,0' + amount;
  } else if( amount < 100 ) {
    return '0,' + amount;
  }
  return prefix + ('' + amount).replace(/..$/, ',$&');
}

var cssHack = (function () {
  var cache = {},
      hacks = {
        overlay: '.aplazame-overlay { font-family: \'Montserrat\', sans-serif; position: fixed; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; width: 100vw; height: 100vh; background: rgba(53, 64, 71, 0.9); text-align:center;} .aplazame-overlay::before{content:\'\';display:block;height:50vh;}.aplazame-logo-wrapper{display:inline-block;margin-top:-50%;} .aplazame-checkout-loading-text{color:#95A6B1;margin-top:14px;font-size:14px;}' +
                '@-webkit-keyframes aplazame-overlay{0%{opacity:0}0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:1}}@keyframes aplazame-overlay{0%{opacity:0}0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:1}}.aplazame-overlay{-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-name:aplazame-overlay;animation-name:aplazame-overlay}' +
                  '@-webkit-keyframes aplazame-logo-rotate360{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}60%,90%,to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes aplazame-logo-rotate360{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}60%,90%,to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes aplazame-logo-rotate360-delay{0%,30%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}90%,to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes aplazame-logo-rotate360-delay{0%,30%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}90%,to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes aplazame-logo-rotate720{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}90%,to{-webkit-transform:rotate(2turn);transform:rotate(2turn)}}@keyframes aplazame-logo-rotate720{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}90%,to{-webkit-transform:rotate(2turn);transform:rotate(2turn)}}.logo-aplazame{position:relative;display:inline-block;-webkit-transform:translateZ(0);transform:translateZ(0);width:150px;height:150px}.logo-aplazame .line-large,.logo-aplazame .line-short,.logo-aplazame .smile{stroke:#ddd}.logo-aplazame .smile-outline{stroke:#485259}@media (min-width:480px){.logo-aplazame{width:200px;height:200px}}.logo-aplazame svg{position:absolute;top:0;left:0;width:100%;height:100%}.logo-aplazame.animate .line-large,.logo-aplazame.animate .line-short,.logo-aplazame.animate .smile{-webkit-animation-timing-function:cubic-beizer(0,.35,.65,1);animation-timing-function:cubic-beizer(0,.35,.65,1);-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.logo-aplazame.animate .smile{-webkit-animation-name:aplazame-logo-rotate720;animation-name:aplazame-logo-rotate720;-webkit-animation-duration:1.5s;animation-duration:1.5s}.logo-aplazame.animate .line-large{-webkit-animation-name:aplazame-logo-rotate360;animation-name:aplazame-logo-rotate360;-webkit-animation-duration:1.5s;animation-duration:1.5s}.logo-aplazame.animate .line-short{-webkit-animation-name:aplazame-logo-rotate360-delay;animation-name:aplazame-logo-rotate360-delay;-webkit-animation-duration:1.5s;animation-duration:1.5s}',
        // blur: 'body > *:not(.aplazame-modal):not(.aplazame-overlay) { -webkit-filter: blur(0px); filter: blur(0px); transition: all 0.4s linear; } body.aplazame-blur > *:not(.aplazame-modal):not(.aplazame-overlay) { -webkit-filter: blur(3px); filter: blur(3px); }',
        blur: '@-webkit-keyframes aplazame-blur{0%{opacity:0;-webkit-filter:blur(0);filter:blur(0)}to{opacity:1;-webkit-filter:blur(3px);filter:blur(3px)}}@keyframes aplazame-blur{0%{opacity:0;-webkit-filter:blur(0);filter:blur(0)}to{opacity:1;-webkit-filter:blur(3px);filter:blur(3px)}}body.aplazame-blur>:not(.aplazame-modal):not(.aplazame-overlay){-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-name:aplazame-blur;animation-name:aplazame-blur;-webkit-filter:blur(3px);filter:blur(3px)}body.aplazame-unblur>:not(.aplazame-modal):not(.aplazame-overlay){-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-name:aplazame-blur;animation-name:aplazame-blur;-webkit-animation-direction:reverse;animation-direction:reverse}',
        // modal: '.aplazame-modal { height: 100%; } html, body { margin: 0; padding: 0; } @media (max-width: 767px) { body > *:not(.aplazame-modal) { display: none; } }'
        modal: '.aplazame-modal { height: 100%; } body { overflow: hidden; } .aplazame-modal.hide {display:none;}' +
               '@media (max-width: 600px) { html { background-color: #333A3E; } html, body { height: 100%; margin: 0; padding: 0; } body > *:not(.aplazame-modal) { display: none; } iframe.aplazame-modal { position: absolute; } }' +
               '@media (min-width: 601px) { .aplazame-modal { position: fixed; } }'
        // overflow: '/* html { height: 100%; } body { overflow: hidden; } */',
        // inputFocus: 'html, body { height: 100vh; overflow: hidden; }'
      };

  return function hack (hackName) {
    if( !cache[hackName] ) {
      var style = document.createElement('style');
      style.setAttribute('rel', 'stylesheet');
      style.setAttribute('type', 'text/css');
      style.textContent = hacks[hackName].replace(/;/g, ' !important;');

      var enabled = false;

      style.hack = function (enable) {
        enable = enable === undefined || enable;

        if( enable ) {
          if( enabled ) { return; }
          enabled = true;
          document.head.appendChild(style);
        } else {
          if( !enabled ) { return; }
          enabled = false;
          document.head.removeChild(style);
        }
      };

      cache[hackName] = style;
    }
    return cache[hackName];
  };
})();

function scrollTop (value) {
  if( value !== undefined ) {
    document.documentElement.scrollTop = value;
    document.body.scrollTop = value;
  }
  return document.documentElement.scrollTop || document.body.scrollTop;
}

var _classActions = {
  add: document.documentElement.classList ? function (element, className) {
    element.classList.add(className);
  } : function (element, className) {
    var RE_CLEANCLASS = new RegExp('\\b' + (className || '') + '\\b','');
    _classActions.remove(element, className);
    element.className += ' ' + className;
  },
  remove: document.documentElement.classList ? function (element, className) {
    element.classList.remove(className);
  } : function (element, className) {
    var RE_CLEANCLASS = new RegExp('\\b' + (className || '') + '\\b','');
    element.className = element.className.replace(RE_CLEANCLASS,'');
  },
  action: function (action, tools) {
    return function (element, className) {
      if( className.indexOf(' ') >= 0 ) {
        className.split(' ').forEach(function (cn) {
          _classActions[action](element, cn);
        });
      } else {
        _classActions[action](element, className);
      }
      return tools;
    };
  }
};

var tools = {
  isObject: _isObject,
  isFunction: _isFunction,
  isString: _isString,
  isNumber: _isNumber,
  isArray: _isArray,
  isDate: _isDate,
  isRegExp: _isRegExp,
  isElement: _isElement,
  listen: listen,
  once: once,
  ready: docReady,
  replaceKeys: replaceKeys,
  merge: merge,
  extend: extend,
  joinPath: joinPath,
  writeIframe: writeIframe,
  getIFrame: getIFrame,
  template: template,
  cssQuery: cssQuery,
  getAmount: getAmount,
  cssHack: cssHack,
  scrollTop: scrollTop,
  addClass: _classActions.action('add', tools),
  removeClass: _classActions.action('remove', tools)
};

module.exports = tools;
<|endoftext|>


<commit_before>'use strict';
/**
 * @file
 * User transformer.
 *
 * Wraps userstatus backend.
 */
import {pbkdf2} from 'crypto';

/**
 * Maps loan item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus loans response
 * @returns response with mapped keys
 */
function loan(loanItem) {

  let result = {loanId: loanItem.loanId.$,
                dueDate: loanItem.dateDue.$,
               title: loanItem.title.$};
  if (loanItem.author) {
    result.author = loanItem.author.$;
  }
  return result;
}

/**
 * Maps debt item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus orders response
 * @returns response with mapped keys
 */
function debt(debtItem) {

  let result = {amount: debtItem.fiscalTransactionAmount.$,
                currency: debtItem.fiscalTransactionCurrency.$,
                date: debtItem.fiscalTransactionDate.$.split('T')[0],
                title: debtItem.title.$};
  if (debtItem.author) {
    result.author = debtItem.author.$;
  }
  return result;
}

/**
 * Maps order item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus orders response
 * @returns response with mapped keys
 */
function order(orderItem) {
  let result = {title: orderItem.title.$,
                orderId: `${orderItem.orderType.$}:${orderItem.orderId.$}`,
                orderDate: orderItem.orderDate.$,
                status: orderItem.orderStatus.$,
                library: orderItem.pickUpAgency.$
               };
  if (orderItem.holdQueuePosition) {
    result.holdQueuePosition = orderItem.holdQueuePosition.$;
  }
  if (orderItem.author) {
    result.author = orderItem.author.$;
  }
  return result;
}

/**
 * Default transformer.
 * Wraps openuserstatus backend and returns user info
 *
 * @param {Object} params parameters from the user (no entries from this object is used)
 * @param {Object} context The context object fetched from smaug
 * @returns promise with result
 * @api public
 */
export default (request, context) => {

  if (!(context.get('user.id') && context.get('user.pin'))) {
    return {statusCode: 300, error: 'not logged in'};
  }
  let params = {
    agencyId: context.get('user.agency'),
    userId: context.get('user.id'),
    userPincode: context.get('user.pin'),
    'authentication.groupIdAut': context.get('netpunkt.group'),
    'authentication.passwordAut': context.get('netpunkt.password'),
    'authentication.userIdAut': context.get('netpunkt.user'),
    action: 'getUserStatus',
    outputType: 'json'
  };

  let idPromise = new Promise((resolve, reject) =>
    pbkdf2(context.get('user.agency').replace(/^DK-/, '') + ' ' + context.get('user.id'),
      context.get('user.salt'), 100000, 24, 'sha512', (err, key) => err ? reject(err) : resolve(key)));

  return context.call('openuserstatus', params).then(body => idPromise.then(id => {

    let loans = [];
    if (body.data.getUserStatusResponse.userStatus.loanedItems.loan) {
      loans = body.data.getUserStatusResponse.userStatus.loanedItems.loan;
    }
    let orders = [];
    if (body.data.getUserStatusResponse.userStatus.orderedItems.order) {
      orders = body.data.getUserStatusResponse.userStatus.orderedItems.order;
    }

    let debts = [];
    if (body.data.getUserStatusResponse.userStatus.fiscalAccount) {
      debts = body.data.getUserStatusResponse.userStatus.fiscalAccount.fiscalTransaction;
    }

    let data = {id: id.toString('base64'),
                loans: loans.map(loan),
                orders: orders.map(order),
                debt: debts.map(debt)
               };

    if (context.get('services.ddbcmsapi')) {
      data.ddbcmsapi = context.get('services.ddbcmsapi');
    }

    return {statusCode: 200, data: data};
  }));
};
<commit_msg>fix error introduced in #515<commit_after>'use strict';
/**
 * @file
 * User transformer.
 *
 * Wraps userstatus backend.
 */
import {pbkdf2} from 'crypto';

/**
 * Maps loan item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus loans response
 * @returns response with mapped keys
 */
function loan(loanItem) {

  let result = {loanId: loanItem.loanId.$,
                dueDate: loanItem.dateDue.$,
               title: loanItem.title.$};
  if (loanItem.author) {
    result.author = loanItem.author.$;
  }
  return result;
}

/**
 * Maps debt item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus orders response
 * @returns response with mapped keys
 */
function debt(debtItem) {

  let result = {amount: debtItem.fiscalTransactionAmount.$,
                currency: debtItem.fiscalTransactionCurrency.$,
                date: debtItem.fiscalTransactionDate.$.split('T')[0],
                title: debtItem.title.$};
  if (debtItem.author) {
    result.author = debtItem.author.$;
  }
  return result;
}

/**
 * Maps order item from backend response to serviceprovider api
 * @param {Object} obj openuserstatus orders response
 * @returns response with mapped keys
 */
function order(orderItem) {
  let result = {title: orderItem.title.$,
                orderId: `${orderItem.orderType.$}:${orderItem.orderId.$}`,
                orderDate: orderItem.orderDate.$,
                status: orderItem.orderStatus.$,
                library: orderItem.pickUpAgency.$
               };
  if (orderItem.holdQueuePosition) {
    result.holdQueuePosition = orderItem.holdQueuePosition.$;
  }
  if (orderItem.author) {
    result.author = orderItem.author.$;
  }
  return result;
}

/**
 * Default transformer.
 * Wraps openuserstatus backend and returns user info
 *
 * @param {Object} params parameters from the user (no entries from this object is used)
 * @param {Object} context The context object fetched from smaug
 * @returns promise with result
 * @api public
 */
export default (request, context) => {

  if (!(context.get('user.id') && context.get('user.pin'))) {
    return {statusCode: 300, error: 'not logged in'};
  }
  let params = {
    agencyId: context.get('user.agency'),
    userId: context.get('user.id'),
    userPincode: context.get('user.pin'),
    'authentication.groupIdAut': context.get('netpunkt.group'),
    'authentication.passwordAut': context.get('netpunkt.password'),
    'authentication.userIdAut': context.get('netpunkt.user'),
    action: 'getUserStatus',
    outputType: 'json'
  };

  let idPromise = new Promise((resolve, reject) =>
    pbkdf2(context.get('user.agency').replace(/^DK-/, '') + ' ' + context.get('user.id'),
      context.get('user.salt'), 100000, 24, 'sha512', (err, key) => err ? reject(err) : resolve(key)));

  return context.call('openuserstatus', params).then(body => idPromise.then(id => {

    let loans = [];
    if (body.data.getUserStatusResponse.userStatus.loanedItems.loan) {
      loans = body.data.getUserStatusResponse.userStatus.loanedItems.loan;
    }
    let orders = [];
    if (body.data.getUserStatusResponse.userStatus.orderedItems.order) {
      orders = body.data.getUserStatusResponse.userStatus.orderedItems.order;
    }

    let debts = [];
    if (body.data.getUserStatusResponse.userStatus.fiscalAccount) {
      debts = body.data.getUserStatusResponse.userStatus.fiscalAccount.fiscalTransaction || [];
    }

    let data = {id: id.toString('base64'),
                loans: loans.map(loan),
                orders: orders.map(order),
                debt: debts.map(debt)
               };

    if (context.get('services.ddbcmsapi')) {
      data.ddbcmsapi = context.get('services.ddbcmsapi');
    }

    return {statusCode: 200, data: data};
  }));
};
<|endoftext|>


<commit_before>// This event executes when a new server (guild) is created

module.exports = async (client, guild) => {

    client.log("log", `${guild.name} (${guild.id}) server was added`, "GCreate");
    client.botLogEmbed(client, `I was added to the server ${guild.name}`, "Guild Create", 0x00FF00);

    // This creates guild settings from the defualt settings defined in the
    // config file
    await client.settings.set(guild.id, client.config.defaultSettings);

    /*
    --- Bot Install Welcome Messages ---
    Sends two messages, one to the guild welcoming them
    The other is to the guild owner explaining how to change the config file
    */

    const settings = client.settings.get(guild.id);

    const guildOwnerWelcome = `Hello! Thanks for adding me to your ${guild.name} server! In order to get started, please edit some of the settings in any channel on your server. To get this done, you'll use the following command structure:\`\`\`${settings.prefix}set edit <key-to-change> <new-value>\`\`\`You can see all of the current settings by just using \`${settings.prefix}set\`.

First, change the **prefix** if you want to. To do this type \`-set edit prefix <new-character>\`.

Use the same command structure *(but with your new prefix, if you changed it)* to allow your **moderators** and **administrators** to use my higher level functions. Right now you're the only one who can probably change these things and use my other system commands.
・\`moderatorRole <your-moderator-role-name>\`
・\`admistratorRole <your-administrator-role-name>\`

In order to get a **log** of the system commands other moderators use, change the mod-log channel to the channel name only your moderators use or a separate channel for logs: \`modLogChannel <channel-name>\`.

Here's a list of other important settings you should consider changing. The \`key\` is listed with the system default **value**:
・\`aChannel\`: **general**
・\`welcomeEnabled\`: **true**
・\`pointsEnabled\`: **false**
・\`roleRewardsEnabled\`: **true** (will only work if \`pointsEnabled\` is **true**)
・\`guildReset\`: **2030** (based on EST, must be 24-hour time)

Now that you have everything configured to your server, use the **\`help\`** command to read more about what I can do. You can also check out my user manual for more in-depth details of my functions: https://rmgirardin.gitbooks.io/mouse-bot-user-manual/`;

    guild.owner.user.send(guildOwnerWelcome).catch(console.error);

    const guildWelcome = `*Vrrrrrvd tkk tkkdtk*
Hello! I am ${client.user.username}! I have a lot of commands you can use (or not use). Type \`${settings.prefix}help\` for all of my commands or check out my user manual to understand everything I can do: https://rmgirardin.gitbooks.io/mouse-bot-user-manual/`;

    client.aMessage(guild, guildWelcome);

};
<commit_msg>Cleaned up introduction<commit_after>// This event executes when a new server (guild) is created

module.exports = async (client, guild) => {

    client.log("log", `${guild.name} (${guild.id}) server was added`, "GCreate");
    client.botLogEmbed(client, `I was added to the server ${guild.name}`, "Guild Create", 0x00FF00);

    // This creates guild settings from the defualt settings defined in the
    // config file
    await client.settings.set(guild.id, client.config.defaultSettings);

    /*
    --- Bot Install Welcome Messages ---
    Sends two messages, one to the guild welcoming them
    The other is to the guild owner explaining how to change the config file
    */

    const settings = client.settings.get(guild.id);

    const guildOwnerWelcome = `Hello! Thanks for adding me to ${guild.name}! You can change the **prefix** by typing \`/set edit prefix <new-character>\`. If you want to modify some of my deeper function, check out the guide here: https://rmgirardin.gitbooks.io/mouse-bot-user-manual/content/server-config.html`;

    guild.owner.user.send(guildOwnerWelcome).catch(console.error);

    const guildWelcome = `*Vrrrrrvd tkk tkkdtk*
Hello! I am ${client.user.username}! I have a lot of commands you can use (or not use). Type \`${settings.prefix}help\` for all of my commands or check out my user manual to understand everything I can do: https://rmgirardin.gitbooks.io/mouse-bot-user-manual/`;

    client.aMessage(guild, guildWelcome);

};
<|endoftext|>


<commit_before>import test from 'ava';
import webpack from 'webpack';
import fs from 'fs';
import path from 'path';

import options from './webpack.config.prod.js';

test.cb('Supports minification as well as CSS modules', t => {
  webpack(options, function(err, stats) {
    if (err) {
      return t.end(err);
    } else if (stats.hasErrors()) {
      return t.end(stats.toString());
    }

    const { assets } = stats.toJson();
    const files = assets.map(x => x.name);

    t.true(files.includes('index.html'));
    t.true(files.includes('who.html'));
    t.true(files.includes('how.html'));
    t.true(files.includes('log-in.html'));
    t.true(files.includes('sig-up.html'));
    t.true(files.includes('404.html'));

    t.true(files.includes('app.css'));
    t.true(files.includes('app.js'));

    // Test size in MB. We want to make sure this bundle was minified since we
    // are using the minify JS plugin
    const bundle = assets[files.indexOf('app.js')];
    t.true((bundle.size / 1000) < 300);

    t.end();
  });
});
<commit_msg>Fix issue with redux test<commit_after>import test from 'ava';
import webpack from 'webpack';
import fs from 'fs';
import path from 'path';

import options from './webpack.config.prod.js';

test.cb('Supports redux apps that must be wrapped in <Provider>', t => {
  webpack(options, function(err, stats) {
    if (err) {
      return t.end(err);
    } else if (stats.hasErrors()) {
      return t.end(stats.toString());
    }

    const { assets } = stats.toJson();
    const files = assets.map(x => x.name);

    t.true(files.includes('index.html'));
    t.true(files.includes('who.html'));
    t.true(files.includes('how.html'));
    t.true(files.includes('log-in.html'));
    t.true(files.includes('sign-up.html'));
    t.true(files.includes('404.html'));

    t.true(files.includes('app.css'));
    t.true(files.includes('app.js'));

    // Test size in MB. We want to make sure this bundle was minified since we
    // are using the minify JS plugin
    const bundle = assets[files.indexOf('app.js')];
    t.true((bundle.size / 1000) < 500);

    t.end();
  });
});
<|endoftext|>


<commit_before>function TreeViewItem( label, id ) {

	this.label = label || '';
	this.children = [];
	this.collapsed = false;
	this.id = id || null;

	this.li = document.createElement( 'li' );
	var p = document.createElement( 'p' );
	p.textContent = this.label;
	this.span = document.createElement( 'span' );
	this.span.className = 'icon item';
	this.li.appendChild( this.span );
	this.li.appendChild( p );

	this.ul = null;
	this.parent = null;
	this.parentNode = null;

	var li = this.li;

	p.addEventListener( 'click', function( e ) {

		this.parent.clear();
		p.classList.add( 'active' );

		this.parent.onSelect( this.id );

		e.preventDefault();

	}.bind( this ) );

	this.span.addEventListener( 'click', function( e ) {

		li.classList.toggle( 'collapsed' );

		e.preventDefault();

	}.bind( this ) );

}

TreeViewItem.prototype.createRootNode = function() {

	this.ul = document.createElement( 'ul' );

}

TreeViewItem.prototype.render = function() {

}

TreeViewItem.prototype.appendChild = function( child ) {

	child.parent = this.parent;
	child.parentNode = this;

	if( this.ul === null ) {
		this.ul = document.createElement( 'ul' );
		this.li.appendChild( this.ul );
	}

	this.children.push( child );
	this.ul.appendChild( child.li );

	this.span.className = 'icon button';

}

TreeViewItem.prototype.removeChild = function( child ) {

	child.parent = null;
	child.parentNode = null;

	if( this.ul === null ) {
		this.li.removeChild( this.ul );
	}

	for( var j = 0; j < this.children.length; j++ ) {
		if( this.children[ j ] === child ) {
			this.children.splice( j, 1 );
			break;
		}
	}
	this.ul.removeChild( child.li );

	if( this.children.length ) {
		this.span.className = 'icon item';
	}

}

function TreeView( base ) {

	this.base = base;
	this.root = new TreeViewItem( 'WebGLRenderer');
	this.root.createRootNode();
	this.root.ul.classList.add( 'treeView' );
	this.root.parent = this;
	this.base.appendChild( this.root.ul );

}

TreeView.prototype.clear = function() {

	var sel = this.root.ul.querySelector( '.active' );
	if( sel ) sel.classList.remove( 'active' );

}

TreeView.prototype.getRoot = function() {

	return this.root;

}

TreeView.prototype.render = function() {

}

TreeView.prototype.onSelect = function() {

}<commit_msg>fixed treeView collapsing single node<commit_after>function TreeViewItem( label, id ) {

	this.label = label || '';
	this.children = [];
	this.collapsed = false;
	this.id = id || null;

	this.li = document.createElement( 'li' );
	var p = document.createElement( 'p' );
	p.textContent = this.label;
	this.span = document.createElement( 'span' );
	this.span.className = 'icon item';
	this.li.appendChild( this.span );
	this.li.appendChild( p );

	this.ul = null;
	this.parent = null;
	this.parentNode = null;

	var li = this.li;

	p.addEventListener( 'click', function( e ) {

		this.parent.clear();
		p.classList.add( 'active' );

		this.parent.onSelect( this.id );

		e.preventDefault();

	}.bind( this ) );

	this.span.addEventListener( 'click', function( e ) {

		if( this.children.length ) {
			li.classList.toggle( 'collapsed' );
		}

		e.preventDefault();

	}.bind( this ) );

}

TreeViewItem.prototype.createRootNode = function() {

	this.ul = document.createElement( 'ul' );

}

TreeViewItem.prototype.render = function() {

}

TreeViewItem.prototype.appendChild = function( child ) {

	child.parent = this.parent;
	child.parentNode = this;

	if( this.ul === null ) {
		this.ul = document.createElement( 'ul' );
		this.li.appendChild( this.ul );
	}

	this.children.push( child );
	this.ul.appendChild( child.li );

	this.span.className = 'icon button';

}

TreeViewItem.prototype.removeChild = function( child ) {

	child.parent = null;
	child.parentNode = null;

	if( this.ul === null ) {
		this.li.removeChild( this.ul );
	}

	for( var j = 0; j < this.children.length; j++ ) {
		if( this.children[ j ] === child ) {
			this.children.splice( j, 1 );
			break;
		}
	}
	this.ul.removeChild( child.li );

	if( this.children.length ) {
		this.span.className = 'icon item';
	}

}

function TreeView( base ) {

	this.base = base;
	this.root = new TreeViewItem( 'WebGLRenderer');
	this.root.createRootNode();
	this.root.ul.classList.add( 'treeView' );
	this.root.parent = this;
	this.base.appendChild( this.root.ul );

}

TreeView.prototype.clear = function() {

	var sel = this.root.ul.querySelector( '.active' );
	if( sel ) sel.classList.remove( 'active' );

}

TreeView.prototype.getRoot = function() {

	return this.root;

}

TreeView.prototype.render = function() {

}

TreeView.prototype.onSelect = function() {

}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

/**
 * Butter Textbox Widget Wrapper
 *
 * A simple input textbox with cross-browser click-to-select functionality.
 * Clicking this textbox will cause the contents to be selected.  The next
 * click will position the cursor.  Getting this to work cross-browser is
 * harder than it should be, especially on Chrome.  See:
 * http://code.google.com/p/chromium/issues/detail?id=4505
 *
 * The textbox manages listeners carefully in order to have mouse clicks
 * do what the user expects.  On creation, `focus` and `mouseup` handlers
 * are added to the element.  When the first `focus` event happens, the
 * contents of the element are selected, and the `focus` handler is removed,
 * so that the next click doesn't re-select.  The `mouseup` event that
 * follows the `focus` click is ignored (needed on WebKit), but subsequent
 * `mouseup` events are processed normally, so the selection can be broken.
 * Once the element receives `blur` the handlers are added back.
 **/

define( [], function(){

  function __highlight( e ){
    var input = e.target;
    input.select();
    input.removeEventListener( "focus", __highlight, false );
  }

  function __ignoreMouseUp( e ){
    e.preventDefault();
    var input = e.target;
    input.removeEventListener( "mouseup", __ignoreMouseUp, false );
  }

  function __addListeners( input ){
    input.addEventListener( "focus", __highlight, false );
    input.addEventListener( "mouseup", __ignoreMouseUp, false );
  }

  return function( input ){
    if( !(input && input.text) ){
      throw "Expected an input element of type text";
    }

    input.addEventListener(
      "blur",
      function( e ){
        __addListeners( e.target );
      },
      false
    );

    __addListeners( input );

    return input;
  };

});
<commit_msg>Fix style nit in textbox.js<commit_after>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

/**
 * Butter Textbox Widget Wrapper
 *
 * A simple input textbox with cross-browser click-to-select functionality.
 * Clicking this textbox will cause the contents to be selected.  The next
 * click will position the cursor.  Getting this to work cross-browser is
 * harder than it should be, especially on Chrome.  See:
 * http://code.google.com/p/chromium/issues/detail?id=4505
 *
 * The textbox manages listeners carefully in order to have mouse clicks
 * do what the user expects.  On creation, `focus` and `mouseup` handlers
 * are added to the element.  When the first `focus` event happens, the
 * contents of the element are selected, and the `focus` handler is removed,
 * so that the next click doesn't re-select.  The `mouseup` event that
 * follows the `focus` click is ignored (needed on WebKit), but subsequent
 * `mouseup` events are processed normally, so the selection can be broken.
 * Once the element receives `blur` the handlers are added back.
 **/

define( [], function(){

  function __highlight( e ){
    var input = e.target;
    input.select();
    input.removeEventListener( "focus", __highlight, false );
  }

  function __ignoreMouseUp( e ){
    e.preventDefault();
    var input = e.target;
    input.removeEventListener( "mouseup", __ignoreMouseUp, false );
  }

  function __addListeners( input ){
    input.addEventListener( "focus", __highlight, false );
    input.addEventListener( "mouseup", __ignoreMouseUp, false );
  }

  return function( input ){
    if( !(input && input.text) ){
      throw "Expected an input element of type text";
    }

    input.addEventListener( "blur", function( e ){
        __addListeners( e.target );
    }, false);

    __addListeners( input );

    return input;
  };

});
<|endoftext|>


<commit_before>import Client from './client';
import getScope from './get-scope.ts';

export default async (sentry, error, apiUrl, configFiles) => {
  // Do not report errors in development
  if (!process.pkg) {
    return;
  }

  let user = null;
  let team = null;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
  }

  if (user || team) {
    sentry.withScope(scope => {
      if (user) {
        const spec = {
          email: user.email,
          id: user.uid
        };

        if (user.username) {
          spec.username = user.username;
        }

        if (user.name) {
          spec.name = user.name;
        }

        scope.setUser(spec);
      }

      if (team) {
        scope.setTag('currentTeam', team.id);
      }

      // Disable sending `argv` until we properly remove sensitive data
      // scope.setExtra('argv', process.argv);

      sentry.captureException(error);
    });
  } else {
    sentry.captureException(error);
  }

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<commit_msg>Added back sending process args to Sentry (#1966)<commit_after>import Client from './client';
import getScope from './get-scope.ts';
import getArgs from './get-args';

export default async (sentry, error, apiUrl, configFiles) => {
  // Do not report errors in development
  if (!process.pkg) {
    return;
  }

  let user = null;
  let team = null;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
  }

  if (user || team) {
    sentry.withScope(scope => {
      if (user) {
        const spec = {
          email: user.email,
          id: user.uid
        };

        if (user.username) {
          spec.username = user.username;
        }

        if (user.name) {
          spec.name = user.name;
        }

        scope.setUser(spec);
      }

      if (team) {
        scope.setTag('currentTeam', team.id);
      }

      // Report process.argv without sensitive data
      let args
      try {
        args = getArgs(process.argv.slice(2), {});
      } catch (_) {}

      if (args) {
        const flags = ['--env', '--build-env', '--token']
        for (const flag of flags) {
          if (args[flag]) args[flag] = 'REDACTED';
        }
        if (args._[0].startsWith('secret') && args._[1] === 'add' && args._[3]) {
          args._[3] = 'REDACTED';
        }
        scope.setExtra('args', args);
      }

      sentry.captureException(error);
    });
  } else {
    sentry.captureException(error);
  }

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<|endoftext|>


<commit_before>/*
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
 * Based on...
 * ChromaCheck 1.16
 * author Roel Nieskens, https://pixelambacht.nl
 * MIT license
 */

let colrFontSupported = undefined;

async function isColrFontSupported() {
    if (colrFontSupported !== undefined) {
        return colrFontSupported;
    }

    console.log("Checking for COLR support");

    // Firefox has supported COLR fonts since version 26
    // but doesn't support the check below with content blocking enabled.
    if (navigator.userAgent.includes("Firefox")) {
        colrFontSupported = true;
        console.log("Browser is Firefox - assuming COLR is supported");
        return colrFontSupported;
    }

    try {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const img = new Image();
        // eslint-disable-next-line
        const fontCOLR = 'd09GRgABAAAAAAKAAAwAAAAAAowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDT0xSAAACVAAAABYAAAAYAAIAJUNQQUwAAAJsAAAAEgAAABLJAAAQT1MvMgAAAYAAAAA6AAAAYBfxJ0pjbWFwAAABxAAAACcAAAAsAAzpM2dseWYAAAH0AAAAGgAAABoNIh0kaGVhZAAAARwAAAAvAAAANgxLumdoaGVhAAABTAAAABUAAAAkCAEEAmhtdHgAAAG8AAAABgAAAAYEAAAAbG9jYQAAAewAAAAGAAAABgANAABtYXhwAAABZAAAABsAAAAgAg4AHW5hbWUAAAIQAAAAOAAAAD4C5wsecG9zdAAAAkgAAAAMAAAAIAADAAB4AWNgZGAAYQ5+qdB4fpuvDNIsDCBwaQGTAIi+VlscBaJZGMDiHAxMIAoAtjIF/QB4AWNgZGBgYQACOAkUQQWMAAGRABAAAAB4AWNgZGBgYGJgAdMMUJILJMQgAWICAAH3AC4AeAFjYGFhYJzAwMrAwDST6QwDA0M/hGZ8zWDMyMmAChgFkDgKQMBw4CXDSwYWEBdIYgAFBgYA/8sIdAAABAAAAAAAAAB4AWNgYGBkYAZiBgYeBhYGBSDNAoRA/kuG//8hpDgjWJ4BAFVMBiYAAAAAAAANAAAAAQAAAAAEAAQAAAMAABEhESEEAPwABAD8AAAAeAEtxgUNgAAAAMHHIQTShTlOAty9/4bf7AARCwlBNhBw4L/43qXjYGUmf19TMuLcj/BJL3XfBg54AWNgZsALAAB9AAR4AWNgYGAEYj4gFgGygGwICQACOwAoAAAAAAABAAEAAQAAAA4AAAAAyP8AAA==';
        const svg = `
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="100" style="background:#fff;fill:#000;">
            <style type="text/css">
                @font-face {
                    font-family: "chromacheck-colr";
                    src: url(data:application/x-font-woff;base64,${fontCOLR}) format("woff");
                }
            </style>
            <text x="0" y="0" font-size="20">
                <tspan font-family="chromacheck-colr" x="0" dy="20"></tspan>
            </text>
        </svg>`;
        canvas.width = 20;
        canvas.height = 100;

        img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);

        // FIXME wait for safari load our colr font
        const wait = ms => new Promise((r, j)=>setTimeout(r, ms));
        await wait(500);

        console.log("Drawing canvas to detect COLR support");
        context.drawImage(img, 0, 0);
        colrFontSupported = (context.getImageData(10, 10, 1, 1).data[0] === 200);
        console.log("Canvas check revealed COLR is supported? " + colrFontSupported);
    } catch (e) {
        console.error("Couldn't load COLR font", e);
        colrFontSupported = false;
    }

    console.log({colrFontSupported});
    return colrFontSupported;
}

export async function fixupColorFonts() {
    if (colrFontSupported !== undefined) {
        return;
    }

    if (await isColrFontSupported()) {
        const path = `url('${require("../../res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2")}')`;
        document.fonts.add(new FontFace("Twemoji", path, {}));
        // For at least Chrome on Windows 10, we have to explictly add extra
        // weights for the emoji to appear in bold messages, etc.
        document.fonts.add(new FontFace("Twemoji", path, { weight: 600 }));
        document.fonts.add(new FontFace("Twemoji", path, { weight: 700 }));
    }
    // if not supported, the browser will fall back to one of the native fonts specified.
}

<commit_msg>Minus one log<commit_after>/*
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
 * Based on...
 * ChromaCheck 1.16
 * author Roel Nieskens, https://pixelambacht.nl
 * MIT license
 */

let colrFontSupported = undefined;

async function isColrFontSupported() {
    if (colrFontSupported !== undefined) {
        return colrFontSupported;
    }

    console.log("Checking for COLR support");

    // Firefox has supported COLR fonts since version 26
    // but doesn't support the check below with content blocking enabled.
    if (navigator.userAgent.includes("Firefox")) {
        colrFontSupported = true;
        console.log("Browser is Firefox - assuming COLR is supported");
        return colrFontSupported;
    }

    try {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const img = new Image();
        // eslint-disable-next-line
        const fontCOLR = 'd09GRgABAAAAAAKAAAwAAAAAAowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDT0xSAAACVAAAABYAAAAYAAIAJUNQQUwAAAJsAAAAEgAAABLJAAAQT1MvMgAAAYAAAAA6AAAAYBfxJ0pjbWFwAAABxAAAACcAAAAsAAzpM2dseWYAAAH0AAAAGgAAABoNIh0kaGVhZAAAARwAAAAvAAAANgxLumdoaGVhAAABTAAAABUAAAAkCAEEAmhtdHgAAAG8AAAABgAAAAYEAAAAbG9jYQAAAewAAAAGAAAABgANAABtYXhwAAABZAAAABsAAAAgAg4AHW5hbWUAAAIQAAAAOAAAAD4C5wsecG9zdAAAAkgAAAAMAAAAIAADAAB4AWNgZGAAYQ5+qdB4fpuvDNIsDCBwaQGTAIi+VlscBaJZGMDiHAxMIAoAtjIF/QB4AWNgZGBgYQACOAkUQQWMAAGRABAAAAB4AWNgZGBgYGJgAdMMUJILJMQgAWICAAH3AC4AeAFjYGFhYJzAwMrAwDST6QwDA0M/hGZ8zWDMyMmAChgFkDgKQMBw4CXDSwYWEBdIYgAFBgYA/8sIdAAABAAAAAAAAAB4AWNgYGBkYAZiBgYeBhYGBSDNAoRA/kuG//8hpDgjWJ4BAFVMBiYAAAAAAAANAAAAAQAAAAAEAAQAAAMAABEhESEEAPwABAD8AAAAeAEtxgUNgAAAAMHHIQTShTlOAty9/4bf7AARCwlBNhBw4L/43qXjYGUmf19TMuLcj/BJL3XfBg54AWNgZsALAAB9AAR4AWNgYGAEYj4gFgGygGwICQACOwAoAAAAAAABAAEAAQAAAA4AAAAAyP8AAA==';
        const svg = `
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="100" style="background:#fff;fill:#000;">
            <style type="text/css">
                @font-face {
                    font-family: "chromacheck-colr";
                    src: url(data:application/x-font-woff;base64,${fontCOLR}) format("woff");
                }
            </style>
            <text x="0" y="0" font-size="20">
                <tspan font-family="chromacheck-colr" x="0" dy="20"></tspan>
            </text>
        </svg>`;
        canvas.width = 20;
        canvas.height = 100;

        img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);

        // FIXME wait for safari load our colr font
        const wait = ms => new Promise((r, j)=>setTimeout(r, ms));
        await wait(500);

        console.log("Drawing canvas to detect COLR support");
        context.drawImage(img, 0, 0);
        colrFontSupported = (context.getImageData(10, 10, 1, 1).data[0] === 200);
        console.log("Canvas check revealed COLR is supported? " + colrFontSupported);
    } catch (e) {
        console.error("Couldn't load COLR font", e);
        colrFontSupported = false;
    }

    return colrFontSupported;
}

export async function fixupColorFonts() {
    if (colrFontSupported !== undefined) {
        return;
    }

    if (await isColrFontSupported()) {
        const path = `url('${require("../../res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2")}')`;
        document.fonts.add(new FontFace("Twemoji", path, {}));
        // For at least Chrome on Windows 10, we have to explictly add extra
        // weights for the emoji to appear in bold messages, etc.
        document.fonts.add(new FontFace("Twemoji", path, { weight: 600 }));
        document.fonts.add(new FontFace("Twemoji", path, { weight: 700 }));
    }
    // if not supported, the browser will fall back to one of the native fonts specified.
}

<|endoftext|>