<commit_before>'use strict';

// Simulate a service provider submitting usage for a service, check the
// response and measure the performance.

// TODO Use Hystrix metrics for internal performance measurements

// Scenarios:
// - single service instance, concurrent individual usage submissions
// - TODO add service, service instance, org, and space variations
// - TODO add batch submissions

const _ = require('underscore');

const request = require('abacus-request');
const throttle = require('abacus-throttle');
const util = require('util');

const map = _.map;
const range = _.range;
const omit = _.omit;

// Setup the debug log
const debug = require('abacus-debug')('abacus-test-perf');

// Take number of usage submissions as first parameter
const submissions = parseInt(process.argv[2]) || 1;

// Take a delta time as second parameter
const delta = parseInt(process.argv[3]) || 0;

describe('abacus-test-perf', () => {
    it.only('measures the performance of concurrent usage submissions', function(done) {
        // Configure the test timeout based on the number of submissions, with
        // a minimum of 20 secs
        const timeout = Math.max(20000, 100 * submissions);
        this.timeout(timeout + 2000);

        // Return a usage with unique start and end time based on a number
        const start = 1435629365220 + delta;
        const end = 1435629465220 + delta;
        const usage = (i) => ({ service_instances: [{ service_instance_id: '0b39fa70-a65f-4183-bae8-385633ca5c87', usage: [{ start: start + i,
            end: end + i, region: 'eu-gb', organization_guid: 'a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27', space_guid: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
            plan_id: 'plan_123', resources: [{ unit: 'BYTE', quantity: 1073741824 }, { unit: 'LIGHT_API_CALL', quantity: 1000 }, { unit: 'HEAVY_API_CALL', quantity: 100 }] }] }]
        });

        // Return the expected usage report for the test organization
        const report = (n) => ({ organization_guid: 'a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27', services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * n }, { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * n }] }],
            spaces: [{ id: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a', services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * n }, { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * n }] }],
            consumers: [{ id: 'all', services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * n }, { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * n }] }] }] }] });

        // Post one usage doc, throttled to 1000 concurrent requests
        const post = throttle((i, cb) => {
            debug('Submission #%d', i + 1);
            request.post('http://localhost:9080/v1/metering/services/storage/usage', { body: usage(i) }, (err, val) => {
                expect(err).to.equal(undefined);
                expect(val.statusCode).to.equal(201);
                debug('Completed submission #%o', i + 1);
                cb(err, val);
            });
        });

        // Post the requested number of usage docs
        let posts = 0;
        const submit = (done) => {
            const cb = () => { if(++posts === submissions) done(); };
            map(range(submissions), (i) => post(i, cb));
        };

        // Print the number of usage docs already processed given a get report
        // response, determined from the aggregated usage quantity found in the
        // report for our test service
        const processed = (val) => {
            try {
                return val.body.services[0].aggregated_usage[1].quantity;
            }
            catch(e) {
                // The response doesn't contain a valid report
                return 0;
            }
        };

        // Format a date like expected by the reporting service
        const day = (d) => util.format('%d-%d-%d', d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());

        // Get a usage report for the test organization
        let gets = 0;
        const get = (done) => {
            request.get('http://localhost:9088' + '/v1/organizations/a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27/usage/:day', { day: day(new Date(start)) }, (err, val) => {
                expect(err).to.equal(undefined);
                expect(val.statusCode).to.equal(200);

                // Compare the usage report we got with the expected report
                console.log('Processed %d usage docs', processed(val));
                try {
                    expect(omit(val.body, [ 'id', 'start', 'end' ])).to.deep.equal(report(submissions));
                    console.log('\n', util.inspect(val.body, { depth: 10 }), '\n');
                    done();
                }
                catch(e) {
                    // If the comparison fails we'll be called again to retry
                    // after 1 second, but give up after the computed timeout
                    if(++gets === timeout / 250)
                        throw e;
                }
            });
        };

        // Wait for the expected usage report, get a report every 250 msec until
        // we get the expected values indicating that all submitted usage has
        // been processed
        const wait = (done) => {
            console.log('\nRetrieving usage report');
            const i = setInterval(() => get(() => done(clearInterval(i))), 250);
        };

        // Run the above steps
        submit(() => wait(done));
    });
});

<commit_msg>Extend performance test to support multiple orgs and service instances<commit_after>'use strict';

// Simulate a service provider submitting usage for a service, check the
// usage report for those submissions and measure the performance.

// TODO Use Hystrix metrics for internal performance measurements

// Scenarios:
// - Concurrent single usage submissions for a service instance
// - Concurrent single usage submissions for multiple service instances
// - Concurrent individual usage submissions for multiple organizations
// - TODO add service and space variations
// - TODO add multiple usage submissions

const _ = require('underscore');

const request = require('abacus-request');
const throttle = require('abacus-throttle');
const util = require('util');

const map = _.map;
const range = _.range;
const omit = _.omit;

// Setup the debug log
const debug = require('abacus-debug')('abacus-test-perf');

// Take number of usage submissions as first parameter
const submissions = parseInt(process.argv[2]) || 1;

// Take a delta time as second parameter
const delta = parseInt(process.argv[3]) || 0;

// Take number of organizations as third parameter
const orgs = parseInt(process.argv[4]) || 1;

// Take number of service instances as fourth parameter
const serviceInstances = parseInt(process.argv[5]) || 1;

describe('abacus-test-perf', () => {
    it.only('measures the performance of concurrent usage submissions', function(done) {
        // Configure the test timeout based on the number of submissions, with
        // a minimum of 20 secs
        const timeout = Math.max(20000, 100 * orgs * serviceInstances * submissions);
        this.timeout(timeout + 2000);

        // Return a usage with unique start and end time based on a number
        const start = 1435629365220 + delta;
        const end = 1435629465220 + delta;
        const siid = (si) => ['0b39fa70-a65f-4183-bae8-385633ca5c87', si + 1].join('-');
        const orgid = (o) => ['a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27', o + 1].join('-');

        const usage = (o, si, i) => ({ service_instances: [{ service_instance_id: siid(si), usage: [{ start: start + i,
            end: end + i, region: 'eu-gb', organization_guid: orgid(o), space_guid: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
            plan_id: 'plan_123', resources: [{ unit: 'BYTE', quantity: 1073741824 }, { unit: 'LIGHT_API_CALL', quantity: 1000 }, { unit: 'HEAVY_API_CALL', quantity: 100 }] }] }]
        });

        // Return the expected usage report for the test organization
        const report = (o, nsi, n) => ({ organization_guid: orgid(o), services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 * nsi }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * nsi * n },
             { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * nsi * n }] }],
            spaces: [{ id: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a', services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 * nsi }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * nsi * n },
             { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * nsi * n }] }],
            consumers: [{ id: 'all', services: [{ id: 'storage', aggregated_usage:
            [{ unit: 'STORAGE_PER_MONTH', quantity: 1 * nsi }, { unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH', quantity: 1 * nsi * n },
             { unit: 'HEAVY_API_CALLS_PER_MONTH', quantity: 100 * nsi * n }] }]
        }] }] });

        // Post one usage doc, throttled to 1000 concurrent requests
        const post = throttle((o, si, i, cb) => {
            debug('Submission org%d si%d sub%d', o + 1, si + 1, i + 1);
            request.post('http://localhost:9080/v1/metering/services/storage/usage', { body: usage(o, si, i) }, (err, val) => {
                expect(err).to.equal(undefined);
                expect(val.statusCode).to.equal(201);
                debug('Completed submission org%d si%d sub%d', o + 1, si + 1, i + 1);
                cb(err, val);
            });
        });

        // Post the requested number of usage docs
        let posts = 0;
        const submit = (done) => {
            const cb = () => { if(++posts === orgs * serviceInstances * submissions) done(); };
            map(range(orgs), (o) => map(range(serviceInstances), (si) => map(range(submissions), (i) => post(o, si, i, cb))));
        };

        // Print the number of usage docs already processed given a get report
        // response, determined from the aggregated usage quantity found in the
        // report for our test service
        const processed = (val) => {
            try {
                return val.body.services[0].aggregated_usage[1].quantity;
            }
            catch(e) {
                // The response doesn't contain a valid report
                return 0;
            }
        };

        // Format a date like expected by the reporting service
        const day = (d) => util.format('%d-%d-%d', d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());

        // Get a usage report for the test organization
        let gets = 0;
        const get = (o, done) => {
            request.get('http://localhost:9088' + '/v1/organizations/' + orgid(o) + '/usage/:day', { day: day(new Date(start)) }, (err, val) => {
                expect(err).to.equal(undefined);
                expect(val.statusCode).to.equal(200);

                // Compare the usage report we got with the expected report
                console.log('Processed %d usage docs for org%d', processed(val), o + 1);
                try {
                    expect(omit(val.body, [ 'id', 'start', 'end' ])).to.deep.equal(report(o, serviceInstances, submissions));
                    console.log('\n', util.inspect(val.body, { depth: 10 }), '\n');
                    done();
                }
                catch(e) {
                    // If the comparison fails we'll be called again to retry
                    // after 1 second, but give up after the computed timeout
                    if(++gets === timeout / 250)
                        throw e;
                }
            });
        };

        // Wait for the expected usage report for all organizations, get an
        // organization usage report every 250 msec until we get the expected
        // values indicating that all submitted usage has been processed
        let verified = 0;
        const wait = (done) => {
            console.log('\nRetrieving usage report');
            const cb = () => { if(++verified === orgs) done(); };
            map(range(orgs), (o) => { const i = setInterval(() => get(o, () => cb(clearInterval(i))), 250); });
        };

        // Run the above steps
        submit(() => wait(done));
    });
});
<|endoftext|>


