<commit_before>import { parse, stringify } from 'qs';
import Route from './Route';

/**
 * A collection of Laravel routes. This class constitutes Ziggy's main API.
 */
export default class Router extends String {
    /**
     * @param {String} name - Route name.
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @param {Boolean} absolute - Whether to include the URL origin.
     * @param {Object} config - Ziggy configuration.
     */
    constructor(name, params, absolute = true, config) {
        super();

        this._config = config ?? Ziggy ?? globalThis?.Ziggy;
        this._config = { ...this._config, absolute };

        if (name) {
            if (!this._config.routes[name]) {
                throw new Error(`Ziggy error: route '${name}' is not in the route list.`);
            }

            this._route = new Route(name, this._config.routes[name], this._config);
            this._params = this._parse(params);
        }
    }

    /**
     * Get the compiled URL string for the current route and parameters.
     *
     * @example
     * // with 'posts.show' route 'posts/{post}'
     * (new Router('posts.show', 1)).toString(); // 'https://ziggy.dev/posts/1'
     *
     * @return {String}
     */
    toString() {
        // Get parameters that don't correspond to any route segments to append them to the query
        const unhandled = Object.keys(this._params)
            .filter((key) => !this._route.parameterSegments.some(({ name }) => name === key))
            .filter((key) => key !== '_query')
            .reduce((result, current) => ({ ...result, [current]: this._params[current] }), {});

        return this._route.compile(this._params) + stringify({ ...unhandled, ...this._params['_query'] }, {
            addQueryPrefix: true,
            arrayFormat: 'indices',
            encodeValuesOnly: true,
            skipNulls: true,
            encoder: (value, encoder) => typeof value === 'boolean' ? Number(value) : encoder(value),
        });
    }

    /**
     * Get the name of the route matching the current window URL, or, given a route name
     * and parameters, check if the current window URL and parameters match that route.
     *
     * @example
     * // at URL https://ziggy.dev/posts/4 with 'posts.show' route 'posts/{post}'
     * route().current(); // 'posts.show'
     * route().current('posts.index'); // false
     * route().current('posts.show'); // true
     * route().current('posts.show', { post: 1 }); // false
     * route().current('posts.show', { post: 4 }); // true
     *
     * @param {String} name - Route name to check.
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @return {(Boolean|String|undefined)}
     */
    current(name, params) {
        const url = this._config.absolute
            ? this._location().host + this._location().pathname
            : this._location().pathname.replace(this._config.url.replace(/^\w*:\/\/[^/]+/, ''), '').replace(/^\/+/, '/');

        // Find the first route that matches the current URL
        const [current, route] = Object.entries(this._config.routes).find(
            ([_, route]) => new Route(name, route, this._config).matchesUrl(url)
        ) || [undefined, undefined];

        // If a name wasn't passed, return the name of the current route
        if (!name) return current;

        // Test the passed name against the current route, matching some
        // basic wildcards, e.g. passing `events.*` matches `events.show`
        const match = new RegExp(`^${name.replace('.', '\\.').replace('*', '.*')}$`).test(current);

        if ([null, undefined].includes(params) || !match) return match;

        const routeObject = new Route(current, route, this._config);

        params = this._parse(params, routeObject);
        const routeParams = this._dehydrate(route);

        // If the current window URL has no route parameters, and the passed parameters are empty, return true
        if (Object.values(params).every(p => !p) && !Object.values(routeParams).length) return true;

        // Check that all passed parameters match their values in the current window URL
        // Use weak equality because all values in the current window URL will be strings
        return Object.entries(params).every(([key, value]) => routeParams[key] == value);
    }

    /**
     * Get an object representing the current location (by default this will be
     * the JavaScript `window` global if it's available).
     *
     * @return {Object}
     */
    _location() {
        const { host = '', pathname = '', search = '' } = typeof window !== 'undefined' ? window.location : {};

        return {
            host: this._config.location?.host ?? host,
            pathname: this._config.location?.pathname ?? pathname,
            search: this._config.location?.search ?? search,
        };
    }

    /**
     * Get all parameter values from the current window URL.
     *
     * @example
     * // at URL https://tighten.ziggy.dev/posts/4?lang=en with 'posts.show' route 'posts/{post}' and domain '{team}.ziggy.dev'
     * route().params; // { team: 'tighten', post: 4, lang: 'en' }
     *
     * @return {Object}
     */
    get params() {
        return this._dehydrate(this._config.routes[this.current()]);
    }

    /**
     * Check whether the given route exists.
     *
     * @param {String} name
     * @return {Boolean}
     */
    has(name) {
        return Object.keys(this._config.routes).includes(name);
    }

    /**
     * Parse Laravel-style route parameters of any type into a normalized object.
     *
     * @example
     * // with route parameter names 'event' and 'venue'
     * _parse(1); // { event: 1 }
     * _parse({ event: 2, venue: 3 }); // { event: 2, venue: 3 }
     * _parse(['Taylor', 'Matt']); // { event: 'Taylor', venue: 'Matt' }
     * _parse([4, { uuid: 56789, name: 'Grand Canyon' }]); // { event: 4, venue: 56789 }
     *
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @param {Route} route - Route instance.
     * @return {Object} Normalized complete route parameters.
     */
    _parse(params = {}, route = this._route) {
        // If `params` is a string or integer, wrap it in an array
        params = ['string', 'number'].includes(typeof params) ? [params] : params;

        // Separate segments with and without defaults, and fill in the default values
        const segments = route.parameterSegments.filter(({ name }) => !this._config.defaults[name]);

        if (Array.isArray(params)) {
            // If the parameters are an array they have to be in order, so we can transform them into
            // an object by keying them with the template segment names in the order they appear
            params = params.reduce((result, current, i) => !!segments[i] ? ({ ...result, [segments[i].name]: current }) : ({ ...result, [current]: '' }), {});
        } else if (
            segments.length === 1
            && !params[segments[0].name]
            && (params.hasOwnProperty(Object.values(route.bindings)[0]) || params.hasOwnProperty('id'))
        ) {
            // If there is only one template segment and `params` is an object, that object is
            // ambiguous—it could contain the parameter key and value, or it could be an object
            // representing just the value (e.g. a model); we can inspect it to find out, and
            // if it's just the parameter value, we can wrap it in an object with its key
            params = { [segments[0].name]: params };
        }

        return {
            ...this._defaults(route),
            ...this._substituteBindings(params, route),
        };
    }

    /**
     * Populate default parameters for the given route.
     *
     * @example
     * // with default parameters { locale: 'en', country: 'US' } and 'posts.show' route '{locale}/posts/{post}'
     * defaults(...); // { locale: 'en' }
     *
     * @param {Route} route
     * @return {Object} Default route parameters.
     */
    _defaults(route) {
        return route.parameterSegments.filter(({ name }) => this._config.defaults[name])
            .reduce((result, { name }, i) => ({ ...result, [name]: this._config.defaults[name] }), {});
    }

    /**
     * Substitute Laravel route model bindings in the given parameters.
     *
     * @example
     * _substituteBindings({ post: { id: 4, slug: 'hello-world', title: 'Hello, world!' } }, { bindings: { post: 'slug' } }); // { post: 'hello-world' }
     *
     * @param {Object} params - Route parameters.
     * @param {Object} route - Route definition.
     * @return {Object} Normalized route parameters.
     */
    _substituteBindings(params, { bindings, parameterSegments }) {
        return Object.entries(params).reduce((result, [key, value]) => {
            // If the value isn't an object, or if the key isn't a named route parameter,
            // there's nothing to substitute so we return it as-is
            if (!value || typeof value !== 'object' || Array.isArray(value) || !parameterSegments.some(({ name }) => name === key)) {
                return { ...result, [key]: value };
            }

            if (!value.hasOwnProperty(bindings[key])) {
                if (value.hasOwnProperty('id')) {
                    // As a fallback, we still accept an 'id' key not explicitly registered as a binding
                    bindings[key] = 'id';
                } else {
                    throw new Error(`Ziggy error: object passed as '${key}' parameter is missing route model binding key '${bindings[key]}'.`)
                }
            }

            return { ...result, [key]: value[bindings[key]] };
        }, {});
    }

    /**
     * Get all parameters and their values from the current window URL, based on the given route definition.
     *
     * @example
     * // at URL https://tighten.ziggy.dev/events/8/venues/chicago?zoom=true
     * _dehydrate({ domain: '{team}.ziggy.dev', uri: 'events/{event}/venues/{venue?}' }); // { team: 'tighten', event: 8, venue: 'chicago', zoom: true }
     *
     * @param {Object} route - Route definition.
     * @return {Object} Parameters.
     */
    _dehydrate(route) {
        let pathname = this._location().pathname
            // If this Laravel app is in a subdirectory, trim the subdirectory from the path
            .replace(this._config.url.replace(/^\w*:\/\/[^/]+/, ''), '')
            .replace(/^\/+/, '');

        // Given part of a valid 'hydrated' URL containing all its parameter values,
        // a route template, and a delimiter, extract the parameters as an object
        // E.g. dehydrate('events/2/chicago', 'events/{event}/{venue}', '/'); // { event: 2, venue: 'chicago' }
        const dehydrate = (hydrated, template = '', delimiter) => {
            const [values, segments] = [hydrated, template].map(s => s.split(delimiter));

            return segments.reduce((result, current, i) => {
                // Only include template segments that are route parameters
                // AND have a value present in the passed hydrated string
                return /{[^}?]+\??}/.test(current) && values[i]
                    ? {
                        ...result,
                        [current.replace(/.*{|\??}.*/g, '')]: values[i]
                            .replace(current.match(/^[^{]*/g), '')
                            .replace(current.match(/[^}]*$/g), ''),
                    }
                    : result;
            }, {});
        }

