<commit_before>#!/usr/bin/env node

process.title = 'npmvsyarn';

let prettyMs = require('pretty-ms'),
    clc = require('cli-color'),
    sh = require('shelljs'),
    Table = require('cli-table2');

let packageName = process.argv[2];

// Move existing node_modules (if any) to temp
console.log('🛀 Saving existing node_modules (if any) to temp');
if (sh.test('-d', 'node_modules')) {
    sh.mv('node_modules', 'temp_npmvsyarn_node_modules');
}

// Check if yarn exists, if not install it.
console.log('🕵 Checking if ' + clc.blue('yarn') + ' is installed...');

if (!sh.which('yarn')) {
    console.log('🤕 ' + clc.blue('yarn') + ' command not found in a global scope.');
    console.log('😴 Installing ' + clc.blue('yarn') + '...');
    sh.exec('npm install -g yarn', { silent: true });
} else {
    console.log('👯 ' + clc.blue('yarn') + ' exists in a global scope');
}

// Install module using yarn add.
console.log('🛀 Adding ' + clc.magenta(packageName) + ' with ' + clc.blue('yarn'));
let timeStamp = Date.now();
sh.exec('yarn add ' + packageName, { silent: true });
let yarnAddTime = Date.now() - timeStamp;
console.log('🚀 Time taken by yarn to add ' + clc.green(prettyMs(yarnAddTime)) + '');

// Uninstall module using yarn remove.
console.log('🛀 Removing ' + clc.magenta(packageName) + ' with ' + clc.blue('yarn'));
timeStamp = Date.now();
sh.exec('yarn remove ' + packageName, { silent: true });
let yarnRemoveTime = Date.now() - timeStamp;
console.log('🚀 Time taken by yarn to remove ' + clc.red(prettyMs(yarnRemoveTime)) + '');
sh.rm('-rf', 'node_modules');

// Install module using npm install.
timeStamp = Date.now();
console.log('🛀 Installing ' + clc.magenta(packageName) + ' with ' + clc.blue('npm'));
sh.exec('npm install ' + packageName, { silent: true });
let npmAddTime = Date.now() - timeStamp;
console.log('🚀 Time taken by npm to install ' + clc.green(prettyMs(npmAddTime)) + '');

// Uninstall module using npm uninstall.
console.log('🛀 Removing ' + clc.magenta(packageName) + ' with ' + clc.blue('npm'));
timeStamp = Date.now();
sh.exec('npm uninstall ' + packageName, { silent: true });
let npmRemoveTime = Date.now() - timeStamp;
console.log('🚀 Time taken by npm to uninstall ' + clc.red(prettyMs(npmRemoveTime)) + '');

// Display results in a table.
var table = new Table({
    head: ['Package Manager', 'Add', 'Remove'],
    colWidths: [20, 10, 10],
    style: {
        head: ['yellow']
    }
});

console.log('🛀 Cleaning up');
sh.rm('-rf', 'node_modules');
if (sh.test('-d', 'temp_npmvsyarn_node_modules')) {
    sh.mv('temp_npmvsyarn_node_modules', 'node_modules');
}

table.push(
    ['npm', prettyMs(npmAddTime), prettyMs(npmRemoveTime)], ['yarn', prettyMs(yarnAddTime), prettyMs(yarnRemoveTime)]
);

console.log(table.toString());
<commit_msg>Add optional install for current working project if package name is not provided.<commit_after>#!/usr/bin/env node

process.title = 'npmvsyarn';

let prettyMs = require('pretty-ms'),
    clc = require('cli-color'),
    sh = require('shelljs'),
    Table = require('cli-table2');

let packageName = process.argv[2];

// Move existing node_modules (if any) to temp
console.log('🛀 Saving existing node_modules (if any) to temp');
if (sh.test('-d', 'node_modules')) {
    sh.mv('node_modules', 'temp_npmvsyarn_node_modules');
}

// Check if yarn exists, if not install it.
console.log('🕵 Checking if ' + clc.blue('yarn') + ' is installed...');

if (!sh.which('yarn')) {
    console.log('🤕 ' + clc.blue('yarn') + ' command not found in a global scope.');
    console.log('😴 Installing ' + clc.blue('yarn') + '...');
    sh.exec('npm install -g yarn', { silent: true });
} else {
    console.log('👯 ' + clc.blue('yarn') + ' exists in a global scope');
}

// Check if package name is provided
if (!packageName) {

    console.log('Running benchmark for current project');

    // Check if package.json exists
    if (!sh.test('-f', 'package.json')) {
        console.log('package.json does not exist');
        console.log('Exit.');
        process.exit(1);
    }

    // Install module using yarn add.
    console.log('🛀 Installing with ' + clc.blue('yarn'));
    let timeStamp = Date.now();
    sh.exec('yarn ', { silent: true });
    let yarnAddTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by yarn to add ' + clc.green(prettyMs(yarnAddTime)) + '');

    // Clear node_modules
    sh.rm('-rf', 'node_modules');

    // Install module using npm install.
    timeStamp = Date.now();
    console.log('🛀 Installing with ' + clc.blue('npm'));
    sh.exec('npm install ', { silent: true });
    let npmAddTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by npm to install ' + clc.green(prettyMs(npmAddTime)) + '');

} else {

    // Install module using yarn add.
    console.log('🛀 Adding ' + clc.magenta(packageName) + ' with ' + clc.blue('yarn'));
    let timeStamp = Date.now();
    sh.exec('yarn add ' + packageName, { silent: true });
    let yarnAddTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by yarn to add ' + clc.green(prettyMs(yarnAddTime)) + '');

    // Uninstall module using yarn remove.
    console.log('🛀 Removing ' + clc.magenta(packageName) + ' with ' + clc.blue('yarn'));
    timeStamp = Date.now();
    sh.exec('yarn remove ' + packageName, { silent: true });
    let yarnRemoveTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by yarn to remove ' + clc.red(prettyMs(yarnRemoveTime)) + '');
    sh.rm('-rf', 'node_modules');

    // Install module using npm install.
    timeStamp = Date.now();
    console.log('🛀 Installing ' + clc.magenta(packageName) + ' with ' + clc.blue('npm'));
    sh.exec('npm install ' + packageName, { silent: true });
    let npmAddTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by npm to install ' + clc.green(prettyMs(npmAddTime)) + '');

    // Uninstall module using npm uninstall.
    console.log('🛀 Removing ' + clc.magenta(packageName) + ' with ' + clc.blue('npm'));
    timeStamp = Date.now();
    sh.exec('npm uninstall ' + packageName, { silent: true });
    let npmRemoveTime = Date.now() - timeStamp;
    console.log('🚀 Time taken by npm to uninstall ' + clc.red(prettyMs(npmRemoveTime)) + '');
}

// Display results in a table.
var table = new Table({
    head: ['Package Manager', 'Add', 'Remove'],
    colWidths: [20, 10, 10],
    style: {
        head: ['yellow']
    }
});

console.log('🛀 Cleaning up');
sh.rm('-rf', 'node_modules');
if (sh.test('-d', 'temp_npmvsyarn_node_modules')) {
    sh.mv('temp_npmvsyarn_node_modules', 'node_modules');
}

table.push(
    ['npm', prettyMs(npmAddTime), prettyMs(npmRemoveTime)], ['yarn', prettyMs(yarnAddTime), prettyMs(yarnRemoveTime)]
);

console.log(table.toString());
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const Promise = require('bluebird');
const debug = require('../src/util/debug')('bin:schedule');
const connection = require('../src/mongo/connection');

// Config
const config = require('../src/config/config').globalConfig;
const sources = Object.keys(config.source);
const aggregations = Object.keys(config.aggregation);
console.log(sources);
console.log(aggregations);

const syncHistory = require('../src/mongo/models/syncHistory');
const schedulerLog = require('../src/mongo/models/schedulerLog');

const processScheduler = require('process-scheduler');

Promise.coroutine(function* () {
    //yield connection();
    const schedulerConfig = {
        threads: 4
    };

    const schedule = [];
    // Create configuration
        for (const collection of sources) {
            schedule.push({
                id: 'source_copy_' + collection,
                worker: path.join(__dirname, '../src/source/copyWorker.js'),
                immediate: false,
                cronRule: config.source[collection].copyCronRule,
                deps: [],
                arg: config.source[collection]
            });
            schedule.push({
                id: 'source_remove_' + collection,
                worker: path.join(__dirname, '../src/source/removeWorker.js'),
                immediate: false,
                cronRule: config.source[collection].removeCronRule,
                deps: [],
                noConcurrency: [],
                arg: config.source[collection]
            });
        }

        for (const collection of aggregations) {
            let aggId = 'aggregation_' + collection;
            schedule.push({
                id: aggId,
                worker: path.join(__dirname, '../src/aggregation/worker.js'),
                immediate: false,
                arg: collection
            });

            let sources = Object.keys(config.aggregation[collection].sources);
            for(const source of sources) {
                let s = schedule.find(s => s.id === 'source_copy_' + source);
                if(s) {
                    s.deps.push(aggId);
                    s.noConcurrency.push(aggId);
                }
                s = schedule.find(s => s.id === 'source_remove_' + source);
                if(s) {
                    s.deps.push(aggId);
                    s.noConcurrency.push(aggId);
                }
            }
        }

    console.log(schedule);

})();<commit_msg>fix scheduler config generation<commit_after>'use strict';

const path = require('path');
const Promise = require('bluebird');
const debug = require('../src/util/debug')('bin:schedule');
const connection = require('../src/mongo/connection');

// Config
const config = require('../src/config/config').globalConfig;
const sources = Object.keys(config.source);
const aggregations = Object.keys(config.aggregation);
console.log(sources);
console.log(aggregations);

const syncHistory = require('../src/mongo/models/syncHistory');
const schedulerLog = require('../src/mongo/models/schedulerLog');

const processScheduler = require('process-scheduler');