<commit_before>var type = 'commercial';
var population, electricity_usage, quantile;

var margin = 25;
var width = window.innerWidth * 3/4 - margin,
    height = window.innerHeight - 50 - margin;

// Equirectangular projection isn't very nice to look at, but the projection
// function is very simple, and that makes it easy to solve for the ideal
// scale and translation, which we do later
var projection = d3.geo.equirectangular();

var path = d3.geo.path().projection(projection);

function format(number) {
    number += '';
    x = number.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1].substr(0, 2) : '';
    var regexp = /(\d+)(\d{3})/;
    while (regexp.test(x1))
        x1 = x1.replace(regexp, '$1' + ',' + '$2');
    return x1 + x2;
}

d3.json('data/zipcodes.json', function(json) {
    //
    // Set up zip code boundary features
    //

    var features = json.features;

    var svg = d3.select('#chart').append('svg')
        .attr('width', width)
        .attr('height', height)
        .call(d3.behavior.zoom().on('zoom', redraw));

    var zipcodes = svg.append('g')
        .attr('id', 'nyc-zipcodes')
        .attr('class', 'Blues');

    zipcodes.selectAll('path')
        .data(features)
        .enter().append('path')
            .attr('d', path);

    // Figure out latitude and longitude bounds for our geography
    var longitude_bounds = [null, null],
        latitude_bounds = [null, null];

    features.forEach(function(feature) {
        var bounds = d3.geo.bounds(feature);
        if (longitude_bounds[0] == null || bounds[0][0] < longitude_bounds[0])
            longitude_bounds[0] = bounds[0][0];
        if (longitude_bounds[1] == null || bounds[1][0] > longitude_bounds[1])
            longitude_bounds[1] = bounds[1][0];
        if (latitude_bounds[0] == null || bounds[0][1] < latitude_bounds[0])
            latitude_bounds[0] = bounds[0][1];
        if (latitude_bounds[1] == null || bounds[1][1] > latitude_bounds[1])
            latitude_bounds[1] = bounds[1][1];
    });

    // Reverse-engineer the appropriate scale and translation factors if we want
    // [(min_longitude + max_longitude) / 2, max_latitude] to [width / 2, 0] and
    // [(min_longitude + max_longitude) / 2, min_latitude] to [width / 2, height]
    // i.e. scale the center-axis of our bounding box to the center-axis of the
    // SVG canvas (this is basically solving a system of three equations in three
    // unknowns)
    var scale = 360 * height / (latitude_bounds[1] - latitude_bounds[0]);
    var translation = [
        width / 2 - scale * (longitude_bounds[0] + longitude_bounds[1]) / 720,
        scale * latitude_bounds[1] / 360,
    ];

    projection.scale(scale);
    projection.translate(translation);
    zipcodes.selectAll('path').attr('d', path);

    // redraw() from http://bl.ocks.org/1283960
    function redraw() {
        // d3.event.translate (an array) stores the current translation from the
        // parent SVG element.  t (an array) stores the projection's default
        // translation.  We add the x and y vales in each array to determine the
        // projection's new translation
        var tx = translation[0] * d3.event.scale + d3.event.translate[0];
        var ty = translation[1] * d3.event.scale + d3.event.translate[1];
        projection.translate([tx, ty]);

        // Now we determine the projection's new scale, but there's a problem:
        // the map doesn't 'zoom onto the mouse point'
        projection.scale(scale * d3.event.scale);

        // Redraw the map
        zipcodes.selectAll('path')
            .attr('d', path);
    }

    //
    // Set up choropleth electricity_usage
    //

    d3.json('data/population.json', function(population_data) {
        population = population_data;

        d3.json('data/electricity.json', function(json) {
            electricity_usage = json;

            var values = [];
            for (var k in electricity_usage) {
                if (electricity_usage[k][type])
                    values.push(electricity_usage[k][type]);
            }
            values.sort(function(a, b) { return a - b; });

            var ranges = [];
            for (var i = 0; i < values.length; ++i)
                ranges[i] = Math.floor(i / (Math.ceil(values.length / 9)));

            quantile = d3.scale.ordinal()
                .domain(values)
                .range(ranges);

            zipcodes.selectAll('path')
                .attr('original-title', function(d) {
                    var output = electricity_usage[d.id] ? (electricity_usage[d.id][type] ? electricity_usage[d.id][type] : 0) : 0;
                    return d.id + ': ' + format(output);
                })
                .html(function(d) {
                    $(this).tipsy({ gravity: 'e' });
                });

            zipcodes.selectAll('path')
                .attr('class', quantize);
        });
    });

    function quantize(d) {
        var type_alias = type == 'residential-per-capita' ? 'residential' : type;
        if (electricity_usage[d.id] && electricity_usage[d.id][type_alias])
            return 'q' + ~~quantile(electricity_usage[d.id][type_alias]) + '-9';
        else
            return 'q0-9';
    }

    //
    // Handle choropleth transitions for drop-down list
    //

    $('#residence-type').change(function(ui) {
        type = ui.target.value;

        var values = [];
        for (var k in electricity_usage) {
            var type_alias = type == 'residential-per-capita' ? 'residential' : type;
            if (electricity_usage[k][type_alias]) {
                if (type == 'residential-per-capita')
                    values.push(electricity_usage[k][type_alias] / population[k]);
                else
                    values.push(electricity_usage[k][type_alias]);
            }
        }
        values.sort(function(a, b) { return a - b; });

        var ranges = [];
        for (var i = 0; i < values.length; ++i)
            ranges[i] = Math.floor(i / (Math.ceil(values.length / 9)));

        quantile = d3.scale.ordinal()
            .domain(values)
            .range(ranges);

        zipcodes.selectAll('path')
            .attr('original-title', function(d) {
                var type_alias = type == 'residential-per-capita' ? 'residential' : type;
                var usage = electricity_usage[d.id];
                var output = usage ? (usage[type_alias] ?
                                      (type == 'residential-per-capita' ?
                                       usage[type_alias] / population[d.id] : usage[type_alias]) : 0) : 0;
                return d.id + ': ' + format(output);
            });

        zipcodes.selectAll('path')
            .attr('class', quantize);
    });
});
<commit_msg>Fix bug with per capita quantile calculation<commit_after>var type = 'commercial';
var population, electricity_usage, quantile;

var margin = 25;
var width = window.innerWidth * 3/4 - margin,
    height = window.innerHeight - 50 - margin;

// Equirectangular projection isn't very nice to look at, but the projection
// function is very simple, and that makes it easy to solve for the ideal
// scale and translation, which we do later
var projection = d3.geo.equirectangular();

var path = d3.geo.path().projection(projection);

function format(number) {
    number += '';
    x = number.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1].substr(0, 2) : '';
    var regexp = /(\d+)(\d{3})/;
    while (regexp.test(x1))
        x1 = x1.replace(regexp, '$1' + ',' + '$2');
    return x1 + x2;
}