        return {
            ...dehydrate(this._location().host, route.domain, '.'), // Domain parameters
            ...dehydrate(pathname, route.uri, '/'), // Path parameters
            ...parse(this._location().search?.replace(/^\?/, '')), // Query parameters
        };
    }

    valueOf() {
        return this.toString();
    }

    /**
     * @deprecated since v1.0, use `has()` instead.
     */
    check(name) {
        return this.has(name);
    }
}
<commit_msg>fix: catch undefined global Ziggy (#462)<commit_after>import { parse, stringify } from 'qs';
import Route from './Route';

/**
 * A collection of Laravel routes. This class constitutes Ziggy's main API.
 */
export default class Router extends String {
    /**
     * @param {String} name - Route name.
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @param {Boolean} absolute - Whether to include the URL origin.
     * @param {Object} config - Ziggy configuration.
     */
    constructor(name, params, absolute = true, config) {
        super();

        this._config = config ?? (typeof Ziggy !== 'undefined' ? Ziggy : globalThis?.Ziggy);
        this._config = { ...this._config, absolute };

        if (name) {
            if (!this._config.routes[name]) {
                throw new Error(`Ziggy error: route '${name}' is not in the route list.`);
            }

            this._route = new Route(name, this._config.routes[name], this._config);
            this._params = this._parse(params);
        }
    }

    /**
     * Get the compiled URL string for the current route and parameters.
     *
     * @example
     * // with 'posts.show' route 'posts/{post}'
     * (new Router('posts.show', 1)).toString(); // 'https://ziggy.dev/posts/1'
     *
     * @return {String}
     */
    toString() {
        // Get parameters that don't correspond to any route segments to append them to the query
        const unhandled = Object.keys(this._params)
            .filter((key) => !this._route.parameterSegments.some(({ name }) => name === key))
            .filter((key) => key !== '_query')
            .reduce((result, current) => ({ ...result, [current]: this._params[current] }), {});

        return this._route.compile(this._params) + stringify({ ...unhandled, ...this._params['_query'] }, {
            addQueryPrefix: true,
            arrayFormat: 'indices',
            encodeValuesOnly: true,
            skipNulls: true,
            encoder: (value, encoder) => typeof value === 'boolean' ? Number(value) : encoder(value),
        });
    }

    /**
     * Get the name of the route matching the current window URL, or, given a route name
     * and parameters, check if the current window URL and parameters match that route.
     *
     * @example
     * // at URL https://ziggy.dev/posts/4 with 'posts.show' route 'posts/{post}'
     * route().current(); // 'posts.show'
     * route().current('posts.index'); // false
     * route().current('posts.show'); // true
     * route().current('posts.show', { post: 1 }); // false
     * route().current('posts.show', { post: 4 }); // true
     *
     * @param {String} name - Route name to check.
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @return {(Boolean|String|undefined)}
     */
    current(name, params) {
        const url = this._config.absolute
            ? this._location().host + this._location().pathname
            : this._location().pathname.replace(this._config.url.replace(/^\w*:\/\/[^/]+/, ''), '').replace(/^\/+/, '/');

        // Find the first route that matches the current URL
        const [current, route] = Object.entries(this._config.routes).find(
            ([_, route]) => new Route(name, route, this._config).matchesUrl(url)
        ) || [undefined, undefined];

        // If a name wasn't passed, return the name of the current route
        if (!name) return current;

        // Test the passed name against the current route, matching some
        // basic wildcards, e.g. passing `events.*` matches `events.show`
        const match = new RegExp(`^${name.replace('.', '\\.').replace('*', '.*')}$`).test(current);

        if ([null, undefined].includes(params) || !match) return match;

        const routeObject = new Route(current, route, this._config);

        params = this._parse(params, routeObject);
        const routeParams = this._dehydrate(route);

        // If the current window URL has no route parameters, and the passed parameters are empty, return true
        if (Object.values(params).every(p => !p) && !Object.values(routeParams).length) return true;

        // Check that all passed parameters match their values in the current window URL
        // Use weak equality because all values in the current window URL will be strings
        return Object.entries(params).every(([key, value]) => routeParams[key] == value);
    }

    /**
     * Get an object representing the current location (by default this will be
     * the JavaScript `window` global if it's available).
     *
     * @return {Object}
     */
    _location() {
        const { host = '', pathname = '', search = '' } = typeof window !== 'undefined' ? window.location : {};

        return {
            host: this._config.location?.host ?? host,
            pathname: this._config.location?.pathname ?? pathname,
            search: this._config.location?.search ?? search,
        };
    }

    /**
     * Get all parameter values from the current window URL.
     *
     * @example
     * // at URL https://tighten.ziggy.dev/posts/4?lang=en with 'posts.show' route 'posts/{post}' and domain '{team}.ziggy.dev'
     * route().params; // { team: 'tighten', post: 4, lang: 'en' }
     *
     * @return {Object}
     */
    get params() {
        return this._dehydrate(this._config.routes[this.current()]);
    }

    /**
     * Check whether the given route exists.
     *
     * @param {String} name
     * @return {Boolean}
     */
    has(name) {
        return Object.keys(this._config.routes).includes(name);
    }

    /**
     * Parse Laravel-style route parameters of any type into a normalized object.
     *
     * @example
     * // with route parameter names 'event' and 'venue'
     * _parse(1); // { event: 1 }
     * _parse({ event: 2, venue: 3 }); // { event: 2, venue: 3 }
     * _parse(['Taylor', 'Matt']); // { event: 'Taylor', venue: 'Matt' }
     * _parse([4, { uuid: 56789, name: 'Grand Canyon' }]); // { event: 4, venue: 56789 }
     *
     * @param {(String|Number|Array|Object)} params - Route parameters.
     * @param {Route} route - Route instance.
     * @return {Object} Normalized complete route parameters.
     */
    _parse(params = {}, route = this._route) {
        // If `params` is a string or integer, wrap it in an array
        params = ['string', 'number'].includes(typeof params) ? [params] : params;

        // Separate segments with and without defaults, and fill in the default values
        const segments = route.parameterSegments.filter(({ name }) => !this._config.defaults[name]);

        if (Array.isArray(params)) {
            // If the parameters are an array they have to be in order, so we can transform them into
            // an object by keying them with the template segment names in the order they appear
            params = params.reduce((result, current, i) => !!segments[i] ? ({ ...result, [segments[i].name]: current }) : ({ ...result, [current]: '' }), {});
        } else if (
            segments.length === 1
            && !params[segments[0].name]
            && (params.hasOwnProperty(Object.values(route.bindings)[0]) || params.hasOwnProperty('id'))
        ) {
            // If there is only one template segment and `params` is an object, that object is
            // ambiguous—it could contain the parameter key and value, or it could be an object
            // representing just the value (e.g. a model); we can inspect it to find out, and
            // if it's just the parameter value, we can wrap it in an object with its key
            params = { [segments[0].name]: params };
        }

        return {
            ...this._defaults(route),
            ...this._substituteBindings(params, route),
        };
    }

    /**
     * Populate default parameters for the given route.
     *
     * @example
     * // with default parameters { locale: 'en', country: 'US' } and 'posts.show' route '{locale}/posts/{post}'
     * defaults(...); // { locale: 'en' }
     *
     * @param {Route} route
     * @return {Object} Default route parameters.
     */
    _defaults(route) {
        return route.parameterSegments.filter(({ name }) => this._config.defaults[name])
            .reduce((result, { name }, i) => ({ ...result, [name]: this._config.defaults[name] }), {});
    }

    /**
     * Substitute Laravel route model bindings in the given parameters.
     *
     * @example
     * _substituteBindings({ post: { id: 4, slug: 'hello-world', title: 'Hello, world!' } }, { bindings: { post: 'slug' } }); // { post: 'hello-world' }
     *
     * @param {Object} params - Route parameters.
     * @param {Object} route - Route definition.
     * @return {Object} Normalized route parameters.
     */
    _substituteBindings(params, { bindings, parameterSegments }) {
        return Object.entries(params).reduce((result, [key, value]) => {
            // If the value isn't an object, or if the key isn't a named route parameter,
            // there's nothing to substitute so we return it as-is
            if (!value || typeof value !== 'object' || Array.isArray(value) || !parameterSegments.some(({ name }) => name === key)) {
                return { ...result, [key]: value };
            }

            if (!value.hasOwnProperty(bindings[key])) {
                if (value.hasOwnProperty('id')) {
                    // As a fallback, we still accept an 'id' key not explicitly registered as a binding
                    bindings[key] = 'id';
                } else {
                    throw new Error(`Ziggy error: object passed as '${key}' parameter is missing route model binding key '${bindings[key]}'.`)
                }
            }

            return { ...result, [key]: value[bindings[key]] };
        }, {});
    }

    /**
     * Get all parameters and their values from the current window URL, based on the given route definition.
     *
     * @example
     * // at URL https://tighten.ziggy.dev/events/8/venues/chicago?zoom=true
     * _dehydrate({ domain: '{team}.ziggy.dev', uri: 'events/{event}/venues/{venue?}' }); // { team: 'tighten', event: 8, venue: 'chicago', zoom: true }
     *
     * @param {Object} route - Route definition.
     * @return {Object} Parameters.
     */
    _dehydrate(route) {
        let pathname = this._location().pathname
            // If this Laravel app is in a subdirectory, trim the subdirectory from the path
            .replace(this._config.url.replace(/^\w*:\/\/[^/]+/, ''), '')
            .replace(/^\/+/, '');

        // Given part of a valid 'hydrated' URL containing all its parameter values,
        // a route template, and a delimiter, extract the parameters as an object
        // E.g. dehydrate('events/2/chicago', 'events/{event}/{venue}', '/'); // { event: 2, venue: 'chicago' }
        const dehydrate = (hydrated, template = '', delimiter) => {
            const [values, segments] = [hydrated, template].map(s => s.split(delimiter));

            return segments.reduce((result, current, i) => {
                // Only include template segments that are route parameters
                // AND have a value present in the passed hydrated string
                return /{[^}?]+\??}/.test(current) && values[i]
                    ? {
                        ...result,
                        [current.replace(/.*{|\??}.*/g, '')]: values[i]
                            .replace(current.match(/^[^{]*/g), '')
                            .replace(current.match(/[^}]*$/g), ''),
                    }
                    : result;
            }, {});
        }

        return {
            ...dehydrate(this._location().host, route.domain, '.'), // Domain parameters
            ...dehydrate(pathname, route.uri, '/'), // Path parameters
            ...parse(this._location().search?.replace(/^\?/, '')), // Query parameters
        };
    }

    valueOf() {
        return this.toString();
    }

    /**
     * @deprecated since v1.0, use `has()` instead.
     */
    check(name) {
        return this.has(name);
    }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @module dhsniem
 *
 * @description
 * Main module of the application.
 */
angular
  .module('dhsniem', [
    'ngAnimate',
    'ngCookies',
    'ui.router',
    'ngSanitize',
    'ngTouch',
    'ui.bootstrap'
  ])
  .constant('SOLR_URL', 'http://192.168.99.101:8983/solr/dhsniem/select')
  .config(function($urlRouterProvider) {
    $urlRouterProvider.when('', '/'); // redirect to root if the state is ''

    $urlRouterProvider.otherwise('/'); // redirect to root if state is not found
  });
<commit_msg>changing solr url to hit the dhsniem-dev instance<commit_after>'use strict';

/**
 * @module dhsniem
 *
 * @description
 * Main module of the application.
 */
angular
  .module('dhsniem', [
    'ngAnimate',
    'ngCookies',
    'ui.router',
    'ngSanitize',
    'ngTouch',
    'ui.bootstrap'
  ])
  .constant('SOLR_URL', 'http://35.164.75.93:8983/solr/dhsniem/select')
  .config(function($urlRouterProvider) {
    $urlRouterProvider.when('', '/'); // redirect to root if the state is ''

    $urlRouterProvider.otherwise('/'); // redirect to root if state is not found
  });
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import { scalePow } from 'd3-scale';
import isEmpty from 'lodash/isEmpty';
import { CALCULATION_OPTIONS } from 'app/data/constants';
import groupBy from 'lodash/groupBy';

import worldPaths from 'app/data/world-50m-paths';

const calculationKeys = Object.keys(CALCULATION_OPTIONS);
const options = calculationKeys.map(
  calculationKey => CALCULATION_OPTIONS[calculationKey]
);
const getCountries = state => state.countries;
const getIso = state => state.iso;
const getData = state => state.data;
const isLoaded = state => state.loaded;
const isLoading = state => state.loading;
const getSources = state => state.meta.data_source || null;
const getSourceSelection = state => state.search.source || false;
const getYear = state => parseInt(state.year, 10);
const getCalculationSelection = state => state.search.calculation || null;
const getCalculationData = state =>
  (state.calculationData && state.calculationData[state.iso]) || [];

export const getCalculationSelected = createSelector(
  [getCalculationSelection],
  selected => {
    if (!selected) return options[0];
    return options.find(calculation => calculation.value === selected);
  }
);

const parseCalculationData = createSelector([getCalculationData], data => {
  if (!data || !data.length) return null;
  return groupBy(data, 'year');
});

const EXCLUDED_INDICATORS = ['WORLD'];
const buckets = [
  '#fffffb',
  '#ffffd5',
  '#f4f6c8',
  '#a6d8b3',
  '#6cc1a7',
  '#439c9a',
  '#2b6c85',
  '#1b4a75',
  '#163449'
];
const steps = [0, 0.4, 0.8, 2, 4, 8, 16, 32, 64];
let colorScale = null;
function setScale(ranges) {
  colorScale = scalePow()
    .domain(ranges)
    .range(buckets);
}

function getRanges(min, max) {
  return buckets.map((el, i) => max / 100 * steps[i]); // eslint-disable-line
}

export const getSourceOptions = createSelector(getSources, sources => {
  if (!sources || !sources.length) return null;
  return sources.map(d => ({
    label: d.label,
    value: d.value
  }));
});

export const getSourceSelected = createSelector(
  [getSourceOptions, getSourceSelection],
  (sources, selected) => {
    if (!sources || !sources.length) return null;
    if (!selected) return sources[0];
    return sources.find(category => category.value === parseInt(selected, 10));
  }
);

export const getYearSelected = createSelector(
  [getData, getYear],
  (data, year) => {
    if (!data || !data.length) return null;
    let selectedYear = year;
    if (!selectedYear) {
      selectedYear = data[0].emissions[data[0].emissions.length - 1].year;
    }
    return selectedYear;
  }
);

const calculatedRatio = (selected, calculationData, x) => {
  if (selected === CALCULATION_OPTIONS.PER_GDP.value) {
    return calculationData[x][0].gdp;
  }
  if (selected === CALCULATION_OPTIONS.PER_CAPITA.value) {
    return calculationData[x][0].population;
  }
  return 1;
};

export const getDataParsed = createSelector(
  [getData, parseCalculationData, getYearSelected, getCalculationSelected],
  (data, calculationData, year, calculationSelected) => {
    if (!data || isEmpty(data) || !calculationData || !calculationSelected) { return null; }
    const dataParsed = {};
    let max = 0;
    let min = 9999999999;

    data.forEach(d => {
      const item = d.emissions.find(e => e.year === year);
      if (item && item.value && !EXCLUDED_INDICATORS.includes(item.iso_code3)) {
        const calculationRatio = calculatedRatio(
          calculationSelected.value,
          calculationData,
          item.year
        );

        const value = item.value / calculationRatio;
        if (value > max) max = value;
        if (value < min) min = value;

        dataParsed[d.iso_code3] = value;
      } else {
        dataParsed[d.iso_code3] = null;
      }
    });
    return {
      min,
      max,
      year,
      values: dataParsed
    };
  }
);

// get selector defaults
export const getDefaultValues = createSelector(
  [getSources, getSourceSelected],
  (sources, sourceSelected) => {
    if (!sources || !sources.length || !sourceSelected) return null;
    const sourceData = sources.find(d => d.value === sourceSelected.value);
    return {
      sector: sourceData.sector[0],
      gas: sourceData.gas[0],
      source: sources[0].value
    };
  }
);

export const getPathsWithStyles = createSelector([getDataParsed], data => {
  if (!data) return worldPaths;

  const { min, max } = data;
  if (min && max) setScale(getRanges(min, max));
  return worldPaths.map(path => {
    let color = '#E5E5EB'; // default color
    const iso = path.properties && path.properties.id;
    if (data && data.values && data.values[iso]) {
      color = colorScale(data.values[iso]);
    }
    const style = {
      default: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.1,
        outline: 'none'
      },
      hover: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.1,
        outline: 'none'
      },
      pressed: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.5,
        outline: 'none'
      }
    };

    return {
      ...path,
      style
    };
  });
});