Promise.coroutine(function* () {
    //yield connection();
    const schedulerConfig = {
        threads: 4
    };

    const schedule = [];
    // Create configuration
        for (const collection of sources) {
            schedule.push({
                id: 'source_copy_' + collection,
                worker: path.join(__dirname, '../src/source/copyWorker.js'),
                immediate: false,
                cronRule: config.source[collection].copyCronRule,
                deps: [],
                noConcurrency: [],
                arg: config.source[collection]
            });
            schedule.push({
                id: 'source_remove_' + collection,
                worker: path.join(__dirname, '../src/source/removeWorker.js'),
                immediate: false,
                cronRule: config.source[collection].removeCronRule,
                deps: [],
                noConcurrency: [],
                arg: config.source[collection]
            });
        }

        for (const collection of aggregations) {
            let aggId = 'aggregation_' + collection;
            schedule.push({
                id: aggId,
                worker: path.join(__dirname, '../src/aggregation/worker.js'),
                immediate: false,
                arg: collection
            });

            let sources = Object.keys(config.aggregation[collection].sources);
            for(const source of sources) {
                let s = schedule.find(s => s.id === 'source_copy_' + source);
                if(s) {
                    s.deps.push(aggId);
                    s.noConcurrency.push(aggId);
                }
                s = schedule.find(s => s.id === 'source_remove_' + source);
                if(s) {
                    s.deps.push(aggId);
                    s.noConcurrency.push(aggId);
                }
            }
        }

    console.log(schedule);

})();<|endoftext|>


<commit_before>var express = require('express')
var browserify = require('browserify')
var React = require('react')
var jsx = require('node-jsx')
var app = express()

jsx.install()

var StatusBox = require('../views/index.jsx')

app.use('/bundle.js', function (req, res) {
  res.setHeader('content-type', 'application/javascript')
  // TODO: We shouldn't be browserifying on the fly because it is slow. Need to
  // add a build process.
  browserify(__dirname + '/app.js', {
    debug: true
  })
    .transform('reactify')
    .bundle()
    .pipe(res)
})

app.use('/', function (req, res) {
  res.setHeader('Content-Type', 'text/html')
  res.end(React.renderToStaticMarkup(
    React.DOM.body(
      null,
      React.DOM.div({
        id: 'container',
        dangerouslySetInnerHTML: {
          __html: React.renderToString(React.createElement(StatusBox))
        }
      }),
      React.DOM.script({
        src: '/bundle.js'
      })
    )
  ))
})

var server = app.listen(3000, function () {
  var addr = server.address()
  console.log('Listening @ http://%s:%d', addr.address, addr.port)
})
<commit_msg>make serverapp executable for ease-of-use<commit_after>#!/usr/bin/env node
var express = require('express')
var browserify = require('browserify')
var React = require('react')
var jsx = require('node-jsx')
var app = express()

jsx.install()

var StatusBox = require('../views/index.jsx')

app.use('/bundle.js', function (req, res) {
  res.setHeader('content-type', 'application/javascript')
  // TODO: We shouldn't be browserifying on the fly because it is slow. Need to
  // add a build process.
  browserify(__dirname + '/app.js', {
    debug: true
  })
    .transform('reactify')
    .bundle()
    .pipe(res)
})

app.use('/', function (req, res) {
  res.setHeader('Content-Type', 'text/html')
  res.end(React.renderToStaticMarkup(
    React.DOM.body(
      null,
      React.DOM.div({
        id: 'container',
        dangerouslySetInnerHTML: {
          __html: React.renderToString(React.createElement(StatusBox))
        }
      }),
      React.DOM.script({
        src: '/bundle.js'
      })
    )
  ))
})

var server = app.listen(3000, function () {
  var addr = server.address()
  console.log('Listening @ http://%s:%d', addr.address, addr.port)
})
<|endoftext|>


<commit_before>import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);

import LiveApi from '../LiveApi';
import ws from 'ws';

describe('unauthenticated', () => {
    let liveApi;

    beforeEach(() => {
        liveApi = new LiveApi({ websocket: ws });
    });

    it('can ping server', () =>
        expect(
            liveApi.ping()
        ).to.eventually.have.property('ping')
    );

    it('can call authorize', () =>
        expect(liveApi.authorize).to.be.ok
    );

    it('can not authorize with an invalid token', () =>
        expect(
            liveApi.authorize('invalid_token')
        ).to.eventually.be.rejected
    );

    it('can get available contracts for symbol', () =>
        expect(
            liveApi.getContractsForSymbol('R_100')
        ).to.eventually.have.property('contracts_for')
    );

    it('can get brief active symbols', () =>
        expect(
            liveApi.getActiveSymbolsBrief()
        ).to.eventually.have.property('active_symbols')
    );

    it('can get full active symbols', () =>
        expect(
            liveApi.getActiveSymbolsFull()
        ).to.eventually.have.property('active_symbols')
    );

    it('can get asset index', () =>
        expect(
            liveApi.getAssetIndex()
        ).to.eventually.have.property('asset_index')
    );

    it('must provide a parameter to getTradingTimes', () =>
        expect(
            liveApi.getTradingTimes(new Date())
        ).to.eventually.have.property('trading_times')
    );

    it('can get trading times', () =>
        expect(
            liveApi.getTradingTimes(new Date())
        ).to.eventually.have.property('trading_times')
    );

    it('can subscribe to tick updates', () =>
        expect(
            liveApi.subscribeToTick('R_100')
        ).to.eventually.have.property('tick')
    );

    it('can subscribe to multiple ticks updates', () =>
        expect(
            liveApi.subscribeToTicks(['R_25', 'R_50', 'R_100'])
        ).to.not.throw
    );

    it('can unsubscribe from all tick updates', () =>
        expect(
            liveApi.unsubscribeFromAllTicks()
        ).to.not.throw
    );

    it('can get tick history with no parameters', () =>
        expect(
            liveApi.getTickHistory('R_100')
        ).to.eventually.have.property('history')
    );

    it('can get tick history with custom intervals', () =>
        expect(
            liveApi.getTickHistory('R_100', { end: 'latest', count: 10 })
        ).to.eventually.have.property('history')
    );

    it('can get price proposal for contract', () =>
        expect(
            liveApi.getPriceProposalForContract({
                amount: 100,
                basis: 'payout',
                contract_type: 'CALL',
                currency: 'USD',
                duration: 60,
                duration_unit: 's',
                symbol: 'R_100',
            })
        ).to.eventually.have.property('proposal')
    );

    it('can subscribe to price proposal updates for contract', () =>
        expect(
            liveApi.subscribeToPriceForContractProposal({
                amount: 100,
                basis: 'payout',
                contract_type: 'CALL',
                currency: 'USD',
                duration: 60,
                duration_unit: 's',
                symbol: 'R_100',
            })
        ).to.eventually.have.property('proposal')
    );

    it('can unsubscribe from all price proposal updates', () =>
        expect(
            liveApi.unsubscribeFromAllProposals()
        ).to.not.throw
    );
});
<commit_msg>Add missing tests for unauthenticated<commit_after>import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);

import LiveApi from '../LiveApi';
import ws from 'ws';

describe('unauthenticated', () => {
    let liveApi;

    beforeEach(() => {
        liveApi = new LiveApi({ websocket: ws });
    });

    it('can ping server', () =>
        expect(
            liveApi.ping()
        ).to.eventually.have.property('ping')
    );

    it('can call authorize', () =>
        expect(liveApi.authorize).to.be.ok
    );

    it('can not authorize with an invalid token', () =>
        expect(
            liveApi.authorize('invalid_token')
        ).to.eventually.be.rejected
    );

    it('can get available contracts for symbol', () =>
        expect(
            liveApi.getContractsForSymbol('R_100')
        ).to.eventually.have.property('contracts_for')
    );

    it('can get brief active symbols', () =>
        expect(
            liveApi.getActiveSymbolsBrief()
        ).to.eventually.have.property('active_symbols')
    );

    it('can get full active symbols', () =>
        expect(
            liveApi.getActiveSymbolsFull()
        ).to.eventually.have.property('active_symbols')
    );

    it('can get asset index', () =>
        expect(
            liveApi.getAssetIndex()
        ).to.eventually.have.property('asset_index')
    );

    it('must provide a parameter to getTradingTimes', () =>
        expect(
            liveApi.getTradingTimes(new Date())
        ).to.eventually.have.property('trading_times')
    );

    it('can get trading times', () =>
        expect(
            liveApi.getTradingTimes(new Date())
        ).to.eventually.have.property('trading_times')
    );

    it('can get residences', () =>
        expect(
            liveApi.getResidences()
        ).to.eventually.have.property('residence_list')
    );

    it('can get states for a country', () =>
        expect(
            liveApi.getStatesForCountry('de')
        ).to.eventually.have.property('states_list')
    );

    it('can subscribe to tick updates', () =>
        expect(
            liveApi.subscribeToTick('R_100')
        ).to.eventually.have.property('tick')
    );

    it('can subscribe to multiple ticks updates', () =>
        expect(
            liveApi.subscribeToTicks(['R_25', 'R_50', 'R_100'])
        ).to.not.throw
    );

    it('can unsubscribe from all tick updates', () =>
        expect(
            liveApi.unsubscribeFromAllTicks()
        ).to.not.throw
    );

    it('can get tick history with no parameters', () =>
        expect(
            liveApi.getTickHistory('R_100')
        ).to.eventually.have.property('history')
    );

    it('can get tick history with custom intervals', () =>
        expect(
            liveApi.getTickHistory('R_100', { end: 'latest', count: 10 })
        ).to.eventually.have.property('history')
    );

    it('can get the landing company for a country', () =>
        expect(
            liveApi.getLandingCompany('de')
        ).to.eventually.have.property('landing_company')
    );

    it('can get details about a landing company', () =>
        expect(
            liveApi.getLandingCompanyDetails('costarica')
        ).to.eventually.have.property('landing_company_details')
    );

    it('can get payment agents for a country', () =>
        expect(
            liveApi.getPaymentAgentsForCountry('id')
        ).to.eventually.have.property('paymentagent_list')
    );

    it('can get payout currencies', () =>
        expect(
            liveApi.getPayoutCurrencies()
        ).to.eventually.have.property('payout_currencies')
    );

    it('can get price proposal for contract', () =>
        expect(
            liveApi.getPriceProposalForContract({
                amount: 100,
                basis: 'payout',
                contract_type: 'CALL',
                currency: 'USD',
                duration: 60,
                duration_unit: 's',
                symbol: 'R_100',
            })
        ).to.eventually.have.property('proposal')
    );

    it('can subscribe to price proposal updates for contract', () =>
        expect(
            liveApi.subscribeToPriceForContractProposal({
                amount: 100,
                basis: 'payout',
                contract_type: 'CALL',
                currency: 'USD',
                duration: 60,
                duration_unit: 's',
                symbol: 'R_100',
            })
        ).to.eventually.have.property('proposal')
    );

    it('can unsubscribe from all price proposal updates', () =>
        expect(
            liveApi.unsubscribeFromAllProposals()
        ).to.not.throw
    );

    it('can get candles for a symbol', () =>
        expect(
            liveApi.getCandles('R_50')
        ).to.eventually.have.property('candles')
    );

    it('can get candles for last N days', () =>
        expect(
            liveApi.getCandlesForLastNDays('R_50', 40)
        ).to.eventually.have.property('candles')
    );

    it('can get server time', () =>
        expect(
            liveApi.getServerTime()
        ).to.eventually.have.property('time')
    );

    it('can verify email', () =>
        expect(
            liveApi.verifyEmail('address@example.com', 'account_opening')
        ).to.eventually.have.property('verify_email')
    );

    it('can get website status', () =>
        expect(
            liveApi.getWebsiteStatus()
        ).to.eventually.have.property('website_status')
    );
});
<|endoftext|>