d3.json('data/zipcodes.json', function(json) {
    //
    // Set up zip code boundary features
    //

    var features = json.features;

    var svg = d3.select('#chart').append('svg')
        .attr('width', width)
        .attr('height', height)
        .call(d3.behavior.zoom().on('zoom', redraw));

    var zipcodes = svg.append('g')
        .attr('id', 'nyc-zipcodes')
        .attr('class', 'Blues');

    zipcodes.selectAll('path')
        .data(features)
        .enter().append('path')
            .attr('d', path);

    // Figure out latitude and longitude bounds for our geography
    var longitude_bounds = [null, null],
        latitude_bounds = [null, null];

    features.forEach(function(feature) {
        var bounds = d3.geo.bounds(feature);
        if (longitude_bounds[0] == null || bounds[0][0] < longitude_bounds[0])
            longitude_bounds[0] = bounds[0][0];
        if (longitude_bounds[1] == null || bounds[1][0] > longitude_bounds[1])
            longitude_bounds[1] = bounds[1][0];
        if (latitude_bounds[0] == null || bounds[0][1] < latitude_bounds[0])
            latitude_bounds[0] = bounds[0][1];
        if (latitude_bounds[1] == null || bounds[1][1] > latitude_bounds[1])
            latitude_bounds[1] = bounds[1][1];
    });

    // Reverse-engineer the appropriate scale and translation factors if we want
    // [(min_longitude + max_longitude) / 2, max_latitude] to [width / 2, 0] and
    // [(min_longitude + max_longitude) / 2, min_latitude] to [width / 2, height]
    // i.e. scale the center-axis of our bounding box to the center-axis of the
    // SVG canvas (this is basically solving a system of three equations in three
    // unknowns)
    var scale = 360 * height / (latitude_bounds[1] - latitude_bounds[0]);
    var translation = [
        width / 2 - scale * (longitude_bounds[0] + longitude_bounds[1]) / 720,
        scale * latitude_bounds[1] / 360,
    ];

    projection.scale(scale);
    projection.translate(translation);
    zipcodes.selectAll('path').attr('d', path);

    // redraw() from http://bl.ocks.org/1283960
    function redraw() {
        // d3.event.translate (an array) stores the current translation from the
        // parent SVG element.  t (an array) stores the projection's default
        // translation.  We add the x and y vales in each array to determine the
        // projection's new translation
        var tx = translation[0] * d3.event.scale + d3.event.translate[0];
        var ty = translation[1] * d3.event.scale + d3.event.translate[1];
        projection.translate([tx, ty]);

        // Now we determine the projection's new scale, but there's a problem:
        // the map doesn't 'zoom onto the mouse point'
        projection.scale(scale * d3.event.scale);

        // Redraw the map
        zipcodes.selectAll('path')
            .attr('d', path);
    }

    //
    // Set up choropleth electricity_usage
    //

    d3.json('data/population.json', function(population_data) {
        population = population_data;

        d3.json('data/electricity.json', function(json) {
            electricity_usage = json;

            var values = [];
            for (var k in electricity_usage) {
                if (electricity_usage[k][type])
                    values.push(electricity_usage[k][type]);
            }
            values.sort(function(a, b) { return a - b; });

            var ranges = [];
            for (var i = 0; i < values.length; ++i)
                ranges[i] = Math.floor(i / (Math.ceil(values.length / 9)));

            quantile = d3.scale.ordinal()
                .domain(values)
                .range(ranges);

            zipcodes.selectAll('path')
                .attr('original-title', function(d) {
                    var output = electricity_usage[d.id] ? (electricity_usage[d.id][type] ? electricity_usage[d.id][type] : 0) : 0;
                    return d.id + ': ' + format(output);
                })
                .html(function(d) {
                    $(this).tipsy({ gravity: 'e' });
                });

            zipcodes.selectAll('path')
                .attr('class', quantize);
        });
    });

    function quantize(d) {
        var type_alias = type == 'residential-per-capita' ? 'residential' : type;
        if (electricity_usage[d.id] && electricity_usage[d.id][type_alias]) {
            var domain_value = electricity_usage[d.id][type_alias];
            if (type == 'residential-per-capita')
                domain_value /= population[d.id];
            return 'q' + ~~quantile(domain_value) + '-9';
        } else
            return 'q0-9';
    }

    //
    // Handle choropleth transitions for drop-down list
    //

    $('#residence-type').change(function(ui) {
        type = ui.target.value;

        var values = [];
        for (var k in electricity_usage) {
            var type_alias = type == 'residential-per-capita' ? 'residential' : type;
            if (electricity_usage[k][type_alias]) {
                if (type == 'residential-per-capita')
                    values.push(electricity_usage[k][type_alias] / population[k]);
                else
                    values.push(electricity_usage[k][type_alias]);
            }
        }
        values.sort(function(a, b) { return a - b; });

        var ranges = [];
        for (var i = 0; i < values.length; ++i)
            ranges[i] = Math.floor(i / (Math.ceil(values.length / 9)));

        quantile = d3.scale.ordinal()
            .domain(values)
            .range(ranges);

        zipcodes.selectAll('path')
            .attr('original-title', function(d) {
                var type_alias = type == 'residential-per-capita' ? 'residential' : type;
                var usage = electricity_usage[d.id];
                var output = usage ? (usage[type_alias] ?
                                      (type == 'residential-per-capita' ?
                                       usage[type_alias] / population[d.id] : usage[type_alias]) : 0) : 0;
                return d.id + ': ' + format(output);
            });

        zipcodes.selectAll('path')
            .attr('class', quantize);
    });
});
<|endoftext|>


<commit_before>'use strict';

// Simulate a service provider submitting usage for a service, check the
// usage report for those submissions and measure the performance.

// TODO Use Hystrix metrics for internal performance measurements

// Scenarios:
// - Concurrently submit a usage doc for a service instance
// - Concurrently submit a usage doc for multiple service instances
// - Concurrently submit a usage doc for multiple organizations
// - TODO add service and space variations
// - TODO submit batch of usage docs in each submission

const _ = require('underscore');

const commander = require('commander');

const request = require('abacus-request');
const throttle = require('abacus-throttle');
const util = require('util');

const map = _.map;
const range = _.range;
const omit = _.omit;

// Setup the debug log
const debug = require('abacus-debug')('abacus-test-perf');

commander.option(
  '-o, --orgs <n>', 'Number of organizations', parseInt);
commander.option(
  '-i, --instances <n>', 'Number of service instances', parseInt);
commander.option(
  '-u, --usagedocs <n>', 'Number of usage docs', parseInt);
commander.option(
  '-d, --delta <d>', 'Usage time window shift in milli-seconds', parseInt);

commander.parse(process.argv);

// Number of organizations
const orgs = commander.orgs || 1;

// Number of service instances
const serviceInstances = commander.instances || 1;

// Number of usage docs
const usage = commander.usagedocs || 1;

// Usage time window shift in milli-seconds
const delta = commander.delta || 0;

