<commit_before>/*globals require, module, Promise, setImmediate, console */

'use strict';

var EventEmitter, JsonStream, error, events, terminators, escapes;

EventEmitter = require('events').EventEmitter;
JsonStream = require('./stream');
error = require('./error');
events = require('./events');

terminators = {
    obj: '}',
    arr: ']'
};

escapes = {
    '"': '"',
    '\\': '\\',
    '/': '/',
    'b': '\b',
    'f': '\f',
    'n': '\n',
    'r': '\r',
    't': '\t'
};

module.exports = initialise;

/**
 * Public function `walk`.
 *
 * Initialises an asynchronous JSON walker and returns an object
 * { stream, emitter }, where `stream` is a Writable instance
 * that represents the incoming JSON stream and `emitter` is an
 * EventEmitter instance that represents the outgoing JSON token
 * events.
 *
 * @option discard: The number of characters to process before
 *                  discarding the processed characters to save
 *                  memory. The default value is `16384`.
 *
 * @option debug:   Log debug messages to the console.
 **/
function initialise (options) {
    var json, position, flags, scopes, handlers,
        resume, emitter, stream, discardThreshold;

    options = options || {};
    json = '';
    position = {
        index: 0,
        current: {
            line: 1,
            column: 1
        },
        previous: {}
    };
    flags = {
        stream: {
            ended: false
        },
        walk: {
            begun: false,
            ended: false,
            string: false
        }
    };
    scopes = [];
    handlers = {
        arr: value,
        obj: property
    };

    emitter = new EventEmitter();
    stream = new JsonStream(proceed);

    discardThreshold = options.discard || 16384;
    if (!options.debug) {
        debug = function () {};
    }

    stream.on('finish', endStream);

    return {
        emitter: emitter,
        stream: stream
    };

    function proceed (chunk) {
        debug('proceed');

        if (!chunk || chunk.length === 0) {
            return;
        }

        json += chunk;

        if (!flags.walk.begun) {
            flags.walk.begun = true;
            setImmediate(value);
        }

        if (resume) {
            setImmediate(resume);
            resume = undefined;
        }
    }

    function debug (caller) {
        console.log(caller + ': ' + debugPosition() + debugFlags());
    }

    function debugPosition () {
        var result;

        if (position.index === json.length) {
            result = 'EOF';
        } else {
            result = character();
        }

        return result + '[' + position.index + ']';
    }

    function character () {
        return json[position.index];
    }

    function debugFlags () {
        var result = '';

        debugFlag('stream', 'ended', 'sx');
        debugFlag('walk', 'begun', 'wb');
        debugFlag('walk', 'string', 'ws');
        debugFlag('walk', 'ended', 'wx');

        return result;

        function debugFlag (type, name, abbreviation) {
            if (flags[type][name]) {
                result += ' ' + abbreviation;
            }
        }
    }

    function value () {
        debug('value');

        awaitNonWhitespace()
            .then(next)
            .then(handleValue);
    }

    function awaitNonWhitespace () {
        var resolve, reject;

        debug('awaitNonWhitespace');

        setImmediate(wait);

        return new Promise(function (res, rej) {
            resolve = res;
            reject = rej;
        });

        function wait () {
            awaitCharacter().then(step).catch(reject);
        }

        function step () {
            debug('awaitNonWhitespace::step');

            if (!isWhitespace(character())) {
                return resolve();
            }

            next().then(wait);
        }
    }

    function awaitCharacter () {
        var resolve, reject;

        debug('awaitCharacter');

        if (position.index < json.length) {
            return Promise.resolve();
        }

        if (flags.stream.ended) {
            setImmediate(endWalk);
            return Promise.reject();
        }

        resume = after;

        return new Promise(function (res, rej) {
            resolve = res;
            reject = rej;
        });

        function after () {
            debug('awaitCharacter::after');

            if (position.index < json.length) {
                return resolve();
            }

            reject();

            if (flags.stream.ended) {
                setImmediate(endWalk);
            }
        }
    }

    function next () {
        var resolve;

        debug('next');

        awaitCharacter().then(after);

        return new Promise(function (r) {
            resolve = r;
        });

        function after () {
            var result;

            debug('next::after');

            result = character();

            position.index += 1;
            position.previous.line = position.current.line;
            position.previous.column = position.current.column;

            if (result === '\n') {
                position.current.line += 1;
                position.current.column = 1;
            } else {
                position.current.column += 1;
            }

            if (position.index === discardThreshold) {
                json = json.substr(position.index);
                position.index = 0;
            }

            resolve(result);
        }
    }

    function handleValue (character) {
        debug('handleValue');

        switch (character) {
            case '[':
                return array();
            case '{':
                return object();
            case '"':
                return string();
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '-':
            case '.':
                return number(character);
            case 'f':
                return literalFalse();
            case 'n':
                return literalNull();
            case 't':
                return literalTrue();
            default:
                fail(character, 'value', 'previous');
                value();
        }
    }

    function array () {
        debug('array');

        scope(events.array, value);
    }

    function scope (event, contentHandler) {
        debug('scope');

        emitter.emit(event);
        scopes.push(event);
        endScope(event).then(contentHandler);
    }

    function endScope (scope) {
        var resolve;

        debug('endScope');

        awaitNonWhitespace().then(after).catch(endWalk);

        return new Promise(function (r) {
            resolve = r;
        });

        function after () {
            debug('endScope::after');

            if (character() !== terminators[scope]) {
                return resolve();
            }

            emitter.emit(events.endPrefix + scope);
            scopes.pop();

            next().then(endValue);
        }
    }

    function endValue () {
        debug('endValue');

        awaitNonWhitespace().then(after).catch(endWalk);

        function after () {
            if (scopes.length === 0) {
                fail(character(), 'EOF', 'current');
                return setImmediate(value);
            }

            checkScope();
        }

        function checkScope () {
            var scope;

            debug('endValue::checkScope');

            scope = scopes[scopes.length - 1];

            endScope(scope).then(function () {
                debug('endValue::checkScope::endScope');

                var handler = handlers[scope];

                if (checkCharacter(character(), ',', 'current')) {
                    next().then(handler);
                } else {
                    setImmediate(handler);
                }
            });
        }
    }

    function fail (actual, expected, positionKey) {
        debug('fail');

        emitter.emit(
            events.error,
            error.create(
                actual,
                expected,
                position[positionKey].line,
                position[positionKey].column
            )
        );
    }

    function checkCharacter (character, expected, positionKey) {
        debug('checkCharacter');

        if (character !== expected) {
            fail(character, expected, positionKey);
            return false;
        }

        return true;
    }

    function object () {
        debug('object');

        scope(events.object, property);
    }

    function property () {
        debug('property');

        awaitNonWhitespace()
            .then(next)
            .then(propertyName);
    }

    function propertyName (character) {
        debug('propertyName');

        checkCharacter(character, '"', 'previous');

        walkString(events.property)
            .then(awaitNonWhitespace)
            .then(next)
            .then(propertyValue);
    }

    function propertyValue (character) {
        debug('propertyValue');

        checkCharacter(character, ':', 'previous');
        setImmediate(value);
    }

    function walkString (event) {
        var isEscaping, string, resolve;

        debug('walkString');

        flags.walk.string = true;
        isEscaping = false;
        string = '';

        next().then(step);

        return new Promise(function (r) {
            resolve = r;
        });

        function step (character) {
            debug('walkString::step');

            if (isEscaping) {
                isEscaping = false;

                return escape(character).then(function (escaped) {
                    string += escaped;
                    next().then(step);
                });
            }

            if (character === '\\') {
                isEscaping = true;
                return next().then(step);
            }

            if (character !== '"') {
                string += character;
                return next().then(step);
            }

            flags.walk.string = false;
            emitter.emit(event, string);
            resolve();
        }
    }

    function escape (character) {
        var promise, resolve;

        debug('escape');

        promise = new Promise(function (r) {
            resolve = r;
        });

        if (escapes[character]) {
            resolve(escapes[character]);
        } else if (character === 'u') {
            escapeHex().then(resolve);
        } else {
            fail(character, 'escape character', 'previous');
            resolve('\\' + character);
        }

        return promise;
    }

    function escapeHex () {
        var hexits, resolve;

        debug('escapeHex');

        hexits = '';

        next().then(step.bind(null, 0));

        return new Promise(function (r) {
            resolve = r;
        });

        function step (index, character) {
            debug('escapeHex::step');

            if (isHexit(character)) {
                hexits += character;
            }

            if (index < 3) {
                return next().then(step.bind(null, index + 1));
            }

            if (hexits.length === 4) {
                return resolve(String.fromCharCode(parseInt(hexits, 16)));
            }

            fail(character, 'hex digit', 'previous');

            resolve('\\u' + hexits + character);
        }
    }

    function string () {
        debug('string');

        walkString(events.string).then(endValue);
    }

    function number (firstCharacter) {
        var digits;

        debug('number');

        digits = firstCharacter;

        walkDigits().then(addDigits.bind(null, checkDecimalPlace));

        function addDigits (step, result) {
            debug('number::addDigits');

            digits += result.digits;

            if (result.atEnd) {
                return endNumber();
            }

            step();
        }

        function checkDecimalPlace () {
            debug('number::checkDecimalPlace');

            if (character() === '.') {
                return next().then(function (character) {
                    digits += character;
                    walkDigits().then(addDigits.bind(null, checkExponent));
                });
            }

            checkExponent();
        }

        function checkExponent () {
            debug('number::checkExponent');

            if (character() === 'e' || character() === 'E') {
                return next().then(function (character) {
                    digits += character;
                    awaitCharacter()
                        .then(checkSign)
                        .catch(fail.bind(null, 'EOF', 'exponent', 'current'));
                });
            }

            endNumber();
        }

        function checkSign () {
            debug('number::checkExponent');

            if (character() === '+' || character() === '-') {
                return next().then(function (character) {
                    digits += character;
                    readExponent();
                });
            }

            readExponent();
        }

        function readExponent () {
            debug('number::readExponent');

            walkDigits().then(addDigits.bind(null, endNumber));
        }

        function endNumber () {
            debug('number::endNumber');

            emitter.emit(events.number, parseFloat(digits));
            setImmediate(endValue);
        }
    }

    function walkDigits () {
        var digits, resolve;

        debug('walkDigits');

        digits = '';

        wait();

        return new Promise(function (r) {
            resolve = r;
        });

        function wait () {
            debug('walkDigits::wait');

            awaitCharacter().then(step).catch(atEnd);
        }

        function step () {
            debug('walkDigits::step');

            if (isDigit(character())) {
                return next().then(function (character) {
                    debug('walkDigits::step::next');

                    digits += character;
                    wait();
                });
            }

            resolve({
                digits: digits,
                atEnd: false
            });
        }

        function atEnd () {
            debug('walkDigits::atEnd');

            resolve({
                digits: digits,
                atEnd: true
            });
        }
    }

    function literalFalse () {
        debug('literalFalse');

        literal([ 'a', 'l', 's', 'e' ], false);
    }

    function literal (expectedCharacters, value) {
        var actual, expected, invalid;

        debug('literal');

        wait();

        function wait () {
            debug('literal::wait');

            awaitCharacter().then(step).catch(atEnd);
        }

        function step () {
            debug('literal::step');

            if (invalid || expectedCharacters.length === 0) {
                return atEnd();
            }

            next().then(afterNext);
        }

        function atEnd () {
            debug('literal::atEnd');

            if (invalid) {
                fail(actual, expected, 'previous');
            } else if (expectedCharacters.length > 0) {
                fail('EOF', expectedCharacters.shift(), 'current');
            } else {
                done();
            }

            setImmediate(endValue);
        }

        function afterNext (character) {
            debug('literal::afterNext');

            actual = character;
            expected = expectedCharacters.shift();

            if (actual !== expected) {
                invalid = true;
            }

            wait();
        }

        function done () {
            debug('literal::done');

            emitter.emit(events.literal, value);
        }
    }

    function literalNull () {
        debug('literalNull');

        literal([ 'u', 'l', 'l' ], null);
    }

    function literalTrue () {
        debug('literalTrue');

        literal([ 'r', 'u', 'e' ], true);
    }

    function endStream () {
        debug('endStream');

        flags.stream.ended = true;

        if (!flags.walk.begun) {
            endWalk();
        }

        if (resume) {
            resume();
        }
    }

    function endWalk () {
        debug('endWalk');

        if (flags.walk.ended) {
            return;
        }

        flags.walk.ended = true;

        if (flags.walk.string) {
            fail('EOF', '"', 'current');
        }

        while (scopes.length > 0) {
            fail('EOF', terminators[scopes.pop()], 'current');
        }

        emitter.emit(events.end);
    }
}

