<commit_before>import test from 'ava'
import { Server } from 'mongodb-topology-manager'
import fs from 'fs'
import path from 'path'
import HAMongoClient from '../src/ha_mongo_client'

const DB_PATH = path.join(__dirname, 'db')

test.before(() => {

    const serverManager = new Server()
    return serverManager.discover().catch(err => { throw Error('Unable to find mongod binary') })
})

test('Correct intervals', (t) => new Promise((resolve, reject) => {

    const attemptsLimit = 5

    let interval = 0.1
    let multiplier = 2
    let maxInterval = 1
    let attemptsCounter = 0

    const hAMongoClient = new HAMongoClient('mongodb://unknown:27017', {}, {
        initInterval: interval,
        multiplier:   multiplier,
        maxInterval:  maxInterval,
    })

    t.plan(attemptsLimit * 2)

    hAMongoClient.on('retry', retry => {

        attemptsCounter += 1
        interval = Math.min(interval * multiplier, maxInterval)

        t.is(retry.attempt, attemptsCounter)
        t.is(retry.interval, interval)

        if (attemptsCounter === attemptsLimit) {

            hAMongoClient.abort()
            resolve()

        }
    })

    hAMongoClient.connect()
}))

test('Connecting to delayed mongod', (t) => {

    t.plan(1)

    const serverManager = new Server('mongod', { dbpath: DB_PATH })

    const hAMongoClient = new HAMongoClient('mongodb://localhost:27017', {})

    return serverManager.discover()
        .then(() => {
            // Starting mongod with delay
            setTimeout(
                () => serverManager.purge()
                    .then(() => serverManager.start())
                    .catch(err => { throw err }),
                1000
            )
        })
        .then(() => hAMongoClient.connect())
        .then(db => db.close())
        .then(() => serverManager.stop())
        .then(() => serverManager.purge())
        .then(() => new Promise((resolve, reject) => {
            fs.rmdir(DB_PATH, (err) => {
                if (err) return reject(err)
                resolve()
            })
        }))
        .then(() => t.pass())
        .catch(err => { throw err })
})
<commit_msg>Update tests code style due to ESLint rules<commit_after>import test from 'ava'
import { Server } from 'mongodb-topology-manager'
import fs from 'fs'
import path from 'path'
import HAMongoClient from '../src/ha_mongo_client'

const DB_PATH = path.join(__dirname, 'db')

test.before(() => {

    const serverManager = new Server()

    return serverManager
        .discover()
        .catch(() => {

            throw Error('Unable to find mongod binary')

        })

})

test('Correct intervals', (t) => new Promise((resolve) => {

    const attemptsLimit = 5
    const multiplier = 2
    const maxInterval = 1
    let interval = 0.1
    let attemptsCounter = 0

    const hAMongoClient = new HAMongoClient('mongodb://unknown:27017', {}, {
        initInterval: interval,
        multiplier,
        maxInterval,
    })

    t.plan(attemptsLimit * 2)

    hAMongoClient.on('retry', retry => {

        attemptsCounter += 1
        interval = Math.min(interval * multiplier, maxInterval)

        t.is(retry.attempt, attemptsCounter)
        t.is(retry.interval, interval)

        if (attemptsCounter === attemptsLimit) {

            hAMongoClient.abort()
            resolve()

        }

    })

    hAMongoClient.connect()

}))

test('Connecting to delayed mongod', (t) => {

    const serverManager = new Server('mongod', { dbpath: DB_PATH })
    const hAMongoClient = new HAMongoClient('mongodb://localhost:27017', {})

    t.plan(1)

    return serverManager.discover()
        .then(() => {

            // Starting mongod with delay
            setTimeout(
                () => serverManager.purge()
                    .then(() => serverManager.start())
                    .catch(err => {

                        throw err

                    }),
                1000
            )

        })
        .then(() => hAMongoClient.connect())
        .then(db => db.close())
        .then(() => serverManager.stop())
        .then(() => serverManager.purge())
        .then(() => new Promise((resolve, reject) => {

            fs.rmdir(DB_PATH, (err) => {

                if (err) return reject(err)

                return resolve()

            })

        }))
        .then(() => t.pass())
        .catch(err => {

            throw err

        })

})
<|endoftext|>


<commit_before>'use strict';

var archiveType = require('archive-type');
var Download = require('../');
var nock = require('nock');
var path = require('path');
var fixture = path.join.bind(path, __dirname, 'fixtures');
var tar = require('gulp-tar');
var test = require('ava');

test('expose a constructor', function (t) {
	t.plan(1);
	t.assert(typeof Download === 'function');
});

test('add a plugin to the middleware stack', function (t) {
	t.plan(1);

	var download = new Download()
		.use(function () {});

	t.assert(download.ware.fns.length === 1);
});

test('set a file to get', function (t) {
	t.plan(1);

	var download = new Download()
		.get('http://example.com/test.jpg');

	t.assert(download._get[0] === 'http://example.com/test.jpg');
});

test('download a file', function (t) {
	t.plan(4);

	var download = new Download()
		.get('http://foo.com/file.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(files[0].path === 'file.zip');
		t.assert(archiveType(files[0].contents) === 'zip');
	});
});

test('download a file and rename it', function (t) {
	t.plan(4);

	var download = new Download()
		.get('http://foo.com/file.zip')
		.rename('foobar.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(path.basename(files[0].path) === 'foobar.zip');
		t.assert(archiveType(files[0].contents) === 'zip');
	});
});

test('download and extract a file', function (t) {
	t.plan(3);

	var download = new Download({ extract: true })
		.get('http://foo.com/file.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(files[0].path === 'file.txt');
	});
});

test('download and perform task on it', function (t) {
	t.plan(3);

	var download = new Download()
		.get('http://foo.com/file.zip')
		.pipe(tar('file.tar'));

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(path.basename(files[0].path) === 'file.tar');
	});
});

test('error on 404', function (t) {
	t.plan(2);

	var download = new Download()
		.get('http://foo.com/error');
	var scope = nock('http://foo.com')
		.get('/error')
		.reply(404);

	download.run(function (err) {
		t.assert(err.message === '404');
		t.assert(scope.isDone());
	});
});
<commit_msg>Provide a helpful error message on connection error<commit_after>'use strict';

var archiveType = require('archive-type');
var Download = require('../');
var nock = require('nock');
var path = require('path');
var fixture = path.join.bind(path, __dirname, 'fixtures');
var tar = require('gulp-tar');
var test = require('ava');

test('expose a constructor', function (t) {
	t.plan(1);
	t.assert(typeof Download === 'function');
});

test('add a plugin to the middleware stack', function (t) {
	t.plan(1);

	var download = new Download()
		.use(function () {});

	t.assert(download.ware.fns.length === 1);
});

test('set a file to get', function (t) {
	t.plan(1);

	var download = new Download()
		.get('http://example.com/test.jpg');

	t.assert(download._get[0] === 'http://example.com/test.jpg');
});

test('download a file', function (t) {
	t.plan(4);

	var download = new Download()
		.get('http://foo.com/file.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(files[0].path === 'file.zip');
		t.assert(archiveType(files[0].contents) === 'zip');
	});
});

test('download a file and rename it', function (t) {
	t.plan(4);

	var download = new Download()
		.get('http://foo.com/file.zip')
		.rename('foobar.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(path.basename(files[0].path) === 'foobar.zip');
		t.assert(archiveType(files[0].contents) === 'zip');
	});
});

test('download and extract a file', function (t) {
	t.plan(3);

	var download = new Download({ extract: true })
		.get('http://foo.com/file.zip');

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(files[0].path === 'file.txt');
	});
});

test('download and perform task on it', function (t) {
	t.plan(3);

	var download = new Download()
		.get('http://foo.com/file.zip')
		.pipe(tar('file.tar'));

	var scope = nock('http://foo.com')
		.get('/file.zip')
		.replyWithFile(200, fixture('file.zip'));

	download.run(function (err, files) {
		t.assert(!err);
		t.assert(scope.isDone());
		t.assert(path.basename(files[0].path) === 'file.tar');
	});
});

test('error on 404', function (t) {
	t.plan(3);

	var download = new Download()
		.get('http://foo.com/error');
	var scope = nock('http://foo.com')
		.get('/error')
		.reply(404);

	download.run(function (err) {
		t.assert(err.message === 'Couldn\'t connect to http://foo.com/error (404)');
		t.assert(err.code === 404);
		t.assert(scope.isDone());
	});
});
<|endoftext|>


<commit_before>/*
 * Ext JS Connect
 * Copyright(c) 2010 Sencha Inc.
 * MIT Licensed
 *
 * Modified by Yahoo!
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Yahoo! Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*
 * Connect staticProvider middleware adapted for Mojito *
 ********************************************************
 * This was modified to allow load all combo urls from the
 * Mojito development environment instead of one static
 * directory.
 ********************************************************
 */

/*jslint anon:true, sloppy:true, nomen:true, stupid:true, node: true*/

/*
DECLAIMER: this is VERY experimental, and the purpose of this
middleware is to provide an easy way to load yui modules by their
names rather than the real static path. Why? because we can then
leverage the YUI Loader on the client side to take care of the
dependencies computations efficiently.
    from: @caridy
*/

/*
 * Module dependencies.
 */
var libfs = require('fs'),
    mime = require('mime'),
    libpath = require('path'),
    existsSync = libfs.existsSync || libpath.existsSync,
    parseUrl = require('url').parse,
    logger,
    NAME = 'ComboHandler',

    MODULE_META_ENTRIES          = ['requires', 'use', 'optional', 'skinnable', 'after', 'condition'],
    // TODO: revisit this list with @davglass
    MODULE_META_PRIVATE_ENTRIES  = ['after', 'expanded', 'supersedes', 'ext', '_parsed', '_inspected',
                                    'skinCache', 'langCache'],

    DEFAULT_HEADERS = {
        '.js': {
            'Content-Type': 'application/javascript; charset=utf-8'
        },
        '.css': {
            'Content-Type': 'text/css; charset=utf-8'
        }
    };