describe('abacus-test-perf', () => {
  it('measures the performance of concurrent usage submissions', (done) => {
    // Configure the test timeout based on the number of usage docs, with
    // a minimum of 20 secs
    const timeout = Math.max(20000,
      100 * orgs * serviceInstances * usage);
    this.timeout(timeout + 2000);

    // Return a usage with unique start and end time based on a number
    const start = 1435629365220 + delta;
    const end = 1435629465220 + delta;
    const siid = (o, si) => ['0b39fa70-a65f-4183-bae8-385633ca5c87',
      o + 1, si + 1].join('-');
    const orgid = (o) => ['a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27',
      o + 1].join('-');

    const usageTemplate = (o, si, i) => ({
        service_instances: [{
          service_instance_id: siid(o, si),
          usage: [{
            start: start + i,
            end: end + i,
            region: 'eu-gb',
            organization_guid: orgid(o),
            space_guid: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
            plan_id: 'plan_123',
            resources: [{
              unit: 'BYTE',
              quantity: 1073741824
            }, {
              unit: 'LIGHT_API_CALL',
              quantity: 1000
            }, {
              unit: 'HEAVY_API_CALL',
              quantity: 100
            }]
          }]
        }]
    });

    // Return the expected usage report for the test organization
    const report = (o, nsi, n) => ({
        organization_guid: orgid(o),
        services: [{
          id: 'storage',
          aggregated_usage: [{
            unit: 'STORAGE_PER_MONTH',
            quantity: 1 * nsi
          }, {
            unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
            quantity: 1 * nsi * n
          },
            {
              unit: 'HEAVY_API_CALLS_PER_MONTH',
              quantity: 100 * nsi * n
            }]
        }],
        spaces: [{
          id: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
          services: [{
            id: 'storage',
            aggregated_usage: [{
              unit: 'STORAGE_PER_MONTH',
              quantity: 1 * nsi
            }, {
              unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
              quantity: 1 * nsi * n
            },
              {
                unit: 'HEAVY_API_CALLS_PER_MONTH',
                quantity: 100 * nsi * n
              }]
          }],
          consumers: [{
            id: 'all',
            services: [{
              id: 'storage',
              aggregated_usage: [{
                unit: 'STORAGE_PER_MONTH',
                quantity: 1 * nsi
              }, {
                unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
                quantity: 1 * nsi * n
              },
                {
                  unit: 'HEAVY_API_CALLS_PER_MONTH',
                  quantity: 100 * nsi * n
                }]
            }]
          }]
        }]
    });

    // Post one usage doc, throttled to 1000 concurrent requests
    const post = throttle((o, si, i, cb) => {
      debug('Submitting org%d instance%d usage%d',
        o + 1, si + 1, i + 1);
      request.post('http://localhost:9080/v1/metering/services/storage/usage',
        { body: usageTemplate(o, si, i) }, (err, val) => {
          expect(err).to.equal(undefined);
          expect(val.statusCode).to.equal(201);
          debug('Completed submission org%d instance%d usage%d',
            o + 1, si + 1, i + 1);
          cb(err, val);
        });
    });

    // Post the requested number of usage docs
    let posts = 0;
    const submit = (done) => {
      const cb = () => {
        if(++posts === orgs * serviceInstances * usage) done();
      };
      map(range(orgs), (o) => map(range(serviceInstances), (si) =>
        map(range(usage), (i) => post(o, si, i, cb))));
    };

    // Print the number of usage docs already processed given a get report
    // response, determined from the aggregated usage quantity found in the
    // report for our test service
    const processed = (val) => {
      try {
        return val.body.services[0].aggregated_usage[1].quantity;
      }
      catch (e) {
        // The response doesn't contain a valid report
        return 0;
      }
    };

    // Format a date like expected by the reporting service
    const day = (d) => util.format('%d-%d-%d',
      d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());

    // Get a usage report for the test organization
    let gets = 0;
    const get = (o, done) => {
      request.get('http://localhost:9088' + '/v1/organizations/' +
        orgid(o) + '/usage/:day', { day: day(new Date(start)) },
        (err, val) => {
          expect(err).to.equal(undefined);
          expect(val.statusCode).to.equal(200);

          // Compare the usage report we got with the expected report
          console.log('Processed %d usage docs for org%d',
            processed(val), o + 1);
          try {
            expect(omit(val.body, ['id', 'start', 'end'])).to.deep.
              equal(report(o, serviceInstances, usage));
            console.log('\n', util.inspect(val.body, { depth: 10 }), '\n');
            done();
          }
          catch (e) {
            // If the comparison fails we'll be called again to retry
            // after 250 msec, but give up after the computed timeout
            if(++gets === timeout * Math.max(1, orgs / 4) / 250) throw e;
          }
        });
    };

    // Wait for the expected usage report for all organizations, get an
    // organization usage report every 250 msec until we get the expected
    // values indicating that all submitted usage has been processed
    let verified = 0;
    const wait = (done) => {
      console.log('\nRetrieving usage reports');
      const cb = () => { if(++verified === orgs) done(); };

      map(range(orgs), (o) => {
        const i = setInterval(() => get(o, () => cb(clearInterval(i))), 250);
      });
    };

    // Run the above steps
    submit(() => wait(done));
  });
});

<commit_msg>Fix Babel transpiler issue with performance test code<commit_after>'use strict';

// Simulate a service provider submitting usage for a service, check the
// usage report for those submissions and measure the performance.

// TODO Use Hystrix metrics for internal performance measurements

// Scenarios:
// - Concurrently submit a usage doc for a service instance
// - Concurrently submit a usage doc for multiple service instances
// - Concurrently submit a usage doc for multiple organizations
// - TODO add service and space variations
// - TODO submit batch of usage docs in each submission

const _ = require('underscore');

const commander = require('commander');

const request = require('abacus-request');
const throttle = require('abacus-throttle');
const util = require('util');

const map = _.map;
const range = _.range;
const omit = _.omit;

// Setup the debug log
const debug = require('abacus-debug')('abacus-test-perf');

commander.option(
  '-o, --orgs <n>', 'Number of organizations', parseInt);
commander.option(
  '-i, --instances <n>', 'Number of service instances', parseInt);
commander.option(
  '-u, --usagedocs <n>', 'Number of usage docs', parseInt);
commander.option(
  '-d, --delta <d>', 'Usage time window shift in milli-seconds', parseInt);

commander.parse(process.argv);

// Number of organizations
const orgs = commander.orgs || 1;

// Number of service instances
const serviceInstances = commander.instances || 1;

// Number of usage docs
const usage = commander.usagedocs || 1;

// Usage time window shift in milli-seconds
const delta = commander.delta || 0;

describe('abacus-test-perf', () => {
  it('measures performance of concurrent usage submissions', function(done) {
    // Configure the test timeout based on the number of usage docs, with
    // a minimum of 20 secs
    const timeout = Math.max(20000,
      100 * orgs * serviceInstances * usage);
    this.timeout(timeout + 2000);

    // Return a usage with unique start and end time based on a number
    const start = 1435629365220 + delta;
    const end = 1435629465220 + delta;
    const siid = (o, si) => ['0b39fa70-a65f-4183-bae8-385633ca5c87',
      o + 1, si + 1].join('-');
    const orgid = (o) => ['a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27',
      o + 1].join('-');

    const usageTemplate = (o, si, i) => ({
        service_instances: [{
          service_instance_id: siid(o, si),
          usage: [{
            start: start + i,
            end: end + i,
            region: 'eu-gb',
            organization_guid: orgid(o),
            space_guid: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
            plan_id: 'plan_123',
            resources: [{
              unit: 'BYTE',
              quantity: 1073741824
            }, {
              unit: 'LIGHT_API_CALL',
              quantity: 1000
            }, {
              unit: 'HEAVY_API_CALL',
              quantity: 100
            }]
          }]
        }]
    });

    // Return the expected usage report for the test organization
    const report = (o, nsi, n) => ({
        organization_guid: orgid(o),
        services: [{
          id: 'storage',
          aggregated_usage: [{
            unit: 'STORAGE_PER_MONTH',
            quantity: 1 * nsi
          }, {
            unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
            quantity: 1 * nsi * n
          },
            {
              unit: 'HEAVY_API_CALLS_PER_MONTH',
              quantity: 100 * nsi * n
            }]
        }],
        spaces: [{
          id: 'aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
          services: [{
            id: 'storage',
            aggregated_usage: [{
              unit: 'STORAGE_PER_MONTH',
              quantity: 1 * nsi
            }, {
              unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
              quantity: 1 * nsi * n
            },
              {
                unit: 'HEAVY_API_CALLS_PER_MONTH',
                quantity: 100 * nsi * n
              }]
          }],
          consumers: [{
            id: 'all',
            services: [{
              id: 'storage',
              aggregated_usage: [{
                unit: 'STORAGE_PER_MONTH',
                quantity: 1 * nsi
              }, {
                unit: 'THOUSAND_LIGHT_API_CALLS_PER_MONTH',
                quantity: 1 * nsi * n
              },
                {
                  unit: 'HEAVY_API_CALLS_PER_MONTH',
                  quantity: 100 * nsi * n
                }]
            }]
          }]
        }]
    });

    // Post one usage doc, throttled to 1000 concurrent requests
    const post = throttle((o, si, i, cb) => {
      debug('Submitting org%d instance%d usage%d',
        o + 1, si + 1, i + 1);
      request.post('http://localhost:9080/v1/metering/services/storage/usage',
        { body: usageTemplate(o, si, i) }, (err, val) => {
          expect(err).to.equal(undefined);
          expect(val.statusCode).to.equal(201);
          debug('Completed submission org%d instance%d usage%d',
            o + 1, si + 1, i + 1);
          cb(err, val);
        });
    });

    // Post the requested number of usage docs
    let posts = 0;
    const submit = (done) => {
      const cb = () => {
        if(++posts === orgs * serviceInstances * usage) done();
      };
      map(range(orgs), (o) => map(range(serviceInstances), (si) =>
        map(range(usage), (i) => post(o, si, i, cb))));
    };

    // Print the number of usage docs already processed given a get report
    // response, determined from the aggregated usage quantity found in the
    // report for our test service
    const processed = (val) => {
      try {
        return val.body.services[0].aggregated_usage[1].quantity;
      }
      catch (e) {
        // The response doesn't contain a valid report
        return 0;
      }
    };

    // Format a date like expected by the reporting service
    const day = (d) => util.format('%d-%d-%d',
      d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());

    // Get a usage report for the test organization
    let gets = 0;
    const get = (o, done) => {
      request.get('http://localhost:9088' + '/v1/organizations/' +
        orgid(o) + '/usage/:day', { day: day(new Date(start)) },
        (err, val) => {
          expect(err).to.equal(undefined);
          expect(val.statusCode).to.equal(200);

          // Compare the usage report we got with the expected report
          console.log('Processed %d usage docs for org%d',
            processed(val), o + 1);
          try {
            expect(omit(val.body, ['id', 'start', 'end'])).to.deep.
              equal(report(o, serviceInstances, usage));
            console.log('\n', util.inspect(val.body, { depth: 10 }), '\n');
            done();
          }
          catch (e) {
            // If the comparison fails we'll be called again to retry
            // after 250 msec, but give up after the computed timeout
            if(++gets === timeout * Math.max(1, orgs / 4) / 250) throw e;
          }
        });
    };

    // Wait for the expected usage report for all organizations, get an
    // organization usage report every 250 msec until we get the expected
    // values indicating that all submitted usage has been processed
    let verified = 0;
    const wait = (done) => {
      console.log('\nRetrieving usage reports');
      const cb = () => { if(++verified === orgs) done(); };

      map(range(orgs), (o) => {
        const i = setInterval(() => get(o, () => cb(clearInterval(i))), 250);
      });
    };

    // Run the above steps
    submit(() => wait(done));
  });
});