export const getLegendData = createSelector(
  [getCalculationSelected, getYearSelected],
  (calculation, year) => {
    let calculationText = '';
    if (
      calculation &&
      calculation.value &&
      calculation.value !== CALCULATION_OPTIONS.ABSOLUTE_VALUE.value
    ) {
      calculationText = `${CALCULATION_OPTIONS[calculation.value].label} `;
    }

    return {
      title: `GHG Emissions ${calculationText}in ${year}`,
      buckets
    };
  }
);

export const getMapReady = createSelector(
  [isLoaded, isLoading],
  (loaded, loading) => loaded && !loading
);

export const getMapCenter = createSelector(
  [getCountries, getIso],
  (countries, iso) => {
    const country = countries.find(c => c.iso_code3 === iso);
    if (!country || !country.centroid) return [0, 20];
    return country.centroid.coordinates;
  }
);

export default {
  getPathsWithStyles,
  getMapCenter
};
<commit_msg>Get country calculation data by year - its slow<commit_after>import { createSelector } from 'reselect';
import { scalePow } from 'd3-scale';
import isEmpty from 'lodash/isEmpty';
import { CALCULATION_OPTIONS } from 'app/data/constants';
import groupBy from 'lodash/groupBy';

import worldPaths from 'app/data/world-50m-paths';

const calculationKeys = Object.keys(CALCULATION_OPTIONS);
const options = calculationKeys.map(
  calculationKey => CALCULATION_OPTIONS[calculationKey]
);
const getCountries = state => state.countries;
const getIso = state => state.iso;
const getData = state => state.data;
const isLoaded = state => state.loaded;
const isLoading = state => state.loading;
const getSources = state => state.meta.data_source || null;
const getSourceSelection = state => state.search.source || false;
const getYear = state => parseInt(state.year, 10);
const getCalculationSelection = state => state.search.calculation || null;
const getCalculationData = state => state.calculationData || null;

export const getCalculationSelected = createSelector(
  [getCalculationSelection],
  selected => {
    if (!selected) return options[0];
    return options.find(calculation => calculation.value === selected);
  }
);

const EXCLUDED_INDICATORS = ['WORLD'];
const buckets = [
  '#fffffb',
  '#ffffd5',
  '#f4f6c8',
  '#a6d8b3',
  '#6cc1a7',
  '#439c9a',
  '#2b6c85',
  '#1b4a75',
  '#163449'
];
const steps = [0, 0.4, 0.8, 2, 4, 8, 16, 32, 64];
let colorScale = null;
function setScale(ranges) {
  colorScale = scalePow()
    .domain(ranges)
    .range(buckets);
}

function getRanges(min, max) {
  return buckets.map((el, i) => max / 100 * steps[i]); // eslint-disable-line
}

export const getSourceOptions = createSelector(getSources, sources => {
  if (!sources || !sources.length) return null;
  return sources.map(d => ({
    label: d.label,
    value: d.value
  }));
});

export const getSourceSelected = createSelector(
  [getSourceOptions, getSourceSelection],
  (sources, selected) => {
    if (!sources || !sources.length) return null;
    if (!selected) return sources[0];
    return sources.find(category => category.value === parseInt(selected, 10));
  }
);