function isWhitespace (character) {
    switch (character) {
        case ' ':
        case '\t':
        case '\r':
        case '\n':
            return true;
    }

    return false;
}

function isHexit (character) {
    return isDigit(character) ||
           isInRange(character, 'A', 'F') ||
           isInRange(character, 'a', 'f');
}

function isDigit (character) {
    return isInRange(character, '0', '9');
}

function isInRange (character, lower, upper) {
    var code = character.charCodeAt(0);

    return code >= lower.charCodeAt(0) && code <= upper.charCodeAt(0);
}

<commit_msg>Eliminate unnecessary setImmediate.<commit_after>/*globals require, module, Promise, setImmediate, console */

'use strict';

var EventEmitter, JsonStream, error, events, terminators, escapes;

EventEmitter = require('events').EventEmitter;
JsonStream = require('./stream');
error = require('./error');
events = require('./events');

terminators = {
    obj: '}',
    arr: ']'
};

escapes = {
    '"': '"',
    '\\': '\\',
    '/': '/',
    'b': '\b',
    'f': '\f',
    'n': '\n',
    'r': '\r',
    't': '\t'
};

module.exports = initialise;

/**
 * Public function `walk`.
 *
 * Initialises an asynchronous JSON walker and returns an object
 * { stream, emitter }, where `stream` is a Writable instance
 * that represents the incoming JSON stream and `emitter` is an
 * EventEmitter instance that represents the outgoing JSON token
 * events.
 *
 * @option discard: The number of characters to process before
 *                  discarding the processed characters to save
 *                  memory. The default value is `16384`.
 *
 * @option debug:   Log debug messages to the console.
 **/
function initialise (options) {
    var json, position, flags, scopes, handlers,
        resume, emitter, stream, discardThreshold;

    options = options || {};
    json = '';
    position = {
        index: 0,
        current: {
            line: 1,
            column: 1
        },
        previous: {}
    };
    flags = {
        stream: {
            ended: false
        },
        walk: {
            begun: false,
            ended: false,
            string: false
        }
    };
    scopes = [];
    handlers = {
        arr: value,
        obj: property
    };

    emitter = new EventEmitter();
    stream = new JsonStream(proceed);

    discardThreshold = options.discard || 16384;
    if (!options.debug) {
        debug = function () {};
    }

    stream.on('finish', endStream);

    return {
        emitter: emitter,
        stream: stream
    };

    function proceed (chunk) {
        debug('proceed');

        if (!chunk || chunk.length === 0) {
            return;
        }

        json += chunk;

        if (!flags.walk.begun) {
            flags.walk.begun = true;
            setImmediate(value);
        }

        if (resume) {
            setImmediate(resume);
            resume = undefined;
        }
    }

    function debug (caller) {
        console.log(caller + ': ' + debugPosition() + debugFlags());
    }

    function debugPosition () {
        var result;

        if (position.index === json.length) {
            result = 'EOF';
        } else {
            result = character();
        }

        return result + '[' + position.index + ']';
    }

    function character () {
        return json[position.index];
    }

    function debugFlags () {
        var result = '';

        debugFlag('stream', 'ended', 'sx');
        debugFlag('walk', 'begun', 'wb');
        debugFlag('walk', 'string', 'ws');
        debugFlag('walk', 'ended', 'wx');

        return result;

        function debugFlag (type, name, abbreviation) {
            if (flags[type][name]) {
                result += ' ' + abbreviation;
            }
        }
    }

    function value () {
        debug('value');

        awaitNonWhitespace()
            .then(next)
            .then(handleValue);
    }

    function awaitNonWhitespace () {
        var resolve, reject;

        debug('awaitNonWhitespace');

        wait();

        return new Promise(function (res, rej) {
            resolve = res;
            reject = rej;
        });

        function wait () {
            awaitCharacter().then(step).catch(reject);
        }

        function step () {
            debug('awaitNonWhitespace::step');

            if (!isWhitespace(character())) {
                return resolve();
            }

            next().then(wait);
        }
    }

    function awaitCharacter () {
        var resolve, reject;

        debug('awaitCharacter');

        if (position.index < json.length) {
            return Promise.resolve();
        }

        if (flags.stream.ended) {
            setImmediate(endWalk);
            return Promise.reject();
        }

        resume = after;

        return new Promise(function (res, rej) {
            resolve = res;
            reject = rej;
        });

        function after () {
            debug('awaitCharacter::after');

            if (position.index < json.length) {
                return resolve();
            }

            reject();

            if (flags.stream.ended) {
                setImmediate(endWalk);
            }
        }
    }

    function next () {
        var resolve;

        debug('next');

        awaitCharacter().then(after);

        return new Promise(function (r) {
            resolve = r;
        });

        function after () {
            var result;

            debug('next::after');

            result = character();

            position.index += 1;
            position.previous.line = position.current.line;
            position.previous.column = position.current.column;

            if (result === '\n') {
                position.current.line += 1;
                position.current.column = 1;
            } else {
                position.current.column += 1;
            }

            if (position.index === discardThreshold) {
                json = json.substr(position.index);
                position.index = 0;
            }

            resolve(result);
        }
    }

    function handleValue (character) {
        debug('handleValue');

        switch (character) {
            case '[':
                return array();
            case '{':
                return object();
            case '"':
                return string();
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '-':
            case '.':
                return number(character);
            case 'f':
                return literalFalse();
            case 'n':
                return literalNull();
            case 't':
                return literalTrue();
            default:
                fail(character, 'value', 'previous');
                value();
        }
    }

    function array () {
        debug('array');

        scope(events.array, value);
    }

    function scope (event, contentHandler) {
        debug('scope');

        emitter.emit(event);
        scopes.push(event);
        endScope(event).then(contentHandler);
    }

    function endScope (scope) {
        var resolve;

        debug('endScope');

        awaitNonWhitespace().then(after).catch(endWalk);

        return new Promise(function (r) {
            resolve = r;
        });

        function after () {
            debug('endScope::after');

            if (character() !== terminators[scope]) {
                return resolve();
            }

            emitter.emit(events.endPrefix + scope);
            scopes.pop();

            next().then(endValue);
        }
    }

    function endValue () {
        debug('endValue');

        awaitNonWhitespace().then(after).catch(endWalk);

        function after () {
            if (scopes.length === 0) {
                fail(character(), 'EOF', 'current');
                return setImmediate(value);
            }

            checkScope();
        }

        function checkScope () {
            var scope;

            debug('endValue::checkScope');

            scope = scopes[scopes.length - 1];

            endScope(scope).then(function () {
                debug('endValue::checkScope::endScope');

                var handler = handlers[scope];

                if (checkCharacter(character(), ',', 'current')) {
                    next().then(handler);
                } else {
                    setImmediate(handler);
                }
            });
        }
    }

    function fail (actual, expected, positionKey) {
        debug('fail');

        emitter.emit(
            events.error,
            error.create(
                actual,
                expected,
                position[positionKey].line,
                position[positionKey].column
            )
        );
    }

    function checkCharacter (character, expected, positionKey) {
        debug('checkCharacter');

        if (character !== expected) {
            fail(character, expected, positionKey);
            return false;
        }

        return true;
    }

    function object () {
        debug('object');

        scope(events.object, property);
    }

    function property () {
        debug('property');

        awaitNonWhitespace()
            .then(next)
            .then(propertyName);
    }

    function propertyName (character) {
        debug('propertyName');

        checkCharacter(character, '"', 'previous');

        walkString(events.property)
            .then(awaitNonWhitespace)
            .then(next)
            .then(propertyValue);
    }

    function propertyValue (character) {
        debug('propertyValue');

        checkCharacter(character, ':', 'previous');
        setImmediate(value);
    }

    function walkString (event) {
        var isEscaping, string, resolve;

        debug('walkString');

        flags.walk.string = true;
        isEscaping = false;
        string = '';

        next().then(step);

        return new Promise(function (r) {
            resolve = r;
        });

        function step (character) {
            debug('walkString::step');

            if (isEscaping) {
                isEscaping = false;

                return escape(character).then(function (escaped) {
                    string += escaped;
                    next().then(step);
                });
            }

            if (character === '\\') {
                isEscaping = true;
                return next().then(step);
            }

            if (character !== '"') {
                string += character;
                return next().then(step);
            }

            flags.walk.string = false;
            emitter.emit(event, string);
            resolve();
        }
    }

    function escape (character) {
        var promise, resolve;

        debug('escape');

        promise = new Promise(function (r) {
            resolve = r;
        });

        if (escapes[character]) {
            resolve(escapes[character]);
        } else if (character === 'u') {
            escapeHex().then(resolve);
        } else {
            fail(character, 'escape character', 'previous');
            resolve('\\' + character);
        }

        return promise;
    }

    function escapeHex () {
        var hexits, resolve;

        debug('escapeHex');

        hexits = '';

        next().then(step.bind(null, 0));

        return new Promise(function (r) {
            resolve = r;
        });

        function step (index, character) {
            debug('escapeHex::step');

            if (isHexit(character)) {
                hexits += character;
            }

            if (index < 3) {
                return next().then(step.bind(null, index + 1));
            }

            if (hexits.length === 4) {
                return resolve(String.fromCharCode(parseInt(hexits, 16)));
            }

            fail(character, 'hex digit', 'previous');

            resolve('\\u' + hexits + character);
        }
    }

    function string () {
        debug('string');

        walkString(events.string).then(endValue);
    }

    function number (firstCharacter) {
        var digits;

        debug('number');

        digits = firstCharacter;

        walkDigits().then(addDigits.bind(null, checkDecimalPlace));

        function addDigits (step, result) {
            debug('number::addDigits');

            digits += result.digits;

            if (result.atEnd) {
                return endNumber();
            }

            step();
        }

        function checkDecimalPlace () {
            debug('number::checkDecimalPlace');

            if (character() === '.') {
                return next().then(function (character) {
                    digits += character;
                    walkDigits().then(addDigits.bind(null, checkExponent));
                });
            }

            checkExponent();
        }

        function checkExponent () {
            debug('number::checkExponent');

            if (character() === 'e' || character() === 'E') {
                return next().then(function (character) {
                    digits += character;
                    awaitCharacter()
                        .then(checkSign)
                        .catch(fail.bind(null, 'EOF', 'exponent', 'current'));
                });
            }

            endNumber();
        }

        function checkSign () {
            debug('number::checkExponent');

            if (character() === '+' || character() === '-') {
                return next().then(function (character) {
                    digits += character;
                    readExponent();
                });
            }

            readExponent();
        }

        function readExponent () {
            debug('number::readExponent');

            walkDigits().then(addDigits.bind(null, endNumber));
        }

        function endNumber () {
            debug('number::endNumber');

            emitter.emit(events.number, parseFloat(digits));
            setImmediate(endValue);
        }
    }

    function walkDigits () {
        var digits, resolve;

        debug('walkDigits');

        digits = '';

        wait();

        return new Promise(function (r) {
            resolve = r;
        });

        function wait () {
            debug('walkDigits::wait');

            awaitCharacter().then(step).catch(atEnd);
        }

        function step () {
            debug('walkDigits::step');

            if (isDigit(character())) {
                return next().then(function (character) {
                    debug('walkDigits::step::next');

                    digits += character;
                    wait();
                });
            }

            resolve({
                digits: digits,
                atEnd: false
            });
        }

        function atEnd () {
            debug('walkDigits::atEnd');

            resolve({
                digits: digits,
                atEnd: true
            });
        }
    }

    function literalFalse () {
        debug('literalFalse');

        literal([ 'a', 'l', 's', 'e' ], false);
    }

    function literal (expectedCharacters, value) {
        var actual, expected, invalid;

        debug('literal');

        wait();

        function wait () {
            debug('literal::wait');

            awaitCharacter().then(step).catch(atEnd);
        }

        function step () {
            debug('literal::step');

            if (invalid || expectedCharacters.length === 0) {
                return atEnd();
            }

            next().then(afterNext);
        }

        function atEnd () {
            debug('literal::atEnd');

            if (invalid) {
                fail(actual, expected, 'previous');
            } else if (expectedCharacters.length > 0) {
                fail('EOF', expectedCharacters.shift(), 'current');
            } else {
                done();
            }

            setImmediate(endValue);
        }

        function afterNext (character) {
            debug('literal::afterNext');

            actual = character;
            expected = expectedCharacters.shift();

            if (actual !== expected) {
                invalid = true;
            }

            wait();
        }

        function done () {
            debug('literal::done');

            emitter.emit(events.literal, value);
        }
    }

    function literalNull () {
        debug('literalNull');

        literal([ 'u', 'l', 'l' ], null);
    }

    function literalTrue () {
        debug('literalTrue');

        literal([ 'r', 'u', 'e' ], true);
    }

    function endStream () {
        debug('endStream');

        flags.stream.ended = true;

        if (!flags.walk.begun) {
            endWalk();
        }

        if (resume) {
            resume();
        }
    }

    function endWalk () {
        debug('endWalk');

        if (flags.walk.ended) {
            return;
        }

        flags.walk.ended = true;

        if (flags.walk.string) {
            fail('EOF', '"', 'current');
        }

        while (scopes.length > 0) {
            fail('EOF', terminators[scopes.pop()], 'current');
        }

        emitter.emit(events.end);
    }
}