<|endoftext|>


<commit_before>var contactExport = {};

contactExport.openIntent = function(phoneNumber, successCallback, errorCallback) {
    if (typeof phoneNumber !== 'string' || !(phoneNumber)) {
        if (errorCallback) {
            errorCallback({
                code: "INVALID_INPUT",
                message: "Invalid Input"
            });
        }
        return;
    }

    cordova.exec(successCallback, errorCallback, "ExternalLinks", "openPhone", [phoneNumber.replace(/[^0-9]/gi, '')]);
};

contactExport.openMap = function(param, successCallback, errorCallback) {
    if (typeof param !== 'object') {
        if (errorCallback) {
            errorCallback({
                code: "INVALID_INPUT",
                message: "Invalid Input"
            });
        }
        return;
    }

    cordova.exec(successCallback, errorCallback, "ExternalLinks", "openMap", [param]);
};

module.exports = contactExport;
<commit_msg>bugfix<commit_after>var contactExport = {};

contactExport.openPhone = function(phoneNumber, successCallback, errorCallback) {
    if (typeof phoneNumber !== 'string' || !(phoneNumber)) {
        if (errorCallback) {
            errorCallback({
                code: "INVALID_INPUT",
                message: "Invalid Input"
            });
        }
        return;
    }

    cordova.exec(successCallback, errorCallback, "ExternalLinks", "openPhone", [phoneNumber.replace(/[^0-9]/gi, '')]);
};

contactExport.openMap = function(param, successCallback, errorCallback) {
    if (typeof param !== 'object') {
        if (errorCallback) {
            errorCallback({
                code: "INVALID_INPUT",
                message: "Invalid Input"
            });
        }
        return;
    }

    cordova.exec(successCallback, errorCallback, "ExternalLinks", "openMap", [param]);
};

module.exports = contactExport;
<|endoftext|>