<commit_before>#!/usr/bin/node

var dash = require('dashdash');
var repl = require('repl');
var URL = require('url');
var chalk;
try {
	chalk = require('chalk');
} catch(e) {
	chalk = {};
	['gray', 'red', 'green', 'red'].forEach(function(col) {
		chalk[col] = function(str) { return str; };
	});
}

var opts = dash.parse({options: [
	{
		names: ['help', 'h'],
		type: 'bool',
		help: 'Print this help and exit.'
	},
	{
		names: ['location', 'l'],
		type: 'bool',
		help: 'Sets only location without actually loading the page'
	},
	{
		names: ['verbose', 'v'],
		type: 'bool',
		help: 'Log requests and responses'
	},
	{
		names: ['show'],
		type: 'bool',
		help: 'Show window'
	},
	{
		names: ['width'],
		type: 'integer',
		help: 'Window width'
	},
	{
		names: ['height'],
		type: 'integer',
		help: 'Window height'
	},
	{
		names: ['bare'],
		type: 'bool',
		help: 'Bare window without decoration'
	},
	{
		names: ['transparent'],
		type: 'bool',
		help: 'Transparent window'
	}
]});

var W = require('../');

var url = opts._args.pop();
if (!url) {
	opts.location = true;
	url = "";
} else {
	var urlObj = URL.parse(url);
	if (!urlObj.protocol) url = "http://" + url;
}

var wk = W.load(url, {
	content: opts.location ? "" : undefined,
	offscreen: !opts.show,
	images: opts.show,
	filter: !opts.show && function() {
		if (/\.css(\?.*)?$/.test(this.uri)) this.cancel = true;
	},
	console: opts.verbose,
	inspector: opts.show,
	width: opts.width,
	height: opts.height,
	decorated: !opts.bare,
	transparent: opts.transparent
}, function(err) {
	start(wk);
});

function dumpCode(cmd) {
	var obj = eval(cmd);
	if (typeof obj == "object") {
		var keys = [];
		for (var k in obj) {
			keys.push(k);
		}
		return keys;
	} else {
		return obj;
	}
}

function start(wk) {
	var pr = repl.start({
		eval: function(cmd, context, filename, cb) {
			if (cmd == ".scope") {
				cmd = "window";
				wk.run(function() {
					return Object.keys(window);
				}, cb);
			} else {
				wk.run(function(cmd) {
					var obj = eval(cmd);
					if (/string|number|boolean/.test(typeof obj)) return obj;
					var ret = {}, val;
					for (var k in obj) {
						try {
							val = obj[k];
						} catch(e) {
							val = null;
						}
						if (!val || /string|number|boolean/.test(typeof val)) {
							ret[k] = val;
							continue;
						}
						try {
							var tmp = JSON.stringify(val);
							if (tmp != null) {
								ret[k] = val;
								continue;
							}
						} catch(e) {
						}

						if (val.nodeType) {
							try {
								var div = val.ownerDocument.createElement('div');
								div.appendChild(val.cloneNode(false));
								ret[k] = div.innerHTML;
							} catch(e) {
							}
						}
						if (ret[k] == null) ret[k] = val + "";
					}
					return ret;
				}, cmd, function(err, result) {
					if (err) console.error(err);
					cb(err, result);
				});
			}
		}
	}).on('exit', function() {
		wk.unload(function() {
			wk.destroy();
		});
	});
	pr.context = {};
}

if (opts.verbose) {
	wk.on('response', function(res) {
		var list = [res.status < 400 ? res.status : chalk.red(res.status)];
		var type = res.headers['Content-Type'];
		if (type) list.push(chalk.gray(onlyMime(type)));
		list.push(onlyPath(wk.uri, res.uri), chalk.green(res.length));
		console.info(list.join(' '));
	});
}

function onlyMime(str) {
	var mime = str.split(';').shift() || str;
	return mime.split('/').pop() || str;
}

function onlyPath(root, str) {
	var len = root.slice(-1) == "/" ? root.length - 1 : root.length;
	if (str.indexOf(root) == 0) str = str.substring(len);
	if (!str) str = ".";
	return str;
}

<commit_msg>eval in global context<commit_after>#!/usr/bin/node

var dash = require('dashdash');
var repl = require('repl');
var URL = require('url');
var chalk;
try {
	chalk = require('chalk');
} catch(e) {
	chalk = {};
	['gray', 'red', 'green', 'red'].forEach(function(col) {
		chalk[col] = function(str) { return str; };
	});
}

var opts = dash.parse({options: [
	{
		names: ['help', 'h'],
		type: 'bool',
		help: 'Print this help and exit.'
	},
	{
		names: ['location', 'l'],
		type: 'bool',
		help: 'Sets only location without actually loading the page'
	},
	{
		names: ['verbose', 'v'],
		type: 'bool',
		help: 'Log requests and responses'
	},
	{
		names: ['show'],
		type: 'bool',
		help: 'Show window'
	},
	{
		names: ['width'],
		type: 'integer',
		help: 'Window width'
	},
	{
		names: ['height'],
		type: 'integer',
		help: 'Window height'
	},
	{
		names: ['bare'],
		type: 'bool',
		help: 'Bare window without decoration'
	},
	{
		names: ['transparent'],
		type: 'bool',
		help: 'Transparent window'
	}
]});

var W = require('../');

var url = opts._args.pop();
if (!url) {
	opts.location = true;
	url = "";
} else {
	var urlObj = URL.parse(url);
	if (!urlObj.protocol) url = "http://" + url;
}

var wk = W.load(url, {
	content: opts.location ? "" : undefined,
	offscreen: !opts.show,
	images: opts.show,
	filter: !opts.show && function() {
		if (/\.css(\?.*)?$/.test(this.uri)) this.cancel = true;
	},
	console: opts.verbose,
	inspector: opts.show,
	width: opts.width,
	height: opts.height,
	decorated: !opts.bare,
	transparent: opts.transparent
}, function(err) {
	start(wk);
});

function dumpCode(cmd) {
	var obj = eval(cmd);
	if (typeof obj == "object") {
		var keys = [];
		for (var k in obj) {
			keys.push(k);
		}
		return keys;
	} else {
		return obj;
	}
}

function start(wk) {
	var pr = repl.start({
		eval: function(cmd, context, filename, cb) {
			if (cmd == ".scope") {
				cmd = "window";
				wk.run(function() {
					return Object.keys(window);
				}, cb);
			} else {
				wk.run(function(cmd) {
					// this trick allows us to eval in global context
					// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
					var geval = eval;
					var obj = geval(cmd);
					if (/string|number|boolean/.test(typeof obj)) return obj;
					var ret = {}, val;
					for (var k in obj) {
						try {
							val = obj[k];
						} catch(e) {
							val = null;
						}
						if (!val || /string|number|boolean/.test(typeof val)) {
							ret[k] = val;
							continue;
						}
						try {
							var tmp = JSON.stringify(val);
							if (tmp != null) {
								ret[k] = val;
								continue;
							}
						} catch(e) {
						}

						if (val.nodeType) {
							try {
								var div = val.ownerDocument.createElement('div');
								div.appendChild(val.cloneNode(false));
								ret[k] = div.innerHTML;
							} catch(e) {
							}
						}
						if (ret[k] == null) ret[k] = val + "";
					}
					return ret;
				}, cmd, function(err, result) {
					if (err) console.error(err);
					cb(err, result);
				});
			}
		}
	}).on('exit', function() {
		wk.unload(function() {
			wk.destroy();
		});
	});
	pr.context = {};
}

if (opts.verbose) {
	wk.on('response', function(res) {
		var list = [res.status < 400 ? res.status : chalk.red(res.status)];
		var type = res.headers['Content-Type'];
		if (type) list.push(chalk.gray(onlyMime(type)));
		list.push(onlyPath(wk.uri, res.uri), chalk.green(res.length));
		console.info(list.join(' '));
	});
}

function onlyMime(str) {
	var mime = str.split(';').shift() || str;
	return mime.split('/').pop() || str;
}

function onlyPath(root, str) {
	var len = root.slice(-1) == "/" ? root.length - 1 : root.length;
	if (str.indexOf(root) == 0) str = str.substring(len);
	if (!str) str = ".";
	return str;
}

<|endoftext|>


<commit_before>function BinaryReader(arraybuffer) {
  this.data_ = new Uint8Array(arraybuffer);
  this.pos_ = 0;
}

BinaryReader.fromWordArray = function(wordArray) {
  var buf = new ArrayBuffer(wordArray.sigBytes);
  var words = new Uint32Array(buf);
  // swap endianness, from http://stackoverflow.com/questions/5320439/#answer-5320624
  words.set(wordArray.words.map(function(val) {
    return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >> 8) & 0xFF00)
           | ((val >> 24) & 0xFF);
  }));
  return new BinaryReader(buf);
};

BinaryReader.prototype.hasNextByte = function() {
  return this.pos_ < this.data_.length;
};

BinaryReader.prototype.hasNextInt = function() {
  return this.pos_ < this.data_.length - 3;
};

BinaryReader.prototype.readByte = function() {
  if (!this.hasNextByte()) {
    throw new RangeError();
  }
  return this.data_[this.pos_++];
};

BinaryReader.prototype.readBytes = function(num) {
  var bytes = [];
  for (var i = 0; i < num; i++) {
    bytes.push(this.readByte());
  }
  return bytes;
};