function isWhitespace (character) {
    switch (character) {
        case ' ':
        case '\t':
        case '\r':
        case '\n':
            return true;
    }

    return false;
}

function isHexit (character) {
    return isDigit(character) ||
           isInRange(character, 'A', 'F') ||
           isInRange(character, 'a', 'f');
}

function isDigit (character) {
    return isInRange(character, '0', '9');
}

function isInRange (character, lower, upper) {
    var code = character.charCodeAt(0);

    return code >= lower.charCodeAt(0) && code <= upper.charCodeAt(0);
}

<|endoftext|>


<commit_before>/** The examiner management methods for StudentsManager.
 *
 * Note that this class depends on the createRecordFromStoreRecord from
 * StudentsManager to be available. */
Ext.define('devilry.extjshelpers.studentsmanager.StudentsManagerManageExaminers', {

    randomDistResultTpl: Ext.create('Ext.XTemplate',
        '
The selected examiners got the following number of groups:
',
        '
',
        '<tpl for="result">',
        '
{examiner}: {groups.length}
',
        '</tpl>',
        '
'
    ),

    /**
     * @private
     */
    onRandomDistributeExaminers: function() {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }
        var win = Ext.widget('window', {
            title: 'Select examiners',
            modal: true,
            width: 500,
            height: 400,
            maximizable: true,
            layout: 'fit',
            items: {
                xtype: 'setlistofusers',
                usernames: ['donald', 'scrooge', 'daisy', 'clarabelle'],
                //usernames: [],
                helptext: '
The username of a single examiner on each line. Example:
',
                listeners: {
                    scope: this,
                    saveClicked: function(setlistofusersobj, usernames) {
                        this.randomDistributeExaminersOnSelected(setlistofusersobj, usernames);
                    }
                }
            },
        });
        win.show();
    },

    /**
     * @private
     */
    randomDistributeExaminersOnSelected: function(setlistofusersobj, examiners) {
        setlistofusersobj.up('window').close();

        this._randomDistributeTmp = {
            remainingExaminers: Ext.Array.clone(examiners),
            allExaminers: examiners,
            totExaminers: examiners.length,
            result: {}
        };
        Ext.each(examiners, function(examiner, index) {
            this._randomDistributeTmp.result[examiner] = [];
        }, this);

        this.down('studentsmanager_studentsgrid').performActionOnSelected({
            scope: this,
            callback: this.randomDistributeExaminers,
            extraArgs: []
        });
    },

    /**
     * @private
     */
    getRandomExaminer: function() {
        var numRemainingExaminers = this._randomDistributeTmp.remainingExaminers.length;
        var randomIndex = Math.floor(Math.random() * (numRemainingExaminers));
        return this._randomDistributeTmp.remainingExaminers[randomIndex];
    },

    /**
     * @private
     */
    removeExaminerIfEnoughStudents: function(examiner, totalSelectedGroups) {
        var totExaminers = this._randomDistributeTmp.totExaminers;
        var groupsPerExaminer = Math.ceil(totalSelectedGroups/totExaminers);
        if(this._randomDistributeTmp.result[examiner].length === groupsPerExaminer) {
            Ext.Array.remove(this._randomDistributeTmp.remainingExaminers, examiner);
        }
    },

    /**
     * @private
     */
    showRandomDistributeResults: function() {
        //console.log(this._randomDistributeTmp.result);
        var resultArray = [];
        Ext.each(this._randomDistributeTmp.allExaminers, function(examiner, index) {
            resultArray.push({
                examiner: examiner,
                groups: this._randomDistributeTmp.result[examiner]
            });
        }, this);

        Ext.MessageBox.show({
            title: 'Random distribution of examiners complete',
            msg: this.randomDistResultTpl.apply({result: resultArray}),
            buttons: Ext.Msg.OK,
            scope: this
        });
    },

    /**
     * @private
     */
    randomDistributeExaminers: function(record, index, totalSelectedGroups) {
        var msg = Ext.String.format('Random distributing examiner to group {0}/{1}', index, totalSelectedGroups);
        this.getEl().mask(msg);

        var editRecord = this.createRecordFromStoreRecord(record);
        var examiner = this.getRandomExaminer();
        this._randomDistributeTmp.result[examiner].push(editRecord);
        this.removeExaminerIfEnoughStudents(examiner, totalSelectedGroups);

        editRecord.data.fake_examiners = [examiner];
        editRecord.save({
            failure: function() {
                console.error('Failed to save record');
            }
        });

        if(index == totalSelectedGroups) {
            this.loadFirstPage();
            this.getEl().unmask();
            this.showRandomDistributeResults();
        }
    },

    /**
     * @private
     */
    onSetExaminers: function(append) {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }
        var win = Ext.widget('window', {
            title: 'Select examiners',
            modal: true,
            width: 500,
            height: 400,
            maximizable: true,
            layout: 'fit',
            items: {
                xtype: 'setlistofusers',
                //usernames: ['donald', 'scrooge'],
                usernames: [],
                helptext: '
The username of a single examiner on each line. Example:
',
                listeners: {
                    scope: this,
                    saveClicked: function(setlistofusersobj, usernames) {
                        this.setExaminersOnSelected(setlistofusersobj, usernames, append);
                    }
                }
            },
        });
        win.show();
    },

    /**
     * @private
     */
    onAddExaminers: function() {
        console.log('TP');
        this.onSetExaminers(true);
    },

    /**
     * @private
     */
    onReplaceExaminers: function() {
        this.onSetExaminers(false);
    },

    /**
     * @private
     */
    onClearExaminers: function() {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }

        Ext.MessageBox.show({
            title: 'Confirm clear examiners?',
            msg: 'Are you sure you want to clear examiners on all the selected groups?',
            buttons: Ext.Msg.YESNO,
            icon: Ext.Msg.WARNING,
            scope: this,
            fn: function(btn) {
                if(btn == 'yes') {
                    this.down('studentsmanager_studentsgrid').performActionOnSelected({
                        scope: this,
                        callback: this.setExaminers,
                        extraArgs: [[], false]
                    });
                }
            }
        });
    },

    /**
     * @private
     */
    setExaminersOnSelected: function(setlistofusersobj, usernames, append) {
        setlistofusersobj.up('window').close();
        this.down('studentsmanager_studentsgrid').performActionOnSelected({
            scope: this,
            callback: this.setExaminers,
            extraArgs: [usernames, append]
        });
    },

    /**
     * @private
     */
    setExaminers: function(record, index, totalSelectedGroups, usernames, append) {
        var msg = Ext.String.format('Setting examiners on group {0}/{1}', index, totalSelectedGroups);
        this.getEl().mask(msg);

        if(append) {
            usernames = Ext.Array.merge(usernames, record.data.examiners__username);
        };

        var editRecord = this.createRecordFromStoreRecord(record);
        editRecord.data.fake_examiners = usernames;
        editRecord.save({
            failure: function() {
                console.error('Failed to save record');
            }
        });

        if(index == totalSelectedGroups) {
            this.loadFirstPage();
            this.getEl().unmask();
        }
    }
});
<commit_msg>apps.extjshelpers: Cleanup.<commit_after>/** The examiner management methods for StudentsManager.
 *
 * Note that this class depends on the createRecordFromStoreRecord from
 * StudentsManager to be available. */