<commit_before>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */
define(function (require) {

    "use strict";

    var $               = require('jquery'),
        Backbone        = require('backbone'),
        Handlebars      = require('handlebars'),
        Helpers         = require('app/utils/HandlebarHelpers'),
        Marionette      = require('marionette'),
        HomeViews       = require('app/views/HomeViews'),
        HelpView        = require('app/views/HelpView'),
        ProjectViews    = require('app/views/ProjectViews'),
        DocumentViews   = require('app/views/DocumentViews'),
        SearchViews     = require('app/views/SearchViews'),
        AdaptViews      = require('app/views/AdaptViews'),
        projModel       = require('app/models/project'),
        chapterModel    = require('app/models/chapter'),
        bookModel       = require('app/models/book'),
        spModel         = require('app/models/sourcephrase'),
        AppRouter       = require('app/router'),
        FastClick       = require('fastclick'),
        PageSlider      = require('app/utils/pageslider'),
        slider          = new PageSlider($('body')),
        lookupView      = null,
        helpView        = null,
        newProjectView  = null,
        editProjectView = null,
        copyProjectView = null,
        homeView        = null,
        importDocView   = null,
        db              = null,
        i18n            = require('i18n'),
        lang            = "",
        models          = [],
        locale          = "en-AU",  // default

        Application = Marionette.Application.extend({
            filterlist: "",
            currentProject: null,

            // app initialization code. Here we'll initialize localization with the current locale
            initialize: function (options) {
                // callback function to initialize / load the localization info
                var initialize_i18n = function (locale) {
                    i18n.init({
                        lng: locale,
                        debug: true,
                        fallbackLng: 'en'
                    }, function () {
                        // Callback when i18next is finished initializing

                        // Load any app-wide collections
                        window.Application.BookList = new bookModel.BookCollection();
                        window.Application.BookList.fetch({reset: true, data: {name: ""}});
                        window.Application.ProjectList = new projModel.ProjectCollection();
                        window.Application.ProjectList.fetch({reset: true, data: {name: ""}});
                        window.Application.ChapterList = new chapterModel.ChapterCollection();
                        window.Application.ChapterList.fetch({reset: true, data: {name: ""}});
                        // Note: sourcephrases are not held as a singleton (for a NT, this could result in ~300MB of memory) --
                        // Instead, they are instantiated on the pages that need them
                        // (DocumentViews for doc import/export and AdaptViews for adapting)

                        // Tell backbone we're ready to start loading the View classes.
                        Backbone.history.start();
                    });
                };
                // add the UI regions (just the main "content" for now)
                this.addRegions({
                    main: '#main'
                });
                // main Region's show event handler -- we use it to do
                // page transition animations.
                this.main.on("show", function (view) {
                  // manipulate the `view` or do something extra
                  // with the region via `this`
                    slider.slidePage(view.$el);
                });
                // sqlitePlugin -- available on DeviceReady (mobile app)
                if (window.sqlitePlugin) {
                    this.db = window.sqlitePlugin.openDatabase({name: "AIM"});
                } else {
                    // running in browser -- use WebSQL (Chrome / Safari ONLY)
                    this.db = openDatabase('AIM', '1', 'AIM database', 2 * 1024 * 1024);
                }
                // create model collections off the Application object
                this.BookList = null;
                this.ProjectList = null;
                this.ChapterList = null;
                this.spList = null;

                // get the user's locale - mobile or web
                if (typeof navigator.globalization !== 'undefined') {
                    navigator.globalization.getLocaleName(
                        function (loc) {
                            locale = loc.value.split("-")[0];
                            initialize_i18n(locale);
                        },
                        function () {console.log('Error getting locale\n'); }
                    );
                } else {
                    // in web browser
                    lang = (navigator.languages) ? navigator.languages[0] : (navigator.language || navigator.userLanguage);
                    locale = lang.split("-")[0];
                    initialize_i18n(locale);
                }

                // initialize the router
                var router  = new AppRouter({controller: this});

                // Attach touch screen function to avoid delay in double-click
                $(function () {
                    FastClick.attach(document.body);
                });

                // Process back arrow button event
                // (not the one in the browser, the one we render on our html page).
                $("body").on("click", ".back-button", function (event) {
                    event.preventDefault();
                    window.history.back();
                });
            },

            // Routes from AppRouter (router.js)
            home: function () {
                // First, look for projects in the project list that aren't complete;
                // this can happen if the user clicks the back button before completing the
                // new project wizard. These objects with no id defined are only in memory;
                // once the source and target language are defined, an id is set and
                // the project is saved in the device's localStorage.
                this.ProjectList.fetch({reset: true, data: {name: ""}});
                this.ProjectList.each(function (model, index) {
//                    console.log("Model: " + model.get('id'));
                    if (model.get('id') === 0) {
                        // empty project -- mark for removal
                        models.push(model);
                    }
                });
                // remove the half-completed project objects
                if (models.length > 0) {
                    this.ProjectList.remove(models);
                }
                if (window.Application.currentProject === null) {
                    // pick the first project in the list, if there is one
                    window.Application.currentProject = this.ProjectList.at(0);
                }
                // now display the home view
                homeView = new HomeViews.HomeView({model: window.Application.currentProject});
                homeView.delegateEvents();
                this.main.show(homeView);
            },

            help: function () {
                helpView = new HelpView();
                helpView.delegateEvents();
                this.main.show(helpView);
            },

            editProject: function (id) {
                // edit the selected project
                var proj = this.ProjectList.where({id: id});
                if (proj !== null) {
                    window.Application.main.show(new ProjectViews.EditProjectView({model: proj[0]}));
                }
            },

            copyProject: function () {
                var proj = new projModel.Project();
                copyProjectView = new ProjectViews.CopyProjectView({model: proj});
                copyProjectView.delegateEvents();
                this.ProjectList.add(proj);
                this.main.show(copyProjectView);
            },

            newProject: function () {
                var proj = new projModel.Project();
                newProjectView = new ProjectViews.NewProjectView({model: proj});
                newProjectView.delegateEvents();
                this.ProjectList.add(proj);
                this.main.show(newProjectView);
            },

            importBooks: function (id) {
                console.log("importBooks");
                var proj = this.ProjectList.where({id: id});
                if (proj === null) {
                    console.log("no project defined");
                }
                importDocView = new DocumentViews.ImportDocumentView({model: proj[0]});
                importDocView.delegateEvents();
                this.main.show(importDocView);
            },

            lookupChapter: function (id) {
                console.log("lookupChapter");
                var proj = this.ProjectList.where({id: id});
                if (proj !== null) {
                    lookupView = new SearchViews.LookupView({model: proj[0]});
                    window.Application.main.show(lookupView);
                } else {
                    console.log("no project defined");
                }
            },

            adaptChapter: function (id) {
                console.log("adaptChapter");
                // refresh the models
                window.Application.BookList.fetch({reset: true, data: {name: ""}});
                window.Application.ProjectList.fetch({reset: true, data: {name: ""}});
                $.when(window.Application.ChapterList.fetch({reset: true, data: {name: ""}})).done(function () {
                    // find the chapter we want to adapt
                    var chapter = window.Application.ChapterList.findWhere({chapterid: id});
                    if (chapter) {
                        var theView = new AdaptViews.ChapterView({model: chapter});
                        var proj = window.Application.ProjectList.where({id: chapter.get('projectid').toString()})[0];
                        var book = window.Application.BookList.where({bookid: chapter.get('bookid').toString()})[0];
                        theView.project = proj;
                        // update the last adapted book and chapter
                        if (proj) {
                            window.Application.filterList = proj.get('FilterMarkers'); // static (always ON) filters + whatever is specified for the project
                            proj.set('lastDocument', book.get('name'));
                            proj.set('lastAdaptedBookID', chapter.get('bookid'));
                            proj.set('lastAdaptedChapterID', chapter.get('chapterid'));
                            proj.set('lastAdaptedName', chapter.get('name'));
                        }
                        window.Application.main.show(theView);
    //                    window.Application.main.show(new AdaptViews.ChapterView({model: chapter}));
                    } else {
                        console.log("No chapter found matching id:" + id);
                    }
                });
            }
        });

    return {
        Application: Application
    };
});<commit_msg>Current Chapter not refreshing on home screen<commit_after>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */
define(function (require) {

    "use strict";

    var $               = require('jquery'),
        Backbone        = require('backbone'),
        Handlebars      = require('handlebars'),
        Helpers         = require('app/utils/HandlebarHelpers'),
        Marionette      = require('marionette'),
        HomeViews       = require('app/views/HomeViews'),
        HelpView        = require('app/views/HelpView'),
        ProjectViews    = require('app/views/ProjectViews'),
        DocumentViews   = require('app/views/DocumentViews'),
        SearchViews     = require('app/views/SearchViews'),
        AdaptViews      = require('app/views/AdaptViews'),
        projModel       = require('app/models/project'),
        chapterModel    = require('app/models/chapter'),
        bookModel       = require('app/models/book'),
        spModel         = require('app/models/sourcephrase'),
        AppRouter       = require('app/router'),
        FastClick       = require('fastclick'),
        PageSlider      = require('app/utils/pageslider'),
        slider          = new PageSlider($('body')),
        lookupView      = null,
        helpView        = null,
        newProjectView  = null,
        editProjectView = null,
        copyProjectView = null,
        homeView        = null,
        importDocView   = null,
        db              = null,
        i18n            = require('i18n'),
        lang            = "",
        models          = [],
        locale          = "en-AU",  // default

        Application = Marionette.Application.extend({
            filterlist: "",
            currentProject: null,

            // app initialization code. Here we'll initialize localization with the current locale
            initialize: function (options) {
                // callback function to initialize / load the localization info
                var initialize_i18n = function (locale) {
                    i18n.init({
                        lng: locale,
                        debug: true,
                        fallbackLng: 'en'
                    }, function () {
                        // Callback when i18next is finished initializing

                        // Load any app-wide collections
                        window.Application.BookList = new bookModel.BookCollection();
                        window.Application.BookList.fetch({reset: true, data: {name: ""}});
                        window.Application.ProjectList = new projModel.ProjectCollection();
                        window.Application.ProjectList.fetch({reset: true, data: {name: ""}});
                        window.Application.ChapterList = new chapterModel.ChapterCollection();
                        window.Application.ChapterList.fetch({reset: true, data: {name: ""}});
                        // Note: sourcephrases are not held as a singleton (for a NT, this could result in ~300MB of memory) --
                        // Instead, they are instantiated on the pages that need them
                        // (DocumentViews for doc import/export and AdaptViews for adapting)

                        // Tell backbone we're ready to start loading the View classes.
                        Backbone.history.start();
                    });
                };
                // add the UI regions (just the main "content" for now)
                this.addRegions({
                    main: '#main'
                });
                // main Region's show event handler -- we use it to do
                // page transition animations.
                this.main.on("show", function (view) {
                  // manipulate the `view` or do something extra
                  // with the region via `this`
                    slider.slidePage(view.$el);
                });
                // sqlitePlugin -- available on DeviceReady (mobile app)
                if (window.sqlitePlugin) {
                    this.db = window.sqlitePlugin.openDatabase({name: "AIM"});
                } else {
                    // running in browser -- use WebSQL (Chrome / Safari ONLY)
                    this.db = openDatabase('AIM', '1', 'AIM database', 2 * 1024 * 1024);
                }
                // create model collections off the Application object
                this.BookList = null;
                this.ProjectList = null;
                this.ChapterList = null;
                this.spList = null;

                // get the user's locale - mobile or web
                if (typeof navigator.globalization !== 'undefined') {
                    navigator.globalization.getLocaleName(
                        function (loc) {
                            locale = loc.value.split("-")[0];
                            initialize_i18n(locale);
                        },
                        function () {console.log('Error getting locale\n'); }
                    );
                } else {
                    // in web browser
                    lang = (navigator.languages) ? navigator.languages[0] : (navigator.language || navigator.userLanguage);
                    locale = lang.split("-")[0];
                    initialize_i18n(locale);
                }

                // initialize the router
                var router  = new AppRouter({controller: this});

                // Attach touch screen function to avoid delay in double-click
                $(function () {
                    FastClick.attach(document.body);
                });

                // Process back arrow button event
                // (not the one in the browser, the one we render on our html page).
                $("body").on("click", ".back-button", function (event) {
                    event.preventDefault();
                    window.history.back();
                });
            },

            // Routes from AppRouter (router.js)
            home: function () {
                // First, look for projects in the project list that aren't complete;
                // this can happen if the user clicks the back button before completing the
                // new project wizard. These objects with no id defined are only in memory;
                // once the source and target language are defined, an id is set and
                // the project is saved in the device's localStorage.
                this.ProjectList.fetch({reset: true, data: {name: ""}});
                this.ProjectList.each(function (model, index) {
//                    console.log("Model: " + model.get('id'));
                    if (model.get('id') === 0) {
                        // empty project -- mark for removal
                        models.push(model);
                    }
                });
                // remove the half-completed project objects
                if (models.length > 0) {
                    this.ProjectList.remove(models);
                }
                if (window.Application.currentProject === null) {
                    // pick the first project in the list, if there is one
                    window.Application.currentProject = this.ProjectList.at(0);
                }
                // now display the home view
                homeView = new HomeViews.HomeView({model: window.Application.currentProject});
                homeView.delegateEvents();
                this.main.show(homeView);
            },

            help: function () {
                helpView = new HelpView();
                helpView.delegateEvents();
                this.main.show(helpView);
            },

            editProject: function (id) {
                // edit the selected project
                var proj = this.ProjectList.where({id: id});
                if (proj !== null) {
                    window.Application.main.show(new ProjectViews.EditProjectView({model: proj[0]}));
                }
            },

            copyProject: function () {
                var proj = new projModel.Project();
                copyProjectView = new ProjectViews.CopyProjectView({model: proj});
                copyProjectView.delegateEvents();
                this.ProjectList.add(proj);
                this.main.show(copyProjectView);
            },

            newProject: function () {
                var proj = new projModel.Project();
                newProjectView = new ProjectViews.NewProjectView({model: proj});
                newProjectView.delegateEvents();
                this.ProjectList.add(proj);
                this.main.show(newProjectView);
            },

            importBooks: function (id) {
                console.log("importBooks");
                var proj = this.ProjectList.where({id: id});
                if (proj === null) {
                    console.log("no project defined");
                }
                importDocView = new DocumentViews.ImportDocumentView({model: proj[0]});
                importDocView.delegateEvents();
                this.main.show(importDocView);
            },

            lookupChapter: function (id) {
                console.log("lookupChapter");
                var proj = this.ProjectList.where({id: id});
                if (proj !== null) {
                    lookupView = new SearchViews.LookupView({model: proj[0]});
                    window.Application.main.show(lookupView);
                } else {
                    console.log("no project defined");
                }
            },

            adaptChapter: function (id) {
                console.log("adaptChapter");
                // refresh the models
                window.Application.BookList.fetch({reset: true, data: {name: ""}});
                window.Application.ProjectList.fetch({reset: true, data: {name: ""}});
                $.when(window.Application.ChapterList.fetch({reset: true, data: {name: ""}})).done(function () {
                    // find the chapter we want to adapt
                    var chapter = window.Application.ChapterList.findWhere({chapterid: id});
                    if (chapter) {
                        var theView = new AdaptViews.ChapterView({model: chapter});
                        var proj = window.Application.ProjectList.where({id: chapter.get('projectid').toString()})[0];
                        var book = window.Application.BookList.where({bookid: chapter.get('bookid').toString()})[0];
                        theView.project = proj;
                        // update the last adapted book and chapter
                        if (proj) {
                            window.Application.filterList = proj.get('FilterMarkers'); // static (always ON) filters + whatever is specified for the project
                            proj.set('lastDocument', book.get('name'));
                            proj.set('lastAdaptedBookID', chapter.get('bookid'));
                            proj.set('lastAdaptedChapterID', chapter.get('chapterid'));
                            proj.set('lastAdaptedName', chapter.get('name'));
                            proj.save();
                            window.Application.currentProject = proj;
                        }
                        window.Application.main.show(theView);
    //                    window.Application.main.show(new AdaptViews.ChapterView({model: chapter}));
                    } else {
                        console.log("No chapter found matching id:" + id);
                    }
                });
            }
        });

    return {
        Application: Application
    };
});<|endoftext|>