BinaryReader.prototype.readNumber_ = function(bytes) {
  var bytes = this.readBytes(bytes);
  var result = 0;
  for (var i = bytes.length - 1; i >= 0; i--) {
    result = (result * 256) + bytes[i];
  }
  return result;
};

BinaryReader.prototype.readShort = function() {
  return this.readNumber_(2);
};

BinaryReader.prototype.readInt = function() {
  return this.readNumber_(4);
};

BinaryReader.prototype.readWord = function() {
  var bytes = this.readBytes(4);
  var result = 0;
  for (var i = 0; i < bytes.length; i++) {
    result = (result * 256) + bytes[i];
  }
  return result;
};

BinaryReader.prototype.readWordArray = function(num) {
  var words = [];
  while (num > 0) {
    words.push(this.readWord());
    num -= 4;
  }
  return CryptoJS.lib.WordArray.create(words);
};

BinaryReader.prototype.readRestToWordArray = function() {
  var restOfFile = [];
  var numBytes = 0;
  while (this.hasNextInt()) {
    restOfFile.push(this.readWord());
    numBytes += 4;
  }
  return CryptoJS.lib.WordArray.create(restOfFile, numBytes);
};

// Reads in a null-terminated string.
BinaryReader.prototype.readString = function() {
  var result = '';
  var b = this.readByte();
  while (b != 0) {
    result += String.fromCharCode(b);
    b = this.readByte();
  }
  return result;
};
<commit_msg>Support content size that's not a multiple of 4.<commit_after>function BinaryReader(arraybuffer, opt_length) {
  this.data_ = new Uint8Array(arraybuffer);
  this.length_ = opt_length || arraybuffer.byteLength;
  this.pos_ = 0;
}

BinaryReader.fromWordArray = function(wordArray) {
  var length = Math.ceil(wordArray.sigBytes / Uint32Array.BYTES_PER_ELEMENT) *
                   Uint32Array.BYTES_PER_ELEMENT;
  var buf = new ArrayBuffer(length);
  var words = new Uint32Array(buf);
  // swap endianness, from http://stackoverflow.com/questions/5320439/#answer-5320624
  words.set(wordArray.words.map(function(val) {
    return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >> 8) & 0xFF00)
           | ((val >> 24) & 0xFF);
  }));
  return new BinaryReader(buf, wordArray.sigBytes);
};

BinaryReader.prototype.hasNextByte = function() {
  return this.pos_ < this.length_;
};

BinaryReader.prototype.hasNextInt = function() {
  return this.pos_ < this.length_ - 3;
};

BinaryReader.prototype.readByte = function() {
  if (!this.hasNextByte()) {
    throw new RangeError();
  }
  return this.data_[this.pos_++];
};

BinaryReader.prototype.readBytes = function(num) {
  var bytes = [];
  for (var i = 0; i < num; i++) {
    bytes.push(this.readByte());
  }
  return bytes;
};

BinaryReader.prototype.readNumber_ = function(bytes) {
  var bytes = this.readBytes(bytes);
  var result = 0;
  for (var i = bytes.length - 1; i >= 0; i--) {
    result = (result * 256) + bytes[i];
  }
  return result;
};

BinaryReader.prototype.readShort = function() {
  return this.readNumber_(2);
};

BinaryReader.prototype.readInt = function() {
  return this.readNumber_(4);
};

BinaryReader.prototype.readWord = function() {
  var bytes = this.readBytes(4);
  var result = 0;
  for (var i = 0; i < bytes.length; i++) {
    result = (result * 256) + bytes[i];
  }
  return result;
};

BinaryReader.prototype.readWordArray = function(num) {
  var words = [];
  while (num > 0) {
    words.push(this.readWord());
    num -= 4;
  }
  return CryptoJS.lib.WordArray.create(words);
};

BinaryReader.prototype.readRestToWordArray = function() {
  var restOfFile = [];
  var numBytes = 0;
  while (this.hasNextInt()) {
    restOfFile.push(this.readWord());
    numBytes += 4;
  }
  return CryptoJS.lib.WordArray.create(restOfFile, numBytes);
};

// Reads in a null-terminated string.
BinaryReader.prototype.readString = function() {
  var result = '';
  var b = this.readByte();
  while (b != 0) {
    result += String.fromCharCode(b);
    b = this.readByte();
  }
  return result;
};
<|endoftext|>


<commit_before>import {addNotification, readNotification, setVisibilityFilter, VisibilityFilters, ADD_NOTIFICATION, READ_NOTIFICATION, SET_VISIBILITY_FILTER} from '../'

describe.only('actions', () => {
    it('actionType should be strings', () => {
        expect(ADD_NOTIFICATION).to.be.a('string');
        expect(READ_NOTIFICATION).to.be.a('string');
        expect(SET_VISIBILITY_FILTER).to.be.a('string');
    } )
    describe('addNotification', () => {
        it('should be a function', () => {
            expect(addNotification).to.be.a('function');
        });
        it('should return an object with a type and a notification property', () => {
            const NOTIF = {content: 'lopez'};
            const addNotificationResult = addNotification(NOTIF);
            expect(addNotificationResult).to.be.an('object');
            expect(addNotificationResult).to.have.ownProperty('type');
            expect(addNotificationResult).to.have.ownProperty('notification');
            expect(addNotificationResult.notification).to.deep.equal(NOTIF);
            expect(addNotificationResult.type).to.equal(ADD_NOTIFICATION);
        });
    });
    describe('readNotification', () => {
        it('should be a function', () => {
            expect(readNotification).to.be.a('function');
        });
        it('should return an object with a type and a notification property', () => {
            const NOTIF_ID = 12345;
            const readNotificationResult = readNotification(NOTIF_ID);
            expect(readNotificationResult).to.be.an('object');
            expect(readNotificationResult).to.have.ownProperty('type');
            expect(readNotificationResult).to.have.ownProperty('notificationId');
            expect(readNotificationResult.notificationId).to.deep.equal(NOTIF_ID);
            expect(readNotificationResult.type).to.equal(READ_NOTIFICATION);
        });
    });
    describe('setVisibilityFilter', () => {
        it('should be a function', () => {
            expect(setVisibilityFilter).to.be.a('function');
        });
    });
    describe('VisibilityFilters', () => {
        it('should be an object', () => {
            expect(VisibilityFilters).to.be.an('object');
        });
    });
});
<commit_msg>[actions] tests<commit_after>import {addNotification, readNotification, setVisibilityFilter, VisibilityFilters, ADD_NOTIFICATION, READ_NOTIFICATION, SET_VISIBILITY_FILTER} from '../'

describe('actions', () => {
    it('actionType should be strings', () => {
        expect(ADD_NOTIFICATION).to.be.a('string');
        expect(READ_NOTIFICATION).to.be.a('string');
        expect(SET_VISIBILITY_FILTER).to.be.a('string');
    } )
    describe('addNotification', () => {
        it('should be a function', () => {
            expect(addNotification).to.be.a('function');
        });
        it('should return an object with a type and a notification property', () => {
            const NOTIF = {content: 'lopez'};
            const addNotificationResult = addNotification(NOTIF);
            expect(addNotificationResult).to.be.an('object');
            expect(addNotificationResult).to.have.ownProperty('type');
            expect(addNotificationResult).to.have.ownProperty('notification');
            expect(addNotificationResult.notification).to.deep.equal(NOTIF);
            expect(addNotificationResult.type).to.equal(ADD_NOTIFICATION);
        });
    });
    describe('readNotification', () => {
        it('should be a function', () => {
            expect(readNotification).to.be.a('function');
        });
        it('should return an object with a type and a notification property', () => {
            const NOTIF_ID = 12345;
            const readNotificationResult = readNotification(NOTIF_ID);
            expect(readNotificationResult).to.be.an('object');
            expect(readNotificationResult).to.have.ownProperty('type');
            expect(readNotificationResult).to.have.ownProperty('notificationId');
            expect(readNotificationResult.notificationId).to.deep.equal(NOTIF_ID);
            expect(readNotificationResult.type).to.equal(READ_NOTIFICATION);
        });
    });
    describe('setVisibilityFilter', () => {
        it('should be a function', () => {
            expect(setVisibilityFilter).to.be.a('function');
        });
    });
    describe('VisibilityFilters', () => {
        it('should be an object', () => {
            expect(VisibilityFilters).to.be.an('object');
        });
    });
});
<|endoftext|>


<commit_before>import {capitalize, toUpper} from 'lodash/string';
import {isFunction, isString} from 'lodash/lang';

const ACTION_BUILDER = 'ACTION_BUILDER';
const ALLOW_ACTION_TYPES = ['load', 'save', 'delete'];
const STRING_EMPTY = '';
const LOAD = ALLOW_ACTION_TYPES[0];
const SAVE = ALLOW_ACTION_TYPES[1];

export const PENDING = 'PENDING';
export const SUCCESS = 'SUCCESS';
export const ERROR = 'ERROR';

// A simple function to create action creators
// Return a function which returns a type and a payload
// example:  _actionCreatorBuilder('REQUEST_LOAD_USER') will return `payload => {type: 'REQUEST_LOAD_USER', payload}`
const _actionCreatorBuilder = (type, name, _meta) => payload => ({...{type, entityPath: name, syncForm: true, _meta}, ...(payload ? {payload} : {})});

// A simple function to create async middleware dispatcher for redux
// You have to provide a object with the following properties
// {
//   service: A `Promise base service` which take one argument (the service entry data) an optional second the options
//   creators : {
//     response:{name, value} where value is the function standing for an action creator
//     request:{name, value} where value is the function standing for an action creator
//     error:{name, value} where value is the function standing for an action creator
//   }
// }
const _asyncActionCreator = ({service: promiseSvc, creators:{response: {value: responseActionCreator}, request: {value: requestActionCreator}, error: {value: errorActionCreator}}}) => (data => {
    return async dispatch => {
        try {
            dispatch(requestActionCreator(data));
            const svcValue = await promiseSvc(data)
            dispatch(responseActionCreator(svcValue));
        } catch(err) {
            dispatch(errorActionCreator(err));
        }
    }
});

// Validate the action builder parameters
const _validateActionBuilderParams = ({name, type, service}) => {
    if(!isString(name) || STRING_EMPTY === name) {
        throw new Error(`${ACTION_BUILDER}: the name parameter should be a string.`);
    }
    if(!isString(type) || ALLOW_ACTION_TYPES.indexOf(type) === -1) {
        throw new Error(`${ACTION_BUILDER}: the type parameter should be a string and the value one of these: ${ALLOW_ACTION_TYPES.join(',')}.`);
    }
    if(!isFunction(service)) {
        throw new Error(`${ACTION_BUILDER}: the service parameter should be a function.`);
    }
}