/*
 * File buffer cache.
 */
var _cache = {};

/*
 * Check if `req` and response `headers`.
 *
 * @param {IncomingMessage} req
 * @param {Object} headers
 * @return {Boolean}
 * @api private
 */
function modified(req, headers) {
    var modifiedSince = req.headers['if-modified-since'],
        lastModified = headers['Last-Modified'],
        noneMatch = req.headers['if-none-match'],
        etag = headers.ETag;

    // Check If-None-Match
    if (noneMatch && etag && noneMatch === etag) {
        return false;
    }

    // Check If-Modified-Since
    if (modifiedSince && lastModified) {
        modifiedSince = new Date(modifiedSince);
        lastModified = new Date(lastModified);
        // Ignore invalid dates
        if (!isNaN(modifiedSince.getTime())) {
            if (lastModified <= modifiedSince) {
                return false;
            }
        }
    }

    return true;
}

/*
 * Check if `req` is a conditional GET request.
 *
 * @method conditionalGET
 * @param {IncomingMessage} req
 * @return {Boolean}
 * @api private
 */
function conditionalGET(req) {
    return req.headers['if-modified-since'] ||
        req.headers['if-none-match'];
}

/*
 * Return an ETag in the form of size-mtime.
 *
 * @method etag
 * @param {Object} stat
 * @return {String}
 * @api private
 */
function etag(stat) {
    return stat.size + '-' + Number(stat.mtime);
}

/*
 * Respond with 304 "Not Modified".
 *
 * @method notModified
 * @param {ServerResponse} res
 * @param {Object} headers
 * @api private
 */
function notModified(res, headers) {
    // Strip Content-* headers
    Object.keys(headers).forEach(function(field) {
        if (0 === field.indexOf('Content')) {
            delete headers[field];
        }
    });
    res.writeHead(304, headers);
    res.end();
}

/*
 * Respond with 403 "Forbidden".
 *
 * @method forbidden
 * @param {ServerResponse} res
 * @api private
 */
function forbidden(res) {
    var body = 'Forbidden';
    res.writeHead(403, {
        'Content-Type': 'text/plain',
        'Content-Length': body.length
    });
    res.end(body);
}

/*
 * Clear the memory cache for `key` or the entire cache.
 *
 * @method clearCache
 * @param {String} key
 * @api public
 */
function clearCache(key) {
    if (key) {
        delete _cache[key];
    } else {
        _cache = {};
    }
}

function processMeta(resolvedMods, modules, expanded_modules, conditions) {
    var m,
        i,
        module;

    for (m in resolvedMods) {
        if (resolvedMods.hasOwnProperty(m)) {
            module = resolvedMods[m];

            if (module.condition && module.condition.test) {
                conditions[module.name] = module.condition.test.toString();
                module.condition.test = "{" + module.name + "}";
            }

            modules[module.name] = {};
            if (module.type === 'css') {
                modules[module.name] = 'css';
            }
            for (i = 0; i < MODULE_META_ENTRIES.length; i += 1) {
                if (module[MODULE_META_ENTRIES[i]]) {
                    modules[module.name][MODULE_META_ENTRIES[i]] =
                        module[MODULE_META_ENTRIES[i]];
                }
            }

            expanded_modules[module.name] = module;
            for (i = 0; i < MODULE_META_PRIVATE_ENTRIES.length; i += 1) {
                delete module[MODULE_META_PRIVATE_ENTRIES[i]];
            }
        }
    }
}

/*
 * Static file server.
 *
 * Options:
 *
 *   - `root`     Root path from which to serve static files.
 *   - `maxAge`   Browser cache maxAge in milliseconds, defaults to 0
 *   - `cache`    When true cache files in memory indefinitely,
 *                until invalidated by a conditional GET request.
 *                When given, maxAge will be derived from this value.
 *
 * @param {Object} options
 * @return {Function}
 * @api public
 */
function staticProvider(store, globalLogger, Y) {
    logger = globalLogger;
    var appConfig = store.getStaticAppConfig(),
        options = appConfig.staticHandling || {},
        cache = options.cache,
        maxAge = options.maxAge,
        urls = store.getAllModulesURLs(),

        // collecting client side metadata
        mojits = store.yui.getConfigAllMojits('client', {}),
        shared = store.yui.getConfigShared('client', {}, false),
        modules_config = Y.merge((mojits.modules || {}), (shared.modules || {})),
        loader,
        resolved,
        appMetaData,
        appResolvedMetaData,

        // other structures
        expanded_modules = {}, // expanded meta (including fullpaths)
        modules = {},          // regular meta  (a la loader-yui3)
        conditions = {},       // hash to store conditional functions
        name;

    // using the loader at the server side to compute the loader metadata
    // to avoid loading the whole thing on demand.
    loader = new Y.Loader(Y.merge({
        ignoreRegistered: true,
        modules: modules_config
    }, {
        require: Y.Object.keys(modules_config)
    }));
    resolved = loader.resolve(true);

    // Need to make a copy otherwise the changes we make deep in this structure
    // will bleed into the loader, especially causing condition.test to fail.
    resolved = Y.mojito.util.copy(resolved);

    if (cache && !maxAge) {
        maxAge = cache;
    }
    maxAge = maxAge || 0;

    processMeta(resolved.jsMods,  modules, expanded_modules, conditions);
    processMeta(resolved.cssMods, modules, expanded_modules, conditions);

    appMetaData         = JSON.stringify(modules);
    appResolvedMetaData = JSON.stringify(expanded_modules);

    for (name in conditions) {
        if (conditions.hasOwnProperty(name)) {
            appMetaData         = appMetaData.replace('"{' + name + '}"', conditions[name]);
            appResolvedMetaData = appResolvedMetaData.replace('"{' + name + '}"', conditions[name]);
        }
    }

    return function(req, res, next) {
        if (req.method !== 'GET' && req.method !== 'HEAD') {
            return next();
        }

        var url = parseUrl(req.url),
            files = [],
            filename = '',
            module = '',
            ext = '',
            yui = '',
            result = [],
            counter = 0,
            i = 0,
            hit,
            head = (req.method === 'HEAD');

        // only combo requests are allow here
        if (url.pathname !== '/combo') {
            return next();
        }

        logger.log('serving static path: ' + url.pathname, 'debug', 'static-handler');

        // YIV might be messing around with the querystring params
        // so we need to revert back to /
        // TODO: this might not work in Windows
        files = url.query.replace(/\=/g, '').replace(/\%2F/g, '/').split('&');

        function readHandler(index, filename) {
            return function (err, data) {
                var headers,
                    content = '',
                    i;

                counter += 1;
                if (err) {
                    logger.log('NOT FOUND: ' + filename, 'warn', NAME);
                } else {
                    result[index].content = data;
                }
                if (counter === files.length) {

                    for (i = 0; i < counter; i += 1) {
                        content += result[i].content;
                    }
                    headers = Y.merge({
                        'Content-Length': content.length,
                        'Last-Modified': new Date().toUTCString(),
                        'Cache-Control': 'public max-age=' + (maxAge / 1000)
                    }, (DEFAULT_HEADERS[ext] || {}));

                    res.writeHead(200, headers);
                    res.end(head ? undefined : content);

                }
            };
        }

        if (files.length === 0) {
            // probably an empty /combo? request
            res.writeHead(400);
            res.end(undefined);
            return;
        }

        // combo response's content-type should be
        // resolved from the first file in the list
        ext = libpath.extname(files[0]);

        if (!ext || !DEFAULT_HEADERS.hasOwnProperty(ext)) {
            // probably an invalid request
            res.writeHead(400);
            res.end(undefined);
            return;
        }

        // validating all files before doing anything else
        // so errors can be found early on.
        for (i = 0; i < files.length; i += 1) {
            yui = libpath.join(__dirname,
                    '../../../node_modules/yui', files[i]);

            // something like foo/bar-min.js should become just "bar"
            module = libpath.basename(files[i], ext).
                replace(/\-(min|debug)$/, '');

            if (module === 'loader-app-base') {
                result[i] = {
                    fullpath: module,
                    content: 'YUI.add("loader",function(Y){' +
                                'YUI.Env[Y.version].modules=YUI.Env[Y.version].modules||' +  appMetaData + ';' +
                             '}, "", {requires:["loader-base"]});'
                };
            } else if (module === 'loader-app-full') {
                result[i] = {
                    fullpath: module,
                    content: 'YUI.add("loader",function(Y){' +
                                'YUI.Env[Y.version].modules=YUI.Env[Y.version].modules||' + appResolvedMetaData + ';' +
                             '}, "", {requires:["loader-base"]});'
                };
            } else if (urls[module]) {
                result[i] = {
                    fullpath: urls[module],
                    content: ''
                };
            } else if ((files[i].indexOf('..') === -1) && existsSync(yui)) {
                result[i] = {
                    fullpath: yui,
                    content: ''
                };
            } else {
                logger.log('Invalid module name: ' + module, 'warn', NAME);
                res.writeHead(400);
                res.end(undefined);
                break;
            }

        }

        // async queue implementation
        if (files.length > 0 && (result.length === files.length)) {
            for (i = 0; i < result.length; i += 1) {
                filename = result[i].fullpath;
                if (result[i].content) {
                    // if we already have content in memory, let's
                    // just use it directly.
                    readHandler(i, filename)(null, result[i].content);
                } else {
                    libfs.readFile(filename, readHandler(i, filename));
                }
            }
        } else {
            // this should never happen, because an invalid
            // module error should happen before reaching this.
            res.writeHead(400);
            res.end(undefined);
            return;
        }

    };
}

/**
 * Export function to create the static handler.
 * @param {Object} config The configuration data for the handler.
 * @return {Object} A static handler.
 */
module.exports = function(config) {
    return staticProvider(config.store, config.logger, config.Y);
};
<commit_msg>review<commit_after>/*
 * Ext JS Connect
 * Copyright(c) 2010 Sencha Inc.
 * MIT Licensed
 *
 * Modified by Yahoo!
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Yahoo! Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*
 * Connect staticProvider middleware adapted for Mojito *
 ********************************************************
 * This was modified to allow load all combo urls from the
 * Mojito development environment instead of one static
 * directory.
 ********************************************************
 */

/*jslint anon:true, sloppy:true, nomen:true, stupid:true, node: true*/

/*
DECLAIMER: this is VERY experimental, and the purpose of this
middleware is to provide an easy way to load yui modules by their
names rather than the real static path. Why? because we can then
leverage the YUI Loader on the client side to take care of the
dependencies computations efficiently.
    from: @caridy
*/

/*
 * Module dependencies.
 */
var libfs = require('fs'),
    mime = require('mime'),
    libpath = require('path'),
    parseUrl = require('url').parse,
    logger,
    NAME = 'ComboHandler',

    MODULE_META_ENTRIES          = ['requires', 'use', 'optional', 'skinnable', 'after', 'condition'],
    // TODO: revisit this list with @davglass
    MODULE_META_PRIVATE_ENTRIES  = ['after', 'expanded', 'supersedes', 'ext', '_parsed', '_inspected',
                                    'skinCache', 'langCache'],

    DEFAULT_HEADERS = {
        '.js': {
            'Content-Type': 'application/javascript; charset=utf-8'
        },
        '.css': {
            'Content-Type': 'text/css; charset=utf-8'
        }
    },

    // This dummy templace will be use to produce the loader content.
    // TODO: we should use YUI.applyConfig() instead of the internal
    //       YUI.Env API, but that's pending due a bug in YUI:
    //       http://yuilibrary.com/projects/yui3/ticket/2532854
    LOADER_MODULE_TEMPLATE = 'YUI.add("loader",function(Y){' +
                                'YUI.Env[Y.version].modules=YUI.Env[Y.version].modules||{metadata};' +
                             '}, "", {requires:["loader-base"]});';

/*
 * File buffer cache.
 */
var _cache = {};

/*
 * Check if `req` and response `headers`.
 *
 * @param {IncomingMessage} req
 * @param {Object} headers
 * @return {Boolean}
 * @api private
 */
function modified(req, headers) {
    var modifiedSince = req.headers['if-modified-since'],
        lastModified = headers['Last-Modified'],
        noneMatch = req.headers['if-none-match'],
        etag = headers.ETag;

    // Check If-None-Match
    if (noneMatch && etag && noneMatch === etag) {
        return false;
    }

    // Check If-Modified-Since
    if (modifiedSince && lastModified) {
        modifiedSince = new Date(modifiedSince);
        lastModified = new Date(lastModified);
        // Ignore invalid dates
        if (!isNaN(modifiedSince.getTime())) {
            if (lastModified <= modifiedSince) {
                return false;
            }
        }
    }

    return true;
}

/*
 * Check if `req` is a conditional GET request.
 *
 * @method conditionalGET
 * @param {IncomingMessage} req
 * @return {Boolean}
 * @api private
 */
function conditionalGET(req) {
    return req.headers['if-modified-since'] ||
        req.headers['if-none-match'];
}

/*
 * Return an ETag in the form of size-mtime.
 *
 * @method etag
 * @param {Object} stat
 * @return {String}
 * @api private
 */
function etag(stat) {
    return stat.size + '-' + Number(stat.mtime);
}

/*
 * Respond with 304 "Not Modified".
 *
 * @method notModified
 * @param {ServerResponse} res
 * @param {Object} headers
 * @api private
 */
function notModified(res, headers) {
    // Strip Content-* headers
    Object.keys(headers).forEach(function(field) {
        if (0 === field.indexOf('Content')) {
            delete headers[field];
        }
    });
    res.writeHead(304, headers);
    res.end();
}

/*
 * Respond with 403 "Forbidden".
 *
 * @method forbidden
 * @param {ServerResponse} res
 * @api private
 */
function forbidden(res) {
    var body = 'Forbidden';
    res.writeHead(403, {
        'Content-Type': 'text/plain',
        'Content-Length': body.length
    });
    res.end(body);
}

/*
 * Clear the memory cache for `key` or the entire cache.
 *
 * @method clearCache
 * @param {String} key
 * @api public
 */
function clearCache(key) {
    if (key) {
        delete _cache[key];
    } else {
        _cache = {};
    }
}

function processMeta(resolvedMods, modules, expanded_modules, conditions) {
    var m,
        i,
        module;

    for (m in resolvedMods) {
        if (resolvedMods.hasOwnProperty(m)) {
            module = resolvedMods[m];

            if (module.condition && module.condition.test) {
                conditions[module.name] = module.condition.test.toString();
                module.condition.test = "{" + module.name + "}";
            }

            modules[module.name] = {};
            if (module.type === 'css') {
                modules[module.name] = 'css';
            }
            for (i = 0; i < MODULE_META_ENTRIES.length; i += 1) {
                if (module[MODULE_META_ENTRIES[i]]) {
                    modules[module.name][MODULE_META_ENTRIES[i]] =
                        module[MODULE_META_ENTRIES[i]];
                }
            }

            expanded_modules[module.name] = module;
            for (i = 0; i < MODULE_META_PRIVATE_ENTRIES.length; i += 1) {
                delete module[MODULE_META_PRIVATE_ENTRIES[i]];
            }
        }
    }
}

/*
 * Static file server.
 *
 * Options:
 *
 *   - `root`     Root path from which to serve static files.
 *   - `maxAge`   Browser cache maxAge in milliseconds, defaults to 0
 *   - `cache`    When true cache files in memory indefinitely,
 *                until invalidated by a conditional GET request.
 *                When given, maxAge will be derived from this value.
 *
 * @param {Object} options
 * @return {Function}
 * @api public
 */
function staticProvider(store, globalLogger, Y) {
    logger = globalLogger;
    var appConfig = store.getStaticAppConfig(),
        options = appConfig.staticHandling || {},
        cache = options.cache,
        maxAge = options.maxAge,
        urls = store.getAllModulesURLs(),

        // collecting client side metadata
        mojits = store.yui.getConfigAllMojits('client', {}),
        shared = store.yui.getConfigShared('client', {}, false),
        modules_config = Y.merge((mojits.modules || {}), (shared.modules || {})),
        loader,
        resolved,
        appMetaData,
        appResolvedMetaData,

        // other structures
        expanded_modules = {}, // expanded meta (including fullpaths)
        modules = {},          // regular meta  (a la loader-yui3)
        conditions = {},       // hash to store conditional functions
        name;

    // using the loader at the server side to compute the loader metadata
    // to avoid loading the whole thing on demand.
    loader = new Y.Loader(Y.merge({
        ignoreRegistered: true,
        modules: modules_config
    }, {
        require: Y.Object.keys(modules_config)
    }));
    resolved = loader.resolve(true);

    // Need to make a copy otherwise the changes we make deep in this structure
    // will bleed into the loader, especially causing condition.test to fail.
    resolved = Y.mojito.util.copy(resolved);

    if (cache && !maxAge) {
        maxAge = cache;
    }
    maxAge = maxAge || 0;

    processMeta(resolved.jsMods,  modules, expanded_modules, conditions);
    processMeta(resolved.cssMods, modules, expanded_modules, conditions);

    appMetaData         = JSON.stringify(modules);
    appResolvedMetaData = JSON.stringify(expanded_modules);

    for (name in conditions) {
        if (conditions.hasOwnProperty(name)) {
            appMetaData         = appMetaData.replace('"{' + name + '}"', conditions[name]);
            appResolvedMetaData = appResolvedMetaData.replace('"{' + name + '}"', conditions[name]);
        }
    }

    return function(req, res, next) {
        if (req.method !== 'GET' && req.method !== 'HEAD') {
            return next();
        }

        var url = parseUrl(req.url),
            files = [],
            filename = '',
            module = '',
            ext = '',
            result = [],
            counter = 0,
            i = 0,
            hit,
            head = (req.method === 'HEAD');

        // only combo requests are allow here
        if (url.pathname !== '/combo') {
            return next();
        }

        logger.log('serving static path: ' + url.pathname, 'debug', 'static-handler');

        // YIV might be messing around with the querystring params
        // trying to formalize them by adding = and transforming /
        // so we need to revert back to / and remove the =
        // TODO: this might not work in Windows
        files = url.query.replace(/[=]/g, '').replace(/%2F/g, '/').split('&');

        function readHandler(index, filename) {
            return function (err, data) {
                var headers,
                    content = '',
                    i;

                counter += 1;
                if (err) {
                    logger.log('NOT FOUND: ' + filename, 'warn', NAME);
                } else {
                    result[index].content = data;
                }
                if (counter === files.length) {

                    for (i = 0; i < counter; i += 1) {
                        content += result[i].content;
                    }
                    headers = Y.merge({
                        'Content-Length': content.length,
                        'Last-Modified': new Date().toUTCString(),
                        'Cache-Control': 'public max-age=' + (maxAge / 1000)
                    }, (DEFAULT_HEADERS[ext] || {}));

                    res.writeHead(200, headers);
                    res.end(head ? undefined : content);

                }
            };
        }

        if (files.length === 0) {
            // probably an empty /combo? request
            res.writeHead(400);
            res.end(undefined);
            return;
        }

        // combo response's content-type should be
        // resolved from the first file in the list
        ext = libpath.extname(files[0]);

        if (!ext || !DEFAULT_HEADERS.hasOwnProperty(ext)) {
            // probably an invalid request
            res.writeHead(400);
            res.end(undefined);
            return;
        }

        // validating all files before doing anything else
        // so errors can be found early on.
        for (i = 0; i < files.length; i += 1) {

            // something like foo/bar-min.js should become just "bar"
            module = libpath.basename(files[i], ext).
                replace(/\-(min|debug)$/, '');

            if (module === 'loader-app-base') {
                result[i] = {
                    fullpath: module,
                    content: LOADER_MODULE_TEMPLATE.replace('{metadata}', appMetaData)
                };
            } else if (module === 'loader-app-full') {
                result[i] = {
                    fullpath: module,
                    content: LOADER_MODULE_TEMPLATE.replace('{metadata}', appResolvedMetaData)
                };
            } else if (urls[module]) {
                result[i] = {
                    fullpath: urls[module],
                    content: ''
                };
            } else if (loader.moduleInfo[module] && loader.moduleInfo[module].path) {
                result[i] = {
                    fullpath: libpath.join(__dirname,
                        '../../../node_modules/yui', loader.moduleInfo[module].path),
                    content: ''
                };
            } else {
                logger.log('Invalid module name: ' + module, 'warn', NAME);
                res.writeHead(400);
                res.end(undefined);
                break;
            }

        }

        // async queue implementation
        if (files.length > 0 && (result.length === files.length)) {
            for (i = 0; i < result.length; i += 1) {
                filename = result[i].fullpath;
                if (result[i].content) {
                    // if we already have content in memory, let's
                    // just use it directly.
                    readHandler(i, filename)(null, result[i].content);
                } else {
                    try {
                        libfs.readFile(filename, readHandler(i, filename));
                    } catch (err) {
                        logger.log('Error reading: ' + filename, 'error', NAME);
                        // this should never happen, if the module is in loader
                        // meta, it should be in disk as well.
                        res.writeHead(400);
                        res.end(undefined);
                        break;
                    }
                }
            }
        } else {
            logger.log('Error producing combo: ' + files, 'error', NAME);
            // this should never happen, because an invalid
            // module error should happen before reaching this.
            res.writeHead(400);
            res.end(undefined);
            return;
        }

    };
}

/**
 * Export function to create the static handler.
 * @param {Object} config The configuration data for the handler.
 * @return {Object} A static handler.
 */
module.exports = function(config) {
    return staticProvider(config.store, config.logger, config.Y);
};
<|endoftext|>


<commit_before>/* global describe, it */
const FILE = 'FILE'
const EMAIL = 'test@mailjet.com'
const EMAIL2 = 'test2@mailjet.com'
const NAME = 'name'
const SUBJECT = 'subject'
const TEXT_PART = 'text'
const VAR = {'Key1': 'Value1', 'Key2': 'Value2'}
const SIMPLE_RECIPIENTS = [{email: EMAIL}, {email: EMAIL2}]
const UNIQUE_RECIPIENT = [{email: EMAIL}]
const RECIPIENTS_NAME = [{email: EMAIL, name: NAME}, {email: EMAIL2, name: NAME}]
const RECIPIENTS_VARS = [{email: EMAIL, vars: VAR}]
const API_KEY = process.env.MJ_APIKEY_PUBLIC
const API_SECRET = process.env.MJ_APIKEY_PRIVATE

var Mailjet = require('../mailjet-client')
var chai = require('chai')
var expect = chai.expect
var should = chai.should() // eslint-disable-line no-unused-vars
var Promise = require('bluebird')
var nock = require('nock')

if (typeof API_KEY === 'undefined' || typeof API_SECRET === 'undefined') {
  throw new Error('Mailjet API_KEY and API_SECRET are required, respectively ' + API_KEY + ' and ' + API_SECRET + ' given ')
}

describe('Basic Usage', function () {
  var client = Mailjet.connect(API_KEY, API_SECRET)

  describe('connection', function () {
    it('creates an instance of the client', function () {
      var connectionType1 = new Mailjet(API_KEY, API_SECRET)
      var connectionType2 = new Mailjet().connect(API_KEY, API_SECRET)
      var connectionType3 = Mailjet.connect(API_KEY, API_SECRET)

      expect('' + connectionType1.apiKey + connectionType1.apiSecret).to.equal('' + API_KEY + API_SECRET)
      expect('' + connectionType2.apiKey + connectionType2.apiSecret).to.equal('' + API_KEY + API_SECRET)
      expect('' + connectionType3.apiKey + connectionType3.apiSecret).to.equal('' + API_KEY + API_SECRET)
    })

    it('creates an instance of the client with options', function () {
      var options = {
        proxyUrl: 'http://localhost:3128',
        timeout: 10000
      }

      var connectionType1 = new Mailjet(API_KEY, API_SECRET, options)
      var connectionType2 = new Mailjet().connect(API_KEY, API_SECRET, options)
      var connectionType3 = Mailjet.connect(API_KEY, API_SECRET, options)

      var connections = [connectionType1, connectionType2, connectionType3]
      connections.forEach(function (connection) {
        expect(connection).to.have.property('apiKey', API_KEY)
        expect(connection).to.have.property('apiSecret', API_SECRET)
        expect(connection.options).to.have.property('proxyUrl', options.proxyUrl)
        expect(connection.options).to.have.property('timeout', 10000)
      })
    })
  })

  describe('method request', function () {
    describe('get', function () {
      var contact = client.get('contact')

      it('calls the contact ressource instance whith no parameters', function (done) {
        contact.request()
          .then(function (result) {
            result.body.should.be.a('object')
            expect(result.response.statusCode).to.equal(200)
            done()
          })
          .catch(function (reason) {
            // We want it to raise an error if it gets here
            expect(reason).to.equal(undefined)
            done()
          })
      })

      it('calls the contact ressource instance whith parameters', function (done) {
        var promise = contact.request({Name: 'Guillaume Badi'})
          .then(function (result) {
            result.body.should.be.a('object')
            expect(result.response.statusCode).to.be.within(200, 201)
            done()
          })
          .catch(function (reason) {
            // We want it to raise an error if it gets here
            expect(reason).to.equal(undefined)
            done()
          })
        expect(Promise.prototype.isPrototypeOf(promise)).to.equal(true)
      })

      it('calls the contact ressource instance with empty parameters', function (done) {
        contact.request({}).then(function (result) {
          result.body.should.be.a('object')
          expect(result.response.statusCode).to.be.within(200, 201)
          done()
        })
      })
    })

    describe('post', function () {
      var sender = client.post('sender')
      // eslint-disable-next-line no-unused-vars
      var deletedErrorMessage = 'There is an already existing deleted sender with the same email. ' +
        'You can use "validate" action in order to activate it.'
      // eslint-disable-next-line no-unused-vars
      var inactiveErrorMessage = 'There is an already existing inactive sender with the same email. ' +
        'You can use "validate" action in order to activate it.'

      it('calls the sender ressource instance whith no parameters', function (done) {
        sender.request().catch(function (reason) {
          //reason.ErrorMessage.should.equal(deletedErrorMessage)
          reason.statusCode.should.equal(400)
          done()
        })
      })

      it('calls the sender ressource instance whith invalid parameters', function (done) {
        sender.request({Name: 'Guillaume Badi'}).catch(function (reason) {
          //expect(reason.ErrorMessage).to.equal(deletedErrorMessage)
          expect(reason.statusCode).to.equal(400)
          done()
        })
      })

      it('calls the sender ressource instance whith valid parameters', function (done) {
        sender.request({email: 'gbadi@mailjet.com'})
          .then(function (result) {
            expect(result.response.statusCode).to.equal(201)
            done()
          })
          .catch(function (reason) {
            // if it fails because the sender already exist. should be 400
            // expect(reason.ErrorMessage).to.equal(inactiveErrorMessage)
            expect(reason.statusCode).to.equal(400)
            done()
          })
      })

      it('calls the sender resource with empty parameters', function (done) {
        sender.request({}).catch(function (reason) {
          // expect(reason.ErrorMessage).to.equal(deletedErrorMessage)
          expect(reason.statusCode).to.equal(400)
          done()
        })
      })
    })
  })
})

describe('Advanced API Calls', function () {
  function Example (fn, payload) {
    this.fn = fn
    this.payload = payload
    this.format = function (obj) { return JSON.stringify(obj).match(/\S+/g).join('') }
    this.call = function () {
      var res = this.fn.request(this.payload)
      var ret = res[0] + ' ' + this.format(res[1])
      return ret
    }
  }

  var client2 = new Mailjet(API_KEY, API_SECRET, null, true)

  const EXAMPLES_SET = [
    new Example(client2.get('contact')),
    new Example(client2.get('contact').id(2)),
    new Example(client2.get('contact/2')),
    new Example(client2.get('contact').id(3).action('getcontactslist')),
    new Example(client2.get('contact'), {countOnly: 1}),
    new Example(client2.get('contact'), {limit: 2}),
    new Example(client2.get('contact'), {offset: 233}),
    new Example(client2.get('contact'), {contatctList: 34}),
    new Example(client2.post('contactslist').id(34).action('managecontact'), {email: EMAIL}),
    new Example(client2.post('contactslist').id(34).action('csvdata'), FILE),
    new Example(client2.get('newsletter'), {filters: {CountOnly: 1}}),
    new Example(client2.get('batchjob').action('csverror')),
    new Example(client2.post('contact'), {email: EMAIL}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': UNIQUE_RECIPIENT}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': SIMPLE_RECIPIENTS}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': RECIPIENTS_NAME}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': RECIPIENTS_VARS})
  ]

  const EXPECTED_SET = [
    'https://api.mailjet.com/v3/REST/contact {}',
    'https://api.mailjet.com/v3/REST/contact/2 {}',
    'https://api.mailjet.com/v3/REST/contact/2 {}',
    'https://api.mailjet.com/v3/REST/contact/3/getcontactslist {}',
    'https://api.mailjet.com/v3/REST/contact/?countOnly=1 {}',
    'https://api.mailjet.com/v3/REST/contact/?limit=2 {}',
    'https://api.mailjet.com/v3/REST/contact/?offset=233 {}',
    'https://api.mailjet.com/v3/REST/contact/?contatctList=34 {}',
    'https://api.mailjet.com/v3/REST/contactslist/34/managecontact {"email":"test@mailjet.com"}',
    'https://api.mailjet.com/v3/DATA/contactslist/34/csvdata "FILE"',
    'https://api.mailjet.com/v3/REST/newsletter/?CountOnly=1 {}',
    'https://api.mailjet.com/v3/DATA/batchjob/csverror {}',
    'https://api.mailjet.com/v3/REST/contact {"email":"test@mailjet.com"}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com"},{"email":"test2@mailjet.com"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com","name":"name"},{"email":"test2@mailjet.com","name":"name"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com","vars":{"Key1":"Value1","Key2":"Value2"}}]}'
  ]

  EXPECTED_SET.forEach(function (test, index) {
    it('should output: ' + test, function () {
      EXAMPLES_SET[index].call().should.equal(test)
    })
  })
})

/* This fixture needs to run last so that it doesn't interfere with the other tests */
describe('Mocked API calls', function () {
  /* Set a very short timeout */
  var client = Mailjet.connect(API_KEY, API_SECRET, { timeout: 10 })

  describe('method request', function () {
    describe('get', function () {
      var contact = client.get('contact')

      it('calls the contact resource instance and the request times out', function (done) {
        /* Simulate a delayed response */
        nock('https://api.mailjet.com')
          .get('/v3/REST/contact')
          .delayConnection(1000)
          .reply(200, {})

        contact.request({})
          .then(function (result) {
            // We want it to raise an error if it gets here
            expect(result).to.equal(undefined)
            done()
          })
          .catch(function (reason) {
            expect(reason.ErrorMessage).to.equal('Timeout of 10ms exceeded')
            expect(reason.statusCode).to.equal(null)
            expect(reason.response).to.equal(null)
            done()
          })
      })
    })
  })
})
<commit_msg>fixed: legacy tests backslash issue<commit_after>/* global describe, it */
const FILE = 'FILE'
const EMAIL = 'test@mailjet.com'
const EMAIL2 = 'test2@mailjet.com'
const NAME = 'name'
const SUBJECT = 'subject'
const TEXT_PART = 'text'
const VAR = {'Key1': 'Value1', 'Key2': 'Value2'}
const SIMPLE_RECIPIENTS = [{email: EMAIL}, {email: EMAIL2}]
const UNIQUE_RECIPIENT = [{email: EMAIL}]
const RECIPIENTS_NAME = [{email: EMAIL, name: NAME}, {email: EMAIL2, name: NAME}]
const RECIPIENTS_VARS = [{email: EMAIL, vars: VAR}]
const API_KEY = process.env.MJ_APIKEY_PUBLIC
const API_SECRET = process.env.MJ_APIKEY_PRIVATE

var Mailjet = require('../mailjet-client')
var chai = require('chai')
var expect = chai.expect
var should = chai.should() // eslint-disable-line no-unused-vars
var Promise = require('bluebird')
var nock = require('nock')

if (typeof API_KEY === 'undefined' || typeof API_SECRET === 'undefined') {
  throw new Error('Mailjet API_KEY and API_SECRET are required, respectively ' + API_KEY + ' and ' + API_SECRET + ' given ')
}

describe('Basic Usage', function () {
  var client = Mailjet.connect(API_KEY, API_SECRET)

  describe('connection', function () {
    it('creates an instance of the client', function () {
      var connectionType1 = new Mailjet(API_KEY, API_SECRET)
      var connectionType2 = new Mailjet().connect(API_KEY, API_SECRET)
      var connectionType3 = Mailjet.connect(API_KEY, API_SECRET)

      expect('' + connectionType1.apiKey + connectionType1.apiSecret).to.equal('' + API_KEY + API_SECRET)
      expect('' + connectionType2.apiKey + connectionType2.apiSecret).to.equal('' + API_KEY + API_SECRET)
      expect('' + connectionType3.apiKey + connectionType3.apiSecret).to.equal('' + API_KEY + API_SECRET)
    })

    it('creates an instance of the client with options', function () {
      var options = {
        proxyUrl: 'http://localhost:3128',
        timeout: 10000
      }

      var connectionType1 = new Mailjet(API_KEY, API_SECRET, options)
      var connectionType2 = new Mailjet().connect(API_KEY, API_SECRET, options)
      var connectionType3 = Mailjet.connect(API_KEY, API_SECRET, options)

      var connections = [connectionType1, connectionType2, connectionType3]
      connections.forEach(function (connection) {
        expect(connection).to.have.property('apiKey', API_KEY)
        expect(connection).to.have.property('apiSecret', API_SECRET)
        expect(connection.options).to.have.property('proxyUrl', options.proxyUrl)
        expect(connection.options).to.have.property('timeout', 10000)
      })
    })
  })

  describe('method request', function () {
    describe('get', function () {
      var contact = client.get('contact')

      it('calls the contact ressource instance whith no parameters', function (done) {
        contact.request()
          .then(function (result) {
            result.body.should.be.a('object')
            expect(result.response.statusCode).to.equal(200)
            done()
          })
          .catch(function (reason) {
            // We want it to raise an error if it gets here
            expect(reason).to.equal(undefined)
            done()
          })
      })

      it('calls the contact ressource instance whith parameters', function (done) {
        var promise = contact.request({Name: 'Guillaume Badi'})
          .then(function (result) {
            result.body.should.be.a('object')
            expect(result.response.statusCode).to.be.within(200, 201)
            done()
          })
          .catch(function (reason) {
            // We want it to raise an error if it gets here
            expect(reason).to.equal(undefined)
            done()
          })
        expect(Promise.prototype.isPrototypeOf(promise)).to.equal(true)
      })

      it('calls the contact ressource instance with empty parameters', function (done) {
        contact.request({}).then(function (result) {
          result.body.should.be.a('object')
          expect(result.response.statusCode).to.be.within(200, 201)
          done()
        })
      })
    })

    describe('post', function () {
      var sender = client.post('sender')
      // eslint-disable-next-line no-unused-vars
      var deletedErrorMessage = 'There is an already existing deleted sender with the same email. ' +
        'You can use "validate" action in order to activate it.'
      // eslint-disable-next-line no-unused-vars
      var inactiveErrorMessage = 'There is an already existing inactive sender with the same email. ' +
        'You can use "validate" action in order to activate it.'

      it('calls the sender ressource instance whith no parameters', function (done) {
        sender.request().catch(function (reason) {
          //reason.ErrorMessage.should.equal(deletedErrorMessage)
          reason.statusCode.should.equal(400)
          done()
        })
      })

      it('calls the sender ressource instance whith invalid parameters', function (done) {
        sender.request({Name: 'Guillaume Badi'}).catch(function (reason) {
          //expect(reason.ErrorMessage).to.equal(deletedErrorMessage)
          expect(reason.statusCode).to.equal(400)
          done()
        })
      })

      it('calls the sender ressource instance whith valid parameters', function (done) {
        sender.request({email: 'gbadi@mailjet.com'})
          .then(function (result) {
            expect(result.response.statusCode).to.equal(201)
            done()
          })
          .catch(function (reason) {
            // if it fails because the sender already exist. should be 400
            // expect(reason.ErrorMessage).to.equal(inactiveErrorMessage)
            expect(reason.statusCode).to.equal(400)
            done()
          })
      })

      it('calls the sender resource with empty parameters', function (done) {
        sender.request({}).catch(function (reason) {
          // expect(reason.ErrorMessage).to.equal(deletedErrorMessage)
          expect(reason.statusCode).to.equal(400)
          done()
        })
      })
    })
  })
})

describe('Advanced API Calls', function () {
  function Example (fn, payload) {
    this.fn = fn
    this.payload = payload
    this.format = function (obj) { return JSON.stringify(obj).match(/\S+/g).join('') }
    this.call = function () {
      var res = this.fn.request(this.payload)
      var ret = res[0].replace(/\\/g, '/') + ' ' + this.format(res[1])
      return ret
    }
  }

  var client2 = new Mailjet(API_KEY, API_SECRET, null, true)

  const EXAMPLES_SET = [
    new Example(client2.get('contact')),
    new Example(client2.get('contact').id(2)),
    new Example(client2.get('contact/2')),
    new Example(client2.get('contact').id(3).action('getcontactslist')),
    new Example(client2.get('contact'), {countOnly: 1}),
    new Example(client2.get('contact'), {limit: 2}),
    new Example(client2.get('contact'), {offset: 233}),
    new Example(client2.get('contact'), {contatctList: 34}),
    new Example(client2.post('contactslist').id(34).action('managecontact'), {email: EMAIL}),
    new Example(client2.post('contactslist').id(34).action('csvdata'), FILE),
    new Example(client2.get('newsletter'), {filters: {CountOnly: 1}}),
    new Example(client2.get('batchjob').action('csverror')),
    new Example(client2.post('contact'), {email: EMAIL}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': UNIQUE_RECIPIENT}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': SIMPLE_RECIPIENTS}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': RECIPIENTS_NAME}),
    new Example(client2.post('send'), {'FromName': NAME, 'FromEmail': EMAIL, 'Subject': SUBJECT, 'Text-Part': TEXT_PART, 'Recipients': RECIPIENTS_VARS})
  ]

  const EXPECTED_SET = [
    'https://api.mailjet.com/v3/REST/contact {}',
    'https://api.mailjet.com/v3/REST/contact/2 {}',
    'https://api.mailjet.com/v3/REST/contact/2 {}',
    'https://api.mailjet.com/v3/REST/contact/3/getcontactslist {}',
    'https://api.mailjet.com/v3/REST/contact/?countOnly=1 {}',
    'https://api.mailjet.com/v3/REST/contact/?limit=2 {}',
    'https://api.mailjet.com/v3/REST/contact/?offset=233 {}',
    'https://api.mailjet.com/v3/REST/contact/?contatctList=34 {}',
    'https://api.mailjet.com/v3/REST/contactslist/34/managecontact {"email":"test@mailjet.com"}',
    'https://api.mailjet.com/v3/DATA/contactslist/34/csvdata "FILE"',
    'https://api.mailjet.com/v3/REST/newsletter/?CountOnly=1 {}',
    'https://api.mailjet.com/v3/DATA/batchjob/csverror {}',
    'https://api.mailjet.com/v3/REST/contact {"email":"test@mailjet.com"}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com"},{"email":"test2@mailjet.com"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com","name":"name"},{"email":"test2@mailjet.com","name":"name"}]}',
    'https://api.mailjet.com/v3/send {"FromName":"name","FromEmail":"test@mailjet.com","Subject":"subject","Text-Part":"text","Recipients":[{"email":"test@mailjet.com","vars":{"Key1":"Value1","Key2":"Value2"}}]}'
  ]

  EXPECTED_SET.forEach(function (test, index) {
    it('should output: ' + test, function () {
      EXAMPLES_SET[index].call().should.equal(test)
    })
  })
})

/* This fixture needs to run last so that it doesn't interfere with the other tests */
describe('Mocked API calls', function () {
  /* Set a very short timeout */
  var client = Mailjet.connect(API_KEY, API_SECRET, { timeout: 10 })

  describe('method request', function () {
    describe('get', function () {
      var contact = client.get('contact')

      it('calls the contact resource instance and the request times out', function (done) {
        /* Simulate a delayed response */
        nock('https://api.mailjet.com')
          .get('/v3/REST/contact')
          .delayConnection(1000)
          .reply(200, {})

        contact.request({})
          .then(function (result) {
            // We want it to raise an error if it gets here
            expect(result).to.equal(undefined)
            done()
          })
          .catch(function (reason) {
            expect(reason.ErrorMessage).to.equal('Timeout of 10ms exceeded')
            expect(reason.statusCode).to.equal(null)
            expect(reason.response).to.equal(null)
            done()
          })
      })
    })
  })
})
<|endoftext|>


<commit_before>//Require chai assert for test
var assert = require('chai').assert

//Require PSICQUIC client
var psicquic = require('../')

var intActClient = psicquic('http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search');

describe('Test PSICQUIC client', function () {

    it('Exists and is called psicquic', function () {
	   assert.isDefined(psicquic);
    })

    it('Exists and is called intActClient', function () {
	   assert.isDefined(intActClient);
    })

    it('It counts human interactions', function (done) {

        intActClient.countByQuery('species:human', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It counts interactions for interactor brca2_human', function(done){

        intActClient.countByInteractor('brca2_human', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It counts interactions for interaction IM-16519-*', function(done){

        intActClient.countByInteraction('IM-16519-*', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It has human interactions', function (done) {

        intActClient.getByQuery('species:human', 'tab25', 1, 1, function(err, resp, body){
            assert.isString(body);
            done();
        });
    })

    it('It has interactions for interactor brca2_human', function(done){

        intActClient.getByInteractor('brca2_human', 'tab25', 1, 1, function(err, resp, body){
            assert.isString(body);
            done();
        });
    })

    it('It has interactions for interaction IM-16519-*', function(done){

        intActClient.getByInteraction('IM-16519-*', 'tab25', 1, 1, function(err, resp, body){
            assert.isString(body);
            done();
        });
    })
});<commit_msg>updated tests<commit_after>//Require chai assert for test
var assert = require('chai').assert

//Require PSICQUIC client
var psicquic = require('../')

describe('Test PSICQUIC client', function () {

    it('Exists and is called psicquic', function () {
	   assert.isDefined(psicquic);
    })

    it('Points to IntAct', function () {

        var url = 'http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search/';
        psicquic.url(url);

        assert.equal(url, psicquic.url());
    })

    it('It counts human interactions', function (done) {

        psicquic.count('species:human', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It counts human interactions by interactor', function(done){

        psicquic.method('interactor').count('brca2_human', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It counts interactions by interaction IM-16519-*', function(done){

        psicquic.method('interaction').count('IM-16519-*', function(err, resp, body){
            assert.isNumber(+body);
            assert.operator(+body, '>', 0);
            done();
        });
    })

    it('It has human interactions', function (done) {

        psicquic.method('query').params({firstResult:1, maxResults:1}).query('species:human', function(err, resp, body){
            assert.isString(body);
            done();
        });
    })

    it('It has interactions for interactor brca2_human', function(done){

        psicquic.method('interactor').query('brca2_human', function(err, resp, body){
            assert.isString(body);
            done();
        });
    })

    it('It has interactions for interaction IM-16519-*', function(done){

        psicquic.method('interaction').query('IM-16519-*', function(err, resp, body){
            assert.isString(body);
            done();
        });
    })

    it('Proxy points to http://localhost/Interaction-Atlas/dist/proxy', function(){
        var proxy = 'http://localhost/Interaction-Atlas/dist/proxy';
        psicquic.proxy(proxy);

        assert.equal(proxy, psicquic.proxy());
    })
});<|endoftext|>


<commit_before>/**
 * ====================================
 *        JavaScript Typewriter
 * ===================================
 *
 * Author.......: Michael Rouse
 * Language.....: JavaScript
 * Date.........: March 2016
 *
 * Description..: Typewriter effect for text
 */
(function(){
  var TypeWriter = function(_elem, _options, _callback)
  {
    // Do not clutter the window scope
    if (this === window) { return new TypeWriter(_elem, _options, _callback); }

    // Set Parent to the element
    this.parent = _elem;
    if (!(this.parent instanceof HTMLElement)) { return null; }

    // Allow _options to be the callback function
    if (_options && (typeof _options === 'function')) { _callback = _options; _options = undefined; }

    // Default options
    this.setOptions((_options === undefined || (typeof _options !== 'object')) ? { content: undefined, delay: undefined} : _options);
    this.setCallback(_callback); // Copy the callback

    // Setup the blinking cursor
    this.cursor = document.createElement(this.options.cursor.tag);
    this.cursor.classList.add(this.options.cursor.class);
    this.parent.appendChild(this.cursor);

    return this;
  };  // End TypWriter

  /**
   *
   * Parameters...: new_options - the new options
   * Description..: Another way to setup options
   */
  TypeWriter.prototype.setOptions = function(new_options)
  {
    // Setup and confirm that the new options are valid
    this.options = (new_options === undefined || (typeof new_options !== 'object')) ? this.options : new_options;
    this.options.content = (this.options.content === undefined || !this.options.content) ? 'I have no clue what to type' : this.options.content;
    this.options.delay = (this.options.delay === undefined || (typeof this.options.delay !== 'number')) ? 2 : this.options.delay;

    this.options.letters = (this.options.letters === undefined || (typeof this.options.letters !== 'object')) ? { tag: undefined, class: undefined } : this.options.letters;
    this.options.letters.tag = (this.options.letters.tag === undefined) ? 'span' : this.options.letters.tag;
    this.options.letters.class = (this.options.letters.class === undefined) ? 'typewriter-letter' : this.options.letters.class;
    this.options.letters.removeClass = (this.options.letters.removeClass === undefined) ? 'typewriter-letter-remove' : this.options.letters.removeClass;

    this.options.cursor = (this.options.cursor === undefined || (typeof this.options.letters !== 'objcet')) ? { tag: undefined, class: undefined } : this.options.cursor;
    this.options.cursor.tag = (this.options.cursor.tag === undefined) ? 'span' : this.options.cursor.tag;
    this.options.cursor.class = (this.options.cursor.class === undefined) ? 'typewriter-cursor' : this.options.cursor.class;
    this.options.cursor.noBlinkClass = (this.options.cursor.noBlinkClass === undefined) ? 'typewriter-solid' : this.options.cursor.noBlinkClass;

    this.options.callbackDelay = (this.options.callbackDelay === undefined) ? 1 : this.options.callbackDelay;

    return this;
  }; // End setOptions

  /**
   *
   * Parameters...: new_content - the new content
   * Description..: Another way to set the content
   */
  TypeWriter.prototype.setContent = function(new_content)
  {
    this.options.content = (new_content === undefined) ? this.options.content : new_content;

    return this;
  }; // End setContent

  /**
   *
   * Parameters...: new_callback - the new callback function
   * Description..: Another way to setup the callback function
   */
  TypeWriter.prototype.setCallback = function(new_callback)
  {
    this.callback = (new_callback === undefined) ? this.callback : new_callback;
    if (this.callback && !(typeof this.callback === 'function')) { this.options.content = 'Callback must be a function, silly!'; this.callback = null; } // Callback not a function error

    return this;
  }; // End setCallback

  /**
   *
   * Description..: Stops the cursor form blinking
   */
  TypeWriter.prototype.cursorNoBlink = function()
  {
    if (!(this.cursor.classList.contains(this.options.cursor.noBlinkClass)))
    {
      this.cursor.classList.add(this.options.cursor.noBlinkClass); // Add the no blink class
    }

    return this;
  }; // End cursorNoBlink

  /**
   *
   * Description..: Sets the cursor to blink
   */
  TypeWriter.prototype.cursorBlink = function()
  {
    if (this.cursor.classList.contains(this.options.cursor.noBlinkClass))
    {
      this.cursor.classList.remove(this.options.cursor.noBlinkClass); // Remove the no blink class
    }

    return this;
  }; // End cursorBlink

  /**
   *
   * Parameters...: _callback - if this is set, it will not berform this.callback, it will perform _callback
   * Description..: Performs a callback function
   */
  TypeWriter.prototype.performCallback = function(_callback, _delay)
  {
    var me = this; // Remember this

    if (typeof _callback == 'number') { _delay = _callback; _callback = undefined; }

    var the_callback = (_callback && (typeof _callback === 'function')) ? _callback : this.callback;

    // Perform the callback after a 0.3s delay
    window.setTimeout(function(){
      if (the_callback && (typeof the_callback === 'function'))
      {
        the_callback(me);
        if (the_callback == me.callback) { me.callback = undefined; }
      }
    }, ((_delay === undefined) ? this.options.callbackDelay : _delay) * 1000);

    return this;
  }; // End performCallback

  /**
   *
   * Parameters...: str - the string to type
   *                reset - will restart if true
   * Description..: Types a string
   */
  TypeWriter.prototype.typeString = function(str, reset)
  {
    var me = this; // Remember this

    this.typeString.index = (this.typeString.index === undefined || reset) ? 0 : this.typeString.index;  // Static variable to keep track of the location in str

    str = (str === undefined || !(typeof str === 'string')) ? 'Invalid String' : str;

    if (this.typeString.index < str.length)
    {
      // Do not let the cursor blink while typing
      this.cursorNoBlink();

      // Create the span element for the letter
      var letter = document.createElement(this.options.letters.tag);
      letter.classList.add(this.options.letters.class);
      letter.innerHTML = (str[this.typeString.index] === ' ') ? ' ' : str[this.typeString.index];

      // Add the animation end event listener, to begin typing the next letter
      letter.addEventListener('animationend', function(){
        me.typeString(str); // Type the next letter
      });

      // Add the letter to the parent, before the cursor
      this.parent.insertBefore(letter, this.cursor);

      // Advance to the next letter
      this.typeString.index++;
    }
    else
    {
      // Let the cursor blink
      this.cursorBlink();

      // Perform the callback
      me.performCallback();
    }

    return this;
  }; // End typeString

  /**
   *
   * Parameters...: _callback - callback for erasing
   * Description..: Erases a typed string
   */
  TypeWriter.prototype.erase = function(_callback)
  {
    var me = this; // Remember this

    if (this.parent.childNodes.length > 1)
    {
      // Prevent cursor from blinking when erasing
      this.cursorNoBlink();

      // backspace, and call me.erase in the callback function
      this.backspace(function(){
          me.erase(_callback);
      });
    }
    else
    {
      // Let the cursor blink
      this.cursorBlink();

      // Perform the callback w/ no delay
      me.performCallback(_callback, 0);
    }

    return this;
  }; // End erase

  /**
   *
   * Parameters...: _callback - callback after backspacing
   * Description..: Backspaces one character
   */
  TypeWriter.prototype.backspace = function(_callback)
  {
    var me = this; // Remember this

    if (this.parent.childNodes.length > 1)
    {
      var last_letter = this.parent.childNodes.length - 2; // Minus two, because arrays start at 0 (that's a -1) and the last child is the cursor (so therefore -2)

      this.parent.childNodes[last_letter].classList.add(this.options.letters.removeClass);

      // Remove the letter after 0.2s
      this.parent.childNodes[last_letter].addEventListener('animationend', function(){
        me.parent.removeChild(me.parent.childNodes[last_letter]); // Remove the letter

        // Perform callback
        if (_callback !== undefined)
        {
          me.performCallback(_callback, 0); // perform callback w/ 100ms delay
        }
      });
    }

    return this;
  }; // End backspace

  /**
   *
   * Parameters...: _callback - possible callback function
   * Description..: Starts the typing
   */
  TypeWriter.prototype.start = function(_callback)
  {
    var me = this; // Remember this

    if (_callback) { this.setCallback(_callback); } // Attempt to make _callback the new callback function

    // Start typing after the specified delay
    window.setTimeout(function(){
      me.typeString(me.options.content, true); // Type the string and reset the index counter
    }, this.options.delay * 1000);

    return this;
  }; // End start

  // Initialize
  if (!window.Typewriter)
  {
    window.Typewriter = TypeWriter;
  }
})();
<commit_msg>Minor changes<commit_after>/**
 * ====================================
 *        JavaScript Typewriter
 * ===================================
 *
 * Author.......: Michael Rouse
 * Language.....: JavaScript
 * Date.........: March 2016
 *
 * Description..: Typewriter effect for text
 */
(function(){
  var TypeWriter = function(_elem, _options, _callback)
  {
    // Do not clutter the window scope
    if (this === window) { return new TypeWriter(_elem, _options, _callback); }

    // Set Parent to the element
    this.setElement(_elem);
    if (typeof this.parent === 'object') { if (typeof _options === 'function') { _callback = _options; } _options = this.parent; this.parent = undefined; }

    // Allow _options to be the callback function
    if (_options && (typeof _options === 'function')) { _callback = _options; _options = undefined; }

    // Default options
    this.setOptions((_options === undefined || (typeof _options !== 'object')) ? { content: undefined, delay: undefined} : _options);
    this.setCallback(_callback); // Copy the callback

    // Setup the blinking cursor

    this.cursor = document.createElement(this.options.cursor.tag);
    this.cursor.classList.add(this.options.cursor.class);
    this.parent.appendChild(this.cursor);

    return this;
  };  // End TypWriter

  /*
   *
   * Parameters...: new_parent - the new parent element
   * Description..: Sets the parent
   */
  TypeWriter.prototype.setElement = function(new_parent)
  {
    if (new_parent instanceof HTMLElement)
    {
      this.parent = new_parent;

      this.cursor = document.createElement(this.options.cursor.tag);
      this.cursor.classList.add(this.options.cursor.class);
      this.parent.appendChild(this.cursor);
    }

    return this;
  };

  /**
   *
   * Parameters...: new_options - the new options
   * Description..: Another way to setup options
   */
  TypeWriter.prototype.setOptions = function(new_options)
  {
    // Setup and confirm that the new options are valid
    this.options = (new_options === undefined || (typeof new_options !== 'object')) ? this.options : new_options;
    this.options.content = (this.options.content === undefined || !this.options.content) ? 'I have no clue what to type' : this.options.content;
    this.options.delay = (this.options.delay === undefined || (typeof this.options.delay !== 'number')) ? 2 : this.options.delay;

    this.options.letters = (this.options.letters === undefined || (typeof this.options.letters !== 'object')) ? { tag: undefined, class: undefined } : this.options.letters;
    this.options.letters.tag = (this.options.letters.tag === undefined) ? 'span' : this.options.letters.tag;
    this.options.letters.class = (this.options.letters.class === undefined) ? 'typewriter-letter' : this.options.letters.class;
    this.options.letters.removeClass = (this.options.letters.removeClass === undefined) ? 'typewriter-letter-remove' : this.options.letters.removeClass;

    this.options.cursor = (this.options.cursor === undefined || (typeof this.options.letters !== 'objcet')) ? { tag: undefined, class: undefined } : this.options.cursor;
    this.options.cursor.tag = (this.options.cursor.tag === undefined) ? 'span' : this.options.cursor.tag;
    this.options.cursor.class = (this.options.cursor.class === undefined) ? 'typewriter-cursor' : this.options.cursor.class;
    this.options.cursor.noBlinkClass = (this.options.cursor.noBlinkClass === undefined) ? 'typewriter-solid' : this.options.cursor.noBlinkClass;

    this.options.callbackDelay = (this.options.callbackDelay === undefined) ? 1 : this.options.callbackDelay;

    return this;
  }; // End setOptions

  /**
   *
   * Parameters...: new_content - the new content
   * Description..: Another way to set the content
   */
  TypeWriter.prototype.setContent = function(new_content)
  {
    this.options.content = (new_content === undefined) ? this.options.content : new_content;

    return this;
  }; // End setContent

  /**
   *
   * Parameters...: new_callback - the new callback function
   * Description..: Another way to setup the callback function
   */
  TypeWriter.prototype.setCallback = function(new_callback)
  {
    this.callback = (new_callback === undefined) ? this.callback : new_callback;
    if (this.callback && !(typeof this.callback === 'function')) { this.options.content = 'Callback must be a function, silly!'; this.callback = null; } // Callback not a function error

    return this;
  }; // End setCallback

  /**
   *
   * Description..: Stops the cursor form blinking
   */
  TypeWriter.prototype.cursorNoBlink = function()
  {
    if (!(this.cursor.classList.contains(this.options.cursor.noBlinkClass)))
    {
      this.cursor.classList.add(this.options.cursor.noBlinkClass); // Add the no blink class
    }

    return this;
  }; // End cursorNoBlink

  /**
   *
   * Description..: Sets the cursor to blink
   */
  TypeWriter.prototype.cursorBlink = function()
  {
    if (this.cursor.classList.contains(this.options.cursor.noBlinkClass))
    {
      this.cursor.classList.remove(this.options.cursor.noBlinkClass); // Remove the no blink class
    }

    return this;
  }; // End cursorBlink

  /**
   *
   * Parameters...: _callback - if this is set, it will not berform this.callback, it will perform _callback
   * Description..: Performs a callback function
   */
  TypeWriter.prototype.performCallback = function(_callback, _delay)
  {
    var me = this; // Remember this

    if (typeof _callback == 'number') { _delay = _callback; _callback = undefined; }

    var the_callback = (_callback && (typeof _callback === 'function')) ? _callback : this.callback;

    // Perform the callback after a 0.3s delay
    window.setTimeout(function(){
      if (the_callback && (typeof the_callback === 'function'))
      {
        the_callback(me);
        if (the_callback == me.callback) { me.callback = undefined; }
      }
    }, ((_delay === undefined) ? this.options.callbackDelay : _delay) * 1000);

    return this;
  }; // End performCallback

  /**
   *
   * Parameters...: str - the string to type
   *                reset - will restart if true
   * Description..: Types a string
   */
  TypeWriter.prototype.typeString = function(str, reset)
  {
    var me = this; // Remember this

    this.typeString.index = (this.typeString.index === undefined || reset) ? 0 : this.typeString.index;  // Static variable to keep track of the location in str

    str = (str === undefined || !(typeof str === 'string')) ? 'Invalid String' : str;

    if (this.typeString.index < str.length)
    {
      // Do not let the cursor blink while typing
      this.cursorNoBlink();

      // Create the span element for the letter
      var letter = document.createElement(this.options.letters.tag);
      letter.classList.add(this.options.letters.class);
      letter.innerHTML = (str[this.typeString.index] === ' ') ? ' ' : str[this.typeString.index];

      // Add the animation end event listener, to begin typing the next letter
      letter.addEventListener('animationend', function(){
        me.typeString(str); // Type the next letter
      });

      // Add the letter to the parent, before the cursor
      this.parent.insertBefore(letter, this.cursor);

      // Advance to the next letter
      this.typeString.index++;
    }
    else
    {
      // Let the cursor blink
      this.cursorBlink();

      // Perform the callback
      me.performCallback();
    }

    return this;
  }; // End typeString

  /**
   *
   * Parameters...: _callback - callback for erasing
   * Description..: Erases a typed string
   */
  TypeWriter.prototype.erase = function(_callback)
  {
    var me = this; // Remember this

    if (this.parent.childNodes.length > 1)
    {
      // Prevent cursor from blinking when erasing
      this.cursorNoBlink();

      // backspace, and call me.erase in the callback function
      this.backspace(function(){
          me.erase(_callback);
      });
    }
    else
    {
      // Let the cursor blink
      this.cursorBlink();

      // Perform the callback w/ no delay
      me.performCallback(_callback, 0);
    }

    return this;
  }; // End erase

  /**
   *
   * Parameters...: _callback - callback after backspacing
   * Description..: Backspaces one character
   */
  TypeWriter.prototype.backspace = function(_callback)
  {
    var me = this; // Remember this

    if (this.parent.childNodes.length > 1)
    {
      var last_letter = this.parent.childNodes.length - 2; // Minus two, because arrays start at 0 (that's a -1) and the last child is the cursor (so therefore -2)

      this.parent.childNodes[last_letter].classList.add(this.options.letters.removeClass);

      // Remove the letter after 0.2s
      this.parent.childNodes[last_letter].addEventListener('animationend', function(){
        me.parent.removeChild(me.parent.childNodes[last_letter]); // Remove the letter

        // Perform callback
        if (_callback !== undefined)
        {
          me.performCallback(_callback, 0); // perform callback w/ 100ms delay
        }
      });
    }

    return this;
  }; // End backspace

  /**
   *
   * Parameters...: _callback - possible callback function
   * Description..: Starts the typing
   */
  TypeWriter.prototype.start = function(_callback)
  {
    if (this.parent && (this.parent instanceof HTMLElement))
    {
      var me = this; // Remember this

      if (_callback) { this.setCallback(_callback); } // Attempt to make _callback the new callback function

      // Start typing after the specified delay
      window.setTimeout(function(){
        me.typeString(me.options.content, true); // Type the string and reset the index counter
      }, this.options.delay * 1000);
    }
    return this;
  }; // End start

  // Initialize
  if (!window.Typewriter)
  {
    window.Typewriter = TypeWriter;
  }
})();
<|endoftext|>


<commit_before>
/**
 * generate ab formulas
 */
App.AB = {
	options: {
		tagline: [
			{
				name: 'tagline-for-free',
				tagline: 'Order the top food %s.
 After you order, everything is saved for future 1 click ordering.
Choose a restaurant:'
			},
			{
				name: 'tagline-no-free',
				tagline: 'Order the top food %s.
 After you order, everything is saved for future 1 click ordering.
Choose a restaurant:'
			}
		],
		slogan: [
			{
				name: 'slogan-push-food',
				slogan: 'Push a button. Get Food.'
			}
		],
		nothingInTheCartMobile: [
			{
				name: 'empty-cart-mobile-begin',
				line: 'Begin by tapping on anything from our curated menu below!'
			},
			{
				name: 'empty-cart-mobile-food',
				line: 'Choose food below!',
				disabled: true
			}
		],
		nothingInTheCartDesktop: [
			{
				name: 'empty-cart-desktop-begin',
				line: 'Begin by clicking on anything from our curated menu on the left!'
			},
			{
				name: 'empty-cart-desktop-food',
				line: 'Choose Food!'
			}
		],
		restaurantPage: [
			{
				name: 'restaurant-page-noimage'
			},
			{
				name: 'restaurant-page-image',
				disabled: true
			}
		],
		dollarSign: [
			{
				name : 'hide'
			},
			{
				name : 'hide'
			}
		]
	},
	init: function() {
		// we dont have ab variables. generate them
		App.AB.create(true);
		App.AB.load();
	},
	create: function(clear) {
		if (clear) {
			App.config.ab = {};
		}

		// Creates the ab object case it don't exist
		if( App.config && !App.config.ab ){
			App.config.ab = {};
		}

		// loop through each option type
		for (var key in App.AB.options) {
			if (App.config.ab[key]) {
				return;
			}
			var optionType = App.AB.options[key];
			var opts = [];

			// loop through all of the options for the specific ab option type
			for (var x in optionType) {
				// remove disabled ones
				if (!optionType[x].disabled || optionType[x].disabled == false) {
					opts[opts.length] = optionType[x];
				}
			}
			var opt = opts[Math.floor(Math.random()*opts.length)];
			App.config.ab[key] = opt.name
		}
		App.AB.save();
	},
	load: function() {
		App.slogan = App.AB.pluck('slogan', App.config.ab.slogan);
		App.tagline = App.AB.pluck('tagline', App.config.ab.tagline);
		if (!App.slogan || !App.tagline) {
			App.AB.create(true);
			App.AB.load(true);
		}
	},
	pluck: function(option, name) {
		for (var x in App.AB.options[option]) {
			if (App.AB.options[option][x].name == name) {
				return App.AB.options[option][x];
			}
		}
		return null;
	},
	save: function() {
		$.ajax({
			url: App.service + 'config',
			data: {ab: App.config.ab},
			dataType: 'json',
			type: 'POST',
			complete: function(json) {}
		});
	}
};
<commit_msg>fixed #4453<commit_after>
/**
 * generate ab formulas
 */
App.AB = {
	options: {
		tagline: [
			{
				name: 'tagline-for-free',
				tagline: 'Order the best delivery food %s.
 After you order, everything is saved for future 1 click ordering.
Choose a restaurant:'
			},
			{
				name: 'tagline-no-free',
				tagline: 'Order the top food %s.
 After you order, everything is saved for future 1 click ordering.
Choose a restaurant:'
			}
		],
		slogan: [
			{
				name: 'slogan-push-food',
				slogan: 'Push a button. Get Delivery.'
			}
		],
		nothingInTheCartMobile: [
			{
				name: 'empty-cart-mobile-begin',
				line: 'Begin by tapping on anything from our curated menu below!'
			},
			{
				name: 'empty-cart-mobile-food',
				line: 'Choose food below!',
				disabled: true
			}
		],
		nothingInTheCartDesktop: [
			{
				name: 'empty-cart-desktop-begin',
				line: 'Begin by clicking on anything from our curated menu on the left!'
			},
			{
				name: 'empty-cart-desktop-food',
				line: 'Choose Food!'
			}
		],
		restaurantPage: [
			{
				name: 'restaurant-page-noimage'
			},
			{
				name: 'restaurant-page-image',
				disabled: true
			}
		],
		dollarSign: [
			{
				name : 'hide'
			},
			{
				name : 'hide'
			}
		]
	},
	init: function() {
		// we dont have ab variables. generate them
		App.AB.create(true);
		App.AB.load();
	},
	create: function(clear) {
		if (clear) {
			App.config.ab = {};
		}

		// Creates the ab object case it don't exist
		if( App.config && !App.config.ab ){
			App.config.ab = {};
		}

		// loop through each option type
		for (var key in App.AB.options) {
			if (App.config.ab[key]) {
				return;
			}
			var optionType = App.AB.options[key];
			var opts = [];

			// loop through all of the options for the specific ab option type
			for (var x in optionType) {
				// remove disabled ones
				if (!optionType[x].disabled || optionType[x].disabled == false) {
					opts[opts.length] = optionType[x];
				}
			}
			var opt = opts[Math.floor(Math.random()*opts.length)];
			App.config.ab[key] = opt.name
		}
		App.AB.save();
	},
	load: function() {
		App.slogan = App.AB.pluck('slogan', App.config.ab.slogan);
		App.tagline = App.AB.pluck('tagline', App.config.ab.tagline);
		if (!App.slogan || !App.tagline) {
			App.AB.create(true);
			App.AB.load(true);
		}
	},
	pluck: function(option, name) {
		for (var x in App.AB.options[option]) {
			if (App.AB.options[option][x].name == name) {
				return App.AB.options[option][x];
			}
		}
		return null;
	},
	save: function() {
		$.ajax({
			url: App.service + 'config',
			data: {ab: App.config.ab},
			dataType: 'json',
			type: 'POST',
			complete: function(json) {}
		});
	}
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';

import NewKeypad from './new-keypad';
import TextLine from './text-line';
import auxStore from './../aux-store';

class AuxApp extends Component {
    select = step => {
        auxStore.dispatch({
            type: 'SELECT_STEP',
            step: step
        });
    };

    render() {
        const style = {
            display: 'flex',
            flexDirection: 'column',
            height: '100vh',
        };

        const containerStyle = {
            flexGrow: 1,
            overflow: 'scroll',
        };

        const lineStyle = {
            textAlign: 'center',
            fontFamily: 'Helvetica-Light',
            fontSize: 30,
        };

        const insertedText = {
            "6": " - 5",
            "11": " - 5",
        };

        return

                {this.props.steps.map((line, i) =>
                    <TextLine
                        {...line}
                        key={i}
                        onClick={() => this.select(i)}
                        active={this.props.activeStep === i}
                    />)
                }

            <NewKeypad />

;
    }
}

module.exports = connect(state => state)(AuxApp);
<commit_msg>added a 'goal' just above the keyboard<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';

import NewKeypad from './new-keypad';
import TextLine from './text-line';
import auxStore from './../aux-store';

class AuxApp extends Component {
    select = step => {
        auxStore.dispatch({
            type: 'SELECT_STEP',
            step: step
        });
    };

    render() {
        const style = {
            display: 'flex',
            flexDirection: 'column',
            height: '100vh',
        };

        const containerStyle = {
            flexGrow: 1,
            overflow: 'scroll',
        };

        const lineStyle = {
            fontFamily: 'Helvetica-Light',
            fontSize: 26,
        };

        const insertedText = {
            "6": " - 5",
            "11": " - 5",
        };

        return

                {this.props.steps.map((line, i) =>
                    <TextLine
                        {...line}
                        key={i}
                        onClick={() => this.select(i)}
                        active={this.props.activeStep === i}
                    />)
                }

                Goal: x = 5 / 2

            <NewKeypad />

;
    }
}

module.exports = connect(state => state)(AuxApp);
<|endoftext|>