<commit_before>describe('script loader', function() {
  var loadScript = require('script-loader')
  if (!window.testPassed) {
    it('can load remote scripts', function(done) {
      mocha.options.globals.push['testPassed']
      window.testPassed = done
      loadScript('https://raw.github.com/timoxley/script-loader/master/test/script-loader.js')
    })
  } else {
    mocha.options.globals.push['testPassed']
    window.testPassed()
  }
})
<commit_msg>Testing...<commit_after>describe('script loader', function() {
  beforeEach(function() {
    mocha.options.globals.push['testPassed']
  })
  var loadScript = require('script-loader')
  if (!window.testPassed) {
    it('can load remote scripts', function(done) {
      window.testPassed = done
      loadScript('https://raw.github.com/timoxley/script-loader/master/test/script-loader.js')
    })
  } else {
    it('loaded remote script', function() {
      window.testPassed()
    })
  }
})
<|endoftext|>


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

  var libBase = 'src/lib/';       // transpiled app JS and map files

  config.set({
    basePath: '',
    frameworks: ['jasmine'],

    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter')
    ],

    client: {
      builtPaths: [libBase], // add more spec base paths as needed
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },

    customLaunchers: {
      // From the CLI. Not used here but interesting
      // chrome setup for travis CI using chromium
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    files: [
      // System.js for module loading
      'node_modules/systemjs/dist/system.src.js',

      // Polyfills
      'node_modules/core-js/client/shim.js',

      // zone.js
      'node_modules/zone.js/dist/zone.js',
      'node_modules/zone.js/dist/long-stack-trace-zone.js',
      'node_modules/zone.js/dist/proxy.js',
      'node_modules/zone.js/dist/sync-test.js',
      'node_modules/zone.js/dist/jasmine-patch.js',
      'node_modules/zone.js/dist/async-test.js',
      'node_modules/zone.js/dist/fake-async-test.js',

      // RxJs
      {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
      {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},

      // Paths loaded via module imports:
      // Angular itself
      {pattern: 'node_modules/@angular/**/*.js', included: false, watched: false},
      {pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false},

      {pattern: 'src/demo/systemjs-angular-loader.js', included: false, watched: false},

      'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels

      // transpiled application & spec code paths loaded via module imports
      {pattern: libBase + '**/*.js', included: false, watched: true},

      // Asset (HTML & CSS) paths loaded via Angular's component compiler
      // (these paths need to be rewritten, see proxies section)
      {pattern: libBase + '**/*.html', included: false, watched: true},
      {pattern: libBase + '**/*.css', included: false, watched: true},

      // Paths for debugging with source maps in dev tools
      {pattern: libBase + '**/*.ts', included: false, watched: false},
      {pattern: libBase + '**/*.js.map', included: false, watched: false}
    ],

    // Proxied base paths for loading assets
    proxies: {
      // required for modules fetched by SystemJS
      '/base/src/lib/node_modules/': '/base/node_modules/',
      '/base/src/lib/demo/': '/base/src/demo/'
    },

    exclude: [],
    preprocessors: {},
    reporters: process.env.CI ? ['junit'] : ['progress', 'kjhtml'],

    junitReporter: {
      outputDir: process.env.JUNIT_REPORT_PATH,
      outputFile: process.env.JUNIT_REPORT_NAME,
      useBrowserName: false
    },

    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  })
}
<commit_msg>added junit reports<commit_after>module.exports = function (config) {

  var libBase = 'src/lib/';       // transpiled app JS and map files

  config.set({
    basePath: '',
    frameworks: ['jasmine'],

    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter')
    ],

    client: {
      builtPaths: [libBase], // add more spec base paths as needed
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },

    customLaunchers: {
      // From the CLI. Not used here but interesting
      // chrome setup for travis CI using chromium
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    files: [
      // System.js for module loading
      'node_modules/systemjs/dist/system.src.js',

      // Polyfills
      'node_modules/core-js/client/shim.js',

      // zone.js
      'node_modules/zone.js/dist/zone.js',
      'node_modules/zone.js/dist/long-stack-trace-zone.js',
      'node_modules/zone.js/dist/proxy.js',
      'node_modules/zone.js/dist/sync-test.js',
      'node_modules/zone.js/dist/jasmine-patch.js',
      'node_modules/zone.js/dist/async-test.js',
      'node_modules/zone.js/dist/fake-async-test.js',

      // RxJs
      {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
      {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},

      // Paths loaded via module imports:
      // Angular itself
      {pattern: 'node_modules/@angular/**/*.js', included: false, watched: false},
      {pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false},

      {pattern: 'src/demo/systemjs-angular-loader.js', included: false, watched: false},

      'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels

      // transpiled application & spec code paths loaded via module imports
      {pattern: libBase + '**/*.js', included: false, watched: true},

      // Asset (HTML & CSS) paths loaded via Angular's component compiler
      // (these paths need to be rewritten, see proxies section)
      {pattern: libBase + '**/*.html', included: false, watched: true},
      {pattern: libBase + '**/*.css', included: false, watched: true},

      // Paths for debugging with source maps in dev tools
      {pattern: libBase + '**/*.ts', included: false, watched: false},
      {pattern: libBase + '**/*.js.map', included: false, watched: false}
    ],

    // Proxied base paths for loading assets
    proxies: {
      // required for modules fetched by SystemJS
      '/base/src/lib/node_modules/': '/base/node_modules/',
      '/base/src/lib/demo/': '/base/src/demo/'
    },

    exclude: [],
    preprocessors: {},
    reporters: process.env.CI ? ['junit'] : ['progress', 'kjhtml'],

    junitReporter: {
      outputDir: '$CIRCLE_TEST_REPORTS/junit/',
      outputFile: 'test-results.xml',
      useBrowserName: false
    },

    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  })
}
<|endoftext|>


<commit_before>const $ = require('jquery');
const _ = require('lodash');
const co = require('co');
const React = require('react');
const ReactDOM = require('react-dom');

const common = require('../common/common.js');
const sdata = require('../common/seller-data.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');

const tabber = require('../components/tabber.js');
const LoadingNotice = require('./LoadingNotice.jsx');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const KeywordAnalyticsTab = require('../components/KeywordAnalyticsTab.jsx');
const CampaignHistoryTab = require('../components/CampaignHistoryTab.jsx');
const AggregateHistory = require('../components/AggregateHistory.jsx');
const AggregateKeywords = require('../components/AggregateKeywords.jsx');

const now = Date.now();
const twoWeeks = 15 * constants.timespan.day;
const ninetyDays = 91 * constants.timespan.day;
const twoWeeksAgo = now - twoWeeks;
const ninetyDaysAgo = now - ninetyDays;

const tabClass = `machete-tab`;

// Map column names to data metrics
const charts = [
    { column: "Impr", label: "Impressions / day", metric: 'impressions' },
    { column: "Clicks", label: "Clicks / day", metric: 'clicks' },
    { column: "Spend", label: "Spend / day", metric: 'spend', },
    { column: "Orders", label: "Orders / day", metric: 'salesCount', },
    { column: "ACoS", label: "ACoS", metric: 'acos' },
    { column: "CTR", label: "CTR", metric: 'ctr' },
    { column: "CPC", label: "Cost per click ($)", metric: 'avgCpc' },
    { column: "Sales", label: "Sales ($) / day", metric: 'salesValue' },
];

const setSessionPromise = common.bgMessage({ action: 'setSession' });

let loadingInterval = window.setInterval(ga.mcatch(() => {
    const navbar = $('.sspa-navigation-bar');
    if (!navbar.length)
        return;

    let container = navbar.find(LoadingNotice.className).parent();
    if (!container.length) {
        container = $('

');
        navbar.prepend(container);
    }

    const loading = React.createElement(LoadingNotice, {});
    ReactDOM.render(loading, container[0]);

    setSessionPromise.then(() => container.remove());
    window.clearInterval(loadingInterval);
}), 100);

common.getUser().then(user => {
    window.setInterval(ga.mcatch(() => {
        let rows = $('.public_fixedDataTableRow_main');
        let header = rows.first();
        if (!header)
            return;

        let columnHeaders = header.find('.sspa-table-header');
        let columns = [];
        for (let i = 0; i < columnHeaders.length; i++) {
            columns.push(columnHeaders[i].innerText);
        }
        if (columns.length == 0)
            return;

        addChartButtons(columns, rows);
    }), 100);

    window.setInterval(ga.mcatch(() => {
        let tabs = $('.a-tabs');
        if (!tabs.length)
            return;
        if (tabs.hasClass(tabClass))
            return;
        tabs.addClass(tabClass);

        const loc = window.location.href;
        if (loc.match(/ad_group\/A\w+\//)) {
            tabber(tabs, { label: "Keyword Analytics", activate: generateKeywordReports });
        }
        else if (loc.match(/ad_groups\//)) {
            tabber(tabs, { label: "Campaign History", activate: generateCampaignHistory });
        }
        else if (loc.match(/campaigns\//)) {
            tabber(tabs, { label: "Aggregate Campaign History", activate: activateAggregateHistoryTab });
            tabber(tabs, { label: "Aggregate Keyword Analytics", activate: activateAggregateKeywordTab });
        }
    }), 100);

    function addChartButtons(columns, rows) {
        for (let row of rows) {
            let cells = $(row).find('.sspa-table-cell-container');
            if (cells.length == 0)
                continue;

            let link = $(cells[2]).find('a')[0];
            if (!link)
                continue;

            for (let chart of charts) {
                let target = cells[columns.indexOf(chart.column)];
                if (!target)
                    continue;

                if ($(target).find(`.${DashboardHistoryButton.chartClass}`).length)
                    continue;

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed: user.isSeller,
                    anonymous: user.isAnon,
                    metric: chart.metric,
                    title: chart.label,
                    dataPromiseFactory: () => fetchDataPromise(window.location.href, link.href).then(data => {
                        const campaignData = common.parallelizeSeries(data);
                        return common.formatParallelData(campaignData, chart.metric);
                    }),
                });
                const container = $('');
                $(target).children().first().append(container);
                ReactDOM.render(btn, container[0]);
            }
        }
    }

    function fetchDataPromise(locationHref, linkHref, startTimestamp, endTimestamp) {
        let { campaignId, adGroupId } = sdata.getCampaignId(linkHref);
        let args = { startTimestamp: startTimestamp || twoWeeksAgo, endTimestamp: endTimestamp || now };

        if (adGroupId && campaignId) {
            // We're on the campaign detail page looking at a link to a particular
            // ad group.
            args = Object.assign(args, { action: 'getAdGroupDataRange', campaignId, adGroupId });
        }
        else if (campaignId) {
            // We're on the overall campaign page, looking at a link to a campaign
            // detail page.
            args = Object.assign(args, { action: 'getCampaignDataRange', campaignId });
        }
        else {
            // We're on the adGroup page, looking at a link to the product. The
            // current location has the campaignId and the adGroupId.
            ({ campaignId, adGroupId } = sdata.getCampaignId(locationHref));
            const asin = sdata.getAsin(linkHref);
            args = Object.assign(args, { action: 'getAdDataRangeByAsin', campaignId, adGroupId, asin });
        }

        return common.bgMessage(args);
    }

    function getKeywordDataAggregate() {
        let { campaignId, adGroupId } = sdata.getCampaignId(window.location.href);
        return keywordDataPromise({ campaignId, adGroupId }, ninetyDaysAgo, now);
    }

    function generateCampaignHistory(container) {
        const content = React.createElement(CampaignHistoryTab, { dataPromise: fetchDataPromise(window.location.href, window.location.href, 1) });
        ReactDOM.render(content, container[0]);
    }

    function generateKeywordReports(container) {
        let content = React.createElement(KeywordAnalyticsTab, {
            dataPromise: getKeywordDataAggregate(),
            updateStatus: (ids, enabled, cb) => updateStatus(ids, enabled).then(cb),
            updateBid: (ids, bid, cb) => updateBid(ids, bid).then(cb),
        });
        ReactDOM.render(content, container[0]);
    }

    function campaignSelectOptions(summaries) {
        let options = [];

        const campaigns = _.groupBy(summaries, x => x.campaignId);
        options = options.concat(..._.keys(campaigns).map(x => ({
            value: campaigns[x],
            label: `Campaign: ${campaigns[x][0].campaignName}`,
        })));

        const adGroups = _.groupBy(summaries, x => x.adGroupId);
        options = options.concat(..._.keys(adGroups).map(x => ({
            value: adGroups[x],
            label: `Campaign: ${adGroups[x][0].campaignName} > Ad Group: ${adGroups[x][0].adGroupName}`
        })));

        options = options.concat(...summaries.map(x => ({
            value: [x],
            label: `Campaign: ${x.campaignName} > Ad Group: ${x.adGroupName} > Ad: ${x.title}`
        })));

        const products = _.groupBy(summaries, x => x.asin);
        options = options.concat(..._.keys(products).map(x => ({
            value: products[x],
            label: `Ads for ${products[x][0].title} (ASIN ${x})`
        })));

        return [{ value: summaries, label: 'All Campaigns' }].concat(..._.sortBy(options, ['label']));
    }

    function keywordSelectOptions(summaries) {
        let options = [];

        const campaigns = _.groupBy(summaries, x => x.campaignId);
        options = options.concat(..._.keys(campaigns).map(x => ({
            value: campaigns[x],
            label: `Campaign: ${campaigns[x][0].campaignName}`,
        })));

        const adGroups = _.groupBy(summaries, x => x.adGroupId);
        options = options.concat(..._.keys(adGroups).map(x => ({
            value: adGroups[x],
            label: `Campaign: ${adGroups[x][0].campaignName} > Ad Group: ${adGroups[x][0].adGroupName}`
        })));

        return [{ value: summaries, label: 'All Campaigns' }].concat(..._.sortBy(options, ['label']));
    }

    function adDataPromise(summary, startTimestamp, endTimestamp) {
        return common.bgMessage({
            action: 'getAdDataRange',
            campaignId: summary.campaignId,
            adGroupId: summary.adGroupId,
            adId: summary.adId,
            startTimestamp,
            endTimestamp
        });
    }

    function keywordDataPromise(summary, startTimestamp, endTimestamp) {
        return common.bgMessage({
            action: 'getKeywordDataRange',
            campaignId: summary.campaignId,
            adGroupId: summary.adGroupId,
            startTimestamp,
            endTimestamp
        }).then(common.accumulateKeywordSeries);
    }

    function activateAggregateHistoryTab(container) {
        let aggContent = React.createElement(AggregateHistory, {
            campaignPromise: sdata.getCampaignSummaries().then(campaignSelectOptions),
            loadDataPromise: (summaries) => co(function*() {
                const histories = yield Promise.all(summaries.map(x => adDataPromise(x, 1, now)));
                const aggregate = histories
                                  .reduce((array, deltas) => array.concat(...deltas), []);
                return aggregate;
            }),
        });
        ReactDOM.render(aggContent, container[0]);
    }

    function activateAggregateKeywordTab(container) {
        let aggContent = React.createElement(AggregateKeywords, {
            campaignPromise: sdata.getCampaignSummaries().then(keywordSelectOptions),
            loadDataPromise: (summaries) => co(function*() {
                const adGroups = _.uniqBy(summaries, x => x.adGroupId);
                const kwSeries = yield Promise.all(adGroups.map(x => keywordDataPromise(x, ninetyDaysAgo, now)));
                const aggregate = common.aggregateKeywords(kwSeries);
                return aggregate;
            }),
            updateStatus: (ids, enabled, callback) => {
                const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
                updateStatus(idList, enabled).then(callback);
            },
            updateBid: (ids, bid, callback) => {
                const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
                updateBid(idList, bid).then(callback);
            },
        });
        ReactDOM.render(aggContent, container[0]);
    }

    function updateKeyword(data) {
        return $.ajax({
            url: 'https://sellercentral.amazon.com/hz/cm/keyword/update',
            method: 'POST',
            data: JSON.stringify(data),
            contentType: 'application/json',
            dataType: 'json',
        })
        .then(() => ({ success: true }))
        .catch(error => ({ error }));
    }

    function updateStatus(keywordIds, enabled) {
        const status = enabled ? 'ENABLED' : 'PAUSED';
        const postData = { entities: keywordIds.map(id => ({ id, status })) };
        return updateKeyword(postData);
    }

    function updateBid(keywordIds, bid) {
        const postData = { entities: keywordIds.map(id => ({ id, bid })) };
        return updateKeyword(postData);
    }
});
<commit_msg>fixed an error on the seller page by sorting before aggregating campaigns<commit_after>const $ = require('jquery');
const _ = require('lodash');
const co = require('co');
const React = require('react');
const ReactDOM = require('react-dom');

const common = require('../common/common.js');
const sdata = require('../common/seller-data.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');

const tabber = require('../components/tabber.js');
const LoadingNotice = require('./LoadingNotice.jsx');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const KeywordAnalyticsTab = require('../components/KeywordAnalyticsTab.jsx');
const CampaignHistoryTab = require('../components/CampaignHistoryTab.jsx');
const AggregateHistory = require('../components/AggregateHistory.jsx');
const AggregateKeywords = require('../components/AggregateKeywords.jsx');

const now = Date.now();
const twoWeeks = 15 * constants.timespan.day;
const ninetyDays = 91 * constants.timespan.day;
const twoWeeksAgo = now - twoWeeks;
const ninetyDaysAgo = now - ninetyDays;

const tabClass = `machete-tab`;

// Map column names to data metrics
const charts = [
    { column: "Impr", label: "Impressions / day", metric: 'impressions' },
    { column: "Clicks", label: "Clicks / day", metric: 'clicks' },
    { column: "Spend", label: "Spend / day", metric: 'spend', },
    { column: "Orders", label: "Orders / day", metric: 'salesCount', },
    { column: "ACoS", label: "ACoS", metric: 'acos' },
    { column: "CTR", label: "CTR", metric: 'ctr' },
    { column: "CPC", label: "Cost per click ($)", metric: 'avgCpc' },
    { column: "Sales", label: "Sales ($) / day", metric: 'salesValue' },
];

const setSessionPromise = common.bgMessage({ action: 'setSession' });

let loadingInterval = window.setInterval(ga.mcatch(() => {
    const navbar = $('.sspa-navigation-bar');
    if (!navbar.length)
        return;

    let container = navbar.find(LoadingNotice.className).parent();
    if (!container.length) {
        container = $('

');
        navbar.prepend(container);
    }

    const loading = React.createElement(LoadingNotice, {});
    ReactDOM.render(loading, container[0]);

    setSessionPromise.then(() => container.remove());
    window.clearInterval(loadingInterval);
}), 100);

common.getUser().then(user => {
    window.setInterval(ga.mcatch(() => {
        let rows = $('.public_fixedDataTableRow_main');
        let header = rows.first();
        if (!header)
            return;

        let columnHeaders = header.find('.sspa-table-header');
        let columns = [];
        for (let i = 0; i < columnHeaders.length; i++) {
            columns.push(columnHeaders[i].innerText);
        }
        if (columns.length == 0)
            return;

        addChartButtons(columns, rows);
    }), 100);

    window.setInterval(ga.mcatch(() => {
        let tabs = $('.a-tabs');
        if (!tabs.length)
            return;
        if (tabs.hasClass(tabClass))
            return;
        tabs.addClass(tabClass);

        const loc = window.location.href;
        if (loc.match(/ad_group\/A\w+\//)) {
            tabber(tabs, { label: "Keyword Analytics", activate: generateKeywordReports });
        }
        else if (loc.match(/ad_groups\//)) {
            tabber(tabs, { label: "Campaign History", activate: generateCampaignHistory });
        }
        else if (loc.match(/campaigns\//)) {
            tabber(tabs, { label: "Aggregate Campaign History", activate: activateAggregateHistoryTab });
            tabber(tabs, { label: "Aggregate Keyword Analytics", activate: activateAggregateKeywordTab });
        }
    }), 100);

    function addChartButtons(columns, rows) {
        for (let row of rows) {
            let cells = $(row).find('.sspa-table-cell-container');
            if (cells.length == 0)
                continue;

            let link = $(cells[2]).find('a')[0];
            if (!link)
                continue;

            for (let chart of charts) {
                let target = cells[columns.indexOf(chart.column)];
                if (!target)
                    continue;

                if ($(target).find(`.${DashboardHistoryButton.chartClass}`).length)
                    continue;

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed: user.isSeller,
                    anonymous: user.isAnon,
                    metric: chart.metric,
                    title: chart.label,
                    dataPromiseFactory: () => fetchDataPromise(window.location.href, link.href).then(data => {
                        const campaignData = common.parallelizeSeries(data);
                        return common.formatParallelData(campaignData, chart.metric);
                    }),
                });
                const container = $('');
                $(target).children().first().append(container);
                ReactDOM.render(btn, container[0]);
            }
        }
    }

    function fetchDataPromise(locationHref, linkHref, startTimestamp, endTimestamp) {
        let { campaignId, adGroupId } = sdata.getCampaignId(linkHref);
        let args = { startTimestamp: startTimestamp || twoWeeksAgo, endTimestamp: endTimestamp || now };

        if (adGroupId && campaignId) {
            // We're on the campaign detail page looking at a link to a particular
            // ad group.
            args = Object.assign(args, { action: 'getAdGroupDataRange', campaignId, adGroupId });
        }
        else if (campaignId) {
            // We're on the overall campaign page, looking at a link to a campaign
            // detail page.
            args = Object.assign(args, { action: 'getCampaignDataRange', campaignId });
        }
        else {
            // We're on the adGroup page, looking at a link to the product. The
            // current location has the campaignId and the adGroupId.
            ({ campaignId, adGroupId } = sdata.getCampaignId(locationHref));
            const asin = sdata.getAsin(linkHref);
            args = Object.assign(args, { action: 'getAdDataRangeByAsin', campaignId, adGroupId, asin });
        }

        return common.bgMessage(args);
    }

    function getKeywordDataAggregate() {
        let { campaignId, adGroupId } = sdata.getCampaignId(window.location.href);
        return keywordDataPromise({ campaignId, adGroupId }, ninetyDaysAgo, now);
    }

    function generateCampaignHistory(container) {
        const content = React.createElement(CampaignHistoryTab, { dataPromise: fetchDataPromise(window.location.href, window.location.href, 1) });
        ReactDOM.render(content, container[0]);
    }

    function generateKeywordReports(container) {
        let content = React.createElement(KeywordAnalyticsTab, {
            dataPromise: getKeywordDataAggregate(),
            updateStatus: (ids, enabled, cb) => updateStatus(ids, enabled).then(cb),
            updateBid: (ids, bid, cb) => updateBid(ids, bid).then(cb),
        });
        ReactDOM.render(content, container[0]);
    }

    function campaignSelectOptions(summaries) {
        let options = [];

        const campaigns = _.groupBy(summaries, x => x.campaignId);
        options = options.concat(..._.keys(campaigns).map(x => ({
            value: campaigns[x],
            label: `Campaign: ${campaigns[x][0].campaignName}`,
        })));

        const adGroups = _.groupBy(summaries, x => x.adGroupId);
        options = options.concat(..._.keys(adGroups).map(x => ({
            value: adGroups[x],
            label: `Campaign: ${adGroups[x][0].campaignName} > Ad Group: ${adGroups[x][0].adGroupName}`
        })));

        options = options.concat(...summaries.map(x => ({
            value: [x],
            label: `Campaign: ${x.campaignName} > Ad Group: ${x.adGroupName} > Ad: ${x.title}`
        })));

        const products = _.groupBy(summaries, x => x.asin);
        options = options.concat(..._.keys(products).map(x => ({
            value: products[x],
            label: `Ads for ${products[x][0].title} (ASIN ${x})`
        })));

        return [{ value: summaries, label: 'All Campaigns' }].concat(..._.sortBy(options, ['label']));
    }

    function keywordSelectOptions(summaries) {
        let options = [];

        const campaigns = _.groupBy(summaries, x => x.campaignId);
        options = options.concat(..._.keys(campaigns).map(x => ({
            value: campaigns[x],
            label: `Campaign: ${campaigns[x][0].campaignName}`,
        })));

        const adGroups = _.groupBy(summaries, x => x.adGroupId);
        options = options.concat(..._.keys(adGroups).map(x => ({
            value: adGroups[x],
            label: `Campaign: ${adGroups[x][0].campaignName} > Ad Group: ${adGroups[x][0].adGroupName}`
        })));

        return [{ value: summaries, label: 'All Campaigns' }].concat(..._.sortBy(options, ['label']));
    }

    function adDataPromise(summary, startTimestamp, endTimestamp) {
        return common.bgMessage({
            action: 'getAdDataRange',
            campaignId: summary.campaignId,
            adGroupId: summary.adGroupId,
            adId: summary.adId,
            startTimestamp,
            endTimestamp
        });
    }

    function keywordDataPromise(summary, startTimestamp, endTimestamp) {
        return common.bgMessage({
            action: 'getKeywordDataRange',
            campaignId: summary.campaignId,
            adGroupId: summary.adGroupId,
            startTimestamp,
            endTimestamp
        }).then(common.accumulateKeywordSeries);
    }

    function activateAggregateHistoryTab(container) {
        let aggContent = React.createElement(AggregateHistory, {
            campaignPromise: sdata.getCampaignSummaries().then(campaignSelectOptions),
            loadDataPromise: (summaries) => co(function*() {
                const histories = yield Promise.all(summaries.map(x => adDataPromise(x, 1, now)));
                const aggregate = histories
                                  .reduce((array, deltas) => array.concat(...deltas), [])
                                  .sort(common.timestampSort);
                return aggregate;
            }),
        });
        ReactDOM.render(aggContent, container[0]);
    }

    function activateAggregateKeywordTab(container) {
        let aggContent = React.createElement(AggregateKeywords, {
            campaignPromise: sdata.getCampaignSummaries().then(keywordSelectOptions),
            loadDataPromise: (summaries) => co(function*() {
                const adGroups = _.uniqBy(summaries, x => x.adGroupId);
                const kwSeries = yield Promise.all(adGroups.map(x => keywordDataPromise(x, ninetyDaysAgo, now)));
                const aggregate = common.aggregateKeywords(kwSeries);
                return aggregate;
            }),
            updateStatus: (ids, enabled, callback) => {
                const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
                updateStatus(idList, enabled).then(callback);
            },
            updateBid: (ids, bid, callback) => {
                const idList = _.uniq(ids.reduce((array, item) => array.concat(...item), []));
                updateBid(idList, bid).then(callback);
            },
        });
        ReactDOM.render(aggContent, container[0]);
    }

    function updateKeyword(data) {
        return $.ajax({
            url: 'https://sellercentral.amazon.com/hz/cm/keyword/update',
            method: 'POST',
            data: JSON.stringify(data),
            contentType: 'application/json',
            dataType: 'json',
        })
        .then(() => ({ success: true }))
        .catch(error => ({ error }));
    }

    function updateStatus(keywordIds, enabled) {
        const status = enabled ? 'ENABLED' : 'PAUSED';
        const postData = { entities: keywordIds.map(id => ({ id, status })) };
        return updateKeyword(postData);
    }

    function updateBid(keywordIds, bid) {
        const postData = { entities: keywordIds.map(id => ({ id, bid })) };
        return updateKeyword(postData);
    }
});
<|endoftext|>


<commit_before>module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [
            'stackframe.js',
            'spec/*-spec.js'
        ],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        customLaunchers: {
            Chrome_No_Sandbox: {
                base: 'Chrome',
                flags: ['--no-sandbox']
            }
        },
        browsers: ['PhantomJS'],
        reporters: ['spec', 'saucelabs', 'coverage', 'coveralls'],
        preprocessors: {
            'error-stack-parser.js': 'coverage'
        },
        coverageReporter: {
            type: 'lcov',
            dir: 'coverage',
            subdir: function(browser) {
                return browser.toLowerCase().split(/[ /-]/)[0];
            }
        },
        singleRun: false
    });
};
<commit_msg>Report coverage for stackframe.js<commit_after>module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [
            'stackframe.js',
            'spec/*-spec.js'
        ],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        customLaunchers: {
            Chrome_No_Sandbox: {
                base: 'Chrome',
                flags: ['--no-sandbox']
            }
        },
        browsers: ['PhantomJS'],
        reporters: ['spec', 'saucelabs', 'coverage', 'coveralls'],
        preprocessors: {
            'stackframe.js': 'coverage'
        },
        coverageReporter: {
            type: 'lcov',
            dir: 'coverage',
            subdir: function(browser) {
                return browser.toLowerCase().split(/[ /-]/)[0];
            }
        },
        singleRun: false
    });
};
<|endoftext|>