<commit_before>
describe('Intercom', function () {

  var options = {
    appId     : 'abc123',
    activator : '#someElement',
  };

  var userId = test.userId
  ,   traits = clone(test.traits);

  extend(traits, {
    company : { id : '123' }
  });

  describe('initialize', function () {

    it('should call ready', function () {
      var spy = sinon.spy();
      analytics.ready(spy);
      analytics.initialize({ 'Intercom' : test['Intercom'] });
      expect(spy.called).to.be(true);
    });

    it('should store options', function () {
      analytics.initialize({ 'Intercom' : test['Intercom'] });
      expect(analytics.providers[0].options.appId).to.equal(test['Intercom']);
    });

    it('should store expanded options', function () {

      analytics.initialize({ 'Intercom' : options });

      options.counter = true;

      expect(analytics.providers[0].options).to.eql(options);
    });
  });

  describe('identify', function () {

    it('should load library', function (done) {

      this.timeout(3000);

      expect(window.intercomSettings).to.be(undefined);

      expect(window.Intercom).to.be(undefined);
      analytics.identify(test.userId, traits);
      expect(window.intercomSettings).not.to.be(undefined);
      expect(window.intercomSettings).to.eql({
        created_at  : Math.floor(traits.created/1000),
        app_id      : options.appId,
        user_id     : userId,
        company     : traits.company,
        custom_data : traits,
        user_hash   : undefined,
        email       : traits.email,
        name        : traits.name,
        widget      : {
          activator   : options.activator,
          use_counter : options.counter
        }
      });

      // Once the Intercom library comes back, the array should be transformed.
      setTimeout(function () {
        expect(window.Intercom).not.to.be(undefined);
        done();
      }, 2500);
    });

    it('shouldnt load library the second time', function () {
      // We're going to test that `window.intercomSettings` doesnt get reset
      // to identified values again.
      window.intercomSettings = undefined;

      analytics.identify(test.userId, test.traits);
      expect(window.intercomSettings).to.be(undefined);
    });

  });

});
<commit_msg>Formatting<commit_after>
describe('Intercom', function () {

  var options = {
    appId     : 'abc123',
    activator : '#someElement',
  };

  var userId = test.userId
    , traits = clone(test.traits);

  extend(traits, {
    company : { id : '123' }
  });

  describe('initialize', function () {

    it('should call ready', function () {
      var spy = sinon.spy();
      analytics.ready(spy);
      analytics.initialize({ 'Intercom' : test['Intercom'] });
      expect(spy.called).to.be(true);
    });

    it('should store options', function () {
      analytics.initialize({ 'Intercom' : test['Intercom'] });
      expect(analytics.providers[0].options.appId).to.equal(test['Intercom']);
    });

    it('should store expanded options', function () {

      analytics.initialize({ 'Intercom' : options });

      options.counter = true;

      expect(analytics.providers[0].options).to.eql(options);
    });
  });

  describe('identify', function () {

    it('should load library', function (done) {

      this.timeout(3000);

      expect(window.intercomSettings).to.be(undefined);

      expect(window.Intercom).to.be(undefined);
      analytics.identify(test.userId, traits);
      expect(window.intercomSettings).not.to.be(undefined);
      expect(window.intercomSettings).to.eql({
        created_at  : Math.floor(traits.created/1000),
        app_id      : options.appId,
        user_id     : userId,
        company     : traits.company,
        custom_data : traits,
        user_hash   : undefined,
        email       : traits.email,
        name        : traits.name,
        widget      : {
          activator   : options.activator,
          use_counter : options.counter
        }
      });

      // Once the Intercom library comes back, the array should be transformed.
      setTimeout(function () {
        expect(window.Intercom).not.to.be(undefined);
        done();
      }, 2500);
    });

    it('shouldnt load library the second time', function () {
      // We're going to test that `window.intercomSettings` doesnt get reset
      // to identified values again.
      window.intercomSettings = undefined;

      analytics.identify(test.userId, test.traits);
      expect(window.intercomSettings).to.be(undefined);
    });

  });

});
<|endoftext|>


<commit_before>'use strict';

var grunt = require('grunt');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports.requirejs_dir = {
  setUp: function(done) {
    done();
  },
  default: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/default.js');
    var expected = grunt.file.read('test/expected/default.js');
    test.equal(actual, expected, 'should work with default options.');

    test.done();
  },
  endComma: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/endComma.js');
    var expected = grunt.file.read('test/expected/endComma.js');
    test.equal(actual, expected, 'should work with endComma option.');

    test.done();
  },
  quot: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/quot.js');
    var expected = grunt.file.read('test/expected/quot.js');
    test.equal(actual, expected, 'should work with quot option.');

    test.done();
  },
  delims: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/delims.js');
    var expected = grunt.file.read('test/expected/delims.js');
    test.equal(actual, expected, 'should work with delims option.');

    test.done();
  },
};
<commit_msg>Line ending fix<commit_after>'use strict';

var grunt = require('grunt');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports.requirejs_dir = {
  setUp: function(done) {
    done();
  },
  default: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/default.js');
    var expected = grunt.file.read('test/expected/default.js');

    actual = actual.replace(/\r\n/g, '\n');
    expected = expected.replace(/\r\n/g, '\n');

    test.equal(actual, expected, 'should work with default options.');

    test.done();
  },
  endComma: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/endComma.js');
    var expected = grunt.file.read('test/expected/endComma.js');
    actual = actual.replace(/\r\n/g, '\n');
    expected = expected.replace(/\r\n/g, '\n');

    test.equal(actual, expected, 'should work with endComma option.');

    test.done();
  },
  quot: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/quot.js');
    var expected = grunt.file.read('test/expected/quot.js');

    actual = actual.replace(/\r\n/g, '\n');
    expected = expected.replace(/\r\n/g, '\n');

    test.equal(actual, expected, 'should work with quot option.');

    test.done();
  },
  delims: function(test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/delims.js');
    var expected = grunt.file.read('test/expected/delims.js');

    actual = actual.replace(/\r\n/g, '\n');
    expected = expected.replace(/\r\n/g, '\n');

    test.equal(actual, expected, 'should work with delims option.');

    test.done();
  },
};
<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var yeoman = require('yeoman-generator');
var path = require("path");
var fs = require("fs");
var beautify = require('js-beautify').js_beautify;
var scriptBase = require('../script-base');

var Generator = module.exports = function Generator(args, options, config) {
    scriptBase.apply(this, arguments);
    this.setUpPaths();
    this._isInModule();
};

util.inherits(Generator, scriptBase);

Generator.prototype.createStructure = function createStructure() {
    this.mkdir("assets");
    this.mkdir("assets/skins");
    this.mkdir("assets/skins/sam");
    this.template("assets/skins/sam/moduleName-skin.css", "assets/skins/sam/"+ this.moduleName +"-skin.css");
    this.template("assets/moduleName-core.css", "assets/"+ this.moduleName +"-core.css");
};
<commit_msg>fix assets<commit_after>'use strict';
var util = require('util');
var yeoman = require('yeoman-generator');
var path = require("path");
var fs = require("fs");
var beautify = require('js-beautify').js_beautify;
var scriptBase = require('../script-base');

var Generator = module.exports = function Generator(args, options, config) {
    scriptBase.apply(this, arguments);
    this.setUpPaths();
    this._isInModule();
};

util.inherits(Generator, scriptBase);

Generator.prototype.createStructure = function createStructure() {
    this.mkdir("assets");
    this.mkdir("assets/skins");
    this.mkdir("assets/skins/sam");
    this.template("assets/skins/sam/moduleName-skin.css", "assets/skins/sam/"+ this.moduleName +"-skin.css");
    this.template("assets/moduleName-core.css", "assets/"+ this.moduleName +"-core.css");
};

Generator.prototype.updateMeta = function updateMeta() {
    var metaFile = JSON.parse(this.readFileAsString(this.metaFilePath));
    metaFile[this.moduleName].skinnable = true;
    this.write(this.metaFilePath, this._beautify(JSON.stringify(metaFile)) );
};

Generator.prototype.advices = function advices() {
    console.log("Please...");
    console.log("shift your module");
    console.log("shift your loader");
};

Generator.prototype._beautify = function _beautify(jsCode) {
    return beautify(jsCode, { indent_size: 3 });
}
<|endoftext|>


<commit_before>'use strict';
/*global json2html*/
/*global UI*/
/*global App*/
/*global getID*/
/*global getClass*/

UI.PostPage = function(elem) {
	var element = elem;
	document.addEventListener('postExpanded', function(e){
		displayContentItems(e.detail);
	});

	var displayContentItems = function(data) {
			//element.className = 'hidden';
			element.innerHTML = json2html.transform(data, mainTemplate);
			commentHandler();
			var postMode = new CustomEvent('postMode', {});
			document.dispatchEvent(postMode);
		};

	var commentHandler = function(){
		function switchButtonClasses(id){
			var tempComments = getID('singlePostComments-' + id);
			if(tempComments.className === 'comments line'){
				tempComments.className = 'comments line active';
			} else {
				tempComments.className = 'comments line';
			}
		}
		element.addEventListener('click', function(e){
			switchButtonClasses(e.target.dataset.id);
		});
	};

	var mainTemplate = {
		'tag':'article',
		'class':'missedConnection line post',
		'html':'
from: ${tempname}
',
		'children' : [
			{
				'tag':'h2',
				'html':'${title}',
				'class':'title line'
			},
			{
				'tag':'section',
				'id':'singlePost-${_id}',
				'children': [
					{
						'tag':'p',
						'html':'${body}'
					}
				],
			},
			{
				'tag' : 'div',
				id: 'singlePostComments-${_id}',
				'class' :'comments line',
				'html' :
					'<form class="comment-box" method="post" action="api/v1/comments/${_id}">'+
						'Submit a comment'+
						'<textarea class="comment-out" name="body"></textarea>'+
						'<button data-id=${_id} class="submit-comment" type="submit" value="send-comment">Submit A Comment</button>' + '</form>',
				'children' : [
					{
						'tag' : 'ul',
						'children' : [
							{
								'tag' : 'li',
								'children' : function(){
									return(json2html.transform(this.comments, commentMicroTemplate));
								}
							}
						]
					}
				]
			},
			{
				'tag':'footer',
				'class':'gesturebar line',
				'children': [
					{
						'tag':'div',
						'class':'vote',
						'children': [
							{
								'tag':'button',
								'class':'upVote',
								'title':'up vote',
								'html':''
							},
							{
								'tag':'button',
								'class': 'downvote',
								'title':'down vote',
								'html':''
							}
						]
					},
					{
						'tag':'button',
						'class':'viewComment',
						'data-id': '${_id}',
						'title':'comment',
						'html':''
					}
				]
			}
		]
	};

	var commentMicroTemplate = {
		'tag':'div',
		'data-id':'${_id}',
		'children': [
			{
				'tag':'h5',
				'html':'${tempname}'
			},
			{
				'tag':'p',
				'html':'${body}'
			}
		]
	};
};
<commit_msg>shows comments by default in single post view<commit_after>'use strict';
/*global json2html*/
/*global UI*/
/*global App*/
/*global getID*/
/*global getClass*/

UI.PostPage = function(elem) {
	var element = elem;
	document.addEventListener('postExpanded', function(e){
		displayContentItems(e.detail);
	});

	var displayContentItems = function(data) {
			//element.className = 'hidden';
			element.innerHTML = json2html.transform(data, mainTemplate);
			var postMode = new CustomEvent('postMode', {});
			document.dispatchEvent(postMode);
		};

	var mainTemplate = {
		'tag':'article',
		'class':'missedConnection line post',
		'html':'
from: ${tempname}
',
		'children' : [
			{
				'tag':'h2',
				'html':'${title}',
				'class':'title line'
			},
			{
				'tag':'section',
				'id':'singlePost-${_id}',
				'children': [
					{
						'tag':'p',
						'html':'${body}'
					}
				],
			},
			{
				'tag' : 'div',
				id: 'singlePostComments-${_id}',
				'class' :'comments line active',
				'html' :
					'<form class="comment-box" method="post" action="api/v1/comments/${_id}">'+
						'Submit a comment'+
						'<textarea class="comment-out" name="body"></textarea>'+
						'<button data-id=${_id} class="submit-comment" type="submit" value="send-comment">Submit A Comment</button>' + '</form>',
				'children' : [
					{
						'tag' : 'ul',
						'children' : [
							{
								'tag' : 'li',
								'children' : function(){
									return(json2html.transform(this.comments, commentMicroTemplate));
								}
							}
						]
					}
				]
			},
			{
				'tag':'footer',
				'class':'gesturebar line',
				'children': [
					{
						'tag':'div',
						'class':'vote',
						'children': [
							{
								'tag':'button',
								'class':'upVote',
								'title':'up vote',
								'html':''
							},
							{
								'tag':'button',
								'class': 'downvote',
								'title':'down vote',
								'html':''
							}
						]
					}
				]
			}
		]
	};

	var commentMicroTemplate = {
		'tag':'div',
		'data-id':'${_id}',
		'children': [
			{
				'tag':'h5',
				'html':'${tempname}'
			},
			{
				'tag':'p',
				'html':'${body}'
			}
		]
	};
};
<|endoftext|>