Ext.define('devilry.extjshelpers.studentsmanager.StudentsManagerManageExaminers', {

    randomDistResultTpl: Ext.create('Ext.XTemplate',
        '
The selected examiners got the following number of groups:
',
        '
',
        '<tpl for="result">',
        '
{examiner}: {groups.length}
',
        '</tpl>',
        '
'
    ),

    /**
     * @private
     */
    onRandomDistributeExaminers: function() {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }
        var win = Ext.widget('window', {
            title: 'Select examiners',
            modal: true,
            width: 500,
            height: 400,
            maximizable: true,
            layout: 'fit',
            items: {
                xtype: 'setlistofusers',
                //usernames: ['donald', 'scrooge', 'daisy', 'clarabelle'],
                usernames: [],
                helptext: '
The username of a single examiner on each line. Example:
',
                listeners: {
                    scope: this,
                    saveClicked: function(setlistofusersobj, usernames) {
                        this.randomDistributeExaminersOnSelected(setlistofusersobj, usernames);
                    }
                }
            },
        });
        win.show();
    },

    /**
     * @private
     */
    randomDistributeExaminersOnSelected: function(setlistofusersobj, examiners) {
        setlistofusersobj.up('window').close();

        this._randomDistributeTmp = {
            remainingExaminers: Ext.Array.clone(examiners),
            allExaminers: examiners,
            totExaminers: examiners.length,
            result: {}
        };
        Ext.each(examiners, function(examiner, index) {
            this._randomDistributeTmp.result[examiner] = [];
        }, this);

        this.down('studentsmanager_studentsgrid').performActionOnSelected({
            scope: this,
            callback: this.randomDistributeExaminers,
            extraArgs: []
        });
    },

    /**
     * @private
     */
    getRandomExaminer: function() {
        var numRemainingExaminers = this._randomDistributeTmp.remainingExaminers.length;
        var randomIndex = Math.floor(Math.random() * (numRemainingExaminers));
        return this._randomDistributeTmp.remainingExaminers[randomIndex];
    },

    /**
     * @private
     */
    removeExaminerIfEnoughStudents: function(examiner, totalSelectedGroups) {
        var totExaminers = this._randomDistributeTmp.totExaminers;
        var groupsPerExaminer = Math.ceil(totalSelectedGroups/totExaminers);
        if(this._randomDistributeTmp.result[examiner].length === groupsPerExaminer) {
            Ext.Array.remove(this._randomDistributeTmp.remainingExaminers, examiner);
        }
    },

    /**
     * @private
     */
    showRandomDistributeResults: function() {
        //console.log(this._randomDistributeTmp.result);
        var resultArray = [];
        Ext.each(this._randomDistributeTmp.allExaminers, function(examiner, index) {
            resultArray.push({
                examiner: examiner,
                groups: this._randomDistributeTmp.result[examiner]
            });
        }, this);

        Ext.MessageBox.show({
            title: 'Random distribution of examiners complete',
            msg: this.randomDistResultTpl.apply({result: resultArray}),
            buttons: Ext.Msg.OK,
            scope: this
        });
    },

    /**
     * @private
     */
    randomDistributeExaminers: function(record, index, totalSelectedGroups) {
        var msg = Ext.String.format('Random distributing examiner to group {0}/{1}', index, totalSelectedGroups);
        this.getEl().mask(msg);

        var editRecord = this.createRecordFromStoreRecord(record);
        var examiner = this.getRandomExaminer();
        this._randomDistributeTmp.result[examiner].push(editRecord);
        this.removeExaminerIfEnoughStudents(examiner, totalSelectedGroups);

        editRecord.data.fake_examiners = [examiner];
        editRecord.save({
            failure: function() {
                console.error('Failed to save record');
            }
        });

        if(index == totalSelectedGroups) {
            this.loadFirstPage();
            this.getEl().unmask();
            this.showRandomDistributeResults();
        }
    },

    /**
     * @private
     */
    onSetExaminers: function(append) {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }
        var win = Ext.widget('window', {
            title: 'Select examiners',
            modal: true,
            width: 500,
            height: 400,
            maximizable: true,
            layout: 'fit',
            items: {
                xtype: 'setlistofusers',
                //usernames: ['donald', 'scrooge'],
                usernames: [],
                helptext: '
The username of a single examiner on each line. Example:
',
                listeners: {
                    scope: this,
                    saveClicked: function(setlistofusersobj, usernames) {
                        this.setExaminersOnSelected(setlistofusersobj, usernames, append);
                    }
                }
            },
        });
        win.show();
    },

    /**
     * @private
     */
    onAddExaminers: function() {
        console.log('TP');
        this.onSetExaminers(true);
    },

    /**
     * @private
     */
    onReplaceExaminers: function() {
        this.onSetExaminers(false);
    },

    /**
     * @private
     */
    onClearExaminers: function() {
        if(this.noneSelected()) {
            this.onSelectNone();
            return;
        }

        Ext.MessageBox.show({
            title: 'Confirm clear examiners?',
            msg: 'Are you sure you want to clear examiners on all the selected groups?',
            buttons: Ext.Msg.YESNO,
            icon: Ext.Msg.WARNING,
            scope: this,
            fn: function(btn) {
                if(btn == 'yes') {
                    this.down('studentsmanager_studentsgrid').performActionOnSelected({
                        scope: this,
                        callback: this.setExaminers,
                        extraArgs: [[], false]
                    });
                }
            }
        });
    },

    /**
     * @private
     */
    setExaminersOnSelected: function(setlistofusersobj, usernames, append) {
        setlistofusersobj.up('window').close();
        this.down('studentsmanager_studentsgrid').performActionOnSelected({
            scope: this,
            callback: this.setExaminers,
            extraArgs: [usernames, append]
        });
    },

    /**
     * @private
     */
    setExaminers: function(record, index, totalSelectedGroups, usernames, append) {
        var msg = Ext.String.format('Setting examiners on group {0}/{1}', index, totalSelectedGroups);
        this.getEl().mask(msg);

        if(append) {
            usernames = Ext.Array.merge(usernames, record.data.examiners__username);
        };

        var editRecord = this.createRecordFromStoreRecord(record);
        editRecord.data.fake_examiners = usernames;
        editRecord.save({
            failure: function() {
                console.error('Failed to save record');
            }
        });

        if(index == totalSelectedGroups) {
            this.loadFirstPage();
            this.getEl().unmask();
        }
    }
});
<|endoftext|>


<commit_before>var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var DBMigrate = require('../../');
var path = require('path');
var cp = require('child_process');

lab.experiment('api', function() {

  lab.test('force process exit after migrations have been run',
    function(done, onCleanup) {

    var process_exit = process.exit,
        argv = process.argv;

    var called = false,
        config = { cwd: __dirname };

    onCleanup(teardown);

    createMigration(function() {

      var dbmigrate = DBMigrate.getInstance(true, config);

      dbmigrate.setConfigParam('force-exit', true);
      dbmigrate.silence(true);

      dbmigrate.up();
    });

    function validate() {

      Code.expect(called).to.equal(true);
      done();
    }

    /**
      * Create a migration with the programatic API and overwrite process.exit.
      */
    function createMigration(callback) {

      var api = DBMigrate.getInstance(true, config);
      api.silence(true);

      api.create( 'test', function() {
        process.exit = function(err) {

          var ret = called;
          called = true;

          process.exit = process_exit;

          if(err)
            process.exit.apply(arguments);

          Code.expect(ret).to.equal(false);
          validate();
        };

        callback();
      } );
    }

    function teardown(next) {

      process.exit = process_exit;
      process.argv = argv;
      cp.exec('rm -r ' + path.join(__dirname, 'migrations'), this.callback);
      return next();
    }
  });

  lab.test('should load config from parameter', function(done) {

    var options = {
      env: 'dev',
      cwd: process.cwd() + '/test/integration',
      config: {
        dev: {
          driver: 'sqlite3',
          filename: ':memory:'
        },
        pg: {
          driver: 'pg',
          database: 'db_api_test'
        }
      }
    };

    var api = DBMigrate.getInstance(true, options);
    var actual = api.config;
    var expected = options.config;

    delete expected.getCurrent;
    delete actual.getCurrent;

    Code.expect(actual).to.equal(expected);
    done();
  });
});
<commit_msg>chore(comments): add some notes to the api tests<commit_after>var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var DBMigrate = require('../../');
var path = require('path');
var cp = require('child_process');