export const getYearSelected = createSelector(
  [getData, getYear],
  (data, year) => {
    if (!data || !data.length) return null;
    let selectedYear = year;
    if (!selectedYear) {
      selectedYear = data[0].emissions[data[0].emissions.length - 1].year;
    }
    return selectedYear;
  }
);

const calculatedRatio = (selected, calculationData, x) => {
  if (selected === CALCULATION_OPTIONS.PER_GDP.value) {
    return calculationData[x][0].gdp;
  }
  if (selected === CALCULATION_OPTIONS.PER_CAPITA.value) {
    return calculationData[x][0].population;
  }
  return 1;
};

const yearHasCalculationData = (data, iso, year) =>
  data[iso] && data[iso].some(d => d.year === year);
const countryCalculationDataByYear = (data, iso) => groupBy(data[iso], 'year');

export const getDataParsed = createSelector(
  [getData, getCalculationData, getYearSelected, getCalculationSelected],
  (data, calculationData, year, calculationSelected) => {
    if (
      !data ||
      isEmpty(data) ||
      isEmpty(calculationData) ||
      !calculationSelected
    ) {
      return null;
    }
    const dataParsed = {};
    let max = 0;
    let min = 9999999999;

    data.forEach(d => {
      const item = d.emissions.find(e => e.year === year);
      if (
        item &&
        item.value &&
        !EXCLUDED_INDICATORS.includes(d.iso_code3) &&
        yearHasCalculationData(calculationData, d.iso_code3, item.year)
      ) {
        const calculationRatio = calculatedRatio(
          calculationSelected.value,
          countryCalculationDataByYear(calculationData, d.iso_code3),
          item.year
        );

        const value = item.value / calculationRatio;
        if (value > max) max = value;
        if (value < min) min = value;
        dataParsed[d.iso_code3] = value;
      } else {
        dataParsed[d.iso_code3] = null;
      }
    });
    return {
      min,
      max,
      year,
      values: dataParsed
    };
  }
);

// get selector defaults
export const getDefaultValues = createSelector(
  [getSources, getSourceSelected],
  (sources, sourceSelected) => {
    if (!sources || !sources.length || !sourceSelected) return null;
    const sourceData = sources.find(d => d.value === sourceSelected.value);
    return {
      sector: sourceData.sector[0],
      gas: sourceData.gas[0],
      source: sources[0].value
    };
  }
);

export const getPathsWithStyles = createSelector([getDataParsed], data => {
  if (!data) return worldPaths;

  const { min, max } = data;
  if (min && max) setScale(getRanges(min, max));
  return worldPaths.map(path => {
    let color = '#E5E5EB'; // default color
    const iso = path.properties && path.properties.id;
    if (data && data.values && data.values[iso]) {
      color = colorScale(data.values[iso]);
    }
    const style = {
      default: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.1,
        outline: 'none'
      },
      hover: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.1,
        outline: 'none'
      },
      pressed: {
        fill: color,
        stroke: '#000',
        strokeWidth: 0.5,
        outline: 'none'
      }
    };

    return {
      ...path,
      style
    };
  });
});

export const getLegendData = createSelector(
  [getCalculationSelected, getYearSelected],
  (calculation, year) => {
    let calculationText = '';
    if (
      calculation &&
      calculation.value &&
      calculation.value !== CALCULATION_OPTIONS.ABSOLUTE_VALUE.value
    ) {
      calculationText = `${CALCULATION_OPTIONS[calculation.value].label} `;
    }

    return {
      title: `GHG Emissions ${calculationText}in ${year}`,
      buckets
    };
  }
);

export const getMapReady = createSelector(
  [isLoaded, isLoading],
  (loaded, loading) => loaded && !loading
);

export const getMapCenter = createSelector(
  [getCountries, getIso],
  (countries, iso) => {
    const country = countries.find(c => c.iso_code3 === iso);
    if (!country || !country.centroid) return [0, 20];
    return country.centroid.coordinates;
  }
);

export default {
  getPathsWithStyles,
  getMapCenter
};
<|endoftext|>


<commit_before>import {browserHistory} from 'react-router'
import {routerMiddleware} from 'react-router-redux'
import {applyMiddleware, createStore} from 'redux'
import {createLogger} from 'redux-logger'
import thunkMiddleware from 'redux-thunk'

import {middleware as fetch} from '../fetch'

import multi from './multi'
import promise from './promise'

const logger = createLogger({
  collapsed: true,
  duration: true
})

const middlewares = [
  routerMiddleware(browserHistory),
  fetch,
  multi,
  promise,
  thunkMiddleware,
  logger
]

if (process.env.LOGROCKET) {
  const LogRocket = require('logrocket')
  middlewares.push(LogRocket.reduxMiddleware())
}