<commit_before>'use strict';
var protobufjs = protobufjs || require('protobufjs/dist/ProtoBuf');
var Message = Message || require('./Message');

var KJUR = require('jsrsasign');
var RootCerts = require('./RootCerts');

// BIP 70 - payment protocol
function PayPro() {
  this.messageType = null;
  this.message = null;
}

PayPro.PAYMENT_REQUEST_MAX_SIZE = 50000;
PayPro.PAYMENT_MAX_SIZE = 50000;
PayPro.PAYMENT_ACK_MAX_SIZE = 60000;
PayPro.PAYMENT_REQUEST_CONTENT_TYPE = "application/bitcoin-paymentrequest";
PayPro.PAYMENT_CONTENT_TYPE = "application/bitcoin-payment";
PayPro.PAYMENT_ACK_CONTENT_TYPE = "application/bitcoin-paymentack";

PayPro.proto = {};

PayPro.proto.Output = "message Output {\
  optional uint64 amount = 1 [default = 0];\
  optional bytes script = 2;\
}\n";

PayPro.proto.PaymentDetails = "message PaymentDetails {\
  optional string network = 1 [default = \"main\"];\
  repeated Output outputs = 2;\
  required uint64 time = 3;\
  optional uint64 expires = 4;\
  optional string memo = 5;\
  optional string payment_url = 6;\
  optional bytes merchant_data = 7;\
}\n";