lab.experiment('api', function() {

  lab.test('force process exit after migrations have been run',
    function(done, onCleanup) {

    var process_exit = process.exit,
        argv = process.argv,
        called = false,
        config = { cwd: __dirname };

    // register cleanup method and start preparing the test
    onCleanup(teardown);
    createMigration(function() {

      var dbmigrate = DBMigrate.getInstance(true, config);

      dbmigrate.setConfigParam('force-exit', true);
      dbmigrate.silence(true);

      /**
        * We have set force-exit above, this should end up in db-migrate
        * executing process.exit on the final callback.
        * Process.exit has been overwritten and will finally call validate.
        *
        * The test validation takes place in validate()
        */
      dbmigrate.up();
    });

    /**
      * Final validation after process.exit should have been called.
      */
    function validate() {

      Code.expect(called).to.equal(true);
      done();
    }

    /**
      * Create a migration with the programatic API and overwrite process.exit.
      */
    function createMigration(callback) {

      var api = DBMigrate.getInstance(true, config);
      api.silence(true);

      api.create( 'test', function() {
        process.exit = function(err) {

          var ret = called;
          called = true;

          process.exit = process_exit;

          if(err)
            process.exit.apply(arguments);

          Code.expect(ret).to.equal(false);
          validate();
        };

        callback();
      } );
    }

    function teardown(next) {

      process.exit = process_exit;
      process.argv = argv;
      cp.exec('rm -r ' + path.join(__dirname, 'migrations'), this.callback);
      return next();
    }
  });

  lab.test('should load config from parameter', function(done) {

    var options = {
      env: 'dev',
      cwd: process.cwd() + '/test/integration',
      config: {
        dev: {
          driver: 'sqlite3',
          filename: ':memory:'
        },
        pg: {
          driver: 'pg',
          database: 'db_api_test'
        }
      }
    };

    var api = DBMigrate.getInstance(true, options);
    var actual = api.config;
    var expected = options.config;

    delete expected.getCurrent;
    delete actual.getCurrent;

    Code.expect(actual).to.equal(expected);
    done();
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global describe, it, expect, dump*/

(function () {
  "use strict";
  //var assert = cha.assert;

  describe("Nothing", function () {

    it("should run", function () {
      var div = document.createElement("div");
      div.setAttribute("id", "tdjs");
      document.body.appendChild(div);
      debugger
      dump('breakpoint');
    });

  });
}());
<commit_msg>Pulled information out of testacular DOM<commit_after>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global describe, it, expect, dump*/

(function () {
  "use strict";
  //var assert = cha.assert;

  describe("Nothing", function () {

    it("should run", function () {
      var div = document.createElement("div");
      div.setAttribute("id", "tdjs");
      div.setAttribute("foo", "bar");
      div.innerHTML = "asdfasdf";
      document.body.appendChild(div);
      dump('breakpoint');

      var extractedDiv = document.getElementById("tdjs");
      expect(extractedDiv.getAttribute('foo')).to.equal('bar');
    });

  });
}());
<|endoftext|>


<commit_before>//imports for Keystone app core
const { AdminUI } = require('@keystone-alpha/admin-ui');
const { Keystone } = require('@keystone-alpha/keystone');
const {
  File,
  Text,
  Relationship,
  Select,
  Password,
  Checkbox,
  CalendarDay,
  DateTime,
} = require('@keystone-alpha/fields');
const { LocalFileAdapter } = require('@keystone-alpha/file-adapters');
const PasswordAuthStrategy = require('@keystone-alpha/keystone/auth/Password');
const { MongooseAdapter } = require('@keystone-alpha/adapter-mongoose');

// config
const path = require('path');
const staticRoute = '/public'; // The URL portion
const staticPath = path.join(process.cwd(), 'public'); // The local path on disk
const getYear = require('date-fns/get_year');

const keystone = new Keystone({
  name: 'Keystone Demo Blog',
  adapter: new MongooseAdapter(),
});

const authStrategy = keystone.createAuthStrategy({
  type: PasswordAuthStrategy,
  list: 'User',
  sortListsAlphabetically: true,
});

const fileAdapter = new LocalFileAdapter({
  directory: `${staticPath}/uploads`,
  route: `${staticRoute}/uploads`,
});

const avatarFileAdapter = new LocalFileAdapter({
  directory: `${staticPath}/avatars`,
  route: `${staticRoute}/avatars`,
});

keystone.createList('User', {
  fields: {
    name: { type: Text },
    email: { type: Text, isUnique: true },
    dob: {
      type: CalendarDay,
      format: 'Do MMMM YYYY',
      yearRangeFrom: 1901,
      yearRangeTo: getYear(new Date()),
    },
    password: { type: Password },
    isAdmin: { type: Checkbox },
    avatar: { type: File, adapter: avatarFileAdapter },
  },
  labelResolver: item => `${item.name} <${item.email}>`,
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    author: {
      type: Relationship,
      ref: 'User',
    },
    categories: {
      type: Relationship,
      ref: 'PostCategory',
      many: true,
    },
    status: {
      type: Select,
      defaultValue: 'draft',
      options: [{ label: 'Draft', value: 'draft' }, { label: 'Published', value: 'published' }],
    },
    body: { type: Text, isMultiline: true },
    posted: { type: DateTime },
    image: { type: File, adapter: fileAdapter },
  },
  adminConfig: {
    defaultPageSize: 20,
    defaultColumns: 'title, status',
    defaultSort: 'title',
  },
  labelResolver: item => item.title,
});

keystone.createList('PostCategory', {
  fields: {
    name: { type: Text },
    slug: { type: Text },
  },
});

keystone.createList('Comment', {
  fields: {
    body: { type: Text, isMultiline: true },
    originalPost: {
      type: Relationship,
      ref: 'Post',
    },
    author: {
      type: Relationship,
      ref: 'User',
    },
    posted: { type: DateTime },
  },
  labelResolver: item => item.body,
});

const admin = new AdminUI(keystone, {
  adminPath: '/admin',
  sortListsAlphabetically: true,
  authStrategy,
});

module.exports = {
  staticRoute,
  staticPath,
  keystone,
  admin,
};
<commit_msg>sortListsAlphabetically is not an option for createAuthStrategy<commit_after>//imports for Keystone app core
const { AdminUI } = require('@keystone-alpha/admin-ui');
const { Keystone } = require('@keystone-alpha/keystone');
const {
  File,
  Text,
  Relationship,
  Select,
  Password,
  Checkbox,
  CalendarDay,
  DateTime,
} = require('@keystone-alpha/fields');
const { LocalFileAdapter } = require('@keystone-alpha/file-adapters');
const PasswordAuthStrategy = require('@keystone-alpha/keystone/auth/Password');
const { MongooseAdapter } = require('@keystone-alpha/adapter-mongoose');

// config
const path = require('path');
const staticRoute = '/public'; // The URL portion
const staticPath = path.join(process.cwd(), 'public'); // The local path on disk
const getYear = require('date-fns/get_year');

const keystone = new Keystone({
  name: 'Keystone Demo Blog',
  adapter: new MongooseAdapter(),
});

const authStrategy = keystone.createAuthStrategy({
  type: PasswordAuthStrategy,
  list: 'User',
});

const fileAdapter = new LocalFileAdapter({
  directory: `${staticPath}/uploads`,
  route: `${staticRoute}/uploads`,
});

const avatarFileAdapter = new LocalFileAdapter({
  directory: `${staticPath}/avatars`,
  route: `${staticRoute}/avatars`,
});

keystone.createList('User', {
  fields: {
    name: { type: Text },
    email: { type: Text, isUnique: true },
    dob: {
      type: CalendarDay,
      format: 'Do MMMM YYYY',
      yearRangeFrom: 1901,
      yearRangeTo: getYear(new Date()),
    },
    password: { type: Password },
    isAdmin: { type: Checkbox },
    avatar: { type: File, adapter: avatarFileAdapter },
  },
  labelResolver: item => `${item.name} <${item.email}>`,
});

keystone.createList('Post', {
  fields: {
    title: { type: Text },
    author: {
      type: Relationship,
      ref: 'User',
    },
    categories: {
      type: Relationship,
      ref: 'PostCategory',
      many: true,
    },
    status: {
      type: Select,
      defaultValue: 'draft',
      options: [{ label: 'Draft', value: 'draft' }, { label: 'Published', value: 'published' }],
    },
    body: { type: Text, isMultiline: true },
    posted: { type: DateTime },
    image: { type: File, adapter: fileAdapter },
  },
  adminConfig: {
    defaultPageSize: 20,
    defaultColumns: 'title, status',
    defaultSort: 'title',
  },
  labelResolver: item => item.title,
});

keystone.createList('PostCategory', {
  fields: {
    name: { type: Text },
    slug: { type: Text },
  },
});

keystone.createList('Comment', {
  fields: {
    body: { type: Text, isMultiline: true },
    originalPost: {
      type: Relationship,
      ref: 'Post',
    },
    author: {
      type: Relationship,
      ref: 'User',
    },
    posted: { type: DateTime },
  },
  labelResolver: item => item.body,
});

const admin = new AdminUI(keystone, {
  adminPath: '/admin',
  sortListsAlphabetically: true,
  authStrategy,
});

module.exports = {
  staticRoute,
  staticPath,
  keystone,
  admin,
};
<|endoftext|>


<commit_before>import { Map as map, fromJS } from 'immutable';
import uuid from 'uuid';

const nameDefault = 'default';

let names = map();
let data = fromJS({
  default: {
    fields: [],
    sorts: {},
    activeSort: null,
    infiniteScroll: false,
    instantSearch: false,
    customResultRenderer: null,
    maxResults: 20,
  },
});

class ConfigStore {
  defaultName() {
    return nameDefault;
  }

  getFields() {
    return data.getIn([nameDefault, 'fields']);
  }

  setFields(fields) {
    data = data.setIn([nameDefault, 'fields'], fields);
  }

  addName(name, project, collection) {
    names = names.set(name, {
      project,
      collection,
    });
  }

  getNameDetails(name) {
    return names.get(name);
  }

  getProjectCollection() {
    return names.get(nameDefault);
  }

  setProjectCollection(project, collection) {
    names = names.set(nameDefault, {
      project,
      collection,
    });
  }

  getSortById(id) {
    return data.getIn([nameDefault, 'sorts', id]);
  }

  getSorts() {
    return data.getIn([nameDefault, 'sorts']);
  }

  setSorts(sorts) {
    const rawSorts = sorts.map((s) => s.raw());
    rawSorts.forEach((s) => {
      data = data.setIn([nameDefault, 'sorts', uuid.v4()], s);
    });
    // console.log(data.getIn([nameDefault, 'sorts']));
    // data = data.setIn([nameDefault, 'sorts'], rawSorts);
  }

  setActiveSort(sort) {
    data = data.setIn([nameDefault, 'activeSort'], sort);
  }

  getActiveSort() {
    return data.getIn([nameDefault, 'activeSort']);
  }

  setInfiniteScroll(enabled) {
    data = data.setIn([nameDefault, 'infiniteScroll'], enabled);
  }

  getInfiniteScroll() {
    return data.getIn([nameDefault, 'infiniteScroll']);
  }

  setInstantSearch(enabled) {
    data = data.setIn([nameDefault, 'instantSearch'], enabled);
  }

  getInstantSearch() {
    return data.getIn([nameDefault, 'instantSearch']);
  }

  setCustomResultRenderer(component) {
    data = data.setIn([nameDefault, 'customResultRenderer'], component);
  }

  getCustomResultRenderer() {
    return data.getIn([nameDefault, 'customResultRenderer']);
  }

  setMaxResults(max) {
    data = data.setIn([nameDefault, 'maxResults'], max);
  }

  getMaxResults() {
    return data.getIn([nameDefault, 'maxResults']);
  }
}

export default new ConfigStore();
<commit_msg>Remove config store<commit_after><|endoftext|>


<commit_before>const isBrowser = typeof window !== 'undefined';
let Modernizr = isBrowser ? require('../utils/modernizr.custom') : undefined;

module.exports = {

  all(styles) {
    let prefixedStyle = {};
    for (let key in styles) {
      prefixedStyle[this.single(key)] = styles[key];
    }
    return prefixedStyle;
  },

  set(style, key, value) {
    style[this.single(key)] = value;
  },

  single(key) {
    return isBrowser ? Modernizr.prefixed(key) : key;
  },

  singleHyphened(key) {
    let str = this.single(key);

    return !str ? key : str.replace(/([A-Z])/g, (str,m1) => {
      return '-' + m1.toLowerCase();
    }).replace(/^ms-/,'-ms-');
  },

};
<commit_msg>Fix false CSS property. Modernizr.prefixed returns false in some cases; updated auto-prefix to use key instead of false.<commit_after>const isBrowser = typeof window !== 'undefined';
let Modernizr = isBrowser ? require('../utils/modernizr.custom') : undefined;

module.exports = {

  all(styles) {
    let prefixedStyle = {};
    for (let key in styles) {
      prefixedStyle[this.single(key)] = styles[key];
    }
    return prefixedStyle;
  },

  set(style, key, value) {
    style[this.single(key)] = value;
  },

  single(key) {
    if (isBrowser) {
      // Windows 7 Firefox has an issue with the implementation of Modernizr.prefixed
      // and is capturing 'false' as the CSS property name instead of the non-prefixed version.
      let prefKey = Modernizr.prefixed(key);
      return prefKey === false ? key : prefKey;
    }
    else {
      return key;
    }
  },

  singleHyphened(key) {
    let str = this.single(key);

    return !str ? key : str.replace(/([A-Z])/g, (str,m1) => {
      return '-' + m1.toLowerCase();
    }).replace(/^ms-/,'-ms-');
  },

};
<|endoftext|>


<commit_before>(function () {
// service to handle all processing realted to the QC. Querying the server, processing the information for example.

'use strict';

angular.module('daApp')
  .factory('qcService', qcService);

qcService.$inject = ['$q', 'dataService', 'deliveryService', 'logger', 'utilityService'];

function qcService($q, dataService, deliveryService, logger, utilityService) {
  var qcHandler = {};
  var delService = deliveryService;
  var util = utilityService;

  qcHandler.notifySend = notifySend;

  // counter for the total times recording.controller.js
  // has notified us of a send during this session
  var totalNotifies = 0;
  // counter to use for QC, counts how many recordings have been sent,
  //   since last reset of counter.
  var modSendCounter = 0;

  return qcHandler;

  //////////

  function notifySend(sessionId, tokenCount) {
    // tokenCount is the total count of token reads by current speaker
    // if tokenCount === 0 it is a new speaker
    totalNotifies++;
    modSendCounter++;
    //console.info(totalNotifies + ' totalNotifies');

    // if totalNotifies is higher than the token count, it probably means
    //   a new user is recording starting at 0 tokens read.
    if (totalNotifies > tokenCount) {
      totalNotifies = 1;
      modSendCounter = 1;
    }

    if (modSendCounter >= (util.getConstant('QCFrequency' || 5))
       && totalNotifies >= (util.getConstant('QCInitRecThreshold') || 10)) {
      modSendCounter = 0;

      return delService.queryQC(sessionId)
      .then(handleQCReport, util.stdErrCallback);

    } else {

      return $q.reject(false);
    }
  }

  function calcAvgAcc(report) {
    /*
      Calculates weighted (right now equally) accuracy of all the modules
      from the QC report.

      parameters:
        report  the QC report as described in client server api.
      return:
        a in [0.0 .. 1.0]
    */
    var acc = 0;
    var cnt = 0;
    for (var mod in report.modules) {
      if (!report.modules.hasOwnProperty(mod)) continue;

      var module = report.modules[mod];
      acc += module.totalStats.accuracy;
      cnt++;
    }
    return acc/cnt;
  }

  function handleQCReport(response) {
    var report = response.data;

    // calulate average accuracy of all QC modules
    var avgAcc = calcAvgAcc(report);
    report.avgAcc = avgAcc;

    var tokenAnnouncement = handleTokenAnnouncements(report);

    // displayReport is in HTML
    var displayReport = prettify(report);
    dataService.set('QCReport', displayReport);

    // message to send back to recording.controller.js notifying that we wish
    //   results to be displayed.
    var displayResults = tokenAnnouncement
                         || avgAcc < util.getConstant('QCAccThreshold');
    if (displayResults) {
      return $q.when(true);
    } else {
      return $q.reject(false);
    }
  }

  // like for example, display, good job! on each 50 tokens read.
  // adds key tokenCount and tokenCountMsg to report.
  function handleTokenAnnouncements(report) {
    var tokensRead = dataService.get('speakerInfo').tokensRead;

    // for tokenAnnouncement to be true modulus of tokenAnnouncement
    // and TokenAnnouncementFrequency need to be 0
    var tokenAnnouncement = totalNotifies > 0
                            && totalNotifies % util.getConstant('TokenAnnouncementFreq') === 0;

    var totalTokens = util.getConstant('TokenCountGoal') || 260;
    if (tokenAnnouncement) {
      var _totalNotifies;
      if (tokensRead){
        _totalNotifies = tokensRead;
      } else {
        _totalNotifies = totalNotifies;
      }

      report.tokenCount =  tokensRead;
      // some gamifying messages to pump up the speakers
      report.tokenCountMsg = 'Nice, '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens read, keep going.';
      if (totalNotifies >= 100 && totalNotifies < 200) {
        report.tokenCountMsg = 'Sweet, '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens.';
      }
      if (totalNotifies >= 200 && totalNotifies < 300) {
        report.tokenCountMsg = util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens? Wow.'
      }
      if (totalNotifies >= 300 && totalNotifies < 400) {
        report.tokenCountMsg = 'Awesome, '+util.percentage(t_otalNotifies, totalTokens, 2)+'% of the tokens. Just awesome';
      }
      if (totalNotifies >= 400) {
        report.tokenCountMsg = 'You are a god: '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens.';
      }

      var tokensLeftToRead = totalTokens - tokensRead;

      if (tokensLeftToRead > 0){
        report.tokensLeftToReadMsg = 'You have ' + tokensLeftToRead + ' tokens of ' + totalTokens + ' left to read.'
      }
    }
    return tokenAnnouncement;
  }

  function messageClassByAccuracy(accuracy){
    if (accuracy <= 0.2){return '_red'}
    if (accuracy > 0.2 && accuracy < 0.4) {return '_orange'}
    if (accuracy >= 0.4 && accuracy < 0.6) {return '_greenish'} else{
      return '_green'
    }
  }

  // parses the JSON object report into some prettified report to display in HTML
  function prettify(report) {
    var out = '';
    if (report.tokenCountMsg) {
      out += '
'+report.tokenCountMsg+'
\n';

    }
    if (report.tokensLeftToReadMsg){
      out += '
'+report.tokensLeftToReadMsg+'
\n';
    }

    if (report.status === 'processing' && report.avgAcc !== NaN) {
      out += '
Total average accuracy: '+util.percentage(report.avgAcc, 1, 3)+'
';

      var modules = report.modules;
      for (var mod in modules) {
        if (!modules.hasOwnProperty(mod)) continue;

        var module = modules[mod];
        var accuracy = module.totalStats.accuracy;
        out += '
'+mod+'
';
        if (accuracy) {
          var message = messageClassByAccuracy(accuracy);

          out += '
Average accuracy: '+util.percentage(accuracy, 1, 3)+'%
';
        }
        if (module.perRecordingStats) {
          out += '
More stats
';
          out += '
';
          for (var i = 0; i < module.perRecordingStats.length; i++) {
            var acc = module.perRecordingStats[i].stats.accuracy;
            out += '
Rec. '+i+', accuracy: '+util.percentage(acc, 1, 3)+'%
';
          }
          out += '
';
        }
      }
    }

    return out;
  }
}
}());
<commit_msg>percentage for the win<commit_after>(function () {
// service to handle all processing realted to the QC. Querying the server, processing the information for example.

'use strict';

angular.module('daApp')
  .factory('qcService', qcService);

qcService.$inject = ['$q', 'dataService', 'deliveryService', 'logger', 'utilityService'];

function qcService($q, dataService, deliveryService, logger, utilityService) {
  var qcHandler = {};
  var delService = deliveryService;
  var util = utilityService;

  qcHandler.notifySend = notifySend;

  // counter for the total times recording.controller.js
  // has notified us of a send during this session
  var totalNotifies = 0;
  // counter to use for QC, counts how many recordings have been sent,
  //   since last reset of counter.
  var modSendCounter = 0;

  return qcHandler;

  //////////

  function notifySend(sessionId, tokenCount) {
    // tokenCount is the total count of token reads by current speaker
    // if tokenCount === 0 it is a new speaker
    totalNotifies++;
    modSendCounter++;
    //console.info(totalNotifies + ' totalNotifies');

    // if totalNotifies is higher than the token count, it probably means
    //   a new user is recording starting at 0 tokens read.
    if (totalNotifies > tokenCount) {
      totalNotifies = 1;
      modSendCounter = 1;
    }

    if (modSendCounter >= (util.getConstant('QCFrequency' || 5))
       && totalNotifies >= (util.getConstant('QCInitRecThreshold') || 10)) {
      modSendCounter = 0;

      return delService.queryQC(sessionId)
      .then(handleQCReport, util.stdErrCallback);

    } else {

      return $q.reject(false);
    }
  }

  function calcAvgAcc(report) {
    /*
      Calculates weighted (right now equally) accuracy of all the modules
      from the QC report.

      parameters:
        report  the QC report as described in client server api.
      return:
        a in [0.0 .. 1.0]
    */
    var acc = 0;
    var cnt = 0;
    for (var mod in report.modules) {
      if (!report.modules.hasOwnProperty(mod)) continue;

      var module = report.modules[mod];
      acc += module.totalStats.accuracy;
      cnt++;
    }
    return acc/cnt;
  }

  function handleQCReport(response) {
    var report = response.data;

    // calulate average accuracy of all QC modules
    var avgAcc = calcAvgAcc(report);
    report.avgAcc = avgAcc;

    var tokenAnnouncement = handleTokenAnnouncements(report);

    // displayReport is in HTML
    var displayReport = prettify(report);
    dataService.set('QCReport', displayReport);

    // message to send back to recording.controller.js notifying that we wish
    //   results to be displayed.
    var displayResults = tokenAnnouncement
                         || avgAcc < util.getConstant('QCAccThreshold');
    if (displayResults) {
      return $q.when(true);
    } else {
      return $q.reject(false);
    }
  }

  // like for example, display, good job! on each 50 tokens read.
  // adds key tokenCount and tokenCountMsg to report.
  function handleTokenAnnouncements(report) {
    var tokensRead = dataService.get('speakerInfo').tokensRead;

    // for tokenAnnouncement to be true modulus of tokenAnnouncement
    // and TokenAnnouncementFrequency need to be 0
    var tokenAnnouncement = totalNotifies > 0
                            && totalNotifies % util.getConstant('TokenAnnouncementFreq') === 0;

    var totalTokens = util.getConstant('TokenCountGoal') || 260;
    if (tokenAnnouncement) {
      var _totalNotifies;
      if (tokensRead){
        _totalNotifies = tokensRead;
      } else {
        _totalNotifies = totalNotifies;
      }

      report.tokenCount =  tokensRead;
      // some gamifying messages to pump up the speakers
      report.tokenCountMsg = 'Nice, '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens read, keep going.';
      if (totalNotifies >= 100 && totalNotifies < 200) {
        report.tokenCountMsg = 'Sweet, '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens.';
      }
      if (totalNotifies >= 200 && totalNotifies < 300) {
        report.tokenCountMsg = util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens? Wow.'
      }
      if (totalNotifies >= 300 && totalNotifies < 400) {
        report.tokenCountMsg = 'Awesome, '+util.percentage(t_otalNotifies, totalTokens, 2)+'% of the tokens. Just awesome';
      }
      if (totalNotifies >= 400) {
        report.tokenCountMsg = 'You are a god: '+util.percentage(_totalNotifies, totalTokens, 2)+'% of the tokens.';
      }

      var tokensLeftToRead = totalTokens - tokensRead;

      if (tokensLeftToRead > 0){
        report.tokensLeftToReadMsg = 'You have ' + tokensLeftToRead + ' tokens of ' + totalTokens + ' left to read.'
      }
    }
    return tokenAnnouncement;
  }

  function messageClassByAccuracy(accuracy){
    if (accuracy <= 0.2){return '_red'}
    if (accuracy > 0.2 && accuracy < 0.4) {return '_orange'}
    if (accuracy >= 0.4 && accuracy < 0.6) {return '_greenish'} else{
      return '_green'
    }
  }

  // parses the JSON object report into some prettified report to display in HTML
  function prettify(report) {
    var out = '';
    if (report.tokenCountMsg) {
      out += '
'+report.tokenCountMsg+'
\n';

    }
    if (report.tokensLeftToReadMsg){
      out += '
'+report.tokensLeftToReadMsg+'
\n';
    }

    if (report.status === 'processing' && report.avgAcc !== NaN) {
      out += '
Total average accuracy: '+util.percentage(report.avgAcc, 1, 3)+'%
';

      var modules = report.modules;
      for (var mod in modules) {
        if (!modules.hasOwnProperty(mod)) continue;

        var module = modules[mod];
        var accuracy = module.totalStats.accuracy;
        out += '
'+mod+'
';
        if (accuracy) {
          var message = messageClassByAccuracy(accuracy);

          out += '
Average accuracy: '+util.percentage(accuracy, 1, 3)+'%
';
        }
        if (module.perRecordingStats) {
          out += '
More stats
';
          out += '
';
          for (var i = 0; i < module.perRecordingStats.length; i++) {
            var acc = module.perRecordingStats[i].stats.accuracy;
            out += '
Rec. '+i+', accuracy: '+util.percentage(acc, 1, 3)+'%
';
          }
          out += '
';
        }
      }
    }

    return out;
  }
}
}());
<|endoftext|>


<commit_before>import { _ } from 'meteor/underscore';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';

import { dbCompanyArchive } from '/db/dbCompanyArchive';
import { dbFoundations } from '/db/dbFoundations';
import { dbLog } from '/db/dbLog';
import { limitMethod } from '/server/imports/rateLimit';
import { debug } from '/server/imports/debug';
import { resourceManager } from '/server/imports/resourceManager';

Meteor.methods({
  investArchiveCompany(companyId) {
    check(this.userId, String);
    check(companyId, String);
    investArchiveCompany(Meteor.user(), companyId);

    return true;
  }
});
export function investArchiveCompany(user, companyId) {
  debug.log('investArchiveCompany', {user, companyId});
  if (user.profile.notPayTax) {
    throw new Meteor.Error(403, '您現在有稅單逾期未繳！');
  }
  if (_.contains(user.profile.ban, 'deal')) {
    throw new Meteor.Error(403, '您現在被金融管理會禁止了所有投資下單行為！');
  }
  const amount = Meteor.settings.public.founderEarnestMoney;
  if (user.profile.money < amount) {
    throw new Meteor.Error(403, '金錢不足，無法投資！');
  }
  const archiveCompanyData = dbCompanyArchive.findOne(companyId, {
    fields: {
      status: 1,
      invest: 1
    }
  });
  if (! archiveCompanyData || archiveCompanyData.status !== 'archived') {
    throw new Meteor.Error(404, '保管庫公司並不存在，可能已經上市或被移除！');
  }
  const userId = user._id;
  if (_.contains(archiveCompanyData.invest, {userId})) {
    throw new Meteor.Error(403, '您已經投資過此保管庫公司了！');
  }
  //先鎖定資源，再重新讀取一次資料進行運算
  resourceManager.throwErrorIsResourceIsLock(['archive' + companyId, 'user' + userId]);
  resourceManager.request('investArchiveCompany', ['archive' + companyId, 'user' + userId], (release) => {
    const user = Meteor.users.findOne(userId, {
      fields: {
        profile: 1
      }
    });
    if (user.profile.money < amount) {
      throw new Meteor.Error(403, '金錢不足，無法投資！');
    }
    const archiveCompanyData = dbCompanyArchive.findOne(companyId);
    if (! archiveCompanyData || archiveCompanyData.status !== 'archived') {
      throw new Meteor.Error(404, '保管庫公司並不存在，可能已經上市或被移除！');
    }
    if (_.contains(archiveCompanyData.invest, {userId})) {
      throw new Meteor.Error(403, '您已經投資過此保管庫公司了！');
    }
    const createdAt = new Date();
    dbLog.insert({
      logType: '參與投資',
      userId: [userId],
      companyId: companyId,
      message: archiveCompanyData.name,
      amount: amount,
      createdAt: createdAt
    });
    Meteor.users.update(userId, {
      $inc: {
        'profile.money': amount * -1
      }
    });
    if (archiveCompanyData.invest.length >= Meteor.settings.public.archiveReviveNeedUsers) {
      archiveCompanyData.invest.push(userId);
      const userIdList = _.shuffle(archiveCompanyData.invest);
      dbLog.insert({
        logType: '公司復活',
        userId: userIdList,
        companyId: companyId,
        message: archiveCompanyData.name,
        amount: amount,
        createdAt: new Date(createdAt.getTime() + 1)
      });
      const invest = _.map(userIdList, (userId) => {
        return {userId, amount};
      });
      dbCompanyArchive.update(companyId, {
        $set: {
          status: 'foundation',
          invest: []
        }
      });
      dbFoundations.insert({
        _id: companyId,
        companyName: archiveCompanyData.name,
        manager: userIdList[0],
        tags: archiveCompanyData.tags,
        pictureSmall: archiveCompanyData.pictureSmall,
        pictureBig: archiveCompanyData.pictureBig,
        description: archiveCompanyData.description,
        invest: invest,
        createdAt: new Date(createdAt.getTime() + 1)
      });
    }
    else {
      const invest = archiveCompanyData.invest;
      invest.push(userId);
      dbCompanyArchive.update(companyId, {
        $set: {
          invest: invest
        }
      });
    }
    release();
  });
}
//兩秒鐘最多一次
limitMethod('investArchiveCompany', 1, 2000);

<commit_msg>修正保管庫公司進入新創計劃的需求門檻人數錯誤的bug。<commit_after>import { _ } from 'meteor/underscore';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';

import { dbCompanyArchive } from '/db/dbCompanyArchive';
import { dbFoundations } from '/db/dbFoundations';
import { dbLog } from '/db/dbLog';
import { limitMethod } from '/server/imports/rateLimit';
import { debug } from '/server/imports/debug';
import { resourceManager } from '/server/imports/resourceManager';

Meteor.methods({
  investArchiveCompany(companyId) {
    check(this.userId, String);
    check(companyId, String);
    investArchiveCompany(Meteor.user(), companyId);

    return true;
  }
});
export function investArchiveCompany(user, companyId) {
  debug.log('investArchiveCompany', {user, companyId});
  if (user.profile.notPayTax) {
    throw new Meteor.Error(403, '您現在有稅單逾期未繳！');
  }
  if (_.contains(user.profile.ban, 'deal')) {
    throw new Meteor.Error(403, '您現在被金融管理會禁止了所有投資下單行為！');
  }
  const amount = Meteor.settings.public.founderEarnestMoney;
  if (user.profile.money < amount) {
    throw new Meteor.Error(403, '金錢不足，無法投資！');
  }
  const archiveCompanyData = dbCompanyArchive.findOne(companyId, {
    fields: {
      status: 1,
      invest: 1
    }
  });
  if (! archiveCompanyData || archiveCompanyData.status !== 'archived') {
    throw new Meteor.Error(404, '保管庫公司並不存在，可能已經上市或被移除！');
  }
  const userId = user._id;
  if (_.contains(archiveCompanyData.invest, {userId})) {
    throw new Meteor.Error(403, '您已經投資過此保管庫公司了！');
  }
  //先鎖定資源，再重新讀取一次資料進行運算
  resourceManager.throwErrorIsResourceIsLock(['archive' + companyId, 'user' + userId]);
  resourceManager.request('investArchiveCompany', ['archive' + companyId, 'user' + userId], (release) => {
    const user = Meteor.users.findOne(userId, {
      fields: {
        profile: 1
      }
    });
    if (user.profile.money < amount) {
      throw new Meteor.Error(403, '金錢不足，無法投資！');
    }
    const archiveCompanyData = dbCompanyArchive.findOne(companyId);
    if (! archiveCompanyData || archiveCompanyData.status !== 'archived') {
      throw new Meteor.Error(404, '保管庫公司並不存在，可能已經上市或被移除！');
    }
    if (_.contains(archiveCompanyData.invest, {userId})) {
      throw new Meteor.Error(403, '您已經投資過此保管庫公司了！');
    }
    const createdAt = new Date();
    dbLog.insert({
      logType: '參與投資',
      userId: [userId],
      companyId: companyId,
      message: archiveCompanyData.name,
      amount: amount,
      createdAt: createdAt
    });
    Meteor.users.update(userId, {
      $inc: {
        'profile.money': amount * -1
      }
    });
    archiveCompanyData.invest.push(userId);
    if (archiveCompanyData.invest.length >= Meteor.settings.public.archiveReviveNeedUsers) {
      const investUserIdList = _.shuffle(archiveCompanyData.invest);
      dbLog.insert({
        logType: '公司復活',
        userId: investUserIdList,
        companyId: companyId,
        message: archiveCompanyData.name,
        amount: amount,
        createdAt: new Date(createdAt.getTime() + 1)
      });
      const invest = _.map(investUserIdList, (userId) => {
        return {userId, amount};
      });
      dbCompanyArchive.update(companyId, {
        $set: {
          status: 'foundation',
          invest: []
        }
      });
      dbFoundations.insert({
        _id: companyId,
        companyName: archiveCompanyData.name,
        manager: investUserIdList[0],
        tags: archiveCompanyData.tags,
        pictureSmall: archiveCompanyData.pictureSmall,
        pictureBig: archiveCompanyData.pictureBig,
        description: archiveCompanyData.description,
        invest: invest,
        createdAt: new Date(createdAt.getTime() + 1)
      });
    }
    else {
      dbCompanyArchive.update(companyId, {
        $set: {
          invest: archiveCompanyData.invest
        }
      });
    }
    release();
  });
}
//兩秒鐘最多一次
limitMethod('investArchiveCompany', 1, 2000);

<|endoftext|>


<commit_before>Tests.prototype.NetworkTests = function() {
	module('Network (navigator.network)');
	test("should exist", function() {
  		expect(1);
  		ok(navigator.network != null, "navigator.network should not be null.");
	});
	test("should contain an isReachable function", function() {
		expect(2);
		ok(typeof navigator.network.isReachable != 'undefined' && navigator.network.isReachable != null, "navigator.network.isReachable should not be null.");
		ok(typeof navigator.network.isReachable == 'function', "navigator.network.isReachable should be a function.");
	});
	test("should define constants for network status", function() {
		expect(4);
		ok(NetworkStatus != null, "NetworkStatus object exists in global scope.");
		equals(NetworkStatus.NOT_REACHABLE, 0, "NetworkStatus.NOT_REACHABLE is equal to 0.");
		equals(NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK, 1, "NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK is equal to 1.");
		equals(NetworkStatus.REACHABLE_VIA_WIFI_NETWORK, 2, "NetworkStatus.REACHABLE_VIA_WIFI_NETWORK is equal to 2.");
	});
	test("isReachable function should return one of the defined NetworkStatus constants to its success callback", function() {
		expect(1);
		QUnit.stop(Tests.TEST_TIMEOUT);
		var hostname = "http://www.google.com";
		var win = function(code) {
			ok(code == NetworkStatus.NOT_REACHABLE || code == NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK || code == NetworkStatus.REACHABLE_VIA_WIFI_NETWORK, "Success callback in isReachable returns one of the defined NetworkStatus constants.");
			start();
		};
		navigator.network.isReachable(hostname, win);
	});
    module('Network Information API');
    test("connection should exist", function() {
        expect(1);
        ok(navigator.network.connection != null, "navigator.network.connection should not be null.");
    });
    test("should contain connection properties", function() {
        expect(2);
        ok(typeof navigator.network.connection.type != 'undefined', "navigator.network.connection.type is defined.");
        ok(typeof navigator.network.connection.networkName != 'undefined', "navigator.network.connection.networkName is defined.");
    });
    test("should define constants for connection status", function() {
        expect(7);
        equals(Connection.UNKNOWN, "unknown", "Connection.UNKNOWN is equal to 'unknown'.");
        equals(Connection.ETHERNET, "ethernet", "Connection.ETHERNET is equal to 'ethernet'.");
        equals(Connection.WIFI, "wifi", "Connection.WIFI is equal to 'wifi'.");
        equals(Connection.CELL_2G, "2g", "Connection.CELL_2G is equal to '2g'.");
        equals(Connection.CELL_3G, "3g", "Connection.CELL_3G is equal to '3g'.");
        equals(Connection.CELL_4G, "4g", "Connection.CELL_4G is equal to '4g'.");
        equals(Connection.NONE, "none", "Connection.NONE is equal to 'none'.");
    });
};<commit_msg>Modifying tests to match Network API spec released June 7th<commit_after>Tests.prototype.NetworkTests = function() {
	module('Network (navigator.network)');
	test("should exist", function() {
  		expect(1);
  		ok(navigator.network != null, "navigator.network should not be null.");
	});
	test("should contain an isReachable function", function() {
		expect(2);
		ok(typeof navigator.network.isReachable != 'undefined' && navigator.network.isReachable != null, "navigator.network.isReachable should not be null.");
		ok(typeof navigator.network.isReachable == 'function', "navigator.network.isReachable should be a function.");
	});
	test("should define constants for network status", function() {
		expect(4);
		ok(NetworkStatus != null, "NetworkStatus object exists in global scope.");
		equals(NetworkStatus.NOT_REACHABLE, 0, "NetworkStatus.NOT_REACHABLE is equal to 0.");
		equals(NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK, 1, "NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK is equal to 1.");
		equals(NetworkStatus.REACHABLE_VIA_WIFI_NETWORK, 2, "NetworkStatus.REACHABLE_VIA_WIFI_NETWORK is equal to 2.");
	});
	test("isReachable function should return one of the defined NetworkStatus constants to its success callback", function() {
		expect(1);
		QUnit.stop(Tests.TEST_TIMEOUT);
		var hostname = "http://www.google.com";
		var win = function(code) {
			ok(code == NetworkStatus.NOT_REACHABLE || code == NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK || code == NetworkStatus.REACHABLE_VIA_WIFI_NETWORK, "Success callback in isReachable returns one of the defined NetworkStatus constants.");
			start();
		};
		navigator.network.isReachable(hostname, win);
	});
    module('Network Information API');
    test("connection should exist", function() {
        expect(1);
        ok(navigator.network.connection != null, "navigator.network.connection should not be null.");
    });
    test("should contain connection properties", function() {
        expect(1);
        ok(typeof navigator.network.connection.type != 'undefined', "navigator.network.connection.type is defined.");
    });
    test("should define constants for connection status", function() {
        expect(7);
        equals(Connection.UNKNOWN, "unknown", "Connection.UNKNOWN is equal to 'unknown'.");
        equals(Connection.ETHERNET, "ethernet", "Connection.ETHERNET is equal to 'ethernet'.");
        equals(Connection.WIFI, "wifi", "Connection.WIFI is equal to 'wifi'.");
        equals(Connection.CELL_2G, "2g", "Connection.CELL_2G is equal to '2g'.");
        equals(Connection.CELL_3G, "3g", "Connection.CELL_3G is equal to '3g'.");
        equals(Connection.CELL_4G, "4g", "Connection.CELL_4G is equal to '4g'.");
        equals(Connection.NONE, "none", "Connection.NONE is equal to 'none'.");
    });
};<|endoftext|>