export default function configureStore (rootReducer, initialState) {
  return createStore(
    rootReducer,
    initialState,
    applyMiddleware(...middlewares),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
}
<commit_msg>fix(redux-devtools): fixes store creation w redux devtools<commit_after>import {browserHistory} from 'react-router'
import {routerMiddleware} from 'react-router-redux'
import {applyMiddleware, compose, createStore} from 'redux'
import {createLogger} from 'redux-logger'
import thunkMiddleware from 'redux-thunk'

import {middleware as fetch} from '../fetch'

import multi from './multi'
import promise from './promise'

const logger = createLogger({
  collapsed: true,
  duration: true
})

const middlewares = [
  routerMiddleware(browserHistory),
  fetch,
  multi,
  promise,
  thunkMiddleware,
  logger
]

if (process.env.LOGROCKET) {
  const LogRocket = require('logrocket')
  middlewares.push(LogRocket.reduxMiddleware())
}
// Use Redux Dev Tools to compose enhancers if available.
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
export default function configureStore (rootReducer, initialState) {
  return createStore(
    rootReducer,
    initialState,
    composeEnhancers(applyMiddleware(...middlewares))
  )
}
<|endoftext|>


<commit_before>(function () {

    "use strict";

    var express = require("express"),
        fluid = require("infusion"),
        fs = require("fs"),
        path = require("path"),
        querystring = require("querystring");

    var gpii = fluid.registerNamespace("gpii");

    var findArgv = function (key) {
        return fluid.find(process.argv, function (arg) {
            if (arg.indexOf(key + "=") === 0) {
                return arg.substr(key.length + 1);
            }
        });
    };

    fluid.require("gpiiFramework", require);
    fluid.require("matchMaker", require);
    fluid.require("deviceReporter", require);
    fluid.require("configurationManager", require);

    fluid.defaults("gpii.flowManager", {
        gradeNames: ["fluid.eventedComponent", "autoInit"],
        preInitFunction: "gpii.flowManager.preInit",
        finalInitFunction: "gpii.flowManager.finalInit",
        components: {
            preferencesDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.preferences.url",
                    termMap: {
                        token: "%token"
                    }
                }
            },
            deviceReporterDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.deviceReporter.url"
                }
            },
            matchMakerDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.matchMaker.url",
                    writable: true
                }
            },
            launchManagerDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.launchManager.url",
                    writable: true
                }
            }
        },
        events: {
            onUserListener: null,
            onPreferences: null,
            onDevice: null,
            onReadyToMatch: {
                events: {
                   preferences: "onPreferences",
                   device: "onDevice"
                },
                args: ["{arguments}.preferences.0", "{arguments}.device.0"]
            },
            onMatch: null
        },
        listeners: {
            onUserListener: [{
                listener: "{gpii.flowManager}.getPreferences"
            }, {
                listener: "{gpii.flowManager}.getDevice"
            }],
            onReadyToMatch: "{gpii.flowManager}.onReadyToMatchHandler",
            onMatch: "{gpii.flowManager}.onMatchHandler"
        }
    });

    gpii.flowManager.preInit = function (that) {
        that.server = express.createServer();
        that.server.configure(function () {
            that.server.use(express.bodyParser());
        });
        that.server.configure("production", function () {
            // Set production options.
            fluid.staticEnvironment.production = fluid.typeTag("gpii.production");
            that.config = JSON.parse(fs.readFileSync(path.join(__dirname, "../config.production.json")));
            fluid.setLogging(false);
        });
        that.server.configure("development", function () {
            // Set development options.
            fluid.staticEnvironment.production = fluid.typeTag("gpii.development");
            that.config = JSON.parse(fs.readFileSync(path.join(__dirname, "../config.development.json")));
            fluid.setLogging(true);
        });

        that.getPreferences = function (token) {
            that.preferencesDataSource.get({
                token: token
            }, function (preferences) {
                if (preferences && preferences.isError) {
                    fluid.log(preferences.message);
                    return;
                }
                fluid.log("Fetched user preferences: ", preferences);
                that.events.onPreferences.fire(preferences);
            });
        };
        that.getDevice = function () {
            that.deviceReporterDataSource.get(undefined, function (device) {
                if (device && device.isError) {
                    fluid.log(device.message);
                    return;
                }
                fluid.log("Fetched device reporter data: ", device);
                that.events.onDevice.fire(device);
            });
        };
        that.onReadyToMatchHandler = function (preferences, device) {
            that.matchMakerDataSource.set(undefined, {
                preferences: preferences,
                device: device
            }, function (match) {
                if (match && match.isError) {
                    fluid.log(match.message);
                    return;
                }
                fluid.log("Matched preferences and device reporter data: ", match);
                that.events.onMatch.fire(match);
            });
        };
        that.onMatchHandler = function (match) {
            that.launchManagerDataSource.set(undefined, match, function (response) {
                if (response && response.isError) {
                    fluid.log(response.message);
                    return;
                }
                fluid.log("Launch manager returned: ", response);
            });
        };
    };

    gpii.flowManager.finalInit = function (that) {

        that.server.get("/user/:token/login", function (req, res) {
            var token = req.params["token"];
            that.events.onUserListener.fire(token);
            res.send("User with token " + token + " was successfully logged in.", 200);
        });

        that.server.get("/user/:token/logout", function (req, res) {
            var token = req.params["token"];
            that.launchManagerDataSource.del(undefined, function (response) {
                if (response && response.isError) {
                    fluid.log(response.message);
                    return;
                }
                fluid.log("Launch manager returned: ", response);
            });
            res.send("User with token " + token + " was successfully logged out.", 200);
        });

        var port = findArgv("port") || 8081;
        fluid.log("Flow Manager is running on port: " + port);
        that.server.listen(typeof port === "string" ? parseInt(port, 10) : port);
    };

    fluid.demands("gpii.urlExpander", ["gpii.development", "gpii.flowManager"], {
        options: {
            vars: {
                db: path.join(__dirname, ".."),
                root: path.join(__dirname, "..")
            }
        }
    });

    fluid.demands("gpii.dataSource", ["gpii.production"], {
        funcName: "gpii.dataSource.URL"
    });

    fluid.demands("gpii.dataSource", ["gpii.development"], {
        funcName: "gpii.dataSource.file"
    });

    fluid.demands("matchMakerDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.matchMaker"
    });

    fluid.demands("deviceReporterDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.deviceReporter"
    });

    fluid.demands("launchManagerDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.configurationManager"
    });

})();<commit_msg>Made a minor change to the flowManager so that it now properly ends the connection.<commit_after>(function () {

    "use strict";

    var express = require("express"),
        fluid = require("infusion"),
        fs = require("fs"),
        path = require("path"),
        querystring = require("querystring");

    var gpii = fluid.registerNamespace("gpii");

    var findArgv = function (key) {
        return fluid.find(process.argv, function (arg) {
            if (arg.indexOf(key + "=") === 0) {
                return arg.substr(key.length + 1);
            }
        });
    };

    fluid.require("gpiiFramework", require);
    fluid.require("matchMaker", require);
    fluid.require("deviceReporter", require);
    fluid.require("configurationManager", require);

    fluid.defaults("gpii.flowManager", {
        gradeNames: ["fluid.eventedComponent", "autoInit"],
        preInitFunction: "gpii.flowManager.preInit",
        finalInitFunction: "gpii.flowManager.finalInit",
        components: {
            preferencesDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.preferences.url",
                    termMap: {
                        token: "%token"
                    }
                }
            },
            deviceReporterDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.deviceReporter.url"
                }
            },
            matchMakerDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.matchMaker.url",
                    writable: true
                }
            },
            launchManagerDataSource: {
                type: "gpii.dataSource",
                options: {
                    url: "{gpii.flowManager}.config.launchManager.url",
                    writable: true
                }
            }
        },
        events: {
            onUserListener: null,
            onPreferences: null,
            onDevice: null,
            onReadyToMatch: {
                events: {
                   preferences: "onPreferences",
                   device: "onDevice"
                },
                args: ["{arguments}.preferences.0", "{arguments}.device.0"]
            },
            onMatch: null
        },
        listeners: {
            onUserListener: [{
                listener: "{gpii.flowManager}.getPreferences"
            }, {
                listener: "{gpii.flowManager}.getDevice"
            }],
            onReadyToMatch: "{gpii.flowManager}.onReadyToMatchHandler",
            onMatch: "{gpii.flowManager}.onMatchHandler"
        }
    });

    gpii.flowManager.preInit = function (that) {
        that.server = express.createServer();
        that.server.configure(function () {
            that.server.use(express.bodyParser());
        });
        that.server.configure("production", function () {
            // Set production options.
            fluid.staticEnvironment.production = fluid.typeTag("gpii.production");
            that.config = JSON.parse(fs.readFileSync(path.join(__dirname, "../config.production.json")));
            fluid.setLogging(false);
        });
        that.server.configure("development", function () {
            // Set development options.
            fluid.staticEnvironment.production = fluid.typeTag("gpii.development");
            that.config = JSON.parse(fs.readFileSync(path.join(__dirname, "../config.development.json")));
            fluid.setLogging(true);
        });

        that.getPreferences = function (token) {
            that.preferencesDataSource.get({
                token: token
            }, function (preferences) {
                if (preferences && preferences.isError) {
                    fluid.log(preferences.message);
                    return;
                }
                fluid.log("Fetched user preferences: ", preferences);
                that.events.onPreferences.fire(preferences);
            });
        };
        that.getDevice = function () {
            that.deviceReporterDataSource.get(undefined, function (device) {
                if (device && device.isError) {
                    fluid.log(device.message);
                    return;
                }
                fluid.log("Fetched device reporter data: ", device);
                that.events.onDevice.fire(device);
            });
        };
        that.onReadyToMatchHandler = function (preferences, device) {
            that.matchMakerDataSource.set(undefined, {
                preferences: preferences,
                device: device
            }, function (match) {
                if (match && match.isError) {
                    fluid.log(match.message);
                    return;
                }
                fluid.log("Matched preferences and device reporter data: ", match);
                that.events.onMatch.fire(match);
            });
        };
        that.onMatchHandler = function (match) {
            that.launchManagerDataSource.set(undefined, match, function (response) {
                if (response && response.isError) {
                    fluid.log(response.message);
                    return;
                }
                fluid.log("Launch manager returned: ", response);
            });
        };
    };

    gpii.flowManager.finalInit = function (that) {

        that.server.get("/user/:token/login", function (req, res) {
            var token = req.params["token"];
            that.events.onUserListener.fire(token);
            res.send("User with token " + token + " was successfully logged in.", 200);
            res.end();
        });

        that.server.get("/user/:token/logout", function (req, res) {
            var token = req.params["token"];
            that.launchManagerDataSource.del(undefined, function (response) {
                if (response && response.isError) {
                    fluid.log(response.message);
                    return;
                }
                fluid.log("Launch manager returned: ", response);
            });
            res.send("User with token " + token + " was successfully logged out.", 201);
            res.end();
        });

        var port = findArgv("port") || 8081;
        fluid.log("Flow Manager is running on port: " + port);
        that.server.listen(typeof port === "string" ? parseInt(port, 10) : port);
    };

    fluid.demands("gpii.urlExpander", ["gpii.development", "gpii.flowManager"], {
        options: {
            vars: {
                db: path.join(__dirname, ".."),
                root: path.join(__dirname, "..")
            }
        }
    });

    fluid.demands("gpii.dataSource", ["gpii.production"], {
        funcName: "gpii.dataSource.URL"
    });

    fluid.demands("gpii.dataSource", ["gpii.development"], {
        funcName: "gpii.dataSource.file"
    });

    fluid.demands("matchMakerDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.matchMaker"
    });

    fluid.demands("deviceReporterDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.deviceReporter"
    });

    fluid.demands("launchManagerDataSource", ["gpii.flowManager", "gpii.development"], {
        funcName: "gpii.configurationManager"
    });

})();
<|endoftext|>


<commit_before>// @flow
import React, {PureComponent} from 'react';
import * as Immutable from 'immutable';

import type {ASTNode, ASTPath} from './types';
import {ArrayExpression, ObjectExpression} from './components/collections';
import Keymap from './components/keymap';
import {BooleanLiteral, NumericLiteral, StringLiteral} from './components/literals';
import {
  isBooleanLiteral, isNumericLiteral, isArrayExpression, isObjectExpression, isEditable
} from './checks';
import example from '../package.json';
import navigate from './navigate';
import parse from './parse';
import renderTypeElement, {injectTypeElements} from './render-type-element';

const {List, Map} = Immutable;

const MAX_HISTORY_LENGTH = 100;

injectTypeElements({
  BooleanLiteral,
  NumericLiteral,
  StringLiteral,
  ArrayExpression,
  ObjectExpression
});

const BooleanNode = new Map({type: 'BooleanLiteral', value: true});
const NumericNode = new Map({type: 'NumericLiteral', value: '0'});
const StringNode = new Map({type: 'StringLiteral', value: ''});
const ArrayNode = Immutable.fromJS({type: 'ArrayExpression', elements: []});
const ObjectNode = Immutable.fromJS({type: 'ObjectExpression', properties: []});

const links = [
  ['Reasoning', 'https://medium.com/@grgtwt/code-is-not-just-text-1082981ae27f'],
  ['Roadmap', 'https://github.com/Gregoor/syntactor#roadmap'],
  ['GitHub', 'https://github.com/gregoor/syntactor'],
  ['Issues', 'https://github.com/Gregoor/syntactor/issues']
];

const cardStyle = {
  padding: 20,
  background: 'white',
  boxShadow: '0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12)'
};

export default class App extends PureComponent {

  state = {
    future: new List(),
    history: new List([
      new Map({
        inputMode: false,
        root: parse(example),
        selected: new List()
      })
    ])
  };

  getSelectedNode() {
    const editorState = this.state.history.first();
    return editorState.get('root').getIn(editorState.get('selected'));
  }

  getClosestCollectionPath(root: ASTNode, selected: ASTPath) {
    const selectedNode = root.getIn(selected);

    if (selectedNode) {
      if (isObjectExpression(selectedNode)) {
        return selected.push('properties');
      } else if (isArrayExpression(selectedNode)) {
        return selected.push('elements');
      }
    }

    const index = selected.findLastIndex((key) => ['elements', 'properties'].includes(key));
    return selected.slice(0, index + 1);
  }

  getDirection(key: string) {
    switch (key) {

      case 'k':
      case 'ArrowUp':
        return 'UP';

      case 'j':
      case 'ArrowDown':
        return 'DOWN';

      case 'h':
      case 'ArrowLeft':
        return 'LEFT';

      case 'l':
      case 'ArrowRight':
        return 'RIGHT';

      default:
        return;

    }
  }