// Action builder is a simple way to create action types, action creator, and an async action
// It takes one object parameter
// ### doc
// ```javascript
// {
//   name, // The name of your data in the store (example user)
//   type, // Which type of action it is: load, save, delete, ...
//   service // an async action to load data
// }
// ```
//
// ### example
// ```javascript
// const loadAction = actionBuilder({name:'user', type:'load', service:loadUserSvc});
//```
//
// will produce
//
//```javascript
// export const loadUserTypes = loadAction.types;
// // which are the action types {REQUEST_LOAD_USER, RESPONSE_LOAD_USER}
// export const loadUserAction = loadAction.action;
// //which is a function taking the criteria as param
// ```
export const actionBuilder = ({name, type, service}) => {
    _validateActionBuilderParams({name, type, service});
    //Case transformation
    const UPPER_TYPE = toUpper(type);
    const UPPER_NAME = toUpper(name);
    const CAPITALIZE_TYPE = capitalize(type);
    const CAPITALIZE_NAME = capitalize(name);

    const constants = {
        request: `REQUEST_${UPPER_TYPE}_${UPPER_NAME}`,
        response: `RESPONSE_${UPPER_TYPE}_${UPPER_NAME}`,
        error: `ERROR_${UPPER_TYPE}_${UPPER_NAME}`
    }
    const loading = type === LOAD;
    const saving = type === SAVE;
    const _metas = {
        request: {status: PENDING, loading, saving},
        response: {status: SUCCESS, loading, saving},
        error: {status: ERROR, loading, saving}
    }
    const creators = {
        request: {name: `request${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.request, name, _metas.request)},
        response: {name: `response${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.response, name, _metas.response)},
        error: {name: `error${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.error, name, _metas.error)}
    }

    const action = _asyncActionCreator({service, creators});
    return {
        types: {
            [constants.request]: constants.request,
            [constants.response]: constants.response,
            [constants.error]: constants.error
        },
        creators: {
            [creators.request.name]: creators.request.value,
            [creators.response.name]: creators.response.value,
            [creators.error.name]: creators.error.value
        },
        action
    }
}

/*
Beginning of an optim way to write it but maybe less readable
['request', 'response', 'error'].reduce((res, actionName)=>{
//creates the type
res[types[actionName]] = `${toUpper(actionName)}_${UPPER_TYPE}_${UPPER_NAME}`;
//creates the action creators
res[creators[actionName]] = {
name: `${actionName}${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`,
value: payload => _actionCreatorBuilder(res[types[actionName]], payload)
};
return res;
}, {types:{}, creators:{}})
*/
<commit_msg>[entity action builder] Add multiple nodes action.<commit_after>import {capitalize, toUpper} from 'lodash/string';
import {isArray, isFunction,isString} from 'lodash/lang';

const ACTION_BUILDER = 'ACTION_BUILDER';
const ALLOW_ACTION_TYPES = ['load', 'save', 'delete'];
const STRING_EMPTY = '';
const LOAD = ALLOW_ACTION_TYPES[0];
const SAVE = ALLOW_ACTION_TYPES[1];

export const PENDING = 'PENDING';
export const SUCCESS = 'SUCCESS';
export const ERROR = 'ERROR';

// A simple function to create action creators
// Return a function which returns a type and a payload
// example:  _actionCreatorBuilder('REQUEST_LOAD_USER') will return `payload => {type: 'REQUEST_LOAD_USER', payload}`
const _actionCreatorBuilder = (type, name, _meta) => payload => ({...{type, entityPath: name, syncForm: true, _meta}, ...(payload ? {payload} : {})});

// A simple function to create async middleware dispatcher for redux
// You have to provide a object with the following properties
// {
//   service: A `Promise base service` which take one argument (the service entry data) an optional second the options
//   actionCreatorsArray : [{
//     name: the name of the concerned node.
//     request: the function standing for an action creator of request
//     response: the function standing for an action creator of response
//     error: the function standing for an action creator of error
//   }]
// }
const _asyncActionCreator = ({service: promiseSvc, actionCreatorsArray}) => (data => {
    return async dispatch => {
        try {
            console.log(actionCreatorsArray);
            actionCreatorsArray.forEach(({name, request: requestActionCreator}) => dispatch(requestActionCreator(data)));
            const svcValue = await promiseSvc(data);
              actionCreatorsArray.forEach(({name, response: responseActionCreator}) => {
                // When there is only one node the complete payload is dispatched.
                if(actionCreatorsArray.length === 1){
                  dispatch(responseActionCreator(svcValue));
                } else {
                  // Whene there is more node only a part of the payload is dispathed.
                  svcValue[name] && dispatch(responseActionCreator(svcValue[name]));
                }
              });

        } catch(err) {
            // maybe this shoud be formated the same way the payload is.
            actionCreatorsArray.forEach(({name, error:errorActionCreator}) => dispatch(errorActionCreator(err)));
        }
    }
});

// Validate the action builder parameters
const _validateActionBuilderParams = ({names = [], type, service}) => {
    if(!isArray(names) || names.length === 0) {
        throw new Error(`${ACTION_BUILDER}: the names parameter should be a non empty array.`);
    }

    names.forEach( (name) => {
      if(!isString(name) || STRING_EMPTY === name) {
        throw new Error(`${ACTION_BUILDER}: the names parameter should be maid of strings.`);
      }
    });

    if(!isString(type) || ALLOW_ACTION_TYPES.indexOf(type) === -1) {
        throw new Error(`${ACTION_BUILDER}: the type parameter should be a string and the value one of these: ${ALLOW_ACTION_TYPES.join(',')}.`);
    }
    if(!isFunction(service)) {
        throw new Error(`${ACTION_BUILDER}: the service parameter should be a function.`);
    }
}

// Action builder is a simple way to create action types, action creator, and an async action
// It takes one object parameter
// ### doc
// ```javascript
// {
//   name, // The name of your data in the store (example user)
//   type, // Which type of action it is: load, save, delete, ...
//   service // an async action to load data
// }
// ```
//
// ### example
// ```javascript
// const loadAction = actionBuilder({name:'user', type:'load', service:loadUserSvc});
//```
//
// will produce
//
//```javascript
// export const loadUserTypes = loadAction.types;
// // which are the action types {REQUEST_LOAD_USER, RESPONSE_LOAD_USER}
// export const loadUserAction = loadAction.action;
// //which is a function taking the criteria as param
// ```
export const actionBuilder = ({names, type, service}) => {
    _validateActionBuilderParams({names, type, service});
    //Case transformation
    const UPPER_TYPE = toUpper(type);
    const CAPITALIZE_TYPE = capitalize(type);
    const loading = type === LOAD;
    const saving = type === SAVE;

    const _metas = {
        request: {status: PENDING, loading, saving},
        response: {status: SUCCESS, loading, saving},
        error: {status: ERROR, loading, saving}
    }
    // for each node action types and action creators are maid.
    const _creatorsAndTypes = names.reduce((res, name) => {
      // commonTreatement
      const UPPER_NAME = toUpper(name);
      const CAPITALIZE_NAME = capitalize(name);

      const constants = {
          request: `REQUEST_${UPPER_TYPE}_${UPPER_NAME}`,
          response: `RESPONSE_${UPPER_TYPE}_${UPPER_NAME}`,
          error: `ERROR_${UPPER_TYPE}_${UPPER_NAME}`
      }
      // todo: find another name for name and value // not crystal clear
      const creators = {
          request: {name: `request${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.request, name, _metas.request)},
          response: {name: `response${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.response, name, _metas.response)},
          error: {name: `error${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`, value: _actionCreatorBuilder(constants.error, name, _metas.error)}
      }

      return {
        types: {
          ...res.types,
          [constants.request]: constants.request,
          [constants.response]: constants.response,
          [constants.error]: constants.error
        },
        creators: {
          ...res.creators,
          [creators.request.name]: creators.request.value,
          [creators.response.name]: creators.response.value,
          [creators.error.name]: creators.error.value
        },
        actionCreatorsArray: [
          ...res.actionCreatorsArray, {
            name,
            request: creators.request.value,
            response: creators.response.value,
            error: creators.error.value
          }
        ]
      };
    }, {types: {}, creators: {}, actionCreatorsArray: []});

    const action = _asyncActionCreator({service, actionCreatorsArray: _creatorsAndTypes.actionCreatorsArray});
    return {
        action,
        types: _creatorsAndTypes.types,
        creators: _creatorsAndTypes.creators
    };
}

/*
Beginning of an optim way to write it but maybe less readable
['request', 'response', 'error'].reduce((res, actionName)=>{
//creates the type
res[types[actionName]] = `${toUpper(actionName)}_${UPPER_TYPE}_${UPPER_NAME}`;
//creates the action creators
res[creators[actionName]] = {
name: `${actionName}${CAPITALIZE_TYPE}${CAPITALIZE_NAME}`,
value: payload => _actionCreatorBuilder(res[types[actionName]], payload)
};
return res;
}, {types:{}, creators:{}})
*/
<|endoftext|>


<commit_before>'use strict';

var directiveModule = angular.module('angularjs-dropdown-multiselect', []);

directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse',
    function ($filter, $document, $compile, $parse) {

        return {
            restrict: 'AE',
            scope: {
                selectedModel: '=',
                options: '=',
                extraSettings: '=',
                events: '=',
                searchFilter: '=?',
                translationTexts: '=',
                groupBy: '@'
            },
            template: function (element, attrs) {
                var checkboxes = attrs.checkboxes ? true : false;
                var groups = attrs.groupBy ? true : false;

                var template = '
';
                template += '<button type="button" class="dropdown-toggle" ng-class="settings.buttonClasses" ng-click="toggleDropdown()">{{getButtonText()}} </button>';
                template += '
';
                template += '
 0">  {{texts.checkAll}}';
                template += '
   {{texts.uncheckAll}}
';
                template += '
 0) && !settings.showUncheckAll" class="divider">
';
                template += '

<input type="text" class="form-control" style="width: 100%;" ng-model="searchFilter" placeholder="{{texts.searchPlaceholder}}" />
';
                template += '

';

                if (groups) {
                	template += '
{{ getGroupTitle(getPropertyForObject(option, settings.groupBy)) }}
';
                	template += '
';
                } else {
                	template += '
';
                }

                template += '';

                if (checkboxes) {
                    template += '
<input class="checkboxInput" type="checkbox" ng-click="checkboxClick($event, getPropertyForObject(option,settings.idProp))" ng-checked="isChecked(getPropertyForObject(option,settings.idProp))" /> {{getPropertyForObject(option, settings.displayProp)}}
';
                } else {
                    template += ' {{getPropertyForObject(option, settings.displayProp)}}';
                }

                template += '
';

                template += '
 1">
';
                template += '
 1">{{selectedModel.length}} {{texts.selectionOf}} {{settings.selectionLimit}} {{texts.selectionCount}}
';

                template += '
';
                template += '
';

                element.html(template);
            },
            link: function ($scope, $element, $attrs) {
                var $dropdownTrigger = $element.children()[0];

                $scope.toggleDropdown = function () {
                    $scope.open = !$scope.open;
                };

                $scope.checkboxClick = function ($event, id) {
                    $scope.setSelectedItem(id);
                    $event.stopImmediatePropagation();
                };

                $scope.externalEvents = {
                    onItemSelect: angular.noop,
                    onItemDeselect: angular.noop,
                    onSelectAll: angular.noop,
                    onDeselectAll: angular.noop,
                    onInitDone: angular.noop,
                    onMaxSelectionReached: angular.noop
                };

                $scope.settings = {
                    dynamicTitle: true,
                    scrollable: false,
                    scrollableHeight: '300px',
                    closeOnBlur: true,
                    displayProp: 'label',
                    idProp: 'id',
                    externalIdProp: 'id',
                    enableSearch: false,
                    selectionLimit: 0,
                    showCheckAll: true,
                    showUncheckAll: true,
                    closeOnSelect: false,
                    buttonClasses: 'btn btn-default',
                    closeOnDeselect: false,
                    groupBy: $attrs.groupBy || undefined,
                    groupByTextProvider: null,
                    smartButtonMaxItems: 0,
                    smartButtonTextConverter: angular.noop,
										styleActive: false
                };

                $scope.texts = {
                    checkAll: 'Check All',
                    uncheckAll: 'Uncheck All',
                    selectionCount: 'checked',
                    selectionOf: '/',
                    searchPlaceholder: 'Search...',
                    buttonDefaultText: 'Select',
                    dynamicButtonTextSuffix: 'checked'
                };

                $scope.searchFilter = $scope.searchFilter || '';

                if (angular.isDefined($scope.settings.groupBy)) {
                    $scope.$watch('options', function (newValue) {
                        if (angular.isDefined(newValue)) {
                            $scope.orderedItems = $filter('orderBy')(newValue, $scope.settings.groupBy);
                        }
                    });
                }

                $scope.$watch('selectedModel', function(newValue) {
                	if (!Array.isArray(newValue)) {
                		   $scope.singleSelection = true;
                	} else {
                		   $scope.singleSelection = false;
                	}
                });

                angular.extend($scope.settings, $scope.extraSettings || []);
                angular.extend($scope.externalEvents, $scope.events || []);
                angular.extend($scope.texts, $scope.translationTexts);

                $scope.singleSelection = $scope.settings.selectionLimit === 1;

                function getFindObj(id) {
                    var findObj = {};

                    if ($scope.settings.externalIdProp === '') {
                        findObj[$scope.settings.idProp] = id;
                    } else {
                        findObj[$scope.settings.externalIdProp] = id;
                    }

                    return findObj;
                }

                function clearObject(object) {
                    for (var prop in object) {
                        delete object[prop];
                    }
                }

                if ($scope.singleSelection) {
                    if (angular.isArray($scope.selectedModel) && $scope.selectedModel.length === 0) {
                        clearObject($scope.selectedModel);
                    }
                }

                if ($scope.settings.closeOnBlur) {
                    $document.on('click', function (e) {
                        var target = e.target.parentElement;
                        var parentFound = false;

                        while (angular.isDefined(target) && target !== null && !parentFound) {
                            if (contains(target.className.split(' '), 'multiselect-parent') && !parentFound) {
                                if(target === $dropdownTrigger) {
                                    parentFound = true;
                                }
                            }
                            target = target.parentElement;
                        }

                        if (!parentFound) {
                            $scope.$apply(function () {
                                $scope.open = false;
                            });
                        }
                    });
                }

                $scope.getGroupTitle = function (groupValue) {
                    if ($scope.settings.groupByTextProvider !== null) {
                        return $scope.settings.groupByTextProvider(groupValue);
                    }

                    return groupValue;
                };

                $scope.getButtonText = function () {
                    if ($scope.settings.dynamicTitle && ($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && Object.keys($scope.selectedModel).length > 0))) {
                        if ($scope.settings.smartButtonMaxItems > 0) {
                            var itemsText = [];

                            angular.forEach($scope.options, function (optionItem) {
                                if ($scope.isChecked($scope.getPropertyForObject(optionItem, $scope.settings.idProp))) {
                                    var displayText = $scope.getPropertyForObject(optionItem, $scope.settings.displayProp);
                                    var converterResponse = $scope.settings.smartButtonTextConverter(displayText, optionItem);

                                    itemsText.push(converterResponse ? converterResponse : displayText);
                                }
                            });

                            if ($scope.selectedModel.length > $scope.settings.smartButtonMaxItems) {
                                itemsText = itemsText.slice(0, $scope.settings.smartButtonMaxItems);
                                itemsText.push('...');
                            }

                            return itemsText.join(', ');
                        } else {
                            var totalSelected;

                            if ($scope.singleSelection) {
                                totalSelected = ($scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp])) ? 1 : 0;
                            } else {
                                totalSelected = angular.isDefined($scope.selectedModel) ? $scope.selectedModel.length : 0;
                            }

                            if (totalSelected === 0) {
                                return $scope.texts.buttonDefaultText;
                            } else {
                                return totalSelected + ' ' + $scope.texts.dynamicButtonTextSuffix;
                            }
                        }
                    } else {
                        return $scope.texts.buttonDefaultText;
                    }
                };

                $scope.getPropertyForObject = function (object, property) {
                    if (angular.isDefined(object) && object.hasOwnProperty(property)) {
                        return object[property];
                    }

                    return '';
                };

                $scope.selectAll = function () {
                    $scope.deselectAll(false);
                    $scope.externalEvents.onSelectAll();

                    angular.forEach($scope.options, function (value) {
                        $scope.setSelectedItem(value[$scope.settings.idProp], true);
                    });
                };

                $scope.deselectAll = function (sendEvent) {
                    sendEvent = sendEvent || true;

                    if (sendEvent) {
                        $scope.externalEvents.onDeselectAll();
                    }

                    if ($scope.singleSelection) {
                        clearObject($scope.selectedModel);
                    } else {
                        $scope.selectedModel.splice(0, $scope.selectedModel.length);
                    }
                };

                $scope.setSelectedItem = function (id, dontRemove) {
                    var findObj = getFindObj(id);
                    var finalObj = null;

                    if ($scope.settings.externalIdProp === '') {
                        finalObj = find($scope.options, findObj);
                    } else {
                        finalObj = findObj;
                    }

                    if ($scope.singleSelection) {
                        clearObject($scope.selectedModel);
                        angular.extend($scope.selectedModel, finalObj);
                        $scope.externalEvents.onItemSelect(finalObj);
                        if ($scope.settings.closeOnSelect) $scope.open = false;

                        return;
                    }

                    dontRemove = dontRemove || false;

                    var exists = findIndex($scope.selectedModel, findObj) !== -1;

                    if (!dontRemove && exists) {
                        $scope.selectedModel.splice(findIndex($scope.selectedModel, findObj), 1);
                        $scope.externalEvents.onItemDeselect(findObj);
                    } else if (!exists && ($scope.settings.selectionLimit === 0 || $scope.selectedModel.length < $scope.settings.selectionLimit)) {
                        $scope.selectedModel.push(finalObj);
                        $scope.externalEvents.onItemSelect(finalObj);
                    }
                    if ($scope.settings.closeOnSelect) $scope.open = false;
                };

                $scope.isChecked = function (id) {
                    if ($scope.singleSelection) {
                        return $scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp]) && $scope.selectedModel[$scope.settings.idProp] === getFindObj(id)[$scope.settings.idProp];
                    }

                    return findIndex($scope.selectedModel, getFindObj(id)) !== -1;
                };

                $scope.externalEvents.onInitDone();
            }
        };
}]);