PayPro.proto.PaymentRequest = "message PaymentRequest {\
  optional uint32 payment_details_version = 1 [default = 1];\
  optional string pki_type = 2 [default = \"none\"];\
  optional bytes pki_data = 3;\
  required bytes serialized_payment_details = 4;\
  optional bytes signature = 5;\
}\n";

PayPro.proto.Payment = "message Payment {\
  optional bytes merchant_data = 1;\
  repeated bytes transactions = 2;\
  repeated Output refund_to = 3;\
  optional string memo = 4;\
}\n";

PayPro.proto.PaymentACK = "message PaymentACK {\
  required Payment payment = 1;\
  optional string memo = 2;\
}\n";

PayPro.proto.X509Certificates = "message X509Certificates {\
  repeated bytes certificate = 1;\
}\n";

PayPro.proto.all = "";
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Output;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentDetails;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentRequest;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Payment;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentACK;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.X509Certificates;

PayPro.builder = protobufjs.loadProto(PayPro.proto.all);

PayPro.Output = PayPro.builder.build("Output");
PayPro.PaymentDetails = PayPro.builder.build("PaymentDetails");
PayPro.PaymentRequest = PayPro.builder.build("PaymentRequest");
PayPro.Payment = PayPro.builder.build("Payment");
PayPro.PaymentACK = PayPro.builder.build("PaymentACK");
PayPro.X509Certificates = PayPro.builder.build("X509Certificates");