  addToHistory(updateFn: (root: ASTNode, selected: ASTPath) => any) {
    this.setState(({history}) => {
      const editorState = history.first();
      const root = editorState.get('root');
      const selected = editorState.get('selected');
      const inputMode = editorState.get('inputMode');
      return {
        future: new List(),
        history: history.unshift(new Map({
          inputMode, root, selected,
          ...updateFn(root, selected, inputMode)
        })).slice(0, MAX_HISTORY_LENGTH)
      };
    });
  }

  updateValue(updateFn: (any) => any) {
    this.addToHistory((root, selected) => ({
      root: root.updateIn(selected.push('value'), updateFn)
    }));
  }

  insert = (node: ASTNode) => this.addToHistory((root, selected) => {
    const collectionPath = this.getClosestCollectionPath(root, selected);
    const itemIndex = selected.get(collectionPath.size) + 1 || 0;

    const isArray = isArrayExpression(root.getIn(collectionPath.slice(0, -1)));

    const newRoot = root.updateIn(collectionPath, (list) => list.insert(
      itemIndex,
      isArray
        ? node
        : new Map({type: 'ObjectProperty', key: StringNode, value: node})
    ));
    const newSelected = isArray
      ? collectionPath.push(itemIndex)
      : collectionPath.push(itemIndex, 'key');
    return {
      inputMode: !isArray || isEditable(newRoot.getIn(newSelected)),
      root: newRoot,
      selected: newSelected
    };
  });

  handleKeyDown = (event: any) => {
    const {ctrlKey, key} = event;

    const direction = this.getDirection(key);

    if (!this.state.history.first().get('inputMode')) {
      event.preventDefault();

      if (direction) {
        return this.addToHistory((root, selected) => ({
          selected: navigate(direction, root, selected)
        }));
      }

      const selectedNode = this.getSelectedNode();

      switch (key) {

        case 's':
        case '\'':
          return this.insert(StringNode);

        case 'n':
          return this.insert(NumericNode);

        case 'b':
          return this.insert(BooleanNode);

        case 'a':
        case '[':
          return this.insert(ArrayNode);

        case 'o':
        case '{':
          return this.insert(ObjectNode);

        case '+':
        case '-':
          return isNumericLiteral(selectedNode) && this.updateValue(
            (value) => value + (key === '+' ? 1 : -1)
          );

        case 't':
        case 'f':
          return isBooleanLiteral(selectedNode) && this.updateValue(
            () => Boolean(key === 't')
          );

        case 'd':
        case 'Delete':
          return this.addToHistory((root, selected) => ({
            root: root.deleteIn(
              selected.slice(0, 1 + selected.findLastIndex((value) => typeof value === 'number'))
            ),
            selected: selected.last() === 'end' ? new List() : navigate('UP', root, selected)
          }));

        case 'i':
        case 'Enter':
          return isEditable(selectedNode) && this.addToHistory(() => ({inputMode: true}));

        default:

      }
    }

    if (ctrlKey && key === 'z') {
      return this.setState(({future, history}) => ({
        future: future.unshift(history.first()),
        history: history.shift()
      }));
    }

    if (ctrlKey && key === 'Z') {
      return this.setState(({future, history}) => ({
        future: future.shift(),
        history: future.isEmpty() ? history : history.unshift(future.first())
      }));
    }

    if (['Enter', 'Escape'].includes(key)) {
      return this.addToHistory((root, selected) => ({
        inputMode: false,
        root: root.getIn(selected.push('type')) === 'NumericLiteral'
          ? root.updateIn(selected.push('value'), (value) => parseFloat(value))
          : root
      }));
    }
  };

  handleChange = ({target: {value}}: any) => {
    this.addToHistory((root, selected, inputMode) => inputMode ? {
      root: root.setIn(selected.push('value'), value)
    } : {});
  };

  render() {
    const {history} = this.state;
    const editorState = history.first();
    const inputMode = editorState.get('inputMode');
    const selected = editorState.get('selected');
    const isInArray = (selected.last() === 'end' ? selected.slice(0, -2) : selected)
        .findLast((key) => ['elements', 'properties'].includes(key)) === 'elements';
    return (

 div && !inputMode && div.focus()}
           tabIndex="0" onKeyDown={this.handleKeyDown}>

Syntactor

              |
              {links.map(([label, link], i) => [
                {label},
                '|'
              ])}

              An editor with two basic goals:

Manage syntax and code style (no syntax errors, no bikeshedding)

At least as fast at making changes as regular editors

              For now, it's only a JSON editor.

            <form onChange={this.handleChange} style={{height: '100%', width: '100%', ...cardStyle}}>
              {renderTypeElement(editorState.get('root'), {inputMode, level: 0, selected})}
            </form>

              <Keymap {...{inputMode, isInArray}} selectedNode={this.getSelectedNode()}/>

    );
  }

}<commit_msg>add tab-key for navigating through editable fields<commit_after>// @flow
import React, {PureComponent} from 'react';
import * as Immutable from 'immutable';

import type {ASTNode, ASTPath} from './types';
import {ArrayExpression, ObjectExpression} from './components/collections';
import Keymap from './components/keymap';
import {BooleanLiteral, NumericLiteral, StringLiteral} from './components/literals';
import {
  isBooleanLiteral, isNumericLiteral, isArrayExpression, isObjectExpression, isEditable
} from './checks';
import example from '../package.json';
import navigate from './navigate';
import parse from './parse';
import renderTypeElement, {injectTypeElements} from './render-type-element';

const {List, Map} = Immutable;

const MAX_HISTORY_LENGTH = 100;

injectTypeElements({
  BooleanLiteral,
  NumericLiteral,
  StringLiteral,
  ArrayExpression,
  ObjectExpression
});

const BooleanNode = new Map({type: 'BooleanLiteral', value: true});
const NumericNode = new Map({type: 'NumericLiteral', value: '0'});
const StringNode = new Map({type: 'StringLiteral', value: ''});
const ArrayNode = Immutable.fromJS({type: 'ArrayExpression', elements: []});
const ObjectNode = Immutable.fromJS({type: 'ObjectExpression', properties: []});

const links = [
  ['Reasoning', 'https://medium.com/@grgtwt/code-is-not-just-text-1082981ae27f'],
  ['Roadmap', 'https://github.com/Gregoor/syntactor#roadmap'],
  ['GitHub', 'https://github.com/gregoor/syntactor'],
  ['Issues', 'https://github.com/Gregoor/syntactor/issues']
];

const cardStyle = {
  padding: 20,
  background: 'white',
  boxShadow: '0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12)'
};

export default class App extends PureComponent {

  state = {
    future: new List(),
    history: new List([
      new Map({
        inputMode: false,
        root: parse(example),
        selected: new List()
      })
    ])
  };

  getSelectedNode() {
    const editorState = this.state.history.first();
    return editorState.get('root').getIn(editorState.get('selected'));
  }

  getClosestCollectionPath(root: ASTNode, selected: ASTPath) {
    const selectedNode = root.getIn(selected);

    if (selectedNode) {
      if (isObjectExpression(selectedNode)) {
        return selected.push('properties');
      } else if (isArrayExpression(selectedNode)) {
        return selected.push('elements');
      }
    }

    const index = selected.findLastIndex((key) => ['elements', 'properties'].includes(key));
    return selected.slice(0, index + 1);
  }

  getDirection(key: string) {
    switch (key) {

      case 'k':
      case 'ArrowUp':
        return 'UP';

      case 'j':
      case 'ArrowDown':
        return 'DOWN';

      case 'h':
      case 'ArrowLeft':
        return 'LEFT';

      case 'l':
      case 'ArrowRight':
        return 'RIGHT';

      default:
        return;

    }
  }

  addToHistory(updateFn: (root: ASTNode, selected: ASTPath) => any) {
    this.setState(({history}) => {
      const editorState = history.first();
      const root = editorState.get('root');
      const selected = editorState.get('selected');
      const inputMode = editorState.get('inputMode');
      return {
        future: new List(),
        history: history.unshift(new Map({
          inputMode, root, selected,
          ...updateFn(root, selected, inputMode)
        })).slice(0, MAX_HISTORY_LENGTH)
      };
    });
  }

  updateValue(updateFn: (any) => any) {
    this.addToHistory((root, selected) => ({
      root: root.updateIn(selected.push('value'), updateFn)
    }));
  }

  insert = (node: ASTNode) => this.addToHistory((root, selected) => {
    const collectionPath = this.getClosestCollectionPath(root, selected);
    const itemIndex = selected.get(collectionPath.size) + 1 || 0;

    const isArray = isArrayExpression(root.getIn(collectionPath.slice(0, -1)));

    const newRoot = root.updateIn(collectionPath, (list) => list.insert(
      itemIndex,
      isArray
        ? node
        : new Map({type: 'ObjectProperty', key: StringNode, value: node})
    ));
    const newSelected = isArray
      ? collectionPath.push(itemIndex)
      : collectionPath.push(itemIndex, 'key');
    return {
      inputMode: !isArray || isEditable(newRoot.getIn(newSelected)),
      root: newRoot,
      selected: newSelected
    };
  });

  goToEditable(direction: 'LEFT' | 'RIGHT') {
    return this.addToHistory((root, selected) => {
      let visitedPaths = new List();
      let lastEditablePath = selected;
      let newSelectedNode;
      let newlySelectedIsEditable;
      do {
        selected = navigate(direction, root, selected);
        if (visitedPaths.includes(selected)) {
          return {selected: lastEditablePath};
        }
        visitedPaths = visitedPaths.push(selected);
        newSelectedNode = root.getIn(selected);
        newlySelectedIsEditable = newSelectedNode && isEditable(newSelectedNode);
        if (newlySelectedIsEditable) {
          lastEditablePath = selected;
        }
      } while (!newlySelectedIsEditable);
      return {selected: lastEditablePath};
    });
  }

  handleKeyDown = (event: any) => {
    const {ctrlKey, key} = event;

    const direction = this.getDirection(key);

    if (!this.state.history.first().get('inputMode')) {

      if (direction) {
        return this.addToHistory((root, selected) => ({
          selected: navigate(direction, root, selected)
        }));
      }

      const selectedNode = this.getSelectedNode();

      switch (key) {

        case 's':
        case '\'':
          return this.insert(StringNode);

        case 'n':
          return this.insert(NumericNode);

        case 'b':
          return this.insert(BooleanNode);

        case 'a':
        case '[':
          return this.insert(ArrayNode);

        case 'o':
        case '{':
          return this.insert(ObjectNode);

        case '+':
        case '-':
          return isNumericLiteral(selectedNode) && this.updateValue(
            (value) => value + (key === '+' ? 1 : -1)
          );

        case 't':
        case 'f':
          return isBooleanLiteral(selectedNode) && this.updateValue(
            () => Boolean(key === 't')
          );

        case 'd':
        case 'Delete':
          return this.addToHistory((root, selected) => ({
            root: root.deleteIn(
              selected.slice(0, 1 + selected.findLastIndex((value) => typeof value === 'number'))
            ),
            selected: selected.last() === 'end' ? new List() : navigate('UP', root, selected)
          }));

        case 'i':
        case 'Enter':
          return isEditable(selectedNode) && this.addToHistory(() => ({inputMode: true}));

        default:

      }
    }

    if (ctrlKey && key.toLowerCase() === 'z') {
      event.preventDefault();
      return this.setState(({future, history}) => key === 'z'
        ? {
          future: future.unshift(history.first()),
          history: history.shift()
        }
        : {
          future: future.shift(),
          history: future.isEmpty() ? history : history.unshift(future.first())
        }
      );
    }

    if (['Enter', 'Escape'].includes(key)) {
      this.addToHistory((root, selected) => ({
        inputMode: false,
        root: root.getIn(selected.push('type')) === 'NumericLiteral'
          ? root.updateIn(selected.push('value'), (value) => parseFloat(value))
          : root
      }));
    }

    if (key === 'Tab') {
      event.preventDefault();
      return this.goToEditable(event.shiftKey ? 'LEFT' : 'RIGHT');
    }
  };

  handleChange = ({target: {value}}: any) => {
    this.addToHistory((root, selected, inputMode) => inputMode ? {
      root: root.setIn(selected.push('value'), value)
    } : {});
  };

  render() {
    const {history} = this.state;
    const editorState = history.first();
    const inputMode = editorState.get('inputMode');
    const selected = editorState.get('selected');
    const isInArray = (selected.last() === 'end' ? selected.slice(0, -2) : selected)
        .findLast((key) => ['elements', 'properties'].includes(key)) === 'elements';
    return (

 div && !inputMode && div.focus()}
           tabIndex="0" onKeyDown={this.handleKeyDown}>

Syntactor

              |
              {links.map(([label, link], i) => [
                {label},
                '|'
              ])}

              An editor with two basic goals:

Manage syntax and code style (no syntax errors, no bikeshedding)

At least as fast at making changes as regular editors

              For now, it's only a JSON editor.

            <form onChange={this.handleChange} style={{height: '100%', width: '100%', ...cardStyle}}>
              {renderTypeElement(editorState.get('root'), {inputMode, level: 0, selected})}
            </form>

              <Keymap {...{inputMode, isInArray}} selectedNode={this.getSelectedNode()}/>

    );
  }

}<|endoftext|>


<commit_before>const Node = require('./Node')

class Linked {
  constructor() {
    this.first = new Node();
    this.N = 1
  }
  isEmpty() {
    return (this.first.next === null);
  }
  interator() {
  }
  size() {
    return this.N
  }
}

module.exports = Linked
<commit_msg>add iterator()<commit_after>const Node = require('./Node')

class Linked {
  constructor() {
    this.first = new Node();
    this.N = 0
  }
  isEmpty() {
    return (this.first.next === null);
  }
  interator() {
    let current = this.first
    return {
      hasNext: () =>( current.next !== null ),
      next: () => {
        let item = current.item
        current = current.next
        return item
      },
      remove: () => {
      }
    }
  }
  size() {
    return this.N
  }
}

module.exports = Linked
<|endoftext|>


<commit_before>'use strict';

/**
 * controller for (rickshaw) graph on entry page
 */
analyticsControllers.controller('RGraphController', ['$scope', '$attrs', 'EntrySvc',
    function($scope, $attrs, EntrySvc) {
		var self = this;
		var graphElement = null; 	// HTML element showing the graph
		var graph = null;			// graph JS object
		var series = null;			// data container

		this.init = function init (element) {
			graphElement = element;
			var d = new Date();
			var t = d.getTime()/1000;
			series = [{
					color : 'steelblue',
					data : [ {x:t, y:0} ],
					name : "Line 1"
				}];

			graph = createGraph(series, element);
		};

		var isRecordedEntry = function isRecordedEntry() {
			return !$scope.entry.isLive && $scope.entry.recordedEntryId && $scope.entry.recordedEntryId != '';
		};

		var graphClickHandler = function graphClickHandler(time) {
			if (isRecordedEntry()) {
				// click - only for recorded entries that are not currently live
				$scope.$emit('gotoTime', time);
			}
		};

		/**
		 * create graph and its parts
		 * @param series
		 * @param element
		 * @return rickshaw graph
		 */
		var createGraph = function createGraph(series, element) {
			var graph = new Rickshaw.Graph({
				element : element[0].children[0],
				width : element.width(),
				height : element.height() - 20,
				renderer : 'line',
				interpolation: 'linear',
				series : series
			});
			graph.render();

			var preview = new Rickshaw.Graph.RangeSlider({
				graph : graph,
				element : element[0].children[1]
			});

			var xAxis = new Rickshaw.Graph.Axis.Time({
				graph : graph,
				ticksTreatment : 'glow',
				timeFixture : new KTime_Local()
			});
			xAxis.render();

			var yAxis = new Rickshaw.Graph.Axis.Y({
				graph : graph,
				tickFormat : Rickshaw.Fixtures.Number.formatKMBT,
				ticksTreatment : 'glow'
			});
			yAxis.render();

			var hoverDetail = new KHoverDetail( {
			    graph: graph,
			    formatter: function(series, x, y, formattedXValue, formattedYValue, point) {
			    	return formattedXValue + '
' + y + ' views';
			    },
				onClick : graphClickHandler
			} );

			return graph;
		};

		/**
		 * @param str	info string
		 * @return Array [{x, y}, ..]
		 */
		var parseData = function parseData(str) {
			var os = str.split(';');
			var objects = new Array();
			os.forEach(function (sLine) {
				if (sLine) {
					var vals = sLine.split(',');
					objects.push({'x':parseInt(vals[0], 10), 'timestamp':parseInt(vals[0], 10), 'y':parseInt(vals[1], 10)});
				}
			});
			return objects;
		};

		/**
		 * add 0 points where no data received
		 * for live - all the way
		 * for dead - after first point, before last point
		 * @param data 		objects array with "holes"
		 * @param fromDate	timestamp, seconds
		 * @param toDate	timestamp, seconds
		 * @param fillAll	boolean, should data outside existing range be filled
		 * @return array without "holes"
		 */
		var balanceData = function balanceData(data, fromDate, toDate, fillAll) {
			var firstPoint = Math.floor(fromDate);	// init like this for when no data
			var lastPoint = firstPoint;
			var curx, nextx, i = 1;
			if (data.length) {
				firstPoint = data[0].x;
				lastPoint = data[data.length-1].x;
			}

			// add points between lastPoint and firstPoint
			curx = firstPoint + 10;
			while (curx < lastPoint) {
				nextx = data[i].x;
				if (curx < nextx) {
					// need to add point here
					data.splice(i, 0, {'x':curx, 'timestamp':curx, 'y':0});
				}
				curx += 10;
				i++;
			}

			if (fillAll) {
				// add points between firstPoint and fromDate
				curx = firstPoint - 10;
				while (curx >= fromDate) {
					data.unshift({'x':curx, 'timestamp':curx, 'y':0});
					curx -= 10;
				}

				// add points after lastPoint
				curx = lastPoint + 10;
				while (curx <= toDate) {
					data.push({'x':curx, 'timestamp':curx, 'y':0});
					curx += 10;
				}
			}
			return data;
		};

		/**
		 * get graph data for the last 36 hrs
		 * @param end of 36 hrs term (timestamp sec)
		 */
		var getGraph36Hrs = function getGraph36Hrs(toDate) {
			var fromDate = toDate - 129600; // 60 sec per minute * 60 minutes per hour * 36 hrs
			EntrySvc.getGraph($scope.entryId, -129600, -2).then(function(data) {
				if (data[0] && graph != null) {
					// parse string into objects
					var objects = parseData(data[0].data);
					// add 0 points where no data received
					objects = balanceData(objects, fromDate, toDate, $scope.entry.isLive);
					if (!$scope.entry.isLive) {
						var firstBroadcast = parseInt($scope.entry.firstBroadcast, 10);
						// trim data edges:
						for (var i = 0; i<objects.length; i++) {
							if (objects[i].timestamp >= firstBroadcast) {
								break;
							}
						}
						for (var j = objects.length - 1; j>=0; j--) {
							if (objects[j].value != 0) {
								j++;
								break;
							}
						}
						objects = objects.slice(i, j);
						$scope.$emit('TimeBoundsSet', objects[0].timestamp, objects[objects.length - 1].timestamp);
					}
					resetGraphContent(objects);
				};
			});
		};

		/**
		 * get graph data for the last 30 secs
		 * @param endTime (timestamp sec) get graph data for 30 secs up to this time
		 */
		var getGraph30Secs = function getGraph30Secs(endTime) {
			var toDate = -2;	//endTime;
			var fromDate = -122;
			EntrySvc.getGraph($scope.entryId, fromDate, toDate).then(function(data) {
				var objects = parseData(data[0].data);
				objects = balanceData(objects, endTime-122, endTime-2, $scope.entry.isLive);
				if (graph != null) {
					updateGraphContent(objects);
				}
			});
		};

		/**
		 * remove all previous data and set new
		 * @param value
		 */
		var resetGraphContent = function resetGraphContent(value) {
			var ar = series[0].data;
			while (ar.length > 0) {
				ar.pop();
			}
			for ( var i = 0; i < value.length; i++) {
				ar.push(value[i]);
			}
			graph.update();
		};

		/**
		 * add new data and purge oldest so we keep only 36 hrs
		 * @param value points to add tot he graph
		 */
		var updateGraphContent = function updateGraphContent(value) {
			var graphData = series[0].data; // already in the graph
			var lastGraphDataX = graphData[graphData.length-1].x;
			// first see if any existing values need to be updated
			for (var i = 0; i<value.length; i++) {
				var valX = value[i].x;
				if (valX <= lastGraphDataX) {
					// this x value is already in the graph
					for (var j = graphData.length-1; j>=0; j--) {
						if (graphData[j].x == valX) {
							graphData[j].y = value[i].y;
							break;
						}
						else if (graphData[j].x < valX) {
							// j will only become smaller
							break;
						}
					}
				}
				else {
					// valX is out of graphData bounds, will be so for any larger i
					break;
				}
			}

			// then shift/push for new ones
			while(i < value.length) {
				graphData.shift();
				graphData.push(value[i]);
				i++;
			}

			graph.update();
		};

		/**
		 * event handler for main screen setup event
		 * @param event
		 * @param time (timestamp sec)
		 */
		var setupScreenHandler = function setupScreenHandler(event, time) {
			getGraph36Hrs(time);
		};

		/**
		 * event handler for main screen update interval
		 * @param event
		 * @param time (timestamp sec)
		 */
		var updateScreenHandler = function updateScreenHandler(event, time) {
			getGraph30Secs(time);
		};

		$scope.$on('setupScreen', setupScreenHandler);
		$scope.$on('updateScreen', updateScreenHandler);
}]);<commit_msg>PLAT-1913 only update values if new value != 0<commit_after>'use strict';