function contains(collection, target) {
	var containsTarget = false;
	collection.some(function(object) {
		if (object === target) {
			containsTarget = true;
			return true;
		}
	});
	return containsTarget;
}

function find(collection, properties) {
	var target;

	collection.some(function(object) {
		var hasAllSameProperties = true;
		Object.keys(properties).forEach(function(key) {
			if (object[key] !== properties[key]) {
				hasAllSameProperties = false;
			}
		});
		if (hasAllSameProperties) {
			target = object;
			return true
		}
	});

	return target;
}

function findIndex(collection, properties) {
	var index = -1;
	var counter = -1;

	collection.some(function(object) {
		var hasAllSameProperties = true;
		counter += 1;
		Object.keys(properties).forEach(function(key) {
			if (object[key] !== properties[key]) {
				hasAllSameProperties = false;
			}
		});
		if (hasAllSameProperties) {
			index = counter;
			return true
		}
	});

	return index;
}
<commit_msg>Fix for closeOnBlur when element without className is clicked<commit_after>'use strict';

var directiveModule = angular.module('angularjs-dropdown-multiselect', []);

directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse',
    function ($filter, $document, $compile, $parse) {

        return {
            restrict: 'AE',
            scope: {
                selectedModel: '=',
                options: '=',
                extraSettings: '=',
                events: '=',
                searchFilter: '=?',
                translationTexts: '=',
                groupBy: '@'
            },
            template: function (element, attrs) {
                var checkboxes = attrs.checkboxes ? true : false;
                var groups = attrs.groupBy ? true : false;

                var template = '
';
                template += '<button type="button" class="dropdown-toggle" ng-class="settings.buttonClasses" ng-click="toggleDropdown()">{{getButtonText()}} </button>';
                template += '
';
                template += '
 0">  {{texts.checkAll}}';
                template += '
   {{texts.uncheckAll}}
';
                template += '
 0) && !settings.showUncheckAll" class="divider">
';
                template += '

<input type="text" class="form-control" style="width: 100%;" ng-model="searchFilter" placeholder="{{texts.searchPlaceholder}}" />
';
                template += '

';

                if (groups) {
                	template += '
{{ getGroupTitle(getPropertyForObject(option, settings.groupBy)) }}
';
                	template += '
';
                } else {
                	template += '
';
                }

                template += '';

                if (checkboxes) {
                    template += '
<input class="checkboxInput" type="checkbox" ng-click="checkboxClick($event, getPropertyForObject(option,settings.idProp))" ng-checked="isChecked(getPropertyForObject(option,settings.idProp))" /> {{getPropertyForObject(option, settings.displayProp)}}
';
                } else {
                    template += ' {{getPropertyForObject(option, settings.displayProp)}}';
                }

                template += '
';

                template += '
 1">
';
                template += '
 1">{{selectedModel.length}} {{texts.selectionOf}} {{settings.selectionLimit}} {{texts.selectionCount}}
';

                template += '
';
                template += '
';

                element.html(template);
            },
            link: function ($scope, $element, $attrs) {
                var $dropdownTrigger = $element.children()[0];

                $scope.toggleDropdown = function () {
                    $scope.open = !$scope.open;
                };

                $scope.checkboxClick = function ($event, id) {
                    $scope.setSelectedItem(id);
                    $event.stopImmediatePropagation();
                };

                $scope.externalEvents = {
                    onItemSelect: angular.noop,
                    onItemDeselect: angular.noop,
                    onSelectAll: angular.noop,
                    onDeselectAll: angular.noop,
                    onInitDone: angular.noop,
                    onMaxSelectionReached: angular.noop
                };

                $scope.settings = {
                    dynamicTitle: true,
                    scrollable: false,
                    scrollableHeight: '300px',
                    closeOnBlur: true,
                    displayProp: 'label',
                    idProp: 'id',
                    externalIdProp: 'id',
                    enableSearch: false,
                    selectionLimit: 0,
                    showCheckAll: true,
                    showUncheckAll: true,
                    closeOnSelect: false,
                    buttonClasses: 'btn btn-default',
                    closeOnDeselect: false,
                    groupBy: $attrs.groupBy || undefined,
                    groupByTextProvider: null,
                    smartButtonMaxItems: 0,
                    smartButtonTextConverter: angular.noop,
										styleActive: false
                };

                $scope.texts = {
                    checkAll: 'Check All',
                    uncheckAll: 'Uncheck All',
                    selectionCount: 'checked',
                    selectionOf: '/',
                    searchPlaceholder: 'Search...',
                    buttonDefaultText: 'Select',
                    dynamicButtonTextSuffix: 'checked'
                };

                $scope.searchFilter = $scope.searchFilter || '';

                if (angular.isDefined($scope.settings.groupBy)) {
                    $scope.$watch('options', function (newValue) {
                        if (angular.isDefined(newValue)) {
                            $scope.orderedItems = $filter('orderBy')(newValue, $scope.settings.groupBy);
                        }
                    });
                }

                $scope.$watch('selectedModel', function(newValue) {
                	if (!Array.isArray(newValue)) {
                		   $scope.singleSelection = true;
                	} else {
                		   $scope.singleSelection = false;
                	}
                });

                angular.extend($scope.settings, $scope.extraSettings || []);
                angular.extend($scope.externalEvents, $scope.events || []);
                angular.extend($scope.texts, $scope.translationTexts);

                $scope.singleSelection = $scope.settings.selectionLimit === 1;

                function getFindObj(id) {
                    var findObj = {};

                    if ($scope.settings.externalIdProp === '') {
                        findObj[$scope.settings.idProp] = id;
                    } else {
                        findObj[$scope.settings.externalIdProp] = id;
                    }

                    return findObj;
                }

                function clearObject(object) {
                    for (var prop in object) {
                        delete object[prop];
                    }
                }

                if ($scope.singleSelection) {
                    if (angular.isArray($scope.selectedModel) && $scope.selectedModel.length === 0) {
                        clearObject($scope.selectedModel);
                    }
                }

                if ($scope.settings.closeOnBlur) {
                    $document.on('click', function (e) {
                        var target = e.target.parentElement;
                        var parentFound = false;

                        while (angular.isDefined(target) && target !== null && !parentFound) {
                            if (!!target.className.split && contains(target.className.split(' '), 'multiselect-parent') && !parentFound) {
                                if(target === $dropdownTrigger) {
                                    parentFound = true;
                                }
                            }
                            target = target.parentElement;
                        }

                        if (!parentFound) {
                            $scope.$apply(function () {
                                $scope.open = false;
                            });
                        }
                    });
                }

                $scope.getGroupTitle = function (groupValue) {
                    if ($scope.settings.groupByTextProvider !== null) {
                        return $scope.settings.groupByTextProvider(groupValue);
                    }

                    return groupValue;
                };

                $scope.getButtonText = function () {
                    if ($scope.settings.dynamicTitle && ($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && Object.keys($scope.selectedModel).length > 0))) {
                        if ($scope.settings.smartButtonMaxItems > 0) {
                            var itemsText = [];

                            angular.forEach($scope.options, function (optionItem) {
                                if ($scope.isChecked($scope.getPropertyForObject(optionItem, $scope.settings.idProp))) {
                                    var displayText = $scope.getPropertyForObject(optionItem, $scope.settings.displayProp);
                                    var converterResponse = $scope.settings.smartButtonTextConverter(displayText, optionItem);

                                    itemsText.push(converterResponse ? converterResponse : displayText);
                                }
                            });

                            if ($scope.selectedModel.length > $scope.settings.smartButtonMaxItems) {
                                itemsText = itemsText.slice(0, $scope.settings.smartButtonMaxItems);
                                itemsText.push('...');
                            }

                            return itemsText.join(', ');
                        } else {
                            var totalSelected;

                            if ($scope.singleSelection) {
                                totalSelected = ($scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp])) ? 1 : 0;
                            } else {
                                totalSelected = angular.isDefined($scope.selectedModel) ? $scope.selectedModel.length : 0;
                            }

                            if (totalSelected === 0) {
                                return $scope.texts.buttonDefaultText;
                            } else {
                                return totalSelected + ' ' + $scope.texts.dynamicButtonTextSuffix;
                            }
                        }
                    } else {
                        return $scope.texts.buttonDefaultText;
                    }
                };

                $scope.getPropertyForObject = function (object, property) {
                    if (angular.isDefined(object) && object.hasOwnProperty(property)) {
                        return object[property];
                    }

                    return '';
                };

                $scope.selectAll = function () {
                    $scope.deselectAll(false);
                    $scope.externalEvents.onSelectAll();

                    angular.forEach($scope.options, function (value) {
                        $scope.setSelectedItem(value[$scope.settings.idProp], true);
                    });
                };

                $scope.deselectAll = function (sendEvent) {
                    sendEvent = sendEvent || true;

                    if (sendEvent) {
                        $scope.externalEvents.onDeselectAll();
                    }

                    if ($scope.singleSelection) {
                        clearObject($scope.selectedModel);
                    } else {
                        $scope.selectedModel.splice(0, $scope.selectedModel.length);
                    }
                };

                $scope.setSelectedItem = function (id, dontRemove) {
                    var findObj = getFindObj(id);
                    var finalObj = null;

                    if ($scope.settings.externalIdProp === '') {
                        finalObj = find($scope.options, findObj);
                    } else {
                        finalObj = findObj;
                    }

                    if ($scope.singleSelection) {
                        clearObject($scope.selectedModel);
                        angular.extend($scope.selectedModel, finalObj);
                        $scope.externalEvents.onItemSelect(finalObj);
                        if ($scope.settings.closeOnSelect) $scope.open = false;

                        return;
                    }

                    dontRemove = dontRemove || false;

                    var exists = findIndex($scope.selectedModel, findObj) !== -1;

                    if (!dontRemove && exists) {
                        $scope.selectedModel.splice(findIndex($scope.selectedModel, findObj), 1);
                        $scope.externalEvents.onItemDeselect(findObj);
                    } else if (!exists && ($scope.settings.selectionLimit === 0 || $scope.selectedModel.length < $scope.settings.selectionLimit)) {
                        $scope.selectedModel.push(finalObj);
                        $scope.externalEvents.onItemSelect(finalObj);
                    }
                    if ($scope.settings.closeOnSelect) $scope.open = false;
                };

                $scope.isChecked = function (id) {
                    if ($scope.singleSelection) {
                        return $scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp]) && $scope.selectedModel[$scope.settings.idProp] === getFindObj(id)[$scope.settings.idProp];
                    }

                    return findIndex($scope.selectedModel, getFindObj(id)) !== -1;
                };

                $scope.externalEvents.onInitDone();
            }
        };
}]);