PayPro.prototype.makeOutput = function(obj) {
  this.messageType = 'Output';
  this.message = new PayPro.Output();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentDetails = function(obj) {
  this.messageType = 'PaymentDetails';
  this.message = new PayPro.PaymentDetails();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentRequest = function(obj) {
  this.messageType = 'PaymentRequest';
  this.message = new PayPro.PaymentRequest();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePayment = function(obj) {
  this.messageType = 'Payment';
  this.message = new PayPro.Payment();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentACK = function(obj) {
  this.messageType = 'Payment';
  this.message = new PayPro.PaymentACK();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makeX509Certificates = function(obj) {
  this.messageType = 'X509Certificates';
  this.message = new PayPro.X509Certificates();
  this.setObj(obj);
  return this;
};

PayPro.prototype.isValidSize = function() {
  var s = this.serialize();
  if (this.messageType == 'PaymentRequest')
    return s.length < PayPro.PAYMENT_REQUEST_MAX_SIZE;
  if (this.messageType == 'Payment')
    return s.length < PayPro.PAYMENT_MAX_SIZE;
  if (this.messageType == 'PaymentACK')
    return s.length < PayPro.PAYMENT_ACK_MAX_SIZE;
  return true;
};

PayPro.prototype.getContentType = function() {
  if (this.messageType == 'PaymentRequest')
    return PayPro.PAYMENT_REQUEST_CONTENT_TYPE;

  if (this.messageType == 'Payment')
    return PayPro.PAYMENT_CONTENT_TYPE;

  if (this.messageType == 'PaymentACK')
    return PayPro.PAYMENT_ACK_CONTENT_TYPE;

  throw new Error('No known content type for this message type');
};

PayPro.prototype.set = function(key, val) {
  this.message.set(key, val);
  return this;
};

PayPro.prototype.get = function(key) {
  var v = this.message.get(key);

  if (v === null)
    return v;

  //protobuf supports longs, javascript naturally does not
  //convert longs (see long.js, e.g. require('long')) to Numbers
  if (typeof v.low !== 'undefined' && typeof v.high !== 'undefined')
    return v.toInt();

  if (typeof v.toBuffer !== 'undefined') {
    var maybebuf = v.toBuffer();
    return Buffer.isBuffer(maybebuf) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
  }

  return v;
};

PayPro.prototype.setObj = function(obj) {
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var val = obj[key];
      this.message.set(key, val);
    }
  }
  return this;
};

PayPro.prototype.serializeForSig = function() {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('serializeForSig is only for PaymentRequest');

  var save = this.message.get('signature');
  this.message.set('signature', new Buffer([]));
  var buf = this.serialize();
  this.message.set('signature', save);
  return buf;
};

PayPro.prototype.serialize = function() {
  //protobufjs returns either a Buffer or an ArrayBuffer
  //but we always want a Buffer (which browserify understands, browser or no)
  var maybebuf = this.message.toBuffer();
  var buf = (Buffer.isBuffer(maybebuf)) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
  return buf;
};

PayPro.prototype.deserialize = function(buf, messageType) {
  this.messageType = messageType || this.messageType;
  if (!this.messageType)
    throw new Error('Must specify messageType');
  this.message = PayPro[this.messageType].decode(buf);
  return this;
};

PayPro.prototype.sign = function(key) {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('Signing can only be performed on a PaymentRequest');

  var pki_type = this.get('pki_type');

  if (pki_type === 'SIN') {
    var sig = this.sinSign(key);
  } else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
    var crypto = require('crypto');
    var pki_data = this.get('pki_data'); // contains one or more x509 certs
    var details = this.get('serialized_payment_details');
    var type = pki_type.split('+')[1].toUpperCase();

    var trusted = [].concat(pki_data).every(function(cert) {
      var der = cert.toString('hex');
      var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
      // var pem = DERtoPEM(der, 'CERTIFICATE');
      return !!RootCerts[pem.replace(/\s+/g, '')];
    });

    if (!trusted) {
      // throw new Error('Unstrusted certificate.');
    }

    var signature = crypto.createSign('RSA-' + type);
    var buf = this.serializeForSig();
    signature.update(buf);
    var sig = signature.sign(key);
  } else if (pki_type === 'none') {
    return this;
  } else {
    throw new Error('Unsupported pki_type');
  }

  this.set('signature', sig);

  return this;
};

PayPro.prototype.verify = function() {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('Verifying can only be performed on a PaymentRequest');

  var pki_type = this.get('pki_type');

  if (pki_type === 'SIN') {
    return this.sinVerify();
  } else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
    var crypto = require('crypto');
    var sig = this.get('signature');
    var pki_data = this.get('pki_data');
    var details = this.get('serialized_payment_details');
    var buf = this.serializeForSig();
    var type = pki_type.split('+')[1].toUpperCase();

    var verifier = crypto.createVerify('RSA-' + type);
    verifier.update(buf);

    return [].concat(pki_data).every(function(cert) {
      var der = cert.toString('hex');
      var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
      // var pem = DERtoPEM(der, 'CERTIFICATE');

      if (!RootCerts[pem.replace(/\s+/g, '')]) {
        // throw new Error('Unstrusted certificate.');
      }

      return verifier.verify(pem, sig);
    });
  } else if (pki_type === 'none') {
    return true;
  }

  throw new Error('Unsupported pki_type');
};

//default signing function for prototype.sign
PayPro.prototype.sinSign = function(key) {
  this.set('pki_data', key.public)
  var buf = this.serializeForSig();
  return Message.sign(buf, key);
};

//default verify function
PayPro.prototype.sinVerify = function() {
  var sig = this.get('signature');
  var pubkey = this.get('pki_data');
  var buf = this.serializeForSig();
  return Message.verifyWithPubKey(pubkey, buf, sig);
};

// Helpers

function PEMtoDER(pem) {
  pem = pem.replace(/^-----END [^-]+-----$/gmi/, '');
  var parts = pem.split(/-----BEGIN [^-]+-----/);
  return parts.map(function(part) {
    part = part.replace(/\s+/g, '');
    return new Buffer(part, 'base64');
  });
}

function PEMtoDERParam(pem, param) {
  var start = new RegExp('(?=-----BEGIN ' + param + '-----)', 'i');
  var end = new RegExp('^-----END ' + param + '-----$', 'gmi');
  pem = pem.replace(end, '');
  var parts = pem.split(start);
  return parts.map(function(part) {
    part = part.replace(/\s+/g, '');
    var type = /-----BEGIN ([^-]+)-----/.exec(part)[1];
    part = part.replace(/-----BEGIN ([^-]+)-----/g, '');
    if (type !== param) return;
    return new Buffer(part, 'base64');
  }).filter(Boolean);
}

function wrapText(text, cols) {
  var j = 0;
  var part = '';
  var parts = [];
  for (var i = 0; i < text.length; i++) {
    if (j === cols) {
      parts.push(part);
      j = 0;
      part = ''
      continue;
    }
    part += text[i];
    j++;
  }
  var total = parts.join('').length;
  if (total < text.length) {
    parts.push(text.slice(-(text.length - total)));
  }
  return parts.join('\n');
}

function DERtoPEM(der, type) {
  var type = type || 'UNKNOWN';
  return ''
    + '-----BEGIN ' + type + '-----'
    + wrapText(der.toString('base64'), 64)
    + '-----END ' + type + '-----';
}

module.exports = PayPro;
<commit_msg>minor: fix typo.<commit_after>'use strict';
var protobufjs = protobufjs || require('protobufjs/dist/ProtoBuf');
var Message = Message || require('./Message');

var KJUR = require('jsrsasign');
var RootCerts = require('./RootCerts');

// BIP 70 - payment protocol
function PayPro() {
  this.messageType = null;
  this.message = null;
}

PayPro.PAYMENT_REQUEST_MAX_SIZE = 50000;
PayPro.PAYMENT_MAX_SIZE = 50000;
PayPro.PAYMENT_ACK_MAX_SIZE = 60000;
PayPro.PAYMENT_REQUEST_CONTENT_TYPE = "application/bitcoin-paymentrequest";
PayPro.PAYMENT_CONTENT_TYPE = "application/bitcoin-payment";
PayPro.PAYMENT_ACK_CONTENT_TYPE = "application/bitcoin-paymentack";

PayPro.proto = {};

PayPro.proto.Output = "message Output {\
  optional uint64 amount = 1 [default = 0];\
  optional bytes script = 2;\
}\n";

PayPro.proto.PaymentDetails = "message PaymentDetails {\
  optional string network = 1 [default = \"main\"];\
  repeated Output outputs = 2;\
  required uint64 time = 3;\
  optional uint64 expires = 4;\
  optional string memo = 5;\
  optional string payment_url = 6;\
  optional bytes merchant_data = 7;\
}\n";

PayPro.proto.PaymentRequest = "message PaymentRequest {\
  optional uint32 payment_details_version = 1 [default = 1];\
  optional string pki_type = 2 [default = \"none\"];\
  optional bytes pki_data = 3;\
  required bytes serialized_payment_details = 4;\
  optional bytes signature = 5;\
}\n";

PayPro.proto.Payment = "message Payment {\
  optional bytes merchant_data = 1;\
  repeated bytes transactions = 2;\
  repeated Output refund_to = 3;\
  optional string memo = 4;\
}\n";

PayPro.proto.PaymentACK = "message PaymentACK {\
  required Payment payment = 1;\
  optional string memo = 2;\
}\n";

PayPro.proto.X509Certificates = "message X509Certificates {\
  repeated bytes certificate = 1;\
}\n";

PayPro.proto.all = "";
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Output;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentDetails;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentRequest;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Payment;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentACK;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.X509Certificates;

PayPro.builder = protobufjs.loadProto(PayPro.proto.all);

PayPro.Output = PayPro.builder.build("Output");
PayPro.PaymentDetails = PayPro.builder.build("PaymentDetails");
PayPro.PaymentRequest = PayPro.builder.build("PaymentRequest");
PayPro.Payment = PayPro.builder.build("Payment");
PayPro.PaymentACK = PayPro.builder.build("PaymentACK");
PayPro.X509Certificates = PayPro.builder.build("X509Certificates");

PayPro.prototype.makeOutput = function(obj) {
  this.messageType = 'Output';
  this.message = new PayPro.Output();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentDetails = function(obj) {
  this.messageType = 'PaymentDetails';
  this.message = new PayPro.PaymentDetails();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentRequest = function(obj) {
  this.messageType = 'PaymentRequest';
  this.message = new PayPro.PaymentRequest();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePayment = function(obj) {
  this.messageType = 'Payment';
  this.message = new PayPro.Payment();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makePaymentACK = function(obj) {
  this.messageType = 'Payment';
  this.message = new PayPro.PaymentACK();
  this.setObj(obj);
  return this;
};

PayPro.prototype.makeX509Certificates = function(obj) {
  this.messageType = 'X509Certificates';
  this.message = new PayPro.X509Certificates();
  this.setObj(obj);
  return this;
};

PayPro.prototype.isValidSize = function() {
  var s = this.serialize();
  if (this.messageType == 'PaymentRequest')
    return s.length < PayPro.PAYMENT_REQUEST_MAX_SIZE;
  if (this.messageType == 'Payment')
    return s.length < PayPro.PAYMENT_MAX_SIZE;
  if (this.messageType == 'PaymentACK')
    return s.length < PayPro.PAYMENT_ACK_MAX_SIZE;
  return true;
};

PayPro.prototype.getContentType = function() {
  if (this.messageType == 'PaymentRequest')
    return PayPro.PAYMENT_REQUEST_CONTENT_TYPE;

  if (this.messageType == 'Payment')
    return PayPro.PAYMENT_CONTENT_TYPE;

  if (this.messageType == 'PaymentACK')
    return PayPro.PAYMENT_ACK_CONTENT_TYPE;

  throw new Error('No known content type for this message type');
};

PayPro.prototype.set = function(key, val) {
  this.message.set(key, val);
  return this;
};

PayPro.prototype.get = function(key) {
  var v = this.message.get(key);

  if (v === null)
    return v;

  //protobuf supports longs, javascript naturally does not
  //convert longs (see long.js, e.g. require('long')) to Numbers
  if (typeof v.low !== 'undefined' && typeof v.high !== 'undefined')
    return v.toInt();

  if (typeof v.toBuffer !== 'undefined') {
    var maybebuf = v.toBuffer();
    return Buffer.isBuffer(maybebuf) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
  }

  return v;
};

PayPro.prototype.setObj = function(obj) {
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var val = obj[key];
      this.message.set(key, val);
    }
  }
  return this;
};

PayPro.prototype.serializeForSig = function() {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('serializeForSig is only for PaymentRequest');

  var save = this.message.get('signature');
  this.message.set('signature', new Buffer([]));
  var buf = this.serialize();
  this.message.set('signature', save);
  return buf;
};

PayPro.prototype.serialize = function() {
  //protobufjs returns either a Buffer or an ArrayBuffer
  //but we always want a Buffer (which browserify understands, browser or no)
  var maybebuf = this.message.toBuffer();
  var buf = (Buffer.isBuffer(maybebuf)) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
  return buf;
};

PayPro.prototype.deserialize = function(buf, messageType) {
  this.messageType = messageType || this.messageType;
  if (!this.messageType)
    throw new Error('Must specify messageType');
  this.message = PayPro[this.messageType].decode(buf);
  return this;
};

PayPro.prototype.sign = function(key) {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('Signing can only be performed on a PaymentRequest');

  var pki_type = this.get('pki_type');

  if (pki_type === 'SIN') {
    var sig = this.sinSign(key);
  } else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
    var crypto = require('crypto');
    var pki_data = this.get('pki_data'); // contains one or more x509 certs
    var details = this.get('serialized_payment_details');
    var type = pki_type.split('+')[1].toUpperCase();

    var trusted = [].concat(pki_data).every(function(cert) {
      var der = cert.toString('hex');
      var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
      // var pem = DERtoPEM(der, 'CERTIFICATE');
      return !!RootCerts[pem.replace(/\s+/g, '')];
    });

    if (!trusted) {
      // throw new Error('Unstrusted certificate.');
    }

    var signature = crypto.createSign('RSA-' + type);
    var buf = this.serializeForSig();
    signature.update(buf);
    var sig = signature.sign(key);
  } else if (pki_type === 'none') {
    return this;
  } else {
    throw new Error('Unsupported pki_type');
  }

  this.set('signature', sig);

  return this;
};

PayPro.prototype.verify = function() {
  if (this.messageType !== 'PaymentRequest')
    throw new Error('Verifying can only be performed on a PaymentRequest');

  var pki_type = this.get('pki_type');

  if (pki_type === 'SIN') {
    return this.sinVerify();
  } else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
    var crypto = require('crypto');
    var sig = this.get('signature');
    var pki_data = this.get('pki_data');
    var details = this.get('serialized_payment_details');
    var buf = this.serializeForSig();
    var type = pki_type.split('+')[1].toUpperCase();

    var verifier = crypto.createVerify('RSA-' + type);
    verifier.update(buf);

    return [].concat(pki_data).every(function(cert) {
      var der = cert.toString('hex');
      var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
      // var pem = DERtoPEM(der, 'CERTIFICATE');

      if (!RootCerts[pem.replace(/\s+/g, '')]) {
        // throw new Error('Unstrusted certificate.');
      }

      return verifier.verify(pem, sig);
    });
  } else if (pki_type === 'none') {
    return true;
  }

  throw new Error('Unsupported pki_type');
};

//default signing function for prototype.sign
PayPro.prototype.sinSign = function(key) {
  this.set('pki_data', key.public)
  var buf = this.serializeForSig();
  return Message.sign(buf, key);
};

//default verify function
PayPro.prototype.sinVerify = function() {
  var sig = this.get('signature');
  var pubkey = this.get('pki_data');
  var buf = this.serializeForSig();
  return Message.verifyWithPubKey(pubkey, buf, sig);
};

// Helpers

function PEMtoDER(pem) {
  pem = pem.replace(/^-----END [^-]+-----$/gmi, '');
  var parts = pem.split(/-----BEGIN [^-]+-----/);
  return parts.map(function(part) {
    part = part.replace(/\s+/g, '');
    return new Buffer(part, 'base64');
  });
}

function PEMtoDERParam(pem, param) {
  var start = new RegExp('(?=-----BEGIN ' + param + '-----)', 'i');
  var end = new RegExp('^-----END ' + param + '-----$', 'gmi');
  pem = pem.replace(end, '');
  var parts = pem.split(start);
  return parts.map(function(part) {
    part = part.replace(/\s+/g, '');
    var type = /-----BEGIN ([^-]+)-----/.exec(part)[1];
    part = part.replace(/-----BEGIN ([^-]+)-----/g, '');
    if (type !== param) return;
    return new Buffer(part, 'base64');
  }).filter(Boolean);
}

function wrapText(text, cols) {
  var j = 0;
  var part = '';
  var parts = [];
  for (var i = 0; i < text.length; i++) {
    if (j === cols) {
      parts.push(part);
      j = 0;
      part = ''
      continue;
    }
    part += text[i];
    j++;
  }
  var total = parts.join('').length;
  if (total < text.length) {
    parts.push(text.slice(-(text.length - total)));
  }
  return parts.join('\n');
}

function DERtoPEM(der, type) {
  var type = type || 'UNKNOWN';
  return ''
    + '-----BEGIN ' + type + '-----'
    + wrapText(der.toString('base64'), 64)
    + '-----END ' + type + '-----';
}

module.exports = PayPro;
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";
const args = require("minimist")(process.argv.slice(2)),
      pkg = require("../package.json"),
      card = require("./card"),
      fs = require("fs-extra"),
      ejs = require("ejs"),
      strftime = require("strftime"),
      watch = require("./watch"),
      chalk = require("chalk");
const action = args._[0] // what are we trying to do?

switch(action) {
  case "in":
    card.waltzIn().then(() => {
      console.log("waltzed in. GO!");
    }).catch(onError);
    break;

  case "out":
    card.waltzOut().then(() => {
      console.log("waltzed out. Go work on something else.");
    }).catch(onError);
    break;

  case "watch":
    watch.watchAndUpdateAccordingly(args);
    break;

  case "init":
    card.cardInit(args).then(() => {
      console.log(`Created ${chalk.cyan('`.timecard.json`')}. Start working with ${chalk.cyan('`waltz in`')}!`);
    }).catch(onError);
    break;

  case "report":
    card.getCard().then((timecard) => {
      getReportTemplate(timecard.reportFormat || "default").then((contents) => {
        let ejs_data = card.getTimecardRenderDetails(timecard);
        let report = ejs.render(contents, ejs_data);
        if (args.print) {
          // push to stdout
          console.log(report);
        } else {
          // write to disk
          fs.writeFile(args.file || "report.html", report, (err) => {
            if (err) {
              onError(err);
            } else {
              console.log("Wrote to", args.file || "report.html");
            }
          });
        }
      }).catch(onError);
    }).catch(onError);
    break;

  case "ls":
  case "list":
  case "print":
    card.getCard().then((data) => {
      console.log(chalk.bold(chalk.green("Unpaid Hours") + " / " + chalk.red("Paid Hours") + ":"));
      let total_duration = totalDuration(data, (day, time) => {
        if (day.disabled) {
          console.log(`${chalk.red(day.date)}\t\t ${time.start} - ${time.end || "(no end)"}`)
        } else {
          console.log(`${chalk.green(day.date)}\t\t ${time.start} - ${time.end || "(no end)"}`)
        }
      });

      // the total time that it took, prettyprinted
      console.log(`About ${Math.floor(total_duration / 3600)} hours, ` +
        `${Math.floor(total_duration / 60) % 60} minutes, ` +
        `and ${total_duration % 60} seconds`
      )
      // if there is an hourly rate present, log out the total cost of the
      // project
      if (data.hourlyRate) {
        console.log(` = $${Math.round( 100 * (total_duration / 3600) * data.hourlyRate) / 100}`)
      }
    }).catch(onError);
    break;

  case "markpaid":
  case "paycheck":
    card.getCard().then((data) => {
      data.card.map((time) => {
        if (!time.disabled) {
          time.disabled = `${strftime(card.DAY_REPR)}\n${strftime(card.TIME_REPR)}`
        }
      });
      card.setCard(data).then(() => {
        console.log(chalk.cyan("All previous times are now disabled."));
      }).catch(onError);
    }).catch(onError);
    break;

  default:
    if (args.help || args.h) {
      showHelp();
    } else if (args.version || args.v) {
      showVersion();
    } else {
      showHelp(true);
    }
    break;
}

function touchDotTimewaltz() {
  fs.mkdirpSync(`${process.env.HOME}/.timewaltz/templates`);
}

// get the name of a report template
function getReportTemplate(filename) {
  return new Promise((resolve, reject) => {
    touchDotTimewaltz();
    if (filename.endsWith(".ejs")) {
      fs.readFile(filename, "utf8", (err, contents) => {
        if (err) {
          reject(err);
        } else {
          resolve(contents);
        }
      });
    } else {
      // use a supplied template
      return getReportTemplate(`${process.env.HOME}/.timewaltz/templates/${filename}.ejs`).then(resolve).catch(reject);
    }
  });
}

// given a timecard, calculate the total amount of time spent on the project.
function totalDuration(data, cbForEach) {
  return data.card.reduce((acc, day) => {
    return acc + day.times.reduce((acc, time) => {
      cbForEach && cbForEach(day, time);
      if (!day.disabled) {
        return acc + card.getDurationFor(day, time);
      } else {
        return acc;
      }
    }, 0);
  }, 0) / 1000 || 0;
}

// there was an error, somewhere
// catch it, and log it out.
function onError(error) {
  console.log(chalk.red(`Oh no! There was an error!\n${error.toString()}`));
}

function showVersion() {
  console.log(`Timewaltz version ${chalk.green(pkg.version)}`);
}

function showHelp() {
console.log(`
Record your project development time.

Set up waltz with \`waltz init\`, then \`waltz in\` or \`waltz out\` to
record start or end time, respectively. Alternatively, use \`waltz watch\` to
manage recording your time automatically. To see a list of all times, run
\`waltz ls\`, and to generate a fancy-looking invoice, use \`waltz report\`.

Commands
  waltz init
  waltz in
  waltz out
  waltz ls (aliased to \`waltz print\` and \`waltz list\`)
  waltz paycheck (will mark all current waltz ins / outs as paid)
  waltz report (by default, writes to report.html in the cwd)
    \`--print\` will print to stdout instead of writing to file.
    \`--file my_report.html\` can send to a different file.
  waltz watch
    \`--oldfile 1000\` amount of milliseconds required of inactivity to
                       be considered "waltzed out". (default = 10 minutes)

Options
    -h, --help              Show this help message
    -v, --version           Show the current timecard version
`.replace(/`(.*?)`/g, (match) => {
  return chalk.cyan(match); // highlight all of the commands
}));
}
<commit_msg>a few copy errors<commit_after>#!/usr/bin/env node
"use strict";
const args = require("minimist")(process.argv.slice(2)),
      pkg = require("../package.json"),
      card = require("./card"),
      fs = require("fs-extra"),
      ejs = require("ejs"),
      strftime = require("strftime"),
      watch = require("./watch"),
      chalk = require("chalk");
const action = args._[0] // what are we trying to do?

switch(action) {
  case "in":
    card.waltzIn().then(() => {
      console.log("Waltzed in. GO!");
    }).catch(onError);
    break;

  case "out":
    card.waltzOut().then(() => {
      console.log("Waltzed out. Go work on something else.");
    }).catch(onError);
    break;

  case "watch":
    watch.watchAndUpdateAccordingly(args);
    break;

  case "init":
    card.cardInit(args).then(() => {
      console.log(`Created ${chalk.cyan('`.timecard.json`')}. Start working with ${chalk.cyan('`waltz in`')}!`);
    }).catch(onError);
    break;

  case "report":
    card.getCard().then((timecard) => {
      getReportTemplate(timecard.reportFormat || "default").then((contents) => {
        let ejs_data = card.getTimecardRenderDetails(timecard);
        let report = ejs.render(contents, ejs_data);
        if (args.print) {
          // push to stdout
          console.log(report);
        } else {
          // write to disk
          fs.writeFile(args.file || "report.html", report, (err) => {
            if (err) {
              onError(err);
            } else {
              console.log("Wrote to", args.file || "report.html");
            }
          });
        }
      }).catch(onError);
    }).catch(onError);
    break;

  case "ls":
  case "list":
  case "print":
    card.getCard().then((data) => {
      console.log(chalk.bold(chalk.green("Unpaid Hours") + " / " + chalk.red("Paid Hours") + ":"));
      let total_duration = totalDuration(data, (day, time) => {
        if (day.disabled) {
          console.log(`${chalk.red(day.date)}\t\t ${time.start} - ${time.end || "(no end)"}`)
        } else {
          console.log(`${chalk.green(day.date)}\t\t ${time.start} - ${time.end || "(no end)"}`)
        }
      });

      // the total time that it took, prettyprinted
      console.log(`About ${Math.floor(total_duration / 3600)} hours, ` +
        `${Math.floor(total_duration / 60) % 60} minutes, ` +
        `and ${total_duration % 60} seconds`
      )
      // if there is an hourly rate present, log out the total cost of the
      // project
      if (data.hourlyRate) {
        console.log(` = $${Math.round( 100 * (total_duration / 3600) * data.hourlyRate) / 100}`)
      }
    }).catch(onError);
    break;

  case "markpaid":
  case "paycheck":
    card.getCard().then((data) => {
      data.card.map((time) => {
        if (!time.disabled) {
          time.disabled = `${strftime(card.DAY_REPR)}\n${strftime(card.TIME_REPR)}`
        }
      });
      card.setCard(data).then(() => {
        console.log(chalk.cyan("All previous times are now disabled."));
      }).catch(onError);
    }).catch(onError);
    break;

  default:
    if (args.help || args.h) {
      showHelp();
    } else if (args.version || args.v) {
      showVersion();
    } else {
      showHelp(true);
    }
    break;
}

function touchDotTimecard() {
  fs.mkdirpSync(`${process.env.HOME}/.timecard/templates`);
}

// get the name of a report template
function getReportTemplate(filename) {
  return new Promise((resolve, reject) => {
    touchDotTimecard();
    if (filename.endsWith(".ejs")) {
      fs.readFile(filename, "utf8", (err, contents) => {
        if (err) {
          reject(err);
        } else {
          resolve(contents);
        }
      });
    } else {
      // use a supplied template
      return getReportTemplate(`${process.env.HOME}/.timecard/templates/${filename}.ejs`).then(resolve).catch(reject);
    }
  });
}

// given a timecard, calculate the total amount of time spent on the project.
function totalDuration(data, cbForEach) {
  return data.card.reduce((acc, day) => {
    return acc + day.times.reduce((acc, time) => {
      cbForEach && cbForEach(day, time);
      if (!day.disabled) {
        return acc + card.getDurationFor(day, time);
      } else {
        return acc;
      }
    }, 0);
  }, 0) / 1000 || 0;
}

// there was an error, somewhere
// catch it, and log it out.
function onError(error) {
  console.log(chalk.red(`Oh no! There was an error!\n${error.toString()}`));
}

function showVersion() {
  console.log(`Waltz CLI version ${chalk.green(pkg.version)}`);
}

function showHelp() {
console.log(`
Record your project development time.

Set up waltz with \`waltz init\`, then \`waltz in\` or \`waltz out\` to
record start or end time, respectively. Alternatively, use \`waltz watch\` to
manage recording your time automatically. To see a list of all times, run
\`waltz ls\`, and to generate a fancy-looking invoice, use \`waltz report\`.

Commands
  waltz init
  waltz in
  waltz out
  waltz ls (aliased to \`waltz print\` and \`waltz list\`)
  waltz paycheck (will mark all current waltz ins / outs as paid)
  waltz report (by default, writes to report.html in the cwd)
    \`--print\` will print to stdout instead of writing to file.
    \`--file my_report.html\` can send to a different file.
  waltz watch
    \`--oldfile 1000\` amount of milliseconds required of inactivity to
                       be considered "waltzed out". (default = 10 minutes)

Options
    -h, --help              Show this help message
    -v, --version           Show the current timecard version
`.replace(/`(.*?)`/g, (match) => {
  return chalk.cyan(match); // highlight all of the commands
}));
}
<|endoftext|>


<commit_before>var parse = require('url').parse,
  querystring = require('querystring');

//
// TODO
//
module.exports = function resource(fn) {
  var routes, self = this;
  var paths;

  if (fn == undefined) {
    throw new Error('No handler provider requires a callback function');
  }

  paths = [];

  var app = {
    resource: function(path, obj) {
      obj._keys = [];
      obj._path = normalizePath(path, obj._keys);
      paths.push(obj);
    }
  }
  fn.call(this, app);

  return function resource(req, res, next) {
    // Match to a resource
    var method = req.method.toLowerCase();
    var url = parse(req.url);
    var pathname = url.pathname;
    for (var i = 0; i < paths.length; i++) {
      var path = paths[i]._path;
      var keys = paths[i]._keys;
      var matches = path.exec(pathname);

      if (matches) {
        // There is a match. Match parameters
        req.params = [];
        for (var j = 1, len = matches.length; j < len; ++j) {
          var key = keys[j - 1],
            val = typeof matches[j] === 'string' ? querystring.unescape(matches[j]) : matches[j];
          if (key) {
            req.params[key] = val;
          }
          else {
            req.params.push(val);
          }
        }

        // Check if the method is supported
        if (paths[i][method]) {
          // Execute the handler
          return paths[i][method](req, res, next);
        }
        else {
          switch(method) {
            case 'head' :
              method = 'get';
              paths[i][method](req, res, next);
              break;
            case 'options' :
              // TODO: Special handling for *

              // Find the supported methods
              var str = '';
              var supported = [];
              for (var name in paths[i]) {
                // TODO: Hate using underscores. There should be a better way to hide these
                if (name.charAt(0) != '_' && paths[i].hasOwnProperty(name)) {
                  str += name.toUpperCase() + ',';
                }
              }
              if(paths[i]['get']) {
                str += 'HEAD';
              }
              if(str.charAt(str.length - 1) == ',') {
                str = str.substring(0, str.length - 1);
              }

              res.writeHead(204, {
                'Allow' : str
              });
              res.end();
            default:
              // TODO: Should map to error handlers
              res.writeHead(405);
              res.end();
          }
        }
      }
    }
    next();
  };
}

// The following function is duplicated from
// https://github.com/senchalabs/connect/blob/master/lib/connect/middleware/router.js
// and hence the following copyright is included from the connect project.
//
//  (The MIT License)
//
//  Copyright (c) 2010 Sencha Inc.
//
//  Permission is hereby granted, free of charge, to any person obtaining
//  a copy of this software and associated documentation files (the
//  'Software'), to deal in the Software without restriction, including
//  without limitation the rights to use, copy, modify, merge, publish,
//  distribute, sublicense, and/or sell copies of the Software, and to
//  permit persons to whom the Software is furnished to do so, subject to
//  the following conditions:
//
//  The above copyright notice and this permission notice shall be
//  included in all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
//  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
function normalizePath(path, keys) {
  path = path
    .concat('/?')
    .replace(/\/\(/g, '(?:/')
    .replace(/(\/)?(\.)?:(\w+)(\?)?/g, function(_, slash, format, key, optional) {
    keys.push(key);
    slash = slash || '';
    return ''
      + (optional ? '' : slash)
      + '(?:'
      + (optional ? slash : '')
      + (format || '') + '([^/.]+))'
      + (optional || '');
  })
    .replace(/([\/.])/g, '\\$1')
    .replace(/\*/g, '(.+)');
  return new RegExp('^' + path + '$', 'i');
}

<commit_msg>Minor fixes<commit_after>var parse = require('url').parse,
  querystring = require('querystring');

/*
 * This is a connect handler for routing HTTP requests to apps.
 */
module.exports = function resource(fn) {
  var routes, self = this;
  var paths;

  if (fn == undefined) {
    throw new Error('No handler provider requires a callback function');
  }

  paths = [];

  var app = {
    resource: function(path, obj) {
      obj._keys = [];
      obj._path = normalizePath(path, obj._keys);
      paths.push(obj);
    }
  };
  fn.call(this, app);

  return function resource(req, res, next) {
    // Match to a resource
    var method = req.method.toLowerCase();
    var url = parse(req.url);
    var pathname = url.pathname;
    for (var i = 0; i < paths.length; i++) {
      var path = paths[i]._path;
      var keys = paths[i]._keys;
      var matches = path.exec(pathname);

      if (matches) {
        // There is a match. Match parameters
        req.params = [];
        for (var j = 1, len = matches.length; j < len; ++j) {
          var key = keys[j - 1],
            val = typeof matches[j] === 'string' ? querystring.unescape(matches[j]) : matches[j];
          if (key) {
            req.params[key] = val;
          }
          else {
            req.params.push(val);
          }
        }

        // Check if the method is supported
        if (paths[i][method]) {
          // Execute the handler
          return paths[i][method](req, res, next);
        }
        else {
          switch(method) {
            case 'head' :
              method = 'get';
              paths[i][method](req, res, next);
              break;
            case 'options' :
              // TODO: Special handling for *

              // Find the supported methods
              var str = '';
              var supported = [];
              for (var name in paths[i]) {
                // TODO: Hate using underscores. There should be a better way to hide these
                if (name.charAt(0) != '_' && paths[i].hasOwnProperty(name)) {
                  str += name.toUpperCase() + ',';
                }
              }
              if(paths[i]['get']) {
                str += 'HEAD';
              }
              if(str.charAt(str.length - 1) == ',') {
                str = str.substring(0, str.length - 1);
              }

              res.writeHead(204, {
                'Allow' : str
              });
              res.end();
              break;
            default:
              // TODO: Should map to error handlers
              res.writeHead(405);
              res.end();
          }
        }
      }
    }
    next();
  };
};

// The following function is duplicated from
// https://github.com/senchalabs/connect/blob/master/lib/connect/middleware/router.js
// and hence the following copyright is included from the connect project.
//
//  (The MIT License)
//
//  Copyright (c) 2010 Sencha Inc.
//
//  Permission is hereby granted, free of charge, to any person obtaining
//  a copy of this software and associated documentation files (the
//  'Software'), to deal in the Software without restriction, including
//  without limitation the rights to use, copy, modify, merge, publish,
//  distribute, sublicense, and/or sell copies of the Software, and to
//  permit persons to whom the Software is furnished to do so, subject to
//  the following conditions:
//
//  The above copyright notice and this permission notice shall be
//  included in all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
//  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
function normalizePath(path, keys) {
  path = path
    .concat('/?')
    .replace(/\/\(/g, '(?:/')
    .replace(/(\/)?(\.)?:(\w+)(\?)?/g, function(_, slash, format, key, optional) {
    keys.push(key);
    slash = slash || '';
    return ''
      + (optional ? '' : slash)
      + '(?:'
      + (optional ? slash : '')
      + (format || '') + '([^/.]+))'
      + (optional || '');
  })
    .replace(/([\/.])/g, '\\$1')
    .replace(/\*/g, '(.+)');
  return new RegExp('^' + path + '$', 'i');
}

<|endoftext|>


<commit_before>const fs = require("fs");
const _ = require("lodash");
const path = require("path");
const findRoot = require("find-root");

/**
 * Receives a camel or pascal cased string, hyphenates it and makes it lowercase.
 * E.g.  myString -> my-string,   MyString -> my-string
 * @param {string} str The string to hyphenate.
 * @returns A hyphenated string in lowercase.
 */
function hyphenate(str) {
    return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}

/**
 * Transforms a camelCased string into PascalCase.
 * @param {string} str A camelCased string.
 * @returns A PascalCased string.
 */
function pascalCase(str) {
    return str.slice(0, 1).toUpperCase() + str.substr(1);
}

/**
 * Checks if a string is in hyphenated format.
 * @param {string} str A string to check.
 * @returns True if the string is in hyphenated-syntax.
 */
function isHyphenated(str) {
    return /(-)+/g.test(str);
}

/**
 * Gets the package.json file.
 * @returns A package.json object.
 */
function getPkg() {
    var root = findRoot(process.cwd());
    return root;
}

/**
 * Gets the application name.
 * @returns A string with the application name in hyphenated syntax.
 */
function getAppName() {
    return require(path.join(getPkg(), "package.json")).name;
}

/**
 * Gets the decorator path relative to src folder.
 * @param {string} [path] An optional path to use for calculating decorator path.
 * @returns A relative to src folder path string for the decorator.
 */
function getDecoratorPath(path) {
    var rootPath = getPkg();
    var srcPath = process.cwd().replace(rootPath, "");
    var paths = srcPath.split("/").slice(2);
    var decoratorPath = "";
    if (paths.length === 0) {
        decoratorPath = "./decorators/decorators";
    } else {
        for (var i = 0; i < paths.length; i++) {
            decoratorPath = decoratorPath.concat("../");
        }
        decoratorPath = decoratorPath.concat("decorators/decorators");
    }

    return decoratorPath;
}

/**
 * Reads all templates inside a template folder.
 * @param {string} relPath Relative path from the templates folder.
 * @returns An object with the name of the template as a key and the content as the value.
 */
function readTemplates(relPath) {
    var fullPath = path.join(__dirname, "..", "templates", relPath);
    var files = fs.readdirSync(fullPath);
    var tpls = {};

    files.forEach(function (file) {
        var filePath = path.join(fullPath, file);
        var content = fs.readFileSync(filePath, "utf8");
        tpls[file] = content;
    });

    return tpls;
}

/**
 * Reads a specific template inside a template folder.
 * @param {string} relPath Relative path from the templates folder.
 * @param {string} templateName Template name to look for.
 * @returns An object with the name of the template as a key and the content as the value.
 */
function readTemplate(relPath, templateName) {
    var filePath = path.join(__dirname, "..", "templates", relPath, templateName);

    var content = fs.readFileSync(filePath, "utf8");
    var template = {};
    template[templateName] = content;

    return template;
}

/**
 * Compiles the templates with the given data.
 * @param {Object} templates An object containing the template name as the key and content as value.
 * @param {Object} values Any values needed for the templates.
 * @param {string} name Hyphenated name that the files will have.
 * @param {boolean} [isService] If is service template names will be PascalCased.
 * @returns The compiled templates object.
 */
function compileTemplates(templates, values, name, isService) {
    var tpls = {};
    for (var key in templates) {
        var tpl = _.template(templates[key]);

        if (isService) {
            if (key.indexOf("Iname") > -1) {
                key = "I" + pascalCase(name) + "Service.ts";
            }
        }

        key = key.replace("name", hyphenate(name));
        key = key.replace("_", "");
        tpls[key] = tpl(values);
    }

    return tpls;
}

/**
 * Writes the template files in the specified folder.
 * @param {Object} templates An object containing the template name as the key and content as value.
 * @param {string} folder Folder in which the files will be written.
 */
function writeFiles(templates, folder) {
    for (var tplName in templates) {
        fs.writeFileSync(path.join(folder, tplName), templates[tplName], "utf8");
    }
}

module.exports = {
    hyphenate: hyphenate,
    pascalCase: pascalCase,
    isHyphenated: isHyphenated,
    getAppName: getAppName,
    getDecoratorPath: getDecoratorPath,
    readTemplates: readTemplates,
    readTemplate: readTemplate,
    compileTemplates: compileTemplates,
    writeFiles: writeFiles
};<commit_msg>Expose camelCase as util function<commit_after>const fs = require("fs");
const _ = require("lodash");
const path = require("path");
const findRoot = require("find-root");

/**
 * Receives a camel or pascal cased string, hyphenates it and makes it lowercase.
 * E.g.  myString -> my-string,   MyString -> my-string
 * @param {string} str The string to hyphenate.
 * @returns A hyphenated string in lowercase.
 */
function hyphenate(str) {
    return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}

/**
 * Transforms a camelCased string into PascalCase.
 * @param {string} str A camelCased string.
 * @returns A PascalCased string.
 */
function pascalCase(str) {
    return str.slice(0, 1).toUpperCase() + str.substr(1);
}

/**
 * Checks if a string is in hyphenated format.
 * @param {string} str A string to check.
 * @returns True if the string is in hyphenated-syntax.
 */
function isHyphenated(str) {
    return /(-)+/g.test(str);
}

/**
 * Gets the package.json file.
 * @returns A package.json object.
 */
function getPkg() {
    var root = findRoot(process.cwd());
    return root;
}

/**
 * Gets the application name.
 * @returns A string with the application name in hyphenated syntax.
 */
function getAppName() {
    return require(path.join(getPkg(), "package.json")).name;
}

/**
 * Gets the decorator path relative to src folder.
 * @param {string} [path] An optional path to use for calculating decorator path.
 * @returns A relative to src folder path string for the decorator.
 */
function getDecoratorPath(path) {
    var rootPath = getPkg();
    var srcPath = process.cwd().replace(rootPath, "");
    var paths = srcPath.split("/").slice(2);
    var decoratorPath = "";
    if (paths.length === 0) {
        decoratorPath = "./decorators/decorators";
    } else {
        for (var i = 0; i < paths.length; i++) {
            decoratorPath = decoratorPath.concat("../");
        }
        decoratorPath = decoratorPath.concat("decorators/decorators");
    }

    return decoratorPath;
}

/**
 * Reads all templates inside a template folder.
 * @param {string} relPath Relative path from the templates folder.
 * @returns An object with the name of the template as a key and the content as the value.
 */
function readTemplates(relPath) {
    var fullPath = path.join(__dirname, "..", "templates", relPath);
    var files = fs.readdirSync(fullPath);
    var tpls = {};

    files.forEach(function (file) {
        var filePath = path.join(fullPath, file);
        var content = fs.readFileSync(filePath, "utf8");
        tpls[file] = content;
    });

    return tpls;
}

/**
 * Reads a specific template inside a template folder.
 * @param {string} relPath Relative path from the templates folder.
 * @param {string} templateName Template name to look for.
 * @returns An object with the name of the template as a key and the content as the value.
 */
function readTemplate(relPath, templateName) {
    var filePath = path.join(__dirname, "..", "templates", relPath, templateName);

    var content = fs.readFileSync(filePath, "utf8");
    var template = {};
    template[templateName] = content;

    return template;
}

/**
 * Compiles the templates with the given data.
 * @param {Object} templates An object containing the template name as the key and content as value.
 * @param {Object} values Any values needed for the templates.
 * @param {string} name Hyphenated name that the files will have.
 * @param {boolean} [isService] If is service template names will be PascalCased.
 * @returns The compiled templates object.
 */
function compileTemplates(templates, values, name, isService) {
    var tpls = {};
    for (var key in templates) {
        var tpl = _.template(templates[key]);

        if (isService) {
            if (key.indexOf("Iname") > -1) {
                key = "I" + pascalCase(name) + "Service.ts";
            }
        }

        key = key.replace("name", hyphenate(name));
        key = key.replace("_", "");
        tpls[key] = tpl(values);
    }

    return tpls;
}

/**
 * Writes the template files in the specified folder.
 * @param {Object} templates An object containing the template name as the key and content as value.
 * @param {string} folder Folder in which the files will be written.
 */
function writeFiles(templates, folder) {
    for (var tplName in templates) {
        fs.writeFileSync(path.join(folder, tplName), templates[tplName], "utf8");
    }
}

module.exports = {
    hyphenate: hyphenate,
    pascalCase: pascalCase,
    camelCase: _.camelCase,
    isHyphenated: isHyphenated,
    getAppName: getAppName,
    getDecoratorPath: getDecoratorPath,
    readTemplates: readTemplates,
    readTemplate: readTemplate,
    compileTemplates: compileTemplates,
    writeFiles: writeFiles
};<|endoftext|>


<commit_before>/* eslint no-process-env: 0, no-console: 0, max-statements: 0 */
'use strict';

var argv         = require('yargs').argv,
    gulpHelp     = require('gulp-help'),
    istanbul     = require('gulp-istanbul'),
    isparta      = require('isparta'),
    mocha        = require('gulp-mocha'),
    util         = require('gulp-util'),
    _            = require('lodash'),
    styleguide   = require('./godaddy-style'),
    notifier     = require('node-notifier'),
    del          = require('del'),
    once         = require('one-time'),
    sonar        = require('gulp-sonar'),
    spawn        = require('child_process').spawn;

var SOURCE_FILES = 'lib/**/*.js',
    UNIT_FILES   = 'test/unit/**/*.js',
    INTEGRATION_FILES = 'test/integration/**/*.js';

var mochaIcon = require.resolve('../assets/mocha.png');

module.exports = function (gulp, options) {
  gulp = gulpHelp(gulp);

  options = _.defaultsDeep(options || {}, {
    reporter: process.env.MOCHA_REPORTER || 'spec',
    sourceFiles: SOURCE_FILES,
    unitTestFiles: UNIT_FILES,
    integrationTestFiles: INTEGRATION_FILES,
    base: 'test',
    istanbul: {
      includeUntested: true
    },
    es6: false,
    lint: {
      jscs: {}
    }
  });

  if (options.es6) {
    options.lint.es6 = true;
    options.istanbul.instrumenter = isparta.Instrumenter;
  }

  var lintOptions = options.lint;
  lintOptions.files = lintOptions.files || [
    options.sourceFiles,
    options.unitTestFiles,
    options.integrationTestFiles,
    'index.js',
    'main.js',
    'gulpfile.js',
    'gulpfile.babel.js',
    'config/**/*.json',
    'config/**/*.js',
    'lib/**/*.json',
    'test/**/*.json'
  ];

  lintOptions.jscs.files = lintOptions.jscs.files || [
    options.sourceFiles,
    options.unitTestFiles,
    options.integrationTestFiles,
    'index.js',
    'main.js',
    'gulpfile.js',
    'gulpfile.babel.js',
    'config/**/*.js'
  ];

  styleguide(gulp, lintOptions);

  gulp.task('default', 'Use --no-notify to turn off OS notifications of success and failure.', ['test']);

  gulp.task('unit', 'Run unit tests', function (cb) {
    runMocha(options.unitTestFiles, cb);
  });

  gulp.task('integration', 'Run integration tests', function (cb) {
    runWithDependencyProcess(runMocha, options.integrationTestFiles, cb);
  });

  gulp.task('unit-coverage', 'Run all the integration tests with code coverage', function (cb) {
    runIstanbul(options.unitTestFiles, cb);
  });

  gulp.task('integration-coverage', 'Run all the integration tests with code coverage', function (cb) {
    runWithDependencyProcess(runIstanbul, options.integrationTestFiles, cb);
  });

  gulp.task('mocha', 'Run all tests without code coverage', function (cb) {
    runWithDependencyProcess(runMocha, [options.unitTestFiles, options.integrationTestFiles], cb);
  });

  gulp.task('test', 'Run all the tests with code coverage', ['lint'], function (cb) {
    runWithDependencyProcess(runIstanbul, [options.unitTestFiles, options.integrationTestFiles], cb);
  });

  var description = 'Watch files for changes and invoke mocha when one changes.';
  gulp.task('watch', description, function () {
    var watchFiles = [options.sourceFiles, options.unitTestFiles, options.integrationTestFiles];
    var watcher = gulp.watch(options.watchFiles || watchFiles);
    var testFiles = [options.unitTestFiles, options.integrationTestFiles];

    runMocha(testFiles);

    watcher.on('change', function (event) {
      notify({
        title: 'Changed: ' + event.path,
        message: 'Running mocha...'
      });

      runMocha(testFiles);
    });
  });

  gulp.task('sonar', 'Run sonar', sonarTask);

  gulp.task('clean', 'Clean up unnecessary files', function (cb) {
    del(options.filesToClean || ['./build', '.sonar']).then(function () {
      cb();
    });
  });

  gulp.task('build', 'Run the clean and test tasks', ['clean', 'test']);

  gulp.task('full', 'Run the build and sonar tasks', ['build'], sonarTask);

  function runMocha(files, cb) {
    var done = once(cb || function () {});

    return gulp.src(files, options)
      .pipe(mocha(_.merge({
        reporter: options.reporter,
        bail: Boolean(argv.bail)
      }, options.mocha)))
      .on('error', function (err) {
        notify({
          title: 'Test error!',
          message: 'There was an error in the tests for ' + files + '.\n' + (err && err.stack || err),
          icon: mochaIcon
        });

        done(err);
      })
      .once('end', function () {
        notify({
          title: 'Tests complete!',
          message: 'The tests for ' + files + ' have finished.',
          icon: mochaIcon
        });

        done();
      });
  }

  function runIstanbul(files, cb) {
    return gulp.src(options.sourceFiles)
      .pipe(istanbul(options.istanbul))
      .pipe(istanbul.hookRequire())
      .on('finish', function () {
        runMocha(files, cb)
          .pipe(istanbul.writeReports(_.defaults(options.istanbulReports || {}, {
            dir: './build/coverage',
            reportOpts: { dir: './build/coverage' },
            reporters: ['lcov', 'json', 'text', 'text-summary', 'cobertura']
          })))
          .on('error', function (err) {
            notify({
              title: 'Istanbul test error!',
              message: 'There was an error in the tests for ' + files + '.\n' + (err && err.stack || err)
            });
          })
          .once('end', function () {
            notify({
              title: 'Istanbul tests complete!',
              message: 'The tests for ' + files + ' have finished.'
            });
          });
      });
  }

  function sonarTask() {
    var config = _.merge({
      sonar: {
        host: {
          url: argv.sonarUrl || 'http://localhost:9000'
        },
        sources: 'lib',
        language: 'js',
        sourceEncoding: 'UTF-8',
        javascript: {
          lcov: {
            reportPath: './build/coverage/lcov.info'
          }
        }
      }
    }, options.sonar);

    return gulp.src('thisFileDoesNotExist.js', { read: false })
      .pipe(sonar(config))
      .on('error', util.log);
  }

  function runWithDependencyProcess(runner, files, cb) {
    var opts = options.dependencyProcess;
    if (!opts) {
      return void runner(files, cb);
    }
    return void createChildProcess(opts, function (err, child) {
      if (err) throw err;
      runner(files, function (err1) {
        child.send('close');
        cb(err1);
      })
    });
  }

  function createChildProcess(opts, cb) {
    notify({ title: opts.name, message: 'Starting...' });
    var child = spawn(opts.command, opts.args, { stdio: [process.stdin, process.stdout, process.stderr, 'ipc'] });
    child.on('message', function (data) {
      if (data === 'started') {
        return void cb(null, child);
      }
      return void 0;
    });
    child.on('error', function (err) {
      notify({ title: opts.name, message: 'Error: ' + JSON.stringify(err) });
    });
    child.on('exit', function () {
      notify({ title: opts.name, message: 'Exited' });
    });
  }
};

function notify(options) {
  util.log(options.title + ': ' + options.message);
  if (!argv.noNotify) {
    notifier.notify(options);
  }
}
<commit_msg>allow using arrays for unitTestFiles, integrationTestFiles, and sourceFiles<commit_after>/* eslint no-process-env: 0, no-console: 0, max-statements: 0 */
'use strict';

var argv         = require('yargs').argv,
    gulpHelp     = require('gulp-help'),
    istanbul     = require('gulp-istanbul'),
    isparta      = require('isparta'),
    mocha        = require('gulp-mocha'),
    util         = require('gulp-util'),
    _            = require('lodash'),
    styleguide   = require('./godaddy-style'),
    notifier     = require('node-notifier'),
    del          = require('del'),
    once         = require('one-time'),
    sonar        = require('gulp-sonar'),
    spawn        = require('child_process').spawn;

var SOURCE_FILES = ['lib/**/*.js'],
    UNIT_FILES   = ['test/unit/**/*.js'],
    INTEGRATION_FILES = ['test/integration/**/*.js'];

var mochaIcon = require.resolve('../assets/mocha.png');

module.exports = function (gulp, options) {
  gulp = gulpHelp(gulp);

  options = _.defaultsDeep(options || {}, {
    reporter: process.env.MOCHA_REPORTER || 'spec',
    sourceFiles: SOURCE_FILES,
    unitTestFiles: UNIT_FILES,
    integrationTestFiles: INTEGRATION_FILES,
    base: 'test',
    istanbul: {
      includeUntested: true
    },
    es6: false,
    lint: {
      jscs: {}
    }
  });

  if (options.es6) {
    options.lint.es6 = true;
    options.istanbul.instrumenter = isparta.Instrumenter;
  }

  var lintOptions = options.lint;
  lintOptions.files = _.flattenDeep(lintOptions.files || [
    options.sourceFiles,
    options.unitTestFiles,
    options.integrationTestFiles,
    'index.js',
    'main.js',
    'gulpfile.js',
    'gulpfile.babel.js',
    'config/**/*.json',
    'config/**/*.js',
    'lib/**/*.json',
    'test/**/*.json'
  ]);

  lintOptions.jscs.files = _.flattenDeep(lintOptions.jscs.files || [
    options.sourceFiles,
    options.unitTestFiles,
    options.integrationTestFiles,
    'index.js',
    'main.js',
    'gulpfile.js',
    'gulpfile.babel.js',
    'config/**/*.js'
  ]);

  styleguide(gulp, lintOptions);

  gulp.task('default', 'Use --no-notify to turn off OS notifications of success and failure.', ['test']);

  gulp.task('unit', 'Run unit tests', function (cb) {
    runMocha(options.unitTestFiles, cb);
  });

  gulp.task('integration', 'Run integration tests', function (cb) {
    runWithDependencyProcess(runMocha, options.integrationTestFiles, cb);
  });

  gulp.task('unit-coverage', 'Run all the unit tests with code coverage', function (cb) {
    runIstanbul(options.unitTestFiles, cb);
  });

  gulp.task('integration-coverage', 'Run all the integration tests with code coverage', function (cb) {
    runWithDependencyProcess(runIstanbul, options.integrationTestFiles, cb);
  });

  gulp.task('mocha', 'Run all tests without code coverage', function (cb) {
    runWithDependencyProcess(runMocha, _.flattenDeep([options.unitTestFiles, options.integrationTestFiles]), cb);
  });

  gulp.task('test', 'Run all the tests with code coverage', ['lint'], function (cb) {
    runWithDependencyProcess(runIstanbul, _.flattenDeep([options.unitTestFiles, options.integrationTestFiles]), cb);
  });

  var description = 'Watch files for changes and invoke mocha when one changes.';
  gulp.task('watch', description, function () {
    var watchFiles = _.flattenDeep([options.sourceFiles, options.unitTestFiles, options.integrationTestFiles]);
    var watcher = gulp.watch(options.watchFiles || watchFiles);
    var testFiles = _.flattenDeep([options.unitTestFiles, options.integrationTestFiles]);

    runMocha(testFiles);

    watcher.on('change', function (event) {
      notify({
        title: 'Changed: ' + event.path,
        message: 'Running mocha...'
      });

      runMocha(testFiles);
    });
  });

  gulp.task('sonar', 'Run sonar', sonarTask);

  gulp.task('clean', 'Clean up unnecessary files', function (cb) {
    del(options.filesToClean || ['./build', '.sonar']).then(function () {
      cb();
    });
  });

  gulp.task('build', 'Run the clean and test tasks', ['clean', 'test']);

  gulp.task('full', 'Run the build and sonar tasks', ['build'], sonarTask);

  function runMocha(files, cb) {
    var done = once(cb || function () {});

    return gulp.src(files, options)
      .pipe(mocha(_.merge({
        reporter: options.reporter,
        bail: Boolean(argv.bail)
      }, options.mocha)))
      .on('error', function (err) {
        notify({
          title: 'Test error!',
          message: 'There was an error in the tests for ' + files + '.\n' + (err && err.stack || err),
          icon: mochaIcon
        });

        done(err);
      })
      .once('end', function () {
        notify({
          title: 'Tests complete!',
          message: 'The tests for ' + files + ' have finished.',
          icon: mochaIcon
        });

        done();
      });
  }

  function runIstanbul(files, cb) {
    return gulp.src(options.sourceFiles)
      .pipe(istanbul(options.istanbul))
      .pipe(istanbul.hookRequire())
      .on('finish', function () {
        runMocha(files, cb)
          .pipe(istanbul.writeReports(_.defaults(options.istanbulReports || {}, {
            dir: './build/coverage',
            reportOpts: { dir: './build/coverage' },
            reporters: ['lcov', 'json', 'text', 'text-summary', 'cobertura']
          })))
          .on('error', function (err) {
            notify({
              title: 'Istanbul test error!',
              message: 'There was an error in the tests for ' + files + '.\n' + (err && err.stack || err)
            });
          })
          .once('end', function () {
            notify({
              title: 'Istanbul tests complete!',
              message: 'The tests for ' + files + ' have finished.'
            });
          });
      });
  }

  function sonarTask() {
    var config = _.merge({
      sonar: {
        host: {
          url: argv.sonarUrl || 'http://localhost:9000'
        },
        sources: 'lib',
        language: 'js',
        sourceEncoding: 'UTF-8',
        javascript: {
          lcov: {
            reportPath: './build/coverage/lcov.info'
          }
        }
      }
    }, options.sonar);

    return gulp.src('thisFileDoesNotExist.js', { read: false })
      .pipe(sonar(config))
      .on('error', util.log);
  }

  function runWithDependencyProcess(runner, files, cb) {
    var opts = options.dependencyProcess;
    if (!opts) {
      return void runner(files, cb);
    }
    return void createChildProcess(opts, function (err, child) {
      if (err) throw err;
      runner(files, function (err1) {
        child.send('close');
        cb(err1);
      })
    });
  }

  function createChildProcess(opts, cb) {
    notify({ title: opts.name, message: 'Starting...' });
    var child = spawn(opts.command, opts.args, { stdio: [process.stdin, process.stdout, process.stderr, 'ipc'] });
    child.on('message', function (data) {
      if (data === 'started') {
        return void cb(null, child);
      }
      return void 0;
    });
    child.on('error', function (err) {
      notify({ title: opts.name, message: 'Error: ' + JSON.stringify(err) });
    });
    child.on('exit', function () {
      notify({ title: opts.name, message: 'Exited' });
    });
  }
};

function notify(options) {
  util.log(options.title + ': ' + options.message);
  if (!argv.noNotify) {
    notifier.notify(options);
  }
}
<|endoftext|>