/**
 * controller for (rickshaw) graph on entry page
 */
analyticsControllers.controller('RGraphController', ['$scope', '$attrs', 'EntrySvc',
    function($scope, $attrs, EntrySvc) {
		var self = this;
		var graphElement = null; 	// HTML element showing the graph
		var graph = null;			// graph JS object
		var series = null;			// data container

		this.init = function init (element) {
			graphElement = element;
			var d = new Date();
			var t = d.getTime()/1000;
			series = [{
					color : 'steelblue',
					data : [ {x:t, y:0} ],
					name : "Line 1"
				}];

			graph = createGraph(series, element);
		};

		var isRecordedEntry = function isRecordedEntry() {
			return !$scope.entry.isLive && $scope.entry.recordedEntryId && $scope.entry.recordedEntryId != '';
		};

		var graphClickHandler = function graphClickHandler(time) {
			if (isRecordedEntry()) {
				// click - only for recorded entries that are not currently live
				$scope.$emit('gotoTime', time);
			}
		};

		/**
		 * create graph and its parts
		 * @param series
		 * @param element
		 * @return rickshaw graph
		 */
		var createGraph = function createGraph(series, element) {
			var graph = new Rickshaw.Graph({
				element : element[0].children[0],
				width : element.width(),
				height : element.height() - 20,
				renderer : 'line',
				interpolation: 'linear',
				series : series
			});
			graph.render();

			var preview = new Rickshaw.Graph.RangeSlider({
				graph : graph,
				element : element[0].children[1]
			});

			var xAxis = new Rickshaw.Graph.Axis.Time({
				graph : graph,
				ticksTreatment : 'glow',
				timeFixture : new KTime_Local()
			});
			xAxis.render();

			var yAxis = new Rickshaw.Graph.Axis.Y({
				graph : graph,
				tickFormat : Rickshaw.Fixtures.Number.formatKMBT,
				ticksTreatment : 'glow'
			});
			yAxis.render();

			var hoverDetail = new KHoverDetail( {
			    graph: graph,
			    formatter: function(series, x, y, formattedXValue, formattedYValue, point) {
			    	return formattedXValue + '
' + y + ' views';
			    },
				onClick : graphClickHandler
			} );

			return graph;
		};

		/**
		 * @param str	info string
		 * @return Array [{x, y}, ..]
		 */
		var parseData = function parseData(str) {
			var os = str.split(';');
			var objects = new Array();
			os.forEach(function (sLine) {
				if (sLine) {
					var vals = sLine.split(',');
					objects.push({'x':parseInt(vals[0], 10), 'timestamp':parseInt(vals[0], 10), 'y':parseInt(vals[1], 10)});
				}
			});
			return objects;
		};

		/**
		 * add 0 points where no data received
		 * for live - all the way
		 * for dead - after first point, before last point
		 * @param data 		objects array with "holes"
		 * @param fromDate	timestamp, seconds
		 * @param toDate	timestamp, seconds
		 * @param fillAll	boolean, should data outside existing range be filled
		 * @return array without "holes"
		 */
		var balanceData = function balanceData(data, fromDate, toDate, fillAll) {
			var firstPoint = Math.floor(fromDate);	// init like this for when no data
			var lastPoint = firstPoint;
			var curx, nextx, i = 1;
			if (data.length) {
				firstPoint = data[0].x;
				lastPoint = data[data.length-1].x;
			}

			// add points between lastPoint and firstPoint
			curx = firstPoint + 10;
			while (curx < lastPoint) {
				nextx = data[i].x;
				if (curx < nextx) {
					// need to add point here
					data.splice(i, 0, {'x':curx, 'timestamp':curx, 'y':0});
				}
				curx += 10;
				i++;
			}

			if (fillAll) {
				// add points between firstPoint and fromDate
				curx = firstPoint - 10;
				while (curx >= fromDate) {
					data.unshift({'x':curx, 'timestamp':curx, 'y':0});
					curx -= 10;
				}

				// add points after lastPoint
				curx = lastPoint + 10;
				while (curx <= toDate) {
					data.push({'x':curx, 'timestamp':curx, 'y':0});
					curx += 10;
				}
			}
			return data;
		};

		/**
		 * get graph data for the last 36 hrs
		 * @param end of 36 hrs term (timestamp sec)
		 */
		var getGraph36Hrs = function getGraph36Hrs(toDate) {
			var fromDate = toDate - 129600; // 60 sec per minute * 60 minutes per hour * 36 hrs
			EntrySvc.getGraph($scope.entryId, -129600, -2).then(function(data) {
				if (data[0] && graph != null) {
					// parse string into objects
					var objects = parseData(data[0].data);
					// add 0 points where no data received
					objects = balanceData(objects, fromDate, toDate, $scope.entry.isLive);
					if (!$scope.entry.isLive) {
						var firstBroadcast = parseInt($scope.entry.firstBroadcast, 10);
						// trim data edges:
						for (var i = 0; i<objects.length; i++) {
							if (objects[i].timestamp >= firstBroadcast) {
								break;
							}
						}
						for (var j = objects.length - 1; j>=0; j--) {
							if (objects[j].value != 0) {
								j++;
								break;
							}
						}
						objects = objects.slice(i, j);
						$scope.$emit('TimeBoundsSet', objects[0].timestamp, objects[objects.length - 1].timestamp);
					}
					resetGraphContent(objects);
				};
			});
		};

		/**
		 * get graph data for the last 30 secs
		 * @param endTime (timestamp sec) get graph data for 30 secs up to this time
		 */
		var getGraph30Secs = function getGraph30Secs(endTime) {
			var toDate = -2;	//endTime;
			var fromDate = -122;
			EntrySvc.getGraph($scope.entryId, fromDate, toDate).then(function(data) {
				var objects = parseData(data[0].data);
				objects = balanceData(objects, endTime-122, endTime-2, $scope.entry.isLive);
				if (graph != null) {
					updateGraphContent(objects);
				}
			});
		};

		/**
		 * remove all previous data and set new
		 * @param value
		 */
		var resetGraphContent = function resetGraphContent(value) {
			var ar = series[0].data;
			while (ar.length > 0) {
				ar.pop();
			}
			for ( var i = 0; i < value.length; i++) {
				ar.push(value[i]);
			}
			graph.update();
		};

		/**
		 * add new data and purge oldest so we keep only 36 hrs
		 * @param value points to add tot he graph
		 */
		var updateGraphContent = function updateGraphContent(value) {
			var graphData = series[0].data; // already in the graph
			var lastGraphDataX = graphData[graphData.length-1].x;
			// first see if any existing values need to be updated
			for (var i = 0; i<value.length; i++) {
				var valX = value[i].x;
				if (valX <= lastGraphDataX) {
					// this x value is already in the graph
					for (var j = graphData.length-1; j>=0; j--) {
						if (graphData[j].x == valX) {
							if ( value[i].y != 0) {
								graphData[j].y = value[i].y;
							}
							break;
						}
						else if (graphData[j].x < valX) {
							// j will only become smaller
							break;
						}
					}
				}
				else {
					// valX is out of graphData bounds, will be so for any larger i
					break;
				}
			}

			// then shift/push for new ones
			while(i < value.length) {
				graphData.shift();
				graphData.push(value[i]);
				i++;
			}

			graph.update();
		};

		/**
		 * event handler for main screen setup event
		 * @param event
		 * @param time (timestamp sec)
		 */
		var setupScreenHandler = function setupScreenHandler(event, time) {
			getGraph36Hrs(time);
		};

		/**
		 * event handler for main screen update interval
		 * @param event
		 * @param time (timestamp sec)
		 */
		var updateScreenHandler = function updateScreenHandler(event, time) {
			getGraph30Secs(time);
		};

		$scope.$on('setupScreen', setupScreenHandler);
		$scope.$on('updateScreen', updateScreenHandler);
}]);<|endoftext|>