function contains(collection, target) {
	var containsTarget = false;
	collection.some(function(object) {
		if (object === target) {
			containsTarget = true;
			return true;
		}
	});
	return containsTarget;
}

function find(collection, properties) {
	var target;

	collection.some(function(object) {
		var hasAllSameProperties = true;
		Object.keys(properties).forEach(function(key) {
			if (object[key] !== properties[key]) {
				hasAllSameProperties = false;
			}
		});
		if (hasAllSameProperties) {
			target = object;
			return true
		}
	});

	return target;
}

function findIndex(collection, properties) {
	var index = -1;
	var counter = -1;

	collection.some(function(object) {
		var hasAllSameProperties = true;
		counter += 1;
		Object.keys(properties).forEach(function(key) {
			if (object[key] !== properties[key]) {
				hasAllSameProperties = false;
			}
		});
		if (hasAllSameProperties) {
			index = counter;
			return true
		}
	});

	return index;
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import times from 'lodash.times'
import isNil from 'lodash.isnil'
import { getSelection, setSelection } from 'react/lib/ReactInputSelection'
import InputMask from 'inputmask-core'
import TextField from 'material-ui/TextField'

const ENTER_KEY = 'Enter'
const BACKSPACE_KEY = 'Backspace'
const KEYCODE_Y = 89
const KEYCODE_Z = 90
const PATTERN = '11-1111-11-11111##'
const OPTIONAL_CHAR = '#'

class CardNumberInput extends Component {
  constructor () {
    super()

    this.state = {
      isValid: false,
      text: ''
    }

    this.mask = null
    this.minLength = 0
    this.maxLength = Number.MAX_SAFE_INTEGER
    this.handlePaste = this.handlePaste.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)
    this.handleBeforeInput = this.handleBeforeInput.bind(this)
    this.handleChange = this.handleChange.bind(this)
  }

  componentWillMount () {
    const selection = this.props.value ? this.props.value.length : 0
    let options = {
      pattern: PATTERN,
      placeholderChar: ' ',
      value: this.props.value,
      selection: {
        start: selection,
        end: selection
      },
      formatCharacters: {
        OPTIONAL_CHAR: {
          validate: char => isNil(char) || /[\s\d]/.test(char)
        }
      }
    }
    this.mask = new InputMask(options)
    this.minLength = this.mask.pattern.pattern.filter(char => char !== OPTIONAL_CHAR).length
    this.maxLength = this.mask.pattern.pattern.length

    const value = getDisplayMaskValue(this.mask)
    this.setState({
      text: value,
      isValid: this.isValidValue(value)
    })
  }

  componentWillReceiveProps (nextProps) {
    if (this.state.text !== nextProps.value) {
      const selection = nextProps.value ? nextProps.value.length : 0

      this.mask.setValue(nextProps.value)
      this.mask.setSelection({
        start: selection,
        end: selection
      })

      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  isValidValue (value) {
    return !isNil(value) &&
      value !== this.mask.emptyValue &&
      (this.minLength <= value.length && this.maxLength >= value.length)
  }

  handleChange (event) {
    const maskValue = getDisplayMaskValue(this.mask)
    const elementValue = event.target.value

    if (elementValue !== maskValue) {
      const sizeDiff = Math.abs(maskValue.length - elementValue.length)

      // Cut, delete operations will have shortened the value
      // or if browser don't support 'KeyboardEvent.key' property
      if (elementValue.length < maskValue.length) {
        this.mask.setSelection(getSelection(event.target))
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      // in case browser don't support 'beforeInput' event
      } else if (elementValue.length > maskValue.length) {
        times(sizeDiff, i => {
          this.mask.input(elementValue[elementValue.length - sizeDiff - i])
        })
      }

      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  handlePaste (event) {
    event.preventDefault()

    this.mask.setSelection(getSelection(event.target))
    let pastedValue = getTextFromClipboardData(event)

    if (this.mask.paste(pastedValue)) {
      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  handleKeyDown (event) {
    if (isUndoEvent(event)) {
      event.preventDefault()

      if (this.mask.undo()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
      return
    } else if (isRedoEvent(event)) {
      event.preventDefault()

      if (this.mask.redo()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
      return
    }

    if (event.key === BACKSPACE_KEY) {
      event.preventDefault()

      this.mask.setSelection(getSelection(event.target))
      if (this.mask.backspace()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
    }
  }

  handleBeforeInput (event) {
    if (event.metaKey || event.altKey || event.ctrlKey || event.key === ENTER_KEY) {
      return
    }

    event.preventDefault()

    this.mask.setSelection(getSelection(event.target))
    if (this.mask.input(event.data)) {
      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => {
        setSelection(this.refs.textField.input, this.mask.selection)
        triggerOnchange(this.state, this.props)
      })
    }
  }

  render () {
    return <TextField type='text'
      {...this.props}
      ref='textField'
      floatingLabelText='Card Number'
      hintText={PATTERN.replace(/[1#]/g, 'X')}
      size={this.mask.pattern.length}
      maxLength={this.mask.pattern.length}
      onChange={this.handleChange}
      onPaste={this.handlePaste}
      onKeyDown={this.handleKeyDown}
      onBeforeInput={this.handleBeforeInput}
      value={this.state.text} />
  }
}

CardNumberInput.propTypes = {
  value: React.PropTypes.string,
  onChange: React.PropTypes.func
}

function getDisplayMaskValue (mask) {
  if (!mask.value.includes(mask.pattern.placeholderChar)) {
    return mask.getValue()
  }

  return mask
    .getValue()
    .slice(0, mask.selection.end)
}

function triggerOnchange (state, props) {
  if (props.onChange) {
    props.onChange({
      text: state.text,
      isValid: state.isValid
    })
  }
}

function isUndoEvent (event) {
  return (event.ctrlKey || event.metaKey) && event.keyCode === (event.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedoEvent (event) {
  return (event.ctrlKey || event.metaKey) && event.keyCode === (event.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getTextFromClipboardData (event) {
  let result

  // IE
  if (window.clipboardData) {
    result = window.clipboardData.getData('Text')
  } else {
    result = (event.originalEvent || event).clipboardData.getData('text/plain')
  }

  return result
}

export default CardNumberInput
<commit_msg>[CardNumber]: show error line in case not valid input.<commit_after>import React, { Component } from 'react'
import times from 'lodash.times'
import isNil from 'lodash.isnil'
import isEmpty from 'lodash.isempty'
import { getSelection, setSelection } from 'react/lib/ReactInputSelection'
import InputMask from 'inputmask-core'
import TextField from 'material-ui/TextField'

const ENTER_KEY = 'Enter'
const BACKSPACE_KEY = 'Backspace'
const KEYCODE_Y = 89
const KEYCODE_Z = 90
const PATTERN = '11-1111-11-11111##'
const OPTIONAL_CHAR = '#'

class CardNumberInput extends Component {
  constructor () {
    super()

    this.state = {
      isValid: false,
      text: ''
    }

    this.mask = null
    this.minLength = 0
    this.maxLength = Number.MAX_SAFE_INTEGER
    this.handlePaste = this.handlePaste.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)
    this.handleBeforeInput = this.handleBeforeInput.bind(this)
    this.handleChange = this.handleChange.bind(this)
  }

  componentWillMount () {
    const selection = this.props.value ? this.props.value.length : 0
    let options = {
      pattern: PATTERN,
      placeholderChar: ' ',
      value: this.props.value,
      selection: {
        start: selection,
        end: selection
      },
      formatCharacters: {
        OPTIONAL_CHAR: {
          validate: char => isNil(char) || /[\s\d]/.test(char)
        }
      }
    }
    this.mask = new InputMask(options)
    this.minLength = this.mask.pattern.pattern.filter(char => char !== OPTIONAL_CHAR).length
    this.maxLength = this.mask.pattern.pattern.length

    const value = getDisplayMaskValue(this.mask)
    this.setState({
      text: value,
      isValid: this.isValidValue(value)
    })
  }

  componentWillReceiveProps (nextProps) {
    if (this.state.text !== nextProps.value) {
      const selection = nextProps.value ? nextProps.value.length : 0

      this.mask.setValue(nextProps.value)
      this.mask.setSelection({
        start: selection,
        end: selection
      })

      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  isValidValue (value) {
    return !isNil(value) &&
      value !== this.mask.emptyValue &&
      (this.minLength <= value.length && this.maxLength >= value.length)
  }

  getErrorText () {
    return this.state.isValid || isEmpty(this.state.text) ? null : ' '
  }

  handleChange (event) {
    const maskValue = getDisplayMaskValue(this.mask)
    const elementValue = event.target.value

    if (elementValue !== maskValue) {
      const sizeDiff = Math.abs(maskValue.length - elementValue.length)

      // Cut, delete operations will have shortened the value
      // or if browser don't support 'KeyboardEvent.key' property
      if (elementValue.length < maskValue.length) {
        this.mask.setSelection(getSelection(event.target))
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      // in case browser don't support 'beforeInput' event
      } else if (elementValue.length > maskValue.length) {
        times(sizeDiff, i => {
          this.mask.input(elementValue[elementValue.length - sizeDiff - i])
        })
      }

      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  handlePaste (event) {
    event.preventDefault()

    this.mask.setSelection(getSelection(event.target))
    let pastedValue = getTextFromClipboardData(event)

    if (this.mask.paste(pastedValue)) {
      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => triggerOnchange(this.state, this.props))
    }
  }

  handleKeyDown (event) {
    if (isUndoEvent(event)) {
      event.preventDefault()

      if (this.mask.undo()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
      return
    } else if (isRedoEvent(event)) {
      event.preventDefault()

      if (this.mask.redo()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
      return
    }

    if (event.key === BACKSPACE_KEY) {
      event.preventDefault()

      this.mask.setSelection(getSelection(event.target))
      if (this.mask.backspace()) {
        const value = getDisplayMaskValue(this.mask)
        this.setState({
          text: value,
          isValid: this.isValidValue(value)
        }, () => triggerOnchange(this.state, this.props))
      }
    }
  }

  handleBeforeInput (event) {
    if (event.metaKey || event.altKey || event.ctrlKey || event.key === ENTER_KEY) {
      return
    }

    event.preventDefault()

    this.mask.setSelection(getSelection(event.target))
    if (this.mask.input(event.data)) {
      const value = getDisplayMaskValue(this.mask)
      this.setState({
        text: value,
        isValid: this.isValidValue(value)
      }, () => {
        setSelection(this.refs.textField.input, this.mask.selection)
        triggerOnchange(this.state, this.props)
      })
    }
  }

  render () {
    return <TextField type='text'
      {...this.props}
      ref='textField'
      floatingLabelText='Card Number'
      hintText={PATTERN.replace(/[1#]/g, 'X')}
      size={this.mask.pattern.length}
      maxLength={this.mask.pattern.length}
      onChange={this.handleChange}
      onPaste={this.handlePaste}
      onKeyDown={this.handleKeyDown}
      onBeforeInput={this.handleBeforeInput}
      errorText={this.getErrorText()}
      value={this.state.text} />
  }
}

CardNumberInput.propTypes = {
  value: React.PropTypes.string,
  onChange: React.PropTypes.func
}

function getDisplayMaskValue (mask) {
  if (!mask.value.includes(mask.pattern.placeholderChar)) {
    return mask.getValue()
  }

  return mask
    .getValue()
    .slice(0, mask.selection.end)
}

function triggerOnchange (state, props) {
  if (props.onChange) {
    props.onChange({
      text: state.text,
      isValid: state.isValid
    })
  }
}

function isUndoEvent (event) {
  return (event.ctrlKey || event.metaKey) && event.keyCode === (event.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedoEvent (event) {
  return (event.ctrlKey || event.metaKey) && event.keyCode === (event.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getTextFromClipboardData (event) {
  let result

  // IE
  if (window.clipboardData) {
    result = window.clipboardData.getData('Text')
  } else {
    result = (event.originalEvent || event).clipboardData.getData('text/plain')
  }

  return result
}

export default CardNumberInput
<|endoftext|>