<commit_before>// Manage rendering styles

import Utils from '../utils/utils';
import ShaderProgram from '../gl/shader_program';
import shaderSources from '../gl/shader_sources'; // built-in shaders
import {Style} from './style';
import {mergeObjects} from './rule';

import log from 'loglevel';

export var StyleManager = {};
export var Styles = {};
export var BaseStyles = {};

StyleManager.styles = Styles;

// Set the base object used to instantiate styles
StyleManager.baseStyle = Style;

// Global configuration for all styles
StyleManager.init = function () {
    if (StyleManager.initialized) {
        return;
    }

    ShaderProgram.removeBlock('global');

    // Layer re-ordering function
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/layer_order']);

    // Feature selection global
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/selection_globals']);

    // World position wrapping
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/world_position_wrap']);

    // Feature selection vertex shader support
    ShaderProgram.replaceBlock('feature-selection-vertex', shaderSources['gl/shaders/selection_vertex']);

    // assume min 16-bit depth buffer, in practice uses 14-bits, 1 extra bit to handle virtual half-layers
    // for outlines (inserted in between layers), another extra bit to prevent precision loss
    ShaderProgram.defines.TANGRAM_LAYER_DELTA = 1 / (1 << 14);

    StyleManager.initialized = true;
};

// Destroy all styles for a given GL context
StyleManager.destroy = function (gl) {
    Object.keys(Styles).forEach((_name) => {
        var style = Styles[_name];
        if (style.gl === gl) {
            log.trace(`StyleManager.destroy: destroying render style ${style.name}`);

            if (!style.isBuiltIn()) {
                StyleManager.remove(style.name);
            }
            style.destroy();
        }
    });
};

// Register a style
StyleManager.register = function (style) {
    Styles[style.name] = style;
    BaseStyles[style.name] = style;
};

// Remove a style
StyleManager.remove = function (name) {
    delete Styles[name];
};

// Load style definitions from external URLs
StyleManager.loadRemoteStyles = function (styles, base) {
    // Collect URLs and modes to import from them
    // This is done as a separate step becuase it is possible to import multiple modes from a single
    // URL, and we want to avoid duplicate calls for the same file.
    var urls = {};
    for (var name in styles) {
        var style = styles[name];
        if (style.url) {
            let url = style.url;
            if (base) {
                url = Utils.addBaseURL(url, base);
            }

            if (!urls[url]) {
                urls[url] = [];
            }

            // Make a list of the styles to import for this URL
            urls[url].push({
                target_name: name,
                source_name: style.name || name
            });
        }
    }

    // As each URL finishes loading, replace the target style(s)
    return Promise.all(Object.keys(urls).map(url => {
        return new Promise((resolve, reject) => {
            Utils.loadResource(url).then((data) => {
                // Mixin remote styles, within each remote file
                // TODO: may not handle multiple levels of mixins, and will not handle nested remote files
                for (var source_name in data) {
                    let source_import = urls[url] && urls[url].find(s => s.source_name === source_name);
                    if (source_import) {
                        // use imported name if different from name in source file
                        data[source_name].name = source_import.target_name;
                    }
                    else {
                        data[source_name].name = source_name;
                    }

                    data[source_name] = StyleManager.mix(data[source_name], data);
                }

                // Add remote styles to local styles
                for (var target of urls[url]) {
                    if (data && data[target.source_name]) {
                        styles[target.target_name] = data[target.source_name];
                    }
                    else {
                        delete styles[target.target_name];
                        return reject(new Error(`StyleManager.loadRemoteStyles: error importing style ${target.target_name}, could not find source style ${target.source_name} in ${url}`));
                    }
                }
                resolve();

                this.selection = false;
            }).catch((error) => {
                log.error(`StyleManager.loadRemoteStyles: error importing style(s) ${JSON.stringify(urls[url])} from ${url}`, error);
            });
        });
    })).then(() => Promise.resolve(styles));
};

// Preload shader blocks from external URLs
StyleManager.loadShaderBlocks = function (styles, base) {
    if (!styles) {
        return Promise.resolve({});
    }
    var queue = [];
    for (var style of Utils.values(styles)) {
        if (style.shaders && style.shaders.blocks) {
            let _blocks = style.shaders.blocks;

            for (let [key, block] of Utils.entries(style.shaders.blocks)) {
                let _key = key;

                // Array of blocks
                if (Array.isArray(block)) {
                    for (let b=0; b < block.length; b++) {
                        if (typeof block[b] === 'object' && block[b].url) {
                            let _index = b;
                            let url = block[b].url;
                            if (base) {
                                url = Utils.addBaseURL(url, base);
                            }

                            queue.push(Utils.io(Utils.cacheBusterForUrl(url)).then((data) => {
                                _blocks[_key][_index] = data;
                            }).catch((error) => {
                                log.error(`StyleManager.loadShaderBlocks: error loading shader block`, _blocks, _key, _index, error);
                            }));
                        }
                    }
                }
                // Single block
                else if (typeof block === 'object' && block.url) {
                    let url = block.url;
                    if (base) {
                        url = Utils.addBaseURL(url, base);
                    }

                    queue.push(Utils.io(Utils.cacheBusterForUrl(url)).then((data) => {
                        _blocks[_key] = data;
                    }).catch((error) => {
                        log.error(`StyleManager.loadShaderBlocks: error loading shader block`, _blocks, _key, error);
                    }));
                }
            }
        }
    }
    return Promise.all(queue).then(() => Promise.resolve(styles)); // TODO: add error
};

StyleManager.mix = function (style, styles) {
    // Exit early if we have already applied mixins to this style
    if (style.mixed) {
        return style;
    }

    // Mixin sources, in order
    let sources = [];
    if (style.mix) {
        if (Array.isArray(style.mix)) {
            sources.push(...style.mix);
        }
        else {
            sources.push(style.mix);
        }
        sources = sources.map(x => styles[x]).filter(x => x && x !== style); // TODO: warning on trying to mix into self
    }
    sources.push(style);

    // Flags - OR'd, true if any style has it set
    style.animated = sources.some(x => x && x.animated);
    style.texcoords = sources.some(x => x && x.texcoords);

    // Overwrites - last definition wins
    style.base = sources.map(x => x.base).filter(x => x).pop();
    style.lighting = sources.map(x => x.lighting).filter(x => x != null).pop();
    style.texture = sources.map(x => x.texture).filter(x => x).pop();
    if (sources.some(x => x.blend)) {
        // only mix blend if explicitly set, otherwise let base style choose blending mode
        style.blend = sources.map(x => x.blend).filter(x => x).pop();
    }

    // Merges - property-specific rules for merging values
    style.defines = Object.assign({}, ...sources.map(x => x.defines).filter(x => x));
    style.material = Object.assign({}, ...sources.map(x => x.material).filter(x => x));

    let merge = sources.map(x => x.shaders).filter(x => x);
    let shaders = {};
    shaders.defines = Object.assign({}, ...merge.map(x => x.defines).filter(x => x));
    shaders.uniforms = Object.assign({}, ...merge.map(x => x.uniforms).filter(x => x));

    // Build a list of unique extensions
    shaders.extensions = Object.keys(merge
        .map(x => x.extensions)
        .filter(x => x)
        .reduce((prev, cur) => {
            // single extension
            if (typeof cur === 'string') {
                prev[cur] = true;
            }
            // array of extensions
            else {
                cur.forEach(x => prev[x] = true);
            }
            return prev;
        }, {}) || {}
    );

    // Mark all shader blocks for the target style as originating with its own name
    if (style.shaders && style.shaders.blocks) {
        style.shaders.block_scopes = style.shaders.block_scopes || {};
        for (let [k, block] of Utils.entries(style.shaders.blocks)) {
            style.shaders.block_scopes[k] = style.shaders.block_scopes[k] || [];
            if (Array.isArray(block)) {
                style.shaders.block_scopes[k].push(...block.map(() => style.name));
            }
            else {
                style.shaders.block_scopes[k].push(style.name);
            }
        }
    }

    // Merge shader blocks, keeping track of which style each block originated from
    for (let source of merge) {
        if (!source.blocks) {
            continue;
        }

        shaders.blocks = shaders.blocks || {};
        shaders.block_scopes = shaders.block_scopes || {};

        for (let [t, block] of Utils.entries(source.blocks)) {
            let block_scope = source.block_scopes[t];

            shaders.blocks[t] = shaders.blocks[t] || [];
            shaders.block_scopes[t] = shaders.block_scopes[t] || [];

            if (Array.isArray(block)) {
                shaders.blocks[t].push(...block);
                shaders.block_scopes[t].push(...block_scope);
            }
            else {
                shaders.blocks[t].push(block);
                shaders.block_scopes[t].push(block_scope);
            }
        }
    }

    style.shaders = shaders;
    style.mixed = true; // track that we already applied mixins (avoid dupe work later)

    return style;
};

// Create a new style
// name: name of new style
// config: properties of new style
// styles: working set of styles being built (used for mixing in existing styles)
StyleManager.create = function (name, config, styles = {}) {
    let style = mergeObjects({}, config); // deep copy
    style.name = name;

    // Style mixins
    style = StyleManager.mix(style, styles);

    // Has base style?
    // Only renderable (instantiated) styles should be included for run-time use
    // Others are intermediary/abstract, used during style composition but not execution
    if (style.base && BaseStyles[style.base]) {
        Styles[name] = style = Object.assign(Object.create(BaseStyles[style.base]), style);
    }

    return style;
};

// Called to create and initialize styles
StyleManager.build = function (styles, scene = {}) {
    // Sort styles by dependency, then build them
    let style_deps = Object.keys(styles).sort(
        (a, b) => StyleManager.inheritanceDepth(a, styles) - StyleManager.inheritanceDepth(b, styles)
    );

    // Only keep built-in base styles
    for (let sname in Styles) {
        if (!BaseStyles[sname]) {
            delete Styles[sname];
        }
        else {
            Styles[sname].reset();
        }
    }

    // Working set of styles being built
    let ws = {};
    for (let sname of style_deps) {
        ws[sname] = StyleManager.create(sname, styles[sname], ws);
    }

    StyleManager.initStyles(scene);
    return Styles;
};

// Initialize all styles
StyleManager.initStyles = function (scene) {
    // Initialize all
    for (let sname in Styles) {
        Styles[sname].init(scene);
    }
};

// Given a style key in a set of styles to add, count the length of the inheritance chain
// TODO: remove current (Styles) and future (styles) duplication, confusing
StyleManager.inheritanceDepth = function (key, styles) {
    let parents = 0;

    while(true) {
        let style = styles[key];
        if (!style) {
            // this is a scene def error, trying to extend a style that doesn't exist
            break;
        }

        // Dependency chain ends when this style isn't mixing in any others
        if (!style.mix) {
            break;
        }

        // Traverse next parent style
        parents++;

        if (Array.isArray(style.mix)) {
            // If multiple mixins, find the deepest one
            parents += Math.max(...style.mix.map(s => {
                // Trying to mix into itself!
                if (key === s) {
                    return;
                }

                return StyleManager.inheritanceDepth(s, styles);
            }));
            break;
        }
        else {
            // Trying to mix into itself!
            if (key === style.mix) {
                break;
            }

            // If single mixin, continue loop up the tree
            key = style.mix;
        }
    }
    return parents;
};

// Compile all styles
StyleManager.compile = function (keys, scene) {
    keys = keys || Object.keys(Styles);
    for (let key of keys) {
        let style = Styles[key];
        try {
            style.compile();
            log.trace(`StyleManager.compile(): compiled style ${key}`);
        }
        catch(error) {
            log.error(`StyleManager.compile(): error compiling style ${key}:`, error);

            scene.trigger('warning', {
                type: 'styles',
                message: `Error compiling style ${key}`,
                style,
                shader_errors: style.program && style.program.shader_errors
            });
        }
    }

    log.debug(`StyleManager.compile(): compiled all styles`);
};

// Get all styles with mesh data for a given tile
StyleManager.stylesForTile = function (tile) {
    let styles = [];
    for (let s in Styles) {
        if (Styles[s].hasDataForTile(tile)) {
            styles.push(s);
        }
    }
    return styles;
};
<commit_msg>styles: don't mixin shader blocks that have already been mixed in from an ancestor style e.g. support a 'diamond'-like mixin hierarchy where: B mixes A C mixes A D mixes [B, C] D should end up with only one copy of A<commit_after>// Manage rendering styles

import Utils from '../utils/utils';
import ShaderProgram from '../gl/shader_program';
import shaderSources from '../gl/shader_sources'; // built-in shaders
import {Style} from './style';
import {mergeObjects} from './rule';

import log from 'loglevel';

export var StyleManager = {};
export var Styles = {};
export var BaseStyles = {};

StyleManager.styles = Styles;

// Set the base object used to instantiate styles
StyleManager.baseStyle = Style;

// Global configuration for all styles
StyleManager.init = function () {
    if (StyleManager.initialized) {
        return;
    }

    ShaderProgram.removeBlock('global');

    // Layer re-ordering function
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/layer_order']);

    // Feature selection global
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/selection_globals']);

    // World position wrapping
    ShaderProgram.addBlock('global', shaderSources['gl/shaders/world_position_wrap']);

    // Feature selection vertex shader support
    ShaderProgram.replaceBlock('feature-selection-vertex', shaderSources['gl/shaders/selection_vertex']);

    // assume min 16-bit depth buffer, in practice uses 14-bits, 1 extra bit to handle virtual half-layers
    // for outlines (inserted in between layers), another extra bit to prevent precision loss
    ShaderProgram.defines.TANGRAM_LAYER_DELTA = 1 / (1 << 14);

    StyleManager.initialized = true;
};

// Destroy all styles for a given GL context
StyleManager.destroy = function (gl) {
    Object.keys(Styles).forEach((_name) => {
        var style = Styles[_name];
        if (style.gl === gl) {
            log.trace(`StyleManager.destroy: destroying render style ${style.name}`);

            if (!style.isBuiltIn()) {
                StyleManager.remove(style.name);
            }
            style.destroy();
        }
    });
};

// Register a style
StyleManager.register = function (style) {
    Styles[style.name] = style;
    BaseStyles[style.name] = style;
};

// Remove a style
StyleManager.remove = function (name) {
    delete Styles[name];
};

// Load style definitions from external URLs
StyleManager.loadRemoteStyles = function (styles, base) {
    // Collect URLs and modes to import from them
    // This is done as a separate step becuase it is possible to import multiple modes from a single
    // URL, and we want to avoid duplicate calls for the same file.
    var urls = {};
    for (var name in styles) {
        var style = styles[name];
        if (style.url) {
            let url = style.url;
            if (base) {
                url = Utils.addBaseURL(url, base);
            }

            if (!urls[url]) {
                urls[url] = [];
            }

            // Make a list of the styles to import for this URL
            urls[url].push({
                target_name: name,
                source_name: style.name || name
            });
        }
    }

    // As each URL finishes loading, replace the target style(s)
    return Promise.all(Object.keys(urls).map(url => {
        return new Promise((resolve, reject) => {
            Utils.loadResource(url).then((data) => {
                // Mixin remote styles, within each remote file
                // TODO: may not handle multiple levels of mixins, and will not handle nested remote files
                for (var source_name in data) {
                    let source_import = urls[url] && urls[url].find(s => s.source_name === source_name);
                    if (source_import) {
                        // use imported name if different from name in source file
                        data[source_name].name = source_import.target_name;
                    }
                    else {
                        data[source_name].name = source_name;
                    }

                    data[source_name] = StyleManager.mix(data[source_name], data);
                }

                // Add remote styles to local styles
                for (var target of urls[url]) {
                    if (data && data[target.source_name]) {
                        styles[target.target_name] = data[target.source_name];
                    }
                    else {
                        delete styles[target.target_name];
                        return reject(new Error(`StyleManager.loadRemoteStyles: error importing style ${target.target_name}, could not find source style ${target.source_name} in ${url}`));
                    }
                }
                resolve();

                this.selection = false;
            }).catch((error) => {
                log.error(`StyleManager.loadRemoteStyles: error importing style(s) ${JSON.stringify(urls[url])} from ${url}`, error);
            });
        });
    })).then(() => Promise.resolve(styles));
};

// Preload shader blocks from external URLs
StyleManager.loadShaderBlocks = function (styles, base) {
    if (!styles) {
        return Promise.resolve({});
    }
    var queue = [];
    for (var style of Utils.values(styles)) {
        if (style.shaders && style.shaders.blocks) {
            let _blocks = style.shaders.blocks;

            for (let [key, block] of Utils.entries(style.shaders.blocks)) {
                let _key = key;

                // Array of blocks
                if (Array.isArray(block)) {
                    for (let b=0; b < block.length; b++) {
                        if (typeof block[b] === 'object' && block[b].url) {
                            let _index = b;
                            let url = block[b].url;
                            if (base) {
                                url = Utils.addBaseURL(url, base);
                            }

                            queue.push(Utils.io(Utils.cacheBusterForUrl(url)).then((data) => {
                                _blocks[_key][_index] = data;
                            }).catch((error) => {
                                log.error(`StyleManager.loadShaderBlocks: error loading shader block`, _blocks, _key, _index, error);
                            }));
                        }
                    }
                }
                // Single block
                else if (typeof block === 'object' && block.url) {
                    let url = block.url;
                    if (base) {
                        url = Utils.addBaseURL(url, base);
                    }

                    queue.push(Utils.io(Utils.cacheBusterForUrl(url)).then((data) => {
                        _blocks[_key] = data;
                    }).catch((error) => {
                        log.error(`StyleManager.loadShaderBlocks: error loading shader block`, _blocks, _key, error);
                    }));
                }
            }
        }
    }
    return Promise.all(queue).then(() => Promise.resolve(styles)); // TODO: add error
};

StyleManager.mix = function (style, styles) {
    // Exit early if we have already applied mixins to this style
    if (style.mixed) {
        return style;
    }

    // Mixin sources, in order
    let sources = [];
    if (style.mix) {
        if (Array.isArray(style.mix)) {
            sources.push(...style.mix);
        }
        else {
            sources.push(style.mix);
        }
        sources = sources.map(x => styles[x]).filter(x => x && x !== style); // TODO: warning on trying to mix into self
    }
    sources.push(style);

    // Flags - OR'd, true if any style has it set
    style.animated = sources.some(x => x && x.animated);
    style.texcoords = sources.some(x => x && x.texcoords);

    // Overwrites - last definition wins
    style.base = sources.map(x => x.base).filter(x => x).pop();
    style.lighting = sources.map(x => x.lighting).filter(x => x != null).pop();
    style.texture = sources.map(x => x.texture).filter(x => x).pop();
    if (sources.some(x => x.blend)) {
        // only mix blend if explicitly set, otherwise let base style choose blending mode
        style.blend = sources.map(x => x.blend).filter(x => x).pop();
    }

    // Merges - property-specific rules for merging values
    style.defines = Object.assign({}, ...sources.map(x => x.defines).filter(x => x));
    style.material = Object.assign({}, ...sources.map(x => x.material).filter(x => x));

    let merge = sources.map(x => x.shaders).filter(x => x);
    let shaders = {};
    shaders.defines = Object.assign({}, ...merge.map(x => x.defines).filter(x => x));
    shaders.uniforms = Object.assign({}, ...merge.map(x => x.uniforms).filter(x => x));

    // Build a list of unique extensions
    shaders.extensions = Object.keys(merge
        .map(x => x.extensions)
        .filter(x => x)
        .reduce((prev, cur) => {
            // single extension
            if (typeof cur === 'string') {
                prev[cur] = true;
            }
            // array of extensions
            else {
                cur.forEach(x => prev[x] = true);
            }
            return prev;
        }, {}) || {}
    );

    // Mark all shader blocks for the target style as originating with its own name
    if (style.shaders && style.shaders.blocks) {
        style.shaders.block_scopes = style.shaders.block_scopes || {};
        for (let [k, block] of Utils.entries(style.shaders.blocks)) {
            style.shaders.block_scopes[k] = style.shaders.block_scopes[k] || [];
            if (Array.isArray(block)) {
                style.shaders.block_scopes[k].push(...block.map(() => style.name));
            }
            else {
                style.shaders.block_scopes[k].push(style.name);
            }
        }
    }

    // Merge shader blocks, keeping track of which style each block originated from
    let mixed = {}; // all scopes mixed so far
    for (let source of merge) {
        if (!source.blocks) {
            continue;
        }

        shaders.blocks = shaders.blocks || {};
        shaders.block_scopes = shaders.block_scopes || {};
        let mixed_source = {}; // scopes mixed for this source style

        for (let [t, block] of Utils.entries(source.blocks)) {
            let block_scope = source.block_scopes[t];

            shaders.blocks[t] = shaders.blocks[t] || [];
            shaders.block_scopes[t] = shaders.block_scopes[t] || [];

            // standardize on arrays (block can be single or multi-value)
            block = Array.isArray(block) ? block : [block];
            block_scope = Array.isArray(block_scope) ? block_scope : [block_scope];

            for (let b=0; b < block.length; b++) {
                // Skip blocks we've already mixed in from the same scope
                // Repeating scope indicates a diamond pattern where a style is being mixed multiple times
                if (mixed[block_scope[b]]) {
                    continue;
                }
                mixed_source[block_scope[b]] = true;

                shaders.blocks[t].push(block[b]);
                shaders.block_scopes[t].push(block_scope[b]);
            }
        }

        Object.assign(mixed, mixed_source); // add scopes mixed from this source
    }

    style.shaders = shaders;
    style.mixed = mixed; // track that we already applied mixins (avoid dupe work later)

    return style;
};

// Create a new style
// name: name of new style
// config: properties of new style
// styles: working set of styles being built (used for mixing in existing styles)
StyleManager.create = function (name, config, styles = {}) {
    let style = mergeObjects({}, config); // deep copy
    style.name = name;

    // Style mixins
    style = StyleManager.mix(style, styles);

    // Has base style?
    // Only renderable (instantiated) styles should be included for run-time use
    // Others are intermediary/abstract, used during style composition but not execution
    if (style.base && BaseStyles[style.base]) {
        Styles[name] = style = Object.assign(Object.create(BaseStyles[style.base]), style);
    }

    return style;
};

// Called to create and initialize styles
StyleManager.build = function (styles, scene = {}) {
    // Sort styles by dependency, then build them
    let style_deps = Object.keys(styles).sort(
        (a, b) => StyleManager.inheritanceDepth(a, styles) - StyleManager.inheritanceDepth(b, styles)
    );

    // Only keep built-in base styles
    for (let sname in Styles) {
        if (!BaseStyles[sname]) {
            delete Styles[sname];
        }
        else {
            Styles[sname].reset();
        }
    }

    // Working set of styles being built
    let ws = {};
    for (let sname of style_deps) {
        ws[sname] = StyleManager.create(sname, styles[sname], ws);
    }

    StyleManager.initStyles(scene);
    return Styles;
};

// Initialize all styles
StyleManager.initStyles = function (scene) {
    // Initialize all
    for (let sname in Styles) {
        Styles[sname].init(scene);
    }
};

// Given a style key in a set of styles to add, count the length of the inheritance chain
// TODO: remove current (Styles) and future (styles) duplication, confusing
StyleManager.inheritanceDepth = function (key, styles) {
    let parents = 0;

    while(true) {
        let style = styles[key];
        if (!style) {
            // this is a scene def error, trying to extend a style that doesn't exist
            break;
        }

        // Dependency chain ends when this style isn't mixing in any others
        if (!style.mix) {
            break;
        }

        // Traverse next parent style
        parents++;

        if (Array.isArray(style.mix)) {
            // If multiple mixins, find the deepest one
            parents += Math.max(...style.mix.map(s => {
                // Trying to mix into itself!
                if (key === s) {
                    return;
                }

                return StyleManager.inheritanceDepth(s, styles);
            }));
            break;
        }
        else {
            // Trying to mix into itself!
            if (key === style.mix) {
                break;
            }

            // If single mixin, continue loop up the tree
            key = style.mix;
        }
    }
    return parents;
};

// Compile all styles
StyleManager.compile = function (keys, scene) {
    keys = keys || Object.keys(Styles);
    for (let key of keys) {
        let style = Styles[key];
        try {
            style.compile();
            log.trace(`StyleManager.compile(): compiled style ${key}`);
        }
        catch(error) {
            log.error(`StyleManager.compile(): error compiling style ${key}:`, error);

            scene.trigger('warning', {
                type: 'styles',
                message: `Error compiling style ${key}`,
                style,
                shader_errors: style.program && style.program.shader_errors
            });
        }
    }

    log.debug(`StyleManager.compile(): compiled all styles`);
};

// Get all styles with mesh data for a given tile
StyleManager.stylesForTile = function (tile) {
    let styles = [];
    for (let s in Styles) {
        if (Styles[s].hasDataForTile(tile)) {
            styles.push(s);
        }
    }
    return styles;
};
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Thu Jun 05 2014 06:45:56 GMT-0400 (EDT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['browserify', 'mocha', 'chai-sinon'],

    // list of files / patterns to load in the browser
    files: [
      'node_modules/angular/angular.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'test/*.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/*.js': ['browserify']
    },

    browserify: {
      debug: true,
      transform: [
        'partialify',
        ['browserify-istanbul', {
          ignore: '**/*.html'
        }],
        'browserify-shim'
      ]
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage'],

    coverageReporter: {
      type: 'lcovonly',
      dir: 'coverage',
      subdir: '.'
    },

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<commit_msg>Generate a full coverage report with lcov and html<commit_after>// Karma configuration
// Generated on Thu Jun 05 2014 06:45:56 GMT-0400 (EDT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['browserify', 'mocha', 'chai-sinon'],

    // list of files / patterns to load in the browser
    files: [
      'node_modules/angular/angular.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'test/*.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/*.js': ['browserify']
    },

    browserify: {
      debug: true,
      transform: [
        'partialify',
        ['browserify-istanbul', {
          ignore: '**/*.html'
        }],
        'browserify-shim'
      ]
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage'],

    coverageReporter: {
      type: 'lcov',
      dir: 'coverage',
      subdir: '.'
    },

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<|endoftext|>


<commit_before>module.exports = function(config){
  config.set({
    var webdriverConfig = {
      hostname: '0.0.0.0',
      port: 4444
    }

    basePath : './',

    files : [
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-aria/angular-aria.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/angular-material/angular-material.js',
      'app/components/**/*.js',
      'app/view*/**/*.js'
    ],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['ChromeSelenium'],

    customLaunchers: {
        'ChromeSelenium': {
            base: 'WebDriver',
            config: webdriverConfig,
            browserName: 'Chrome',
        }
    },

    plugins : [
            'karma-jasmine',
            'karma-junit-reporter'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};
<commit_msg>Fixing karma config<commit_after>module.exports = function(config){
  var webdriverConfig = {
      hostname: '0.0.0.0',
      port: 4444
    }

  config.set({
    basePath : './',

    files : [
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-aria/angular-aria.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/angular-material/angular-material.js',
      'app/components/**/*.js',
      'app/view*/**/*.js'
    ],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['ChromeSelenium'],

    customLaunchers: {
        'ChromeSelenium': {
            base: 'WebDriver',
            config: webdriverConfig,
            browserName: 'Chrome',
        }
    },

    plugins : [
            'karma-jasmine',
            'karma-junit-reporter'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};
<|endoftext|>


<commit_before>//jshint strict: false
module.exports = function(config) {
  config.set({

    basePath: './app',

    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-route/angular-route.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'components/**/*.js',
      'view*/**/*.js'
    ],

    autoWatch: true,

    frameworks: ['jasmine'],

    browsers: ['Chrome'],

    plugins: [
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine',
      'karma-junit-reporter'
    ],

    junitReporter: {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};
<commit_msg>fixing unit test<commit_after>//jshint strict: false
module.exports = function(config) {
  config.set({

    basePath: './app',

    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-mocks/angular-mocks.js',
      '**/*.module.js',
      '*!(.module|.spec).js',
      '!(bower_components)/**/*!(.module|.spec).js',
      '**/*.spec.js'
    ],

    autoWatch: true,

    frameworks: ['jasmine'],

    browsers: ['Chrome'],

    plugins: [
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine'
    ]

  });
};
<|endoftext|>


<commit_before>// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
    config.set({
        basePath: './src',
        frameworks: ['jasmine', '@angular/cli'],
        plugins: [
            require('karma-jasmine'),
            require('karma-chrome-launcher'),
            require('karma-jasmine-html-reporter'),
            require('karma-coverage-istanbul-reporter'),
            require('@angular/cli/plugins/karma')
        ],
        client: {
            clearContext: false // leave Jasmine Spec Runner output visible in browser
        },
        coverageIstanbulReporter: {
            reports: ['html', 'lcovonly'],
            fixWebpackSourcePaths: true
        },
        angularCli: {
            environment: 'dev'
        },
        reporters: ['progress', 'kjhtml'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: true
    });
};
<commit_msg>Change log level<commit_after>// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
    config.set({
        basePath: './src',
        frameworks: ['jasmine', '@angular/cli'],
        plugins: [
            require('karma-jasmine'),
            require('karma-chrome-launcher'),
            require('karma-jasmine-html-reporter'),
            require('karma-coverage-istanbul-reporter'),
            require('@angular/cli/plugins/karma')
        ],
        client: {
            clearContext: false // leave Jasmine Spec Runner output visible in browser
        },
        coverageIstanbulReporter: {
            reports: ['html', 'lcovonly'],
            fixWebpackSourcePaths: true
        },
        angularCli: {
            environment: 'dev'
        },
        reporters: ['progress', 'kjhtml'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_ERROR,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: true
    });
};
<|endoftext|>


<commit_before>import minimist from 'minimist';
import defined from 'defined';
import path from 'path';

const customLaunchers = {};

const args = minimist(process.argv.slice(2), {
  string: ['env', 'build-branch', 'build-number', 'suace-username', 'sauce-key'],
  default: {
    env: process.env.NODE_ENV,
    'build-branch': process.env.BUILD_BRANCH,
    'build-number': process.env.BUILD_NUMBER,
    'sauce-username': process.env.SAUCE_USERNAME,
    'sauce-key': process.env.SAUCE_ACCESS_KEY
  }
});

args.istanbul = defined(args.istanbul, args.env !== 'CI');
args['sauce-labs'] = defined(args['sauce-labs'], args.env === 'CI');

// Overridable arguments are denoted below. Other arguments can be found in the
// [Karma configuration](http://karma-runner.github.io/0.12/config/configuration-file.html)

['chrome', 'firefox', 'iphone', 'ipad', 'android'].forEach(browser => {
  customLaunchers['sl_' + browser] = {
    base: 'SauceLabs',
    browserName: browser
  };
});

// Safari defaults to version 5 on Windows 7 (huh?)
customLaunchers.sl_safari = {
  base: 'SauceLabs',
  browserName: 'safari',
  platform: 'OS X 10.9'
};

[9, 10, 11].forEach(version => {
  customLaunchers['sl_ie_' + version] = {
    base: 'SauceLabs',
    browserName: 'internet explorer',
    version: version
  };
});

const reporters = ['mocha', 'beep'];

if (args.istanbul) {
  reporters.push('coverage');
}

let filePattern;
if (args.package) {
  filePattern = 'packages/' + args.package + '/test/**/*.js';
} else {
  filePattern = 'packages/*/test/**/*.js';
}

// Overridable with a comma-separated list with `--browsers`
let browsers;

if (typeof args.browsers === 'string') {
  browsers = args.browsers.split(',').filter(b => b !== '');
} else if (typeof args.browsers === 'boolean') {
  browsers = [];
} else {
  browsers = args['sauce-labs'] ? Object.keys(customLaunchers) : [
    'Chrome',
    // 'Firefox',
    // 'Safari'
  ];
}

module.exports = function(config) {
  config.set({
    frameworks: ['browserify', 'mocha'],

    files: [
      {
        pattern: filePattern,
        watched: true,
        included: true,
        served: true
      }
    ],

    preprocessors: {
      'packages/*/test/**/*.js': ['browserify']
    },

    // Overridable with a comma-separated list with `--reporters`
    reporters: reporters,

    // Overridable with `[--no]-colors
    colors: true,

    /* Overridable with `--log-level=<level>`.
     *
     * Possible 'level' options include:
     *  * disable
     *  * error
     *  * warn
     *  * info
     *  * debug
     */
    logLevel: config.LOG_INFO,

    // Overridable with a comma-separated list with `--browsers`
    browsers: browsers,

    sauceLabs: {
      username: args['sauce-username'],
      accessKey: args['sauce-key'],
      testName: require('./package.json').name,
      tags: args['build-branch'],
      build: args['build-number']
    },

    browserify: {
      debug: true,
      transform: [
        'babelify'
      ].concat(args.istanbul && [
        ['browserify-istanbul', {
          instrumenter: require('isparta'),
          ignore: [
            '**/lib/**',
            path.resolve(__dirname, 'utils/**')
          ]
        }]
      ] || []),
      configure(bundle) {
        bundle.external('react/lib/ReactContext');
        bundle.external('react/lib/ExecutionEnvironment');
      }
    },

    coverageReporter: {
      reporters: [
        {
          type: 'html'
        },
        {
          type: 'text'
        }
      ]
    },

    client: {
      // '--grep' arguments are passed directly to mocha.
      args: args.grep && ['--grep', args.grep],
      mocha: {
        reporter: 'html'
      }
    },

    customLaunchers: customLaunchers
  });
};
<commit_msg>Typo in saucelabs config (#6)<commit_after>import minimist from 'minimist';
import defined from 'defined';
import path from 'path';

const customLaunchers = {};

const args = minimist(process.argv.slice(2), {
  string: ['env', 'build-branch', 'build-number', 'sauce-username', 'sauce-key'],
  default: {
    env: process.env.NODE_ENV,
    'build-branch': process.env.BUILD_BRANCH,
    'build-number': process.env.BUILD_NUMBER,
    'sauce-username': process.env.SAUCE_USERNAME,
    'sauce-key': process.env.SAUCE_ACCESS_KEY
  }
});

args.istanbul = defined(args.istanbul, args.env !== 'CI');
args['sauce-labs'] = defined(args['sauce-labs'], args.env === 'CI');

// Overridable arguments are denoted below. Other arguments can be found in the
// [Karma configuration](http://karma-runner.github.io/0.12/config/configuration-file.html)

['chrome', 'firefox', 'iphone', 'ipad', 'android'].forEach(browser => {
  customLaunchers['sl_' + browser] = {
    base: 'SauceLabs',
    browserName: browser
  };
});

// Safari defaults to version 5 on Windows 7 (huh?)
customLaunchers.sl_safari = {
  base: 'SauceLabs',
  browserName: 'safari',
  platform: 'OS X 10.9'
};

[9, 10, 11].forEach(version => {
  customLaunchers['sl_ie_' + version] = {
    base: 'SauceLabs',
    browserName: 'internet explorer',
    version: version
  };
});

const reporters = ['mocha', 'beep'];

if (args.istanbul) {
  reporters.push('coverage');
}

let filePattern;
if (args.package) {
  filePattern = 'packages/' + args.package + '/test/**/*.js';
} else {
  filePattern = 'packages/*/test/**/*.js';
}

// Overridable with a comma-separated list with `--browsers`
let browsers;

if (typeof args.browsers === 'string') {
  browsers = args.browsers.split(',').filter(b => b !== '');
} else if (typeof args.browsers === 'boolean') {
  browsers = [];
} else {
  browsers = args['sauce-labs'] ? Object.keys(customLaunchers) : [
    'Chrome',
    // 'Firefox',
    // 'Safari'
  ];
}

module.exports = function(config) {
  config.set({
    frameworks: ['browserify', 'mocha'],

    files: [
      {
        pattern: filePattern,
        watched: true,
        included: true,
        served: true
      }
    ],

    preprocessors: {
      'packages/*/test/**/*.js': ['browserify']
    },

    // Overridable with a comma-separated list with `--reporters`
    reporters: reporters,

    // Overridable with `[--no]-colors
    colors: true,

    /* Overridable with `--log-level=<level>`.
     *
     * Possible 'level' options include:
     *  * disable
     *  * error
     *  * warn
     *  * info
     *  * debug
     */
    logLevel: config.LOG_INFO,

    // Overridable with a comma-separated list with `--browsers`
    browsers: browsers,

    sauceLabs: {
      username: args['sauce-username'],
      accessKey: args['sauce-key'],
      testName: require('./package.json').name,
      tags: args['build-branch'],
      build: args['build-number']
    },

    browserify: {
      debug: true,
      transform: [
        'babelify'
      ].concat(args.istanbul && [
        ['browserify-istanbul', {
          instrumenter: require('isparta'),
          ignore: [
            '**/lib/**',
            path.resolve(__dirname, 'utils/**')
          ]
        }]
      ] || []),
      configure(bundle) {
        bundle.external('react/lib/ReactContext');
        bundle.external('react/lib/ExecutionEnvironment');
      }
    },

    coverageReporter: {
      reporters: [
        {
          type: 'html'
        },
        {
          type: 'text'
        }
      ]
    },

    client: {
      // '--grep' arguments are passed directly to mocha.
      args: args.grep && ['--grep', args.grep],
      mocha: {
        reporter: 'html'
      }
    },

    customLaunchers: customLaunchers
  });
};
<|endoftext|>


<commit_before>var istanbul = require('browserify-istanbul');
module.exports = function(config) {
  config.set({
    frameworks: ['browserify', 'jasmine'],
    files: [
      {pattern: 'src/**/*.js', watched: false, included: true, served: true},
      'test/**/*_spec.js'
    ],
    preprocessors: {
      'test/**/*.js': ['jshint', 'browserify'],
      'src/**/*.js': ['jshint', 'browserify', 'coverage']
    },
    browsers: ['PhantomJS'],
    browserify: {
      debug: true
    },
    reporters: ['progress', 'coverage', 'coveralls'],
    browserify: {
      debug: true,
      transform: [istanbul({
        ignore: ['**/node_modules/**', '**/test/**'],
      })],
    },
    coverageReporter: {
      dir: 'coverage/',
      reporters: [
        {type: 'lcov', subdir: 'report-lcov'},
        {type: 'text-summary'}
      ]
    }
  })
}<commit_msg>remote duplicate browserify property<commit_after>var istanbul = require('browserify-istanbul');
module.exports = function(config) {
  config.set({
    frameworks: ['browserify', 'jasmine'],
    files: [
      {pattern: 'src/**/*.js', watched: false, included: true, served: true},
      'test/**/*_spec.js'
    ],
    preprocessors: {
      'test/**/*.js': ['jshint', 'browserify'],
      'src/**/*.js': ['jshint', 'browserify', 'coverage']
    },
    browsers: ['PhantomJS'],
    reporters: ['progress', 'coverage', 'coveralls'],
    browserify: {
      debug: true,
      transform: [istanbul({
        ignore: ['**/node_modules/**', '**/test/**'],
      })],
    },
    coverageReporter: {
      dir: 'coverage/',
      reporters: [
        {type: 'lcov', subdir: 'report-lcov'},
        {type: 'text-summary'}
      ]
    }
  })
}<|endoftext|>


<commit_before>module.exports = function(config) {
  config.set({

    // base path, that will be used to resolve files and exclude
    basePath: '.',

    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      // angular-ui
      'src/**/*.js',
      'test/**/*.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    preprocessors: {

    },

    // use dots reporter, as travis terminal does not support escaping sequences
    // possible values: 'dots', 'progress'
    // CLI --reporters progress
    reporters: ['progress'],

    hostname: process.env.IP ? process.env.IP : 'localhost',

    // web server port
    // CLI --port 9876
    port: process.env.IP ? process.env.PORT : 9018,

    // enable / disable colors in the output (reporters and logs)
    // CLI --colors --no-colors
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    // CLI --log-level debug
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    // CLI --auto-watch --no-auto-watch
    autoWatch: false,

    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera
    // - Safari (only Mac)
    // - PhantomJS
    // - IE (only Windows)
    // CLI --browsers Chrome,Firefox,Safari
    browsers: process.env.IP ? ['PhantomJS'] : [
      'Chrome'
    ],

    // If browser does not capture in given timeout [ms], kill it
    // CLI --capture-timeout 5000
    captureTimeout: 20000,

    // Auto run tests on start (when browsers are captured) and exit
    // CLI --single-run --no-single-run
    singleRun: false,

    // report which specs are slower than 500ms
    // CLI --report-slower-than 500
    reportSlowerThan: 500,

    // plugins: [
    //   'karma-jasmine',
    //   'karma-chrome-launcher',
    //   'karma-firefox-launcher',
    //   'karma-junit-reporter',
    //   'karma-commonjs'
    // ]

  })
};
<commit_msg>chore: don't use PhantomJS when testing on Cloud9<commit_after>module.exports = function(config) {
  config.set({

    // base path, that will be used to resolve files and exclude
    basePath: '.',

    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      // angular-ui
      'src/**/*.js',
      'test/**/*.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    preprocessors: {

    },

    // use dots reporter, as travis terminal does not support escaping sequences
    // possible values: 'dots', 'progress'
    // CLI --reporters progress
    reporters: ['progress'],

    hostname: process.env.IP ? process.env.IP : 'localhost',

    // web server port
    // CLI --port 9876
    port: process.env.IP ? process.env.PORT : 9018,

    // enable / disable colors in the output (reporters and logs)
    // CLI --colors --no-colors
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    // CLI --log-level debug
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    // CLI --auto-watch --no-auto-watch
    autoWatch: false,

    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera
    // - Safari (only Mac)
    // - PhantomJS
    // - IE (only Windows)
    // CLI --browsers Chrome,Firefox,Safari
    browsers: process.env.IP ? [] : [
      'Chrome'
    ],

    // If browser does not capture in given timeout [ms], kill it
    // CLI --capture-timeout 5000
    captureTimeout: 20000,

    // Auto run tests on start (when browsers are captured) and exit
    // CLI --single-run --no-single-run
    singleRun: false,

    // report which specs are slower than 500ms
    // CLI --report-slower-than 500
    reportSlowerThan: 500,

    // plugins: [
    //   'karma-jasmine',
    //   'karma-chrome-launcher',
    //   'karma-firefox-launcher',
    //   'karma-junit-reporter',
    //   'karma-commonjs'
    // ]

  })
};
<|endoftext|>


<commit_before>define(["underscore", "backbone", "moxie.conf"], function(_, Backbone, conf){
    var EVENT_POSITION_UPDATED = 'position:updated';
    function UserPosition() {
        _.extend(this, Backbone.Events);
        var supportsGeoLocation = Boolean(navigator.geolocation),
            spamReduction = false,
            missedUpdate = null,
            watchID;
        this.getLocation = function(cb, options) {
            options = options || {};
            // Default options:
            // errorMargin = 50 meters
            // timeout     = 30 seconds
            //
            // If we don't get a location within the errorMargin after before the Timeout
            // we return the most recent position reported by watchPosition
            options.errorMargin = options.errorMargin || 50;
            options.timeout = options.timeout || 30000;
            var watchID;
            var latestPosition;
            var accuracyTimeout = setTimeout(function() {
                navigator.geolocation.clearWatch(watchID);
            }, options.timeout);
            watchID = navigator.geolocation.watchPosition(function(position) {
                latestPosition = position;
                if (latestPosition.coords && latestPosition.coords.accuracy && latestPosition.coords.accuracy < options.errorMargin) {
                    window.clearTimeout(accuracyTimeout);
                    navigator.geolocation.clearWatch(watchID);
                    cb(latestPosition);
                }
            }, _.bind(locationError, this),
            {
                    enableHighAccuracy: true,
                    maximumAge: 120000,  // 2 minutes
            });

        };
        function locationSuccess(position) {
            // Trigger relevant events for any components listening to the user position
            //
            // The complexity here comes from not wanting to "spam" the geolocation API's
            // Previously we fired callbacks whenever the position updated, If the callbacks
            // were drawing the map it would severely effect performance on the device as
            // redrawing the map wouldn't finish before starting the next redraw.
            //
            // Now we have a configurable throttle to reduce this effect. No callbacks will
            // be fired within length of this throttle.
            if (spamReduction) {
                // Capture any missed updates whilst we're in "spam reduction mode"
                missedUpdate = position;
            } else {
                this.trigger(EVENT_POSITION_UPDATED, position);
                this.latest = position;
                spamReduction = true;
                setTimeout(_.bind(function() {
                    spamReduction = false;
                    if (missedUpdate) {
                        locationSuccess.apply(this, [missedUpdate]);
                        missedUpdate = null;
                    }
                }, this), conf.position.updateThrottle);
            }
        }
        function locationError() {
            if (!this.latest) {
                // We have no good position data so update to the default location
                this.latest = conf.defaultLocation;
                this.trigger(EVENT_POSITION_UPDATED, conf.defaultLocation);
            }
        }
        function startWatching() {
            if (supportsGeoLocation) {
                watchID = navigator.geolocation.watchPosition(_.bind(locationSuccess, this), _.bind(locationError, this),
                    {
                        enableHighAccuracy: true,
                        maximumAge: 120000,  // 2 minutes
                        timeout: 25000,      // 25 seconds
                    });
            } else {
                locationError.apply(this);
            }
        }
        var count = 0;
        this.follow = function(cb) {
            if (!watchID) {
                // Call the "private" function with the correct context
                startWatching.apply(this);
            }
            this.on(EVENT_POSITION_UPDATED, cb);
            if (this.latest) {
                // New subscribers should get the latest location fix
                this.trigger(EVENT_POSITION_UPDATED, this.latest);
            }
            this.count++;
        };
        this.unfollow = function(cb) {
            this.off(EVENT_POSITION_UPDATED, cb);
            this.count--;
            if (this.count === 0) {
                navigator.geolocation.clearWatch(watchID);
            }
        };
    }
    return new UserPosition();
});
<commit_msg>Typo and renamed variable for clarrification<commit_after>define(["underscore", "backbone", "moxie.conf"], function(_, Backbone, conf){
    var EVENT_POSITION_UPDATED = 'position:updated';
    function UserPosition() {
        _.extend(this, Backbone.Events);
        var supportsGeoLocation = Boolean(navigator.geolocation),
            spamReduction = false,
            missedUpdate = null,
            watchID;
        this.getLocation = function(cb, options) {
            options = options || {};
            // Default options:
            // errorMargin = 50 meters
            // timeout     = 30 seconds
            //
            // If we don't get a location within the errorMargin before the Timeout
            // we return the most recent position reported by watchPosition
            options.errorMargin = options.errorMargin || 50;
            options.timeout = options.timeout || 30000;
            var temporaryGeoWatchID;
            var latestPosition;
            var accuracyTimeout = setTimeout(function() {
                navigator.geolocation.clearWatch(temporaryGeoWatchID);
            }, options.timeout);
            temporaryGeoWatchID = navigator.geolocation.watchPosition(function(position) {
                latestPosition = position;
                if (latestPosition.coords && latestPosition.coords.accuracy && latestPosition.coords.accuracy < options.errorMargin) {
                    window.clearTimeout(accuracyTimeout);
                    navigator.geolocation.clearWatch(temporaryGeoWatchID);
                    cb(latestPosition);
                }
            }, _.bind(locationError, this),
            {
                    enableHighAccuracy: true,
                    maximumAge: 120000,  // 2 minutes
            });

        };
        function locationSuccess(position) {
            // Trigger relevant events for any components listening to the user position
            //
            // The complexity here comes from not wanting to "spam" the geolocation API's
            // Previously we fired callbacks whenever the position updated, If the callbacks
            // were drawing the map it would severely effect performance on the device as
            // redrawing the map wouldn't finish before starting the next redraw.
            //
            // Now we have a configurable throttle to reduce this effect. No callbacks will
            // be fired within length of this throttle.
            if (spamReduction) {
                // Capture any missed updates whilst we're in "spam reduction mode"
                missedUpdate = position;
            } else {
                this.trigger(EVENT_POSITION_UPDATED, position);
                this.latest = position;
                spamReduction = true;
                setTimeout(_.bind(function() {
                    spamReduction = false;
                    if (missedUpdate) {
                        locationSuccess.apply(this, [missedUpdate]);
                        missedUpdate = null;
                    }
                }, this), conf.position.updateThrottle);
            }
        }
        function locationError() {
            if (!this.latest) {
                // We have no good position data so update to the default location
                this.latest = conf.defaultLocation;
                this.trigger(EVENT_POSITION_UPDATED, conf.defaultLocation);
            }
        }
        function startWatching() {
            if (supportsGeoLocation) {
                watchID = navigator.geolocation.watchPosition(_.bind(locationSuccess, this), _.bind(locationError, this),
                    {
                        enableHighAccuracy: true,
                        maximumAge: 120000,  // 2 minutes
                        timeout: 25000,      // 25 seconds
                    });
            } else {
                locationError.apply(this);
            }
        }
        var count = 0;
        this.follow = function(cb) {
            if (!watchID) {
                // Call the "private" function with the correct context
                startWatching.apply(this);
            }
            this.on(EVENT_POSITION_UPDATED, cb);
            if (this.latest) {
                // New subscribers should get the latest location fix
                this.trigger(EVENT_POSITION_UPDATED, this.latest);
            }
            this.count++;
        };
        this.unfollow = function(cb) {
            this.off(EVENT_POSITION_UPDATED, cb);
            this.count--;
            if (this.count === 0) {
                navigator.geolocation.clearWatch(watchID);
            }
        };
    }
    return new UserPosition();
});
<|endoftext|>


<commit_before>import { Model, ORM, attr, many } from '../';
import { createTestSessionWithData, measureMs } from './utils';

describe('Big Data Test', () => {
    let orm;
    let session;

    beforeEach(() => {
        const Item = class extends Model {};
        Item.modelName = 'Item';
        Item.fields = {
            id: attr(),
            name: attr(),
        };
        orm = new ORM();
        orm.register(Item);
        session = orm.session(orm.getEmptyState());
    });

    it('adds a big amount of items in acceptable time', () => {
        const { Item } = session;
        const start = measureMs();

        const amount = 10000;
        for (let i = 0; i < amount; i++) {
            Item.create({ id: i, name: 'TestItem' });
        }
        const tookSeconds = measureMs(start) / 1000;
        console.log(`Creating ${amount} objects took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 1 : 0.5);
    });

    it('looks up items by id in a large table in acceptable time', () => {
        const { Item } = session;

        const rowCount = 20000;
        for (let i = 0; i < rowCount; i++) {
            Item.create({ id: i, name: 'TestItem' });
        }

        const lookupCount = 10000;
        const maxId = rowCount - 1;
        const start = measureMs();
        for (let j = maxId; j > maxId - lookupCount; j--) {
            Item.withId(j);
        }
        const tookSeconds = measureMs(start) / 1000;
        console.log(`Looking up ${lookupCount} objects by id took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 1 : 0.75);
    });
});

describe('Many-to-many relationship performance', () => {
    let orm;
    let session;

    beforeEach(() => {
        const Parent = class extends Model {};
        Parent.modelName = 'Parent';
        Parent.fields = {
            id: attr(),
            name: attr(),
            children: many('Child', 'parent'),
        };
        const Child = class extends Model {};
        Child.modelName = 'Child';
        orm = new ORM();
        orm.register(Parent, Child);
        session = orm.session(orm.getEmptyState());
    });

    it('adds many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 8000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const start = measureMs();
        const childAmount = 2500;
        for (let i = 0; i < childAmount; i++) {
            parent.children.add(i);
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Adding ${childAmount} relations took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 13.5 : 4);
    });

    it('queries many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 10000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const relationshipAmount = 3000;
        for (let i = 0; i < relationshipAmount; i++) {
            parent.children.add(i);
        }

        const start = measureMs();
        const queryCount = 500;
        for (let j = 0; j < queryCount; j++) {
            parent.children.count();
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Performing ${queryCount} queries took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 15 : 4);
    });

    it('removes many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 10000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const childAmount = 2000;
        for (let i = 0; i < childAmount; i++) {
            parent.children.add(i);
        }

        const removeCount = 1000;
        const start = measureMs();
        for (let j = 0; j < removeCount; j++) {
            parent.children.remove(j);
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Removing ${removeCount} relations took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 15 : 4);
    });
});
<commit_msg>increase Travis CI performance threshold for creating many Model instances<commit_after>import { Model, ORM, attr, many } from '../';
import { createTestSessionWithData, measureMs } from './utils';

describe('Big Data Test', () => {
    let orm;
    let session;

    beforeEach(() => {
        const Item = class extends Model {};
        Item.modelName = 'Item';
        Item.fields = {
            id: attr(),
            name: attr(),
        };
        orm = new ORM();
        orm.register(Item);
        session = orm.session(orm.getEmptyState());
    });

    it('adds a big amount of items in acceptable time', () => {
        const { Item } = session;
        const start = measureMs();

        const amount = 10000;
        for (let i = 0; i < amount; i++) {
            Item.create({ id: i, name: 'TestItem' });
        }
        const tookSeconds = measureMs(start) / 1000;
        console.log(`Creating ${amount} objects took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 1.5 : 0.5);
    });

    it('looks up items by id in a large table in acceptable time', () => {
        const { Item } = session;

        const rowCount = 20000;
        for (let i = 0; i < rowCount; i++) {
            Item.create({ id: i, name: 'TestItem' });
        }

        const lookupCount = 10000;
        const maxId = rowCount - 1;
        const start = measureMs();
        for (let j = maxId; j > maxId - lookupCount; j--) {
            Item.withId(j);
        }
        const tookSeconds = measureMs(start) / 1000;
        console.log(`Looking up ${lookupCount} objects by id took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 1 : 0.75);
    });
});

describe('Many-to-many relationship performance', () => {
    let orm;
    let session;

    beforeEach(() => {
        const Parent = class extends Model {};
        Parent.modelName = 'Parent';
        Parent.fields = {
            id: attr(),
            name: attr(),
            children: many('Child', 'parent'),
        };
        const Child = class extends Model {};
        Child.modelName = 'Child';
        orm = new ORM();
        orm.register(Parent, Child);
        session = orm.session(orm.getEmptyState());
    });

    it('adds many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 8000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const start = measureMs();
        const childAmount = 2500;
        for (let i = 0; i < childAmount; i++) {
            parent.children.add(i);
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Adding ${childAmount} relations took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 13.5 : 4);
    });

    it('queries many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 10000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const relationshipAmount = 3000;
        for (let i = 0; i < relationshipAmount; i++) {
            parent.children.add(i);
        }

        const start = measureMs();
        const queryCount = 500;
        for (let j = 0; j < queryCount; j++) {
            parent.children.count();
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Performing ${queryCount} queries took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 15 : 4);
    });

    it('removes many-to-many relationships in acceptable time', () => {
        const { Child, Parent } = session;

        const totalAmount = 10000;
        for (let i = 0; i < totalAmount; i++) {
            Child.create({ id: i, name: 'TestChild' });
        }

        const parent = Parent.create({});
        const childAmount = 2000;
        for (let i = 0; i < childAmount; i++) {
            parent.children.add(i);
        }

        const removeCount = 1000;
        const start = measureMs();
        for (let j = 0; j < removeCount; j++) {
            parent.children.remove(j);
        }

        const tookSeconds = measureMs(start) / 1000;
        console.log(`Removing ${removeCount} relations took ${tookSeconds}s`);
        expect(tookSeconds).toBeLessThanOrEqual(process.env.TRAVIS ? 15 : 4);
    });
});
<|endoftext|>


<commit_before>// @flow
import os from 'os';
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import ReactModal from 'react-modal';
import NetworkStatus from '../../components/status/NetworkStatus';
import styles from './NetworkStatusDialog.scss';
import type { InjectedProps } from '../../types/injectedPropsType';

type Props = InjectedProps;

@inject('stores', 'actions')
@observer
export default class NetworkStatusDialog extends Component<Props> {
  static defaultProps = { actions: null, stores: null };

  render() {
    const { actions, stores } = this.props;
    const { closeNetworkStatusDialog } = actions.app;
    const { restartNode } = actions.networkStatus;
    const { app, networkStatus } = stores;
    const { openExternalLink } = app;
    const {
      // Node state
      cardanoNodeState,
      isNodeResponding,
      isNodeSubscribed,
      isNodeSyncing,
      isNodeInSync,
      isNodeTimeCorrect,
      // Application state
      isConnected,
      isSynced,
      syncPercentage,
      hasBeenConnected,
      localTimeDifference,
      isSystemTimeCorrect,
      forceCheckTimeDifferenceRequest,
      forceCheckLocalTimeDifference,
      getNetworkStatusRequest,
      localBlockHeight,
      networkBlockHeight,
      latestLocalBlockTimestamp,
      latestNetworkBlockTimestamp,
      isSystemTimeIgnored,
      environment,
      diskSpaceAvailable,
      tlsConfig,
      cardanoNodeID,
    } = networkStatus;

    const systemInfo = {
      platform: environment.os,
      platformVersion: environment.platformVersion,
      cpu: Array.isArray(environment.cpu) ? environment.cpu[0].model : '',
      ram: this.convertBytesToSize(environment.ram),
      availableDiskSpace: diskSpaceAvailable,
    };

    const coreInfo = {
      daedalusVersion: environment.version,
      daedalusProcessID: environment.rendererProcessID,
      daedalusMainProcessID: environment.mainProcessID,
      isInSafeMode: environment.isInSafeMode,
      cardanoVersion: environment.buildNumber,
      cardanoProcessID: cardanoNodeID,
      cardanoAPIPort: tlsConfig ? tlsConfig.port : 0,
      cardanoNetwork: environment.network,
      daedalusStateDirectory: environment.stateDirectoryPath,
    };

    return (
      <ReactModal
        isOpen
        closeOnOverlayClick
        onRequestClose={closeNetworkStatusDialog.trigger}
        className={styles.dialog}
        overlayClassName={styles.overlay}
        ariaHideApp={false}
      >
        <NetworkStatus
          systemInfo={systemInfo}
          coreInfo={coreInfo}
          cardanoNodeState={cardanoNodeState}
          isDev={environment.isDev}
          isMainnet={environment.isMainnet}
          isStaging={environment.isStaging}
          isTestnet={environment.isTestnet}
          isNodeResponding={isNodeResponding}
          isNodeSubscribed={isNodeSubscribed}
          isNodeSyncing={isNodeSyncing}
          isNodeInSync={isNodeInSync}
          isNodeTimeCorrect={isNodeTimeCorrect}
          isConnected={isConnected}
          isSynced={isSynced}
          syncPercentage={syncPercentage}
          hasBeenConnected={hasBeenConnected}
          localTimeDifference={localTimeDifference}
          isSystemTimeCorrect={isSystemTimeCorrect}
          isForceCheckingNodeTime={forceCheckTimeDifferenceRequest.isExecuting}
          isSystemTimeIgnored={isSystemTimeIgnored}
          latestLocalBlockTimestamp={latestLocalBlockTimestamp}
          latestNetworkBlockTimestamp={latestNetworkBlockTimestamp}
          nodeConnectionError={
            getNetworkStatusRequest.error ||
            forceCheckTimeDifferenceRequest.error
          }
          localBlockHeight={localBlockHeight}
          networkBlockHeight={networkBlockHeight}
          onForceCheckLocalTimeDifference={forceCheckLocalTimeDifference}
          onOpenExternalLink={openExternalLink}
          onRestartNode={restartNode}
          onClose={closeNetworkStatusDialog.trigger}
        />
      </ReactModal>
    );
  }

  convertBytesToSize = (bytes: number): string => {
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return 'n/a';
    const i = parseInt(
      Math.floor(Math.log(Math.abs(bytes)) / Math.log(1024)),
      10
    );
    if (i === 0) return `${bytes} ${sizes[i]})`;
    return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`;
  };
}
<commit_msg>[DDW-675] Remove os package from renderer process<commit_after>// @flow
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import ReactModal from 'react-modal';
import NetworkStatus from '../../components/status/NetworkStatus';
import styles from './NetworkStatusDialog.scss';
import type { InjectedProps } from '../../types/injectedPropsType';

type Props = InjectedProps;

@inject('stores', 'actions')
@observer
export default class NetworkStatusDialog extends Component<Props> {
  static defaultProps = { actions: null, stores: null };

  render() {
    const { actions, stores } = this.props;
    const { closeNetworkStatusDialog } = actions.app;
    const { restartNode } = actions.networkStatus;
    const { app, networkStatus } = stores;
    const { openExternalLink } = app;
    const {
      // Node state
      cardanoNodeState,
      isNodeResponding,
      isNodeSubscribed,
      isNodeSyncing,
      isNodeInSync,
      isNodeTimeCorrect,
      // Application state
      isConnected,
      isSynced,
      syncPercentage,
      hasBeenConnected,
      localTimeDifference,
      isSystemTimeCorrect,
      forceCheckTimeDifferenceRequest,
      forceCheckLocalTimeDifference,
      getNetworkStatusRequest,
      localBlockHeight,
      networkBlockHeight,
      latestLocalBlockTimestamp,
      latestNetworkBlockTimestamp,
      isSystemTimeIgnored,
      environment,
      diskSpaceAvailable,
      tlsConfig,
      cardanoNodeID,
    } = networkStatus;

    const systemInfo = {
      platform: environment.os,
      platformVersion: environment.platformVersion,
      cpu: Array.isArray(environment.cpu) ? environment.cpu[0].model : '',
      ram: this.convertBytesToSize(environment.ram),
      availableDiskSpace: diskSpaceAvailable,
    };

    const coreInfo = {
      daedalusVersion: environment.version,
      daedalusProcessID: environment.rendererProcessID,
      daedalusMainProcessID: environment.mainProcessID,
      isInSafeMode: environment.isInSafeMode,
      cardanoVersion: environment.buildNumber,
      cardanoProcessID: cardanoNodeID,
      cardanoAPIPort: tlsConfig ? tlsConfig.port : 0,
      cardanoNetwork: environment.network,
      daedalusStateDirectory: environment.stateDirectoryPath,
    };

    return (
      <ReactModal
        isOpen
        closeOnOverlayClick
        onRequestClose={closeNetworkStatusDialog.trigger}
        className={styles.dialog}
        overlayClassName={styles.overlay}
        ariaHideApp={false}
      >
        <NetworkStatus
          systemInfo={systemInfo}
          coreInfo={coreInfo}
          cardanoNodeState={cardanoNodeState}
          isDev={environment.isDev}
          isMainnet={environment.isMainnet}
          isStaging={environment.isStaging}
          isTestnet={environment.isTestnet}
          isNodeResponding={isNodeResponding}
          isNodeSubscribed={isNodeSubscribed}
          isNodeSyncing={isNodeSyncing}
          isNodeInSync={isNodeInSync}
          isNodeTimeCorrect={isNodeTimeCorrect}
          isConnected={isConnected}
          isSynced={isSynced}
          syncPercentage={syncPercentage}
          hasBeenConnected={hasBeenConnected}
          localTimeDifference={localTimeDifference}
          isSystemTimeCorrect={isSystemTimeCorrect}
          isForceCheckingNodeTime={forceCheckTimeDifferenceRequest.isExecuting}
          isSystemTimeIgnored={isSystemTimeIgnored}
          latestLocalBlockTimestamp={latestLocalBlockTimestamp}
          latestNetworkBlockTimestamp={latestNetworkBlockTimestamp}
          nodeConnectionError={
            getNetworkStatusRequest.error ||
            forceCheckTimeDifferenceRequest.error
          }
          localBlockHeight={localBlockHeight}
          networkBlockHeight={networkBlockHeight}
          onForceCheckLocalTimeDifference={forceCheckLocalTimeDifference}
          onOpenExternalLink={openExternalLink}
          onRestartNode={restartNode}
          onClose={closeNetworkStatusDialog.trigger}
        />
      </ReactModal>
    );
  }

  convertBytesToSize = (bytes: number): string => {
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return 'n/a';
    const i = parseInt(
      Math.floor(Math.log(Math.abs(bytes)) / Math.log(1024)),
      10
    );
    if (i === 0) return `${bytes} ${sizes[i]})`;
    return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`;
  };
}
<|endoftext|>