<commit_before>/*

test.js - test script

The MIT License (MIT)

Copyright (c) 2016 Dale Schumacher

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

*/
"use strict";

var test = module.exports = {};

//var log = console.log;
var log = function () {};
var warn = console.log;

//var tart = require('tart-tracing');
var actor = require('../humus/hybrid.js');

test['actor model defines create and send'] = function (test) {
    test.expect(2);

    test.strictEqual(typeof actor.create, 'function');
    test.strictEqual(typeof actor.send, 'function');

    test.done();
};

var null_beh = function null_beh(msg) {  // no-op actor behavior
    return {
        actors: [],
        events: [],
        behavior: undefined
    };
};

test['behavior returns actors, events, and optional behavior'] = function (test) {
    test.expect(4);

    var m = 'Hello!';
    var r = null_beh(m);
    log('r:', r);

    test.strictEqual(typeof r, 'object');
    test.ok(Array.isArray(r.actors));
    test.ok(Array.isArray(r.events));
    test.ok((r.behavior === undefined) || ('function' === typeof r.behavior));

    test.done();
};

test['create returns actor address'] = function (test) {
    test.expect(1);

    var a = actor.create(null_beh);

    test.strictEqual(typeof a, 'function');  // address encoded as a function

    test.done();
};

test['send returns message-event'] = function (test) {
    test.expect(3);

    var a = actor.create(null_beh);
    var m = 'Hello!';
    var e = actor.send(a, m);

    test.strictEqual(typeof e, 'object');
    test.strictEqual(e.target, a);
    test.strictEqual(e.message, m);

    test.done();
};

test['one shot actor should forward first message, then ignore everything'] = function (test) {
    test.expect(4);

    var null_beh = function null_beh(msg) {
        log('null'+actor.self+':', msg);
        test.strictEqual(++count, 2);
        return {
            actors: [],
            events: [],
            behavior: undefined
        };
    };
    var one_shot = function one_shot(fwd) {
        return function one_shot_beh(msg) {
            log('one_shot'+actor.self+':', msg);
            test.strictEqual(++count, 1);
            return {
                actors: [],
                events: [
                    actor.send(fwd, msg)
                ],
                behavior: null_beh
            };
        };
    };
    var done_beh = function done_beh(msg) {
        log('done'+actor.self+':', msg);
        test.done();
        return {
            actors: [],
            events: [],
            behavior: undefined
        };
    };

    var count = 0;
    var a = actor.create(function end_beh(msg) {
        log('end'+actor.self+':', msg);
        test.strictEqual(++count, 3);
        var d = actor.create(done_beh);
        return {
            actors: [d],
            events: [
                actor.send(d, 'Z')
            ],
            behavior: undefined
        };
    });
    var b = actor.create(one_shot(a));
    actor.apply({
        actors: [a, b],
        events: [
            actor.send(b, 'X'),
            actor.send(b, 'Y')
        ]
    });
    test.strictEqual(actor.self, undefined);
};

var Y = function Y(a, b) {  // fork in a tree with `a` left branch and `b` right branch
    this.a = a;
    this.b = b;
};
// Y.prototype.toString = function toString() { return '<' + this.a + ', ' + this.b + '>' };
var aTree = new Y(1, new Y(new Y(2, 3), 4));  // <1, <<2, 3>, 4>>

test['<1, <<2, 3>, 4>> has fringe [1, 2, 3, 4]'] = function (test) {
    test.expect(1);

    var fringe = function fringe(tree) {
        if (tree instanceof Y) {
            return fringe(tree.a).concat(fringe(tree.b));
        }
        return [ tree ];
    };

    test.deepEqual(fringe(aTree), [ 1, 2, 3, 4 ]);

    test.done();
};

test['fringe(<1, <<2, 3>, 4>>) = fringe(<<1, 2>, <3, 4>>)'] = function (test) {
    test.expect(1);

    var fringe = function fringe(tree) {
        if (tree instanceof Y) {
            return fringe(tree.a).concat(fringe(tree.b));
        }
        return [ tree ];
    };

    var bTree = new Y(new Y(1, 2), new Y(3, 4));  // <<1, 2>, <3, 4>>
    test.deepEqual(fringe(aTree), fringe(bTree));

    test.done();
};
<commit_msg>Suspend calculations in closures<commit_after>/*

test.js - test script

The MIT License (MIT)

Copyright (c) 2016 Dale Schumacher

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

*/
"use strict";

var test = module.exports = {};

//var log = console.log;
var log = function () {};
var warn = console.log;

//var tart = require('tart-tracing');
var actor = require('../humus/hybrid.js');

test['actor model defines create and send'] = function (test) {
    test.expect(2);

    test.strictEqual(typeof actor.create, 'function');
    test.strictEqual(typeof actor.send, 'function');

    test.done();
};

var null_beh = function null_beh(msg) {  // no-op actor behavior
    return {
        actors: [],
        events: [],
        behavior: undefined
    };
};

test['behavior returns actors, events, and optional behavior'] = function (test) {
    test.expect(4);

    var m = 'Hello!';
    var r = null_beh(m);
    log('r:', r);

    test.strictEqual(typeof r, 'object');
    test.ok(Array.isArray(r.actors));
    test.ok(Array.isArray(r.events));
    test.ok((r.behavior === undefined) || ('function' === typeof r.behavior));

    test.done();
};

test['create returns actor address'] = function (test) {
    test.expect(1);

    var a = actor.create(null_beh);

    test.strictEqual(typeof a, 'function');  // address encoded as a function

    test.done();
};

test['send returns message-event'] = function (test) {
    test.expect(3);

    var a = actor.create(null_beh);
    var m = 'Hello!';
    var e = actor.send(a, m);

    test.strictEqual(typeof e, 'object');
    test.strictEqual(e.target, a);
    test.strictEqual(e.message, m);

    test.done();
};

test['one shot actor should forward first message, then ignore everything'] = function (test) {
    test.expect(4);

    var null_beh = function null_beh(msg) {
        log('null'+actor.self+':', msg);
        test.strictEqual(++count, 2);
        return {
            actors: [],
            events: [],
            behavior: undefined
        };
    };
    var one_shot = function one_shot(fwd) {
        return function one_shot_beh(msg) {
            log('one_shot'+actor.self+':', msg);
            test.strictEqual(++count, 1);
            return {
                actors: [],
                events: [
                    actor.send(fwd, msg)
                ],
                behavior: null_beh
            };
        };
    };
    var done_beh = function done_beh(msg) {
        log('done'+actor.self+':', msg);
        test.done();
        return {
            actors: [],
            events: [],
            behavior: undefined
        };
    };

    var count = 0;
    var a = actor.create(function end_beh(msg) {
        log('end'+actor.self+':', msg);
        test.strictEqual(++count, 3);
        var d = actor.create(done_beh);
        return {
            actors: [d],
            events: [
                actor.send(d, 'Z')
            ],
            behavior: undefined
        };
    });
    var b = actor.create(one_shot(a));
    actor.apply({
        actors: [a, b],
        events: [
            actor.send(b, 'X'),
            actor.send(b, 'Y')
        ]
    });
    test.strictEqual(actor.self, undefined);
};

var Y = function Y(a, b) {  // fork in a tree with `a` left branch and `b` right branch
    this.a = a;
    this.b = b;
};
// Y.prototype.toString = function toString() { return '<' + this.a + ', ' + this.b + '>' };
var aTree = new Y(1, new Y(new Y(2, 3), 4));  // <1, <<2, 3>, 4>>

test['<1, <<2, 3>, 4>> has fringe [1, 2, 3, 4]'] = function (test) {
    test.expect(1);

    var fringe = function fringe(tree) {
        if (tree instanceof Y) {
            return fringe(tree.a).concat(fringe(tree.b));
        }
        return [ tree ];
    };

    test.deepEqual(fringe(aTree), [ 1, 2, 3, 4 ]);

    test.done();
};

test['fringe(<1, <<2, 3>, 4>>) = fringe(<<1, 2>, <3, 4>>)'] = function (test) {
    test.expect(1);

    var fringe = function fringe(tree) {
        if (tree instanceof Y) {
            return fringe(tree.a).concat(fringe(tree.b));
        }
        return [ tree ];
    };

    var bTree = new Y(new Y(1, 2), new Y(3, 4));  // <<1, 2>, <3, 4>>
    test.deepEqual(fringe(aTree), fringe(bTree));

    test.done();
};

test['suspend calculations in closures'] = function (test) {
    test.expect(1);

    var nextFringe = function nextFringe(tree, next) {
        return function getLeaf() {
            if (tree instanceof Y) {
                next = nextFringe(tree.a, nextFringe(tree.b, next));
                return next();
            }
            return { value: tree, next: next };
        };
    };

    var fringe = [];
    var next = nextFringe(aTree);
    while (next) {
        var leaf = next();
        fringe.push(leaf.value);
        next = leaf.next;
    };
    test.deepEqual(fringe, [ 1, 2, 3, 4 ]);

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


<commit_before>/**!
 * parse-latin
 *
 * Licensed under MIT.
 * Copyright (c) 2014 Titus Wormer <tituswormer@gmail.com>
 */

'use strict';

/**
 * Dependencies.
 */

var parser,
    expressions,
    pluginFactory,
    modifierFactory;

parser = require('./parser');
expressions = require('./expressions');
pluginFactory = require('./plugin');
modifierFactory = require('./modifier');

/**
 * == CLASSIFY ===============================================================
 */

/**
 * Constants.
 */

var EXPRESSION_TOKEN,
    EXPRESSION_WORD,
    EXPRESSION_PUNCTUATION,
    EXPRESSION_WHITE_SPACE;

/**
 * Match all tokens:
 * - One or more number, alphabetic, or
 *   combining characters;
 * - One or more white space characters;
 * - One or more astral plane characters;
 * - One or more of the same character;
 */

EXPRESSION_TOKEN = expressions.token;

/**
 * Match a word.
 */

EXPRESSION_WORD = expressions.word;

/**
 * Match a string containing ONLY punctuation.
 */

EXPRESSION_PUNCTUATION = expressions.punctuation;

/**
 * Match a string containing ONLY white space.
 */

EXPRESSION_WHITE_SPACE = expressions.whiteSpace;

/**
 * Classify a token.
 *
 * @param {string?} value
 * @return {string} - value's type.
 */

function classify(value) {
    if (EXPRESSION_WHITE_SPACE.test(value)) {
        return 'WhiteSpace';
    }

    if (EXPRESSION_WORD.test(value)) {
        return 'Word';
    }

    if (EXPRESSION_PUNCTUATION.test(value)) {
        return 'Punctuation';
    }

    return 'Symbol';
}

/**
 * Transform a `value` into a list of `NLCSTNode`s.
 *
 * @param {ParseLatin} parser
 * @param {string?} value
 * @return {Array.<NLCSTNode>}
 */

function tokenize(parser, value) {
    var tokens,
        token,
        start,
        end,
        match;

    if (value === null || value === undefined) {
        value = '';
    } else if (value instanceof String) {
        value = value.toString();
    }

    if (typeof value !== 'string') {
        throw new Error(
            'Illegal invocation: \'' + value + '\'' +
            ' is not a valid argument for \'ParseLatin\''
        );
    }

    tokens = [];

    if (!value) {
        return tokens;
    }

    EXPRESSION_TOKEN.lastIndex = 0;
    start = 0;
    match = EXPRESSION_TOKEN.exec(value);

    while (match) {
        /**
         * Move the pointer over to after its last
         * character.
         */

        end = match.index + match[0].length;

        /**
         * Slice the found content, from (including)
         * start to (not including) end, classify it,
         * and add the result.
         */

        token = value.substring(start, end);

        tokens.push(parser['tokenize' + classify(token)](token));

        match = EXPRESSION_TOKEN.exec(value);

        start = end;
    }

    return tokens;
}

/**
 * == PARSE LATIN ============================================================
 */

/**
 * Transform Latin-script natural language into
 * an NLCST-tree.
 *
 * @constructor
 */

function ParseLatin() {
    /**
     * TODO: This should later be removed (when this
     * change bubbles through to dependants).
     */

    if (!(this instanceof ParseLatin)) {
        return new ParseLatin();
    }
}

/**
 * Quick access to the prototype.
 */

var parseLatinPrototype;

parseLatinPrototype = ParseLatin.prototype;

/**
 * == TOKENIZE ===============================================================
 */

/**
 * Transform a `value` into a list of `NLCSTNode`s.
 *
 * @see tokenize
 */

parseLatinPrototype.tokenize = function (value) {
    return tokenize(this, value);
};

/**
 * == TEXT NODES =============================================================
 */

/**
 * Factory to create a `Text`.
 *
 * @param {string?} type
 * @return {function(value): NLCSTText}
 */

function createTextFactory(type) {
    type += 'Node';

    /**
     * Construct a `Text` from a bound `type`
     *
     * @param {value} value
     * @return {NLCSTText}
     */

    return function (value) {
        if (value === null || value === undefined) {
            value = '';
        }

        return {
            'type': type,
            'value': String(value)
        };
    };
}

/**
 * Create a `SymbolNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTSymbolNode}
 */

parseLatinPrototype.tokenizeSymbol = createTextFactory('Symbol');

/**
 * Create a `WhiteSpaceNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTWhiteSpaceNode}
 */

parseLatinPrototype.tokenizeWhiteSpace = createTextFactory('WhiteSpace');

/**
 * Create a `PunctuationNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTPunctuationNode}
 */

parseLatinPrototype.tokenizePunctuation = createTextFactory('Punctuation');

/**
 * Create a `SourceNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTSourceNode}
 */

parseLatinPrototype.tokenizeSource = createTextFactory('Source');

/**
 * Create a `TextNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTTextNode}
 */

parseLatinPrototype.tokenizeText = createTextFactory('Text');

/**
 * == PARENT NODES ===========================================================
 *
 * All these nodes are `pluggable`: they come with a
 * `use` method which accepts a plugin
 * (`function(NLCSTNode)`). Every time one of these
 * methods are called, the plugin is invoked with the
 * node, allowing for easy modification.
 *
 * In fact, the internal transformation from `tokenize`
 * (a list of words, white space, punctuation, and
 * symbols) to `tokenizeRoot` (an NLCST tree), is also
 * implemented through this mechanism.
 */

/**
 * @param {Function} Constructor
 * @param {string} key
 * @param {function(*): undefined} callback
 * @return {undefined}
 */

function pluggable(Constructor, key, callback) {
    var wareKey;

    wareKey = key + 'Plugins';

    Constructor.prototype[key] = function () {
        var self,
            result,
            plugins,
            index;

        self = this;

        result = callback.apply(self, arguments);

        plugins = self[wareKey];

        if (plugins) {
            index = -1;

            while (plugins[++index]) {
                plugins[index](result);
            }
        }

        return result;
    };
}

/**
 * Factory to inject `plugins`. Takes `callback` for
 * the actual inserting.
 *
 * @param {fucntion(Object, string, Array.<Function>)} callback
 * @return {function(string, Array.<Function>)}
 */

function useFactory(callback) {
    /**
     * Validate if `plugins` can be inserted. Invokes
     * the bound `callback` to do the actual inserting.
     *
     * @param {string} key - Method to inject on
     * @param {Array.<Function>|Function} plugins - One
     *   or more plugins.
     */

    return function (key, plugins) {
        var self,
            wareKey;

        self = this;

        /**
         * Throw if the method is not pluggable.
         */

        if (!(key in self)) {
            throw new Error(
                'Illegal Invocation: Unsupported `key` for ' +
                '`use(key, plugins)`. Make sure `key` is a ' +
                'supported function'
            );
        }

        /**
         * Fail silently when no plugins are given.
         */

        if (!plugins) {
            return;
        }

        wareKey = key + 'Plugins';

        /**
         * Make sure `plugins` is a list.
         */

        if (typeof plugins === 'function') {
            plugins = [plugins];
        } else {
            plugins = plugins.concat();
        }

        /**
         * Make sure `wareKey` exists.
         */

        if (!self[wareKey]) {
            self[wareKey] = [];
        }

        /**
         * Invoke callback with the ware key and plugins.
         */

        callback(self, wareKey, plugins);
    };
}

/**
 * Inject `plugins` to modifiy the result of the method
 * at `key` on the operated on context.
 *
 * @param {string} key
 * @param {Function|Array.<Function>} plugins
 * @this {ParseLatin|Object}
 */

parseLatinPrototype.use = useFactory(function (context, key, plugins) {
    context[key] = context[key].concat(plugins);
});

/**
 * Inject `plugins` to modifiy the result of the method
 * at `key` on the operated on context, before any other.
 *
 * @param {string} key
 * @param {Function|Array.<Function>} plugins
 * @this {ParseLatin|Object}
 */

parseLatinPrototype.useFirst = useFactory(function (context, key, plugins) {
    context[key] = plugins.concat(context[key]);
});

/**
 * Create a `WordNode` with its children set to a single
 * `TextNode`, its value set to the given `value`.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTWordNode}
 */

pluggable(ParseLatin, 'tokenizeWord', function (value) {
    return {
        'type': 'WordNode',
        'children': [
            this.tokenizeText(value)
        ]
    };
});

/**
 * Create a `SentenceNode` with its children set to
 * `Node`s, their values set to the tokenized given
 * `value`.
 *
 * Unless plugins add new nodes, the sentence is
 * populated by `WordNode`s, `SymbolNode`s,
 * `PunctuationNode`s, and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTSentenceNode}
 */

pluggable(ParseLatin, 'tokenizeSentence', parser({
    'type': 'SentenceNode',
    'tokenizer': 'tokenize'
}));

/**
 * Create a `ParagraphNode` with its children set to
 * `Node`s, their values set to the tokenized given
 * `value`.
 *
 * Unless plugins add new nodes, the paragraph is
 * populated by `SentenceNode`s and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTParagraphNode}
 */

pluggable(ParseLatin, 'tokenizeParagraph', parser({
    'type': 'ParagraphNode',
    'delimiter': expressions.terminalMarker,
    'delimiterType': 'PunctuationNode',
    'tokenizer': 'tokenizeSentence'
}));

/**
 * Create a `RootNode` with its children set to `Node`s,
 * their values set to the tokenized given `value`.
 *
 * Unless plugins add new nodes, the root is populated by
 * `ParagraphNode`s and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTRootNode}
 */

pluggable(ParseLatin, 'tokenizeRoot', parser({
    'type': 'RootNode',
    'delimiter': expressions.newLine,
    'delimiterType': 'WhiteSpaceNode',
    'tokenizer': 'tokenizeParagraph'
}));

/**
 * Easy access to the document parser.
 *
 * @see ParseLatin#tokenizeRoot
 */

parseLatinPrototype.parse = function (value) {
    return this.tokenizeRoot(value);
};

/**
 * == PLUGINS ================================================================
 */

parseLatinPrototype.use('tokenizeSentence', [
    require('./plugin/merge-initial-word-symbol'),
    require('./plugin/merge-final-word-symbol'),
    require('./plugin/merge-inner-word-symbol'),
    require('./plugin/merge-initialisms')
]);

parseLatinPrototype.use('tokenizeParagraph', [
    require('./plugin/merge-non-word-sentences'),
    require('./plugin/merge-affix-symbol'),
    require('./plugin/merge-initial-lower-case-letter-sentences'),
    require('./plugin/merge-prefix-exceptions'),
    require('./plugin/merge-affix-exceptions'),
    require('./plugin/merge-remaining-full-stops'),
    require('./plugin/make-initial-white-space-siblings'),
    require('./plugin/make-final-white-space-siblings'),
    require('./plugin/break-implicit-sentences'),
    require('./plugin/remove-empty-nodes')
]);

parseLatinPrototype.use('tokenizeRoot', [
    require('./plugin/make-initial-white-space-siblings'),
    require('./plugin/make-final-white-space-siblings'),
    require('./plugin/remove-empty-nodes')
]);

/**
 * == EXPORT =================================================================
 */

/**
 * Expose `ParseLatin`.
 */

module.exports = ParseLatin;

/**
 * Expose `pluginFactory` on `ParseLatin` as `plugin`.
 */

ParseLatin.plugin = pluginFactory;

/**
 * Expose `modifierFactory` on `ParseLatin` as `modifier`.
 */

ParseLatin.modifier = modifierFactory;
<commit_msg>Fix style issue found in new eslint<commit_after>/**!
 * parse-latin
 *
 * Licensed under MIT.
 * Copyright (c) 2014 Titus Wormer <tituswormer@gmail.com>
 */

'use strict';

/**
 * Dependencies.
 */

var createParser,
    expressions,
    pluginFactory,
    modifierFactory;

createParser = require('./parser');
expressions = require('./expressions');
pluginFactory = require('./plugin');
modifierFactory = require('./modifier');

/**
 * == CLASSIFY ===============================================================
 */

/**
 * Constants.
 */

var EXPRESSION_TOKEN,
    EXPRESSION_WORD,
    EXPRESSION_PUNCTUATION,
    EXPRESSION_WHITE_SPACE;

/**
 * Match all tokens:
 * - One or more number, alphabetic, or
 *   combining characters;
 * - One or more white space characters;
 * - One or more astral plane characters;
 * - One or more of the same character;
 */

EXPRESSION_TOKEN = expressions.token;

/**
 * Match a word.
 */

EXPRESSION_WORD = expressions.word;

/**
 * Match a string containing ONLY punctuation.
 */

EXPRESSION_PUNCTUATION = expressions.punctuation;

/**
 * Match a string containing ONLY white space.
 */

EXPRESSION_WHITE_SPACE = expressions.whiteSpace;

/**
 * Classify a token.
 *
 * @param {string?} value
 * @return {string} - value's type.
 */

function classify(value) {
    if (EXPRESSION_WHITE_SPACE.test(value)) {
        return 'WhiteSpace';
    }

    if (EXPRESSION_WORD.test(value)) {
        return 'Word';
    }

    if (EXPRESSION_PUNCTUATION.test(value)) {
        return 'Punctuation';
    }

    return 'Symbol';
}

/**
 * Transform a `value` into a list of `NLCSTNode`s.
 *
 * @param {ParseLatin} parser
 * @param {string?} value
 * @return {Array.<NLCSTNode>}
 */

function tokenize(parser, value) {
    var tokens,
        token,
        start,
        end,
        match;

    if (value === null || value === undefined) {
        value = '';
    } else if (value instanceof String) {
        value = value.toString();
    }

    if (typeof value !== 'string') {
        throw new Error(
            'Illegal invocation: \'' + value + '\'' +
            ' is not a valid argument for \'ParseLatin\''
        );
    }

    tokens = [];

    if (!value) {
        return tokens;
    }

    EXPRESSION_TOKEN.lastIndex = 0;
    start = 0;
    match = EXPRESSION_TOKEN.exec(value);

    while (match) {
        /**
         * Move the pointer over to after its last
         * character.
         */

        end = match.index + match[0].length;

        /**
         * Slice the found content, from (including)
         * start to (not including) end, classify it,
         * and add the result.
         */

        token = value.substring(start, end);

        tokens.push(parser['tokenize' + classify(token)](token));

        match = EXPRESSION_TOKEN.exec(value);

        start = end;
    }

    return tokens;
}

/**
 * == PARSE LATIN ============================================================
 */

/**
 * Transform Latin-script natural language into
 * an NLCST-tree.
 *
 * @constructor
 */

function ParseLatin() {
    /**
     * TODO: This should later be removed (when this
     * change bubbles through to dependants).
     */

    if (!(this instanceof ParseLatin)) {
        return new ParseLatin();
    }
}

/**
 * Quick access to the prototype.
 */

var parseLatinPrototype;

parseLatinPrototype = ParseLatin.prototype;

/**
 * == TOKENIZE ===============================================================
 */

/**
 * Transform a `value` into a list of `NLCSTNode`s.
 *
 * @see tokenize
 */

parseLatinPrototype.tokenize = function (value) {
    return tokenize(this, value);
};

/**
 * == TEXT NODES =============================================================
 */

/**
 * Factory to create a `Text`.
 *
 * @param {string?} type
 * @return {function(value): NLCSTText}
 */

function createTextFactory(type) {
    type += 'Node';

    /**
     * Construct a `Text` from a bound `type`
     *
     * @param {value} value
     * @return {NLCSTText}
     */

    return function (value) {
        if (value === null || value === undefined) {
            value = '';
        }

        return {
            'type': type,
            'value': String(value)
        };
    };
}

/**
 * Create a `SymbolNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTSymbolNode}
 */

parseLatinPrototype.tokenizeSymbol = createTextFactory('Symbol');

/**
 * Create a `WhiteSpaceNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTWhiteSpaceNode}
 */

parseLatinPrototype.tokenizeWhiteSpace = createTextFactory('WhiteSpace');

/**
 * Create a `PunctuationNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTPunctuationNode}
 */

parseLatinPrototype.tokenizePunctuation = createTextFactory('Punctuation');

/**
 * Create a `SourceNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTSourceNode}
 */

parseLatinPrototype.tokenizeSource = createTextFactory('Source');

/**
 * Create a `TextNode` with the given `value`.
 *
 * @param {string?} value
 * @return {NLCSTTextNode}
 */

parseLatinPrototype.tokenizeText = createTextFactory('Text');

/**
 * == PARENT NODES ===========================================================
 *
 * All these nodes are `pluggable`: they come with a
 * `use` method which accepts a plugin
 * (`function(NLCSTNode)`). Every time one of these
 * methods are called, the plugin is invoked with the
 * node, allowing for easy modification.
 *
 * In fact, the internal transformation from `tokenize`
 * (a list of words, white space, punctuation, and
 * symbols) to `tokenizeRoot` (an NLCST tree), is also
 * implemented through this mechanism.
 */

/**
 * @param {Function} Constructor
 * @param {string} key
 * @param {function(*): undefined} callback
 * @return {undefined}
 */

function pluggable(Constructor, key, callback) {
    var wareKey;

    wareKey = key + 'Plugins';

    Constructor.prototype[key] = function () {
        var self,
            result,
            plugins,
            index;

        self = this;

        result = callback.apply(self, arguments);

        plugins = self[wareKey];

        if (plugins) {
            index = -1;

            while (plugins[++index]) {
                plugins[index](result);
            }
        }

        return result;
    };
}

/**
 * Factory to inject `plugins`. Takes `callback` for
 * the actual inserting.
 *
 * @param {fucntion(Object, string, Array.<Function>)} callback
 * @return {function(string, Array.<Function>)}
 */

function useFactory(callback) {
    /**
     * Validate if `plugins` can be inserted. Invokes
     * the bound `callback` to do the actual inserting.
     *
     * @param {string} key - Method to inject on
     * @param {Array.<Function>|Function} plugins - One
     *   or more plugins.
     */

    return function (key, plugins) {
        var self,
            wareKey;

        self = this;

        /**
         * Throw if the method is not pluggable.
         */

        if (!(key in self)) {
            throw new Error(
                'Illegal Invocation: Unsupported `key` for ' +
                '`use(key, plugins)`. Make sure `key` is a ' +
                'supported function'
            );
        }

        /**
         * Fail silently when no plugins are given.
         */

        if (!plugins) {
            return;
        }

        wareKey = key + 'Plugins';

        /**
         * Make sure `plugins` is a list.
         */

        if (typeof plugins === 'function') {
            plugins = [plugins];
        } else {
            plugins = plugins.concat();
        }

        /**
         * Make sure `wareKey` exists.
         */

        if (!self[wareKey]) {
            self[wareKey] = [];
        }

        /**
         * Invoke callback with the ware key and plugins.
         */

        callback(self, wareKey, plugins);
    };
}

/**
 * Inject `plugins` to modifiy the result of the method
 * at `key` on the operated on context.
 *
 * @param {string} key
 * @param {Function|Array.<Function>} plugins
 * @this {ParseLatin|Object}
 */

parseLatinPrototype.use = useFactory(function (context, key, plugins) {
    context[key] = context[key].concat(plugins);
});

/**
 * Inject `plugins` to modifiy the result of the method
 * at `key` on the operated on context, before any other.
 *
 * @param {string} key
 * @param {Function|Array.<Function>} plugins
 * @this {ParseLatin|Object}
 */

parseLatinPrototype.useFirst = useFactory(function (context, key, plugins) {
    context[key] = plugins.concat(context[key]);
});

/**
 * Create a `WordNode` with its children set to a single
 * `TextNode`, its value set to the given `value`.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTWordNode}
 */

pluggable(ParseLatin, 'tokenizeWord', function (value) {
    return {
        'type': 'WordNode',
        'children': [
            this.tokenizeText(value)
        ]
    };
});

/**
 * Create a `SentenceNode` with its children set to
 * `Node`s, their values set to the tokenized given
 * `value`.
 *
 * Unless plugins add new nodes, the sentence is
 * populated by `WordNode`s, `SymbolNode`s,
 * `PunctuationNode`s, and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTSentenceNode}
 */

pluggable(ParseLatin, 'tokenizeSentence', createParser({
    'type': 'SentenceNode',
    'tokenizer': 'tokenize'
}));

/**
 * Create a `ParagraphNode` with its children set to
 * `Node`s, their values set to the tokenized given
 * `value`.
 *
 * Unless plugins add new nodes, the paragraph is
 * populated by `SentenceNode`s and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTParagraphNode}
 */

pluggable(ParseLatin, 'tokenizeParagraph', createParser({
    'type': 'ParagraphNode',
    'delimiter': expressions.terminalMarker,
    'delimiterType': 'PunctuationNode',
    'tokenizer': 'tokenizeSentence'
}));

/**
 * Create a `RootNode` with its children set to `Node`s,
 * their values set to the tokenized given `value`.
 *
 * Unless plugins add new nodes, the root is populated by
 * `ParagraphNode`s and `WhiteSpaceNode`s.
 *
 * @see pluggable
 *
 * @param {string?} value
 * @return {NLCSTRootNode}
 */

pluggable(ParseLatin, 'tokenizeRoot', createParser({
    'type': 'RootNode',
    'delimiter': expressions.newLine,
    'delimiterType': 'WhiteSpaceNode',
    'tokenizer': 'tokenizeParagraph'
}));

/**
 * Easy access to the document parser.
 *
 * @see ParseLatin#tokenizeRoot
 */

parseLatinPrototype.parse = function (value) {
    return this.tokenizeRoot(value);
};

/**
 * == PLUGINS ================================================================
 */

parseLatinPrototype.use('tokenizeSentence', [
    require('./plugin/merge-initial-word-symbol'),
    require('./plugin/merge-final-word-symbol'),
    require('./plugin/merge-inner-word-symbol'),
    require('./plugin/merge-initialisms')
]);

parseLatinPrototype.use('tokenizeParagraph', [
    require('./plugin/merge-non-word-sentences'),
    require('./plugin/merge-affix-symbol'),
    require('./plugin/merge-initial-lower-case-letter-sentences'),
    require('./plugin/merge-prefix-exceptions'),
    require('./plugin/merge-affix-exceptions'),
    require('./plugin/merge-remaining-full-stops'),
    require('./plugin/make-initial-white-space-siblings'),
    require('./plugin/make-final-white-space-siblings'),
    require('./plugin/break-implicit-sentences'),
    require('./plugin/remove-empty-nodes')
]);

parseLatinPrototype.use('tokenizeRoot', [
    require('./plugin/make-initial-white-space-siblings'),
    require('./plugin/make-final-white-space-siblings'),
    require('./plugin/remove-empty-nodes')
]);

/**
 * == EXPORT =================================================================
 */

/**
 * Expose `ParseLatin`.
 */

module.exports = ParseLatin;

/**
 * Expose `pluginFactory` on `ParseLatin` as `plugin`.
 */

ParseLatin.plugin = pluginFactory;

/**
 * Expose `modifierFactory` on `ParseLatin` as `modifier`.
 */

ParseLatin.modifier = modifierFactory;
<|endoftext|>


<commit_before>byteArrayToGame = function(bytes) {
    var headerSplit = bytes.indexOf(10);
    var header = String.fromCharCode.apply(null, bytes.slice(0, headerSplit))
    if (header != "HLT 9") {
		alert("Invalid header: " + header)
    }

    var detailSplit = bytes.indexOf(10, headerSplit + 1);
    var details = String.fromCharCode.apply(null, bytes.slice(headerSplit + 1, detailSplit))

    var game = {version: header};
    [game.width, game.height, game.numPlayers, game.numFrames] = details.split(" ");
    game.width = parseInt(game.width);
    game.height = parseInt(game.height);
    game.numPlayers = parseInt(game.numPlayers);
    game.numFrames = parseInt(game.numFrames);
    var cellCount = game.height * game.width;

    game.players = []
    var playerSplit = detailSplit;
    game.players.push({name: 'NULL', color: "0x888888"});
    for(i = 0; i < game.numPlayers; i++) {
		var nextPlayerSplit = bytes.indexOf(10, playerSplit + 1);
		var playerLine = String.fromCharCode.apply(null, bytes.slice(playerSplit + 1, nextPlayerSplit));
		var playerDetails = playerLine.split("\0");
		var playerColorString = playerDetails[1].split(' ');
		function compToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; };
		game.players.push({name: playerDetails[0], color: "0x" + compToHex(Math.round(parseFloat(playerColorString[0]) * 255)) + compToHex(Math.round(parseFloat(playerColorString[1]) * 255)) + compToHex(Math.round(parseFloat(playerColorString[2]) * 255)) });
		console.log(game.players[game.players.length - 1].color);
		playerSplit = nextPlayerSplit;
    }

    game.productions = []
    var offset = 0, maxProd = 0;
    for(var i = 0; i < cellCount; ++i) {
		game.productions.push(bytes[playerSplit + 1 + i]);
		if(bytes[playerSplit + 1 + i] > maxProd) maxProd = bytes[playerSplit + 1 + i];
    }

    game.productionNormals = []
    for(var i = 0; i < game.productions.length; i++) game.productionNormals.push(game.productions[i] / maxProd);

    game.frames = []
    game.moves = []
    var currIndex = playerSplit + 1 + cellCount + 1;
    for(var frameCount = 0; frameCount < game.numFrames; frameCount++) {
		var cellsRead = 0;
		var frame = [];
		while (cellsRead < cellCount) {
		    var counter = bytes[currIndex++]
		    var owner = bytes[currIndex++];
		    if (counter + cellsRead <= cellCount) {
				for(var i = 0; i < counter; i++) {
				    var strength = bytes[currIndex++]
				    frame.push({owner: owner, strength: strength});
				}
				cellsRead += counter;
		    } else {
				console.log("Gonna read: " + counter);
				cellsRead = cellCount
		    }
		}
		game.frames.push(frame);

		if (frameCount < game.numFrames - 1) {
		    var moves = [];
		    for(var i = 0; i < cellCount; i++) {
				moves.push(bytes[currIndex++])
		    }
		    game.moves.push(moves);
		}
    }

    //Get game statistics:
    for(var a = 1; a <= game.numPlayers; a++) {
    	game.players[a].territories = [];
    	game.players[a].productions = [];
    	game.players[a].strengths = [];
	    for(var b = 0; b < game.numFrames; b++) {
    		var ter = 0, prod = 0, str = 0;
    		for(var c = 0; c < game.height * game.width; c++) {
    			if(game.frames[b][c].owner == a) {
                    ter++;
                    prod += game.productions[c];
                    str += game.frames[b][c].strength;
                }
    		}
            game.players[a].territories.push(ter);
            game.players[a].productions.push(prod);
            game.players[a].strengths.push(str);
	    }
	}

    return game
}<commit_msg>Added normalization of statistics for rendering<commit_after>byteArrayToGame = function(bytes) {
    var headerSplit = bytes.indexOf(10);
    var header = String.fromCharCode.apply(null, bytes.slice(0, headerSplit))
    if (header != "HLT 9") {
		alert("Invalid header: " + header)
    }

    var detailSplit = bytes.indexOf(10, headerSplit + 1);
    var details = String.fromCharCode.apply(null, bytes.slice(headerSplit + 1, detailSplit))

    var game = {version: header};
    [game.width, game.height, game.numPlayers, game.numFrames] = details.split(" ");
    game.width = parseInt(game.width);
    game.height = parseInt(game.height);
    game.numPlayers = parseInt(game.numPlayers);
    game.numFrames = parseInt(game.numFrames);
    var cellCount = game.height * game.width;

    game.players = []
    var playerSplit = detailSplit;
    game.players.push({name: 'NULL', color: "0x888888"});
    for(i = 0; i < game.numPlayers; i++) {
		var nextPlayerSplit = bytes.indexOf(10, playerSplit + 1);
		var playerLine = String.fromCharCode.apply(null, bytes.slice(playerSplit + 1, nextPlayerSplit));
		var playerDetails = playerLine.split("\0");
		var playerColorString = playerDetails[1].split(' ');
		function compToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; };
		game.players.push({name: playerDetails[0], color: "0x" + compToHex(Math.round(parseFloat(playerColorString[0]) * 255)) + compToHex(Math.round(parseFloat(playerColorString[1]) * 255)) + compToHex(Math.round(parseFloat(playerColorString[2]) * 255)) });
		console.log(game.players[game.players.length - 1].color);
		playerSplit = nextPlayerSplit;
    }

    game.productions = []
    var offset = 0, maxProd = 0;
    for(var i = 0; i < cellCount; ++i) {
		game.productions.push(bytes[playerSplit + 1 + i]);
		if(bytes[playerSplit + 1 + i] > maxProd) maxProd = bytes[playerSplit + 1 + i];
    }

    game.productionNormals = []
    for(var i = 0; i < game.productions.length; i++) game.productionNormals.push(game.productions[i] / maxProd);

    game.frames = []
    game.moves = []
    var currIndex = playerSplit + 1 + cellCount + 1;
    for(var frameCount = 0; frameCount < game.numFrames; frameCount++) {
		var cellsRead = 0;
		var frame = [];
		while (cellsRead < cellCount) {
		    var counter = bytes[currIndex++]
		    var owner = bytes[currIndex++];
		    if (counter + cellsRead <= cellCount) {
				for(var i = 0; i < counter; i++) {
				    var strength = bytes[currIndex++]
				    frame.push({owner: owner, strength: strength});
				}
				cellsRead += counter;
		    } else {
				console.log("Gonna read: " + counter);
				cellsRead = cellCount
		    }
		}
		game.frames.push(frame);

		if (frameCount < game.numFrames - 1) {
		    var moves = [];
		    for(var i = 0; i < cellCount; i++) {
				moves.push(bytes[currIndex++])
		    }
		    game.moves.push(moves);
		}
    }

    //Get game statistics:
    for(var a = 1; a <= game.numPlayers; a++) {
    	game.players[a].territories = [];
    	game.players[a].productions = [];
    	game.players[a].strengths = [];
	    for(var b = 0; b < game.numFrames; b++) {
    		var ter = 0, prod = 0, str = 0;
    		for(var c = 0; c < game.height * game.width; c++) {
    			if(game.frames[b][c].owner == a) {
                    ter++;
                    prod += game.productions[c];
                    str += game.frames[b][c].strength;
                }
    		}
            game.players[a].territories.push(ter);
            game.players[a].productions.push(prod);
            game.players[a].strengths.push(str);
	    }
	}

    //Normalize game statistics for display
    var maxPlayerTer = 0, maxPlayerProd = 0, maxPlayerStr = 0;
    for(var a = 1; a <= game.numPlayers; a++) {
        for(var b = 0; b < game.numFrames; b++) {
            if(game.players[a].territories[b] > maxPlayerTer) maxPlayerTer = game.players[a].territories[b];
            if(game.players[a].productions[b] > maxPlayerProd) maxPlayerProd = game.players[a].productions[b];
            if(game.players[a].strengths[b] > maxPlayerStr) maxPlayerStr = game.players[a].strengths[b];
        }
    }
    for(var a = 1; a <= game.numPlayers; a++) {
        game.players[a].normTers = [];
        game.players[a].normProds = [];
        game.players[a].normStrs = [];
        for(var b = 0; b < game.numFrames; b++) {
            game.players[a].normTers.push(game.players[a].territories[b] / maxPlayerTer);
            game.players[a].normProds.push(game.players[a].productions[b] / maxPlayerProd);
            game.players[a].normStrs.push(game.players[a].strengths[b] / maxPlayerStr);
        }
    }

    return game
}<|endoftext|>


<commit_before>import React from 'react';

const Tooltip = ({text}) => {
    return (

            { text }

    );
}

export default Tooltip;
<commit_msg>Added tooltip class to the tooltip componento for styling purposes<commit_after>import React from 'react';

const Tooltip = ({text}) => {
    return (

            { text }

    );
}

export default Tooltip;
<|endoftext|>


<commit_before>/**
 *  Mincer
 *
 *  This is a main entry point of the module (an object that you get by calling
 *  `require('mincer')`. You can get acces to some of the internal classes using
 *  this object.
 *
 *      var env = new (require('mincer').Environment)(__dirname);
 *
 *  `Mincer` depends on some 3rd-party modules. Most valueble (for understanding
 *  an API) are:
 *
 *  - [Hike](http://nodeca.github.com/hike-js/)
 *  - [Mimoza](http://nodeca.github.com/mimoza/)
 *
 *
 *  ##### EXTENDED BY
 *
 *  - [[Engines]]
 *  - [[Mime]]
 *  - [[Processing]]
 *  - [[Paths]]
 **/

'use strict';

// Node < 0.8 shims
var fs    = require('fs');
var path  = require('path');

fs.exists     = fs.exists || path.exists;
fs.existsSync = fs.existsSync || path.existsSync;

path.sep = path.sep || (path.resolve('.')[0] === '/' ? '/' : '\\');

// 3rd-party
var Mimoza  = require('mimoza');
var Trail   = require('hike').Trail;

// internal
var mixin = require('./mincer/common').mixin;
var prop  = require('./mincer/common').prop;
var Hash  = require('./mincer/common/hash');

var Mincer = module.exports = {};

// Engines /////////////////////////////////////////////////////////////////////

/**
 *  Mincer.EjsEngine -> EjsEngine
 **/
Mincer.EjsEngine          = require('./mincer/engines/ejs_engine');

/**
 *  Mincer.EcoEngine -> EcoEngine
 **/
Mincer.EcoEngine          = require('./mincer/engines/eco_engine');

/**
 *  Mincer.HamlCoffeeEngine -> HamlCoffeeEngine
 **/
Mincer.HamlCoffeeEngine   = require('./mincer/engines/haml_coffee_engine');

/**
 *  Mincer.HandlebarsEngine -> HandlebarsEngine
 **/
Mincer.HandlebarsEngine   = require('./mincer/engines/handlebars_engine');

/**
 *  Mincer.JadeEngine -> JadeEngine
 **/
Mincer.JadeEngine         = require('./mincer/engines/jade_engine');

/**
 *  Mincer.JstEngine -> JstEngine
 **/
Mincer.JstEngine          = require('./mincer/engines/jst_engine');

/**
 *  Mincer.LessEngine -> LessEngine
 **/
Mincer.LessEngine         = require('./mincer/engines/less_engine');

/**
 *  Mincer.StylusEngine -> StylusEngine
 **/
Mincer.StylusEngine       = require('./mincer/engines/stylus_engine');

/**
 *  Mincer.LiveScriptEngine -> LiveScriptEngine
 **/
Mincer.LiveScriptEngine   = require('./mincer/engines/live_script_engine');

/**
 *  Mincer.CoffeeEngine -> CoffeeEngine
 **/
Mincer.CoffeeEngine       = require('./mincer/engines/coffee_engine');

/**
 *  Mincer.CocoEngine -> CocoEngine
 **/
Mincer.CocoEngine         = require('./mincer/engines/coco_engine');

/**
 *  Mincer.SassEngine -> SassEngine
 **/
Mincer.SassEngine         = require('./mincer/engines/sass_engine');

// Processors //////////////////////////////////////////////////////////////////

/**
 *  Mincer.DirectiveProcessor -> DirectiveProcessor
 **/
Mincer.DirectiveProcessor = require('./mincer/processors/directive_processor');

/**
 *  Mincer.CharsetNormalizer -> CharsetNormalizer
 **/
Mincer.CharsetNormalizer  = require('./mincer/processors/charset_normalizer');

/**
 *  Mincer.SafetyColons -> SafetyColons
 **/
Mincer.SafetyColons       = require('./mincer/processors/safety_colons');

/**
 *  Mincer.Autoprefixer -> Autoprefixer
 **/
Mincer.Autoprefixer       = require('./mincer/processors/autoprefixer');

// Compressors /////////////////////////////////////////////////////////////////

/**
 *  Mincer.CssoCompressor -> CssoCompressor
 **/
Mincer.CssoCompressor     = require('./mincer/compressors/csso_compressor');

/**
 *  Mincer.CsswringCompressor -> CsswringCompressor
 **/
Mincer.CsswringCompressor  = require('./mincer/compressors/csswring_compressor');

/**
 *  Mincer.UglifyCompressor -> UglifyCompressor
 **/
Mincer.UglifyCompressor   = require('./mincer/compressors/uglify_compressor');

// Main exported classes ///////////////////////////////////////////////////////

/**
 *  Mincer.Environment -> Environment
 **/
Mincer.Environment        = require('./mincer/environment');

/**
 *  Mincer.Manifest -> Manifest
 **/
Mincer.Manifest           = require('./mincer/manifest');

/**
 *  Mincer.Template -> Template
 **/
Mincer.Template           = require('./mincer/template');

/**
 *  Mincer.Server -> Server
 **/
Mincer.Server             = require('./mincer/server');

/**
 *  Mincer.FileStore -> FileStore
 **/
Mincer.FileStore          = require('./mincer/file_store');

// Main exported functions /////////////////////////////////////////////////////

/** alias of: Server.createServer
 *  Mincer.createServer(environment[, manifest]) -> Function
 **/
Mincer.createServer       = Mincer.Server.createServer;

// Main exported properties ////////////////////////////////////////////////////

/** read-only
 *  Mincer.VERSION -> String
 **/
prop(Mincer, 'VERSION', require('./mincer/version'));

/** read-only
 *  Mincer.logger -> Logger
 **/
prop(Mincer, 'logger',  require('./mincer/logger'));

// Spicy Sauce :)) /////////////////////////////////////////////////////////////

// main internal properties.
// each new environment clone these properties for initial states,
// so they can be used to set 'defaults' for all environment instances.
prop(Mincer, '__trail__',             new Trail(__dirname));
prop(Mincer, '__engines__',           {});
prop(Mincer, '__mimeTypes__',         new Mimoza());
prop(Mincer, '__preProcessors__',     new Hash(function () { return []; }));
prop(Mincer, '__postProcessors__',    new Hash(function () { return []; }));
prop(Mincer, '__bundleProcessors__',  new Hash(function () { return []; }));
prop(Mincer, '__compressors__',       new Hash(function () { return {}; }));
prop(Mincer, '__configurations__',    {});

// mixin helpers
mixin(Mincer, require('./mincer/helpers/engines'));
mixin(Mincer, require('./mincer/helpers/mime'));
mixin(Mincer, require('./mincer/helpers/processing'));
mixin(Mincer, require('./mincer/helpers/compressing'));
mixin(Mincer, require('./mincer/helpers/configuring'));
mixin(Mincer, require('./mincer/helpers/paths'));

// register basic mimetypes
Mincer.registerMimeType('text/css',               '.css');
Mincer.registerMimeType('application/javascript', '.js');

// register default pre-processors
Mincer.registerPreProcessor('text/css',                 Mincer.DirectiveProcessor);
Mincer.registerPreProcessor('application/javascript',   Mincer.DirectiveProcessor);

// register default post-processors
Mincer.registerPostProcessor('application/javascript',  Mincer.SafetyColons);

// register default bundle-processors
Mincer.registerBundleProcessor('text/css',              Mincer.CharsetNormalizer);

// register predifined js compressors
Mincer.registerCompressor('application/javascript', 'uglify',   Mincer.UglifyCompressor);

// register predifined css compressors
Mincer.registerCompressor('text/css',               'csso',     Mincer.CssoCompressor);
Mincer.registerCompressor('text/css',               'csswring', Mincer.CsswringCompressor);

// Register JS engines
Mincer.registerEngine('.ls',        Mincer.LiveScriptEngine);
Mincer.registerEngine('.coffee',    Mincer.CoffeeEngine);
Mincer.registerEngine('.litcoffee', Mincer.CoffeeEngine);
Mincer.registerEngine('.co',        Mincer.CocoEngine);

// Register CSS engines
Mincer.registerEngine('.less',      Mincer.LessEngine);
Mincer.registerEngine('.styl',      Mincer.StylusEngine);
Mincer.registerEngine('.sass',      Mincer.SassEngine);
Mincer.registerEngine('.scss',      Mincer.SassEngine);

// JST engines
Mincer.registerEngine('.jst',       Mincer.JstEngine);

// Mixed type (JST / processor) engines
Mincer.registerEngine('.eco',       Mincer.EcoEngine);
Mincer.registerEngine('.ejs',       Mincer.EjsEngine);
Mincer.registerEngine('.hamlc',     Mincer.HamlCoffeeEngine);
Mincer.registerEngine('.hbs',       Mincer.HandlebarsEngine);
Mincer.registerEngine('.jade',      Mincer.JadeEngine);

// Configurations //////////////////////////////////////////////////////////////

Mincer.registerConfiguration('autoprefixer', {
  enable: function (self) {
    self.registerPostProcessor('text/css', Mincer.Autoprefixer);
  },
  disable: function (self) {
    self.unregisterPostProcessor('text/css', Mincer.Autoprefixer);
  }
});

Mincer.registerConfiguration('source_maps', {state: 'disabled'});
<commit_msg>Add source_map -> source_maps configuiration alias<commit_after>/**
 *  Mincer
 *
 *  This is a main entry point of the module (an object that you get by calling
 *  `require('mincer')`. You can get acces to some of the internal classes using
 *  this object.
 *
 *      var env = new (require('mincer').Environment)(__dirname);
 *
 *  `Mincer` depends on some 3rd-party modules. Most valueble (for understanding
 *  an API) are:
 *
 *  - [Hike](http://nodeca.github.com/hike-js/)
 *  - [Mimoza](http://nodeca.github.com/mimoza/)
 *
 *
 *  ##### EXTENDED BY
 *
 *  - [[Engines]]
 *  - [[Mime]]
 *  - [[Processing]]
 *  - [[Paths]]
 **/

'use strict';

// Node < 0.8 shims
var fs    = require('fs');
var path  = require('path');

fs.exists     = fs.exists || path.exists;
fs.existsSync = fs.existsSync || path.existsSync;

path.sep = path.sep || (path.resolve('.')[0] === '/' ? '/' : '\\');

// 3rd-party
var Mimoza  = require('mimoza');
var Trail   = require('hike').Trail;

// internal
var mixin = require('./mincer/common').mixin;
var prop  = require('./mincer/common').prop;
var Hash  = require('./mincer/common/hash');

var Mincer = module.exports = {};

// Engines /////////////////////////////////////////////////////////////////////

/**
 *  Mincer.EjsEngine -> EjsEngine
 **/
Mincer.EjsEngine          = require('./mincer/engines/ejs_engine');

/**
 *  Mincer.EcoEngine -> EcoEngine
 **/
Mincer.EcoEngine          = require('./mincer/engines/eco_engine');

/**
 *  Mincer.HamlCoffeeEngine -> HamlCoffeeEngine
 **/
Mincer.HamlCoffeeEngine   = require('./mincer/engines/haml_coffee_engine');

/**
 *  Mincer.HandlebarsEngine -> HandlebarsEngine
 **/
Mincer.HandlebarsEngine   = require('./mincer/engines/handlebars_engine');

/**
 *  Mincer.JadeEngine -> JadeEngine
 **/
Mincer.JadeEngine         = require('./mincer/engines/jade_engine');

/**
 *  Mincer.JstEngine -> JstEngine
 **/
Mincer.JstEngine          = require('./mincer/engines/jst_engine');

/**
 *  Mincer.LessEngine -> LessEngine
 **/
Mincer.LessEngine         = require('./mincer/engines/less_engine');

/**
 *  Mincer.StylusEngine -> StylusEngine
 **/
Mincer.StylusEngine       = require('./mincer/engines/stylus_engine');

/**
 *  Mincer.LiveScriptEngine -> LiveScriptEngine
 **/
Mincer.LiveScriptEngine   = require('./mincer/engines/live_script_engine');

/**
 *  Mincer.CoffeeEngine -> CoffeeEngine
 **/
Mincer.CoffeeEngine       = require('./mincer/engines/coffee_engine');

/**
 *  Mincer.CocoEngine -> CocoEngine
 **/
Mincer.CocoEngine         = require('./mincer/engines/coco_engine');

/**
 *  Mincer.SassEngine -> SassEngine
 **/
Mincer.SassEngine         = require('./mincer/engines/sass_engine');

// Processors //////////////////////////////////////////////////////////////////

/**
 *  Mincer.DirectiveProcessor -> DirectiveProcessor
 **/
Mincer.DirectiveProcessor = require('./mincer/processors/directive_processor');

/**
 *  Mincer.CharsetNormalizer -> CharsetNormalizer
 **/
Mincer.CharsetNormalizer  = require('./mincer/processors/charset_normalizer');

/**
 *  Mincer.SafetyColons -> SafetyColons
 **/
Mincer.SafetyColons       = require('./mincer/processors/safety_colons');

/**
 *  Mincer.Autoprefixer -> Autoprefixer
 **/
Mincer.Autoprefixer       = require('./mincer/processors/autoprefixer');

// Compressors /////////////////////////////////////////////////////////////////

/**
 *  Mincer.CssoCompressor -> CssoCompressor
 **/
Mincer.CssoCompressor     = require('./mincer/compressors/csso_compressor');

/**
 *  Mincer.CsswringCompressor -> CsswringCompressor
 **/
Mincer.CsswringCompressor  = require('./mincer/compressors/csswring_compressor');

/**
 *  Mincer.UglifyCompressor -> UglifyCompressor
 **/
Mincer.UglifyCompressor   = require('./mincer/compressors/uglify_compressor');

// Main exported classes ///////////////////////////////////////////////////////

/**
 *  Mincer.Environment -> Environment
 **/
Mincer.Environment        = require('./mincer/environment');

/**
 *  Mincer.Manifest -> Manifest
 **/
Mincer.Manifest           = require('./mincer/manifest');

/**
 *  Mincer.Template -> Template
 **/
Mincer.Template           = require('./mincer/template');

/**
 *  Mincer.Server -> Server
 **/
Mincer.Server             = require('./mincer/server');

/**
 *  Mincer.FileStore -> FileStore
 **/
Mincer.FileStore          = require('./mincer/file_store');

// Main exported functions /////////////////////////////////////////////////////

/** alias of: Server.createServer
 *  Mincer.createServer(environment[, manifest]) -> Function
 **/
Mincer.createServer       = Mincer.Server.createServer;

// Main exported properties ////////////////////////////////////////////////////

/** read-only
 *  Mincer.VERSION -> String
 **/
prop(Mincer, 'VERSION', require('./mincer/version'));

/** read-only
 *  Mincer.logger -> Logger
 **/
prop(Mincer, 'logger',  require('./mincer/logger'));

// Spicy Sauce :)) /////////////////////////////////////////////////////////////

// main internal properties.
// each new environment clone these properties for initial states,
// so they can be used to set 'defaults' for all environment instances.
prop(Mincer, '__trail__',             new Trail(__dirname));
prop(Mincer, '__engines__',           {});
prop(Mincer, '__mimeTypes__',         new Mimoza());
prop(Mincer, '__preProcessors__',     new Hash(function () { return []; }));
prop(Mincer, '__postProcessors__',    new Hash(function () { return []; }));
prop(Mincer, '__bundleProcessors__',  new Hash(function () { return []; }));
prop(Mincer, '__compressors__',       new Hash(function () { return {}; }));
prop(Mincer, '__configurations__',    {});

// mixin helpers
mixin(Mincer, require('./mincer/helpers/engines'));
mixin(Mincer, require('./mincer/helpers/mime'));
mixin(Mincer, require('./mincer/helpers/processing'));
mixin(Mincer, require('./mincer/helpers/compressing'));
mixin(Mincer, require('./mincer/helpers/configuring'));
mixin(Mincer, require('./mincer/helpers/paths'));

// register basic mimetypes
Mincer.registerMimeType('text/css',               '.css');
Mincer.registerMimeType('application/javascript', '.js');

// register default pre-processors
Mincer.registerPreProcessor('text/css',                 Mincer.DirectiveProcessor);
Mincer.registerPreProcessor('application/javascript',   Mincer.DirectiveProcessor);

// register default post-processors
Mincer.registerPostProcessor('application/javascript',  Mincer.SafetyColons);

// register default bundle-processors
Mincer.registerBundleProcessor('text/css',              Mincer.CharsetNormalizer);

// register predifined js compressors
Mincer.registerCompressor('application/javascript', 'uglify',   Mincer.UglifyCompressor);

// register predifined css compressors
Mincer.registerCompressor('text/css',               'csso',     Mincer.CssoCompressor);
Mincer.registerCompressor('text/css',               'csswring', Mincer.CsswringCompressor);

// Register JS engines
Mincer.registerEngine('.ls',        Mincer.LiveScriptEngine);
Mincer.registerEngine('.coffee',    Mincer.CoffeeEngine);
Mincer.registerEngine('.litcoffee', Mincer.CoffeeEngine);
Mincer.registerEngine('.co',        Mincer.CocoEngine);

// Register CSS engines
Mincer.registerEngine('.less',      Mincer.LessEngine);
Mincer.registerEngine('.styl',      Mincer.StylusEngine);
Mincer.registerEngine('.sass',      Mincer.SassEngine);
Mincer.registerEngine('.scss',      Mincer.SassEngine);

// JST engines
Mincer.registerEngine('.jst',       Mincer.JstEngine);

// Mixed type (JST / processor) engines
Mincer.registerEngine('.eco',       Mincer.EcoEngine);
Mincer.registerEngine('.ejs',       Mincer.EjsEngine);
Mincer.registerEngine('.hamlc',     Mincer.HamlCoffeeEngine);
Mincer.registerEngine('.hbs',       Mincer.HandlebarsEngine);
Mincer.registerEngine('.jade',      Mincer.JadeEngine);

// Configurations //////////////////////////////////////////////////////////////

Mincer.registerConfiguration('autoprefixer', {
  enable: function (self) {
    self.registerPostProcessor('text/css', Mincer.Autoprefixer);
  },
  disable: function (self) {
    self.unregisterPostProcessor('text/css', Mincer.Autoprefixer);
  }
});

Mincer.registerConfiguration('source_maps', {state: 'disabled'});

// Alias for `source_maps`
Mincer.registerConfiguration('source_map', {
  state:    'disabled',
  enable:   function (self) { self.enable('source_maps'); },
  disable:  function (self) { self.disable('source_maps'); }
});
<|endoftext|>


<commit_before>/*global define*/
define([
    'underscore',
    'app',
    'marionette',
    'helpers/uri',
    'collections/configs',
    'models/config',
    'apps/settings/show/showView',
    'fileSaver'
], function (_, App, Marionette, URI, Configs, Config, View, saveAs) {
    'use strict';

    var Show = App.module('AppSettings.Show');

    /**
     * Settings controller
     */
    Show.Controller = Marionette.Controller.extend({
        initialize: function () {
            _.bindAll(this, 'show');

            this.configs = new Configs();
            this.configs.fetch();

            // Events
            this.configs.on('changeSetting', this.save, this);
            this.configs.on('create:profile', this.createProfile, this);
            this.configs.on('remove:profile', this.removeProfile, this);
        },

        // Show settings form in modal window
        // ---------------------------------
        show : function (args) {
            var view = new View({
                collection : this.configs,
                args: args
            });
            App.modal.show(view);

            view.on('redirect', this.redirect, this);
            view.on('import', this.importSettings, this);
            view.on('export', this.exportSettings, this);
        },

        // Create new profile
        // -------------------
        createProfile: function (profile) {
            this.configs.createProfile(profile);
            App.configs.fetch();
        },

        // Create new profile
        // -------------------
        removeProfile: function (profile) {
            this.configs.removeProfile(profile);
        },

        // Export all settings
        // -------------------
        exportSettings: function () {
            var blob = new Blob(
                [JSON.stringify( this.configs )],
                {type: 'text/plain;charset=utf-8'}
            );

            saveAs(blob, 'laverna-settings.json');
        },

        // Import settings (encryption settings, hashes etc..)
        // -----------------------------
        importSettings: function (data) {
            var reader = new FileReader(),
                self = this,
                settings;

            if (data.length === 0) {
                return;
            }

            reader.onload = function (event) {
                try {
                    settings = JSON.parse(event.target.result);
                    settings = _.extend(settings, {'appVersion' : App.constants.VERSION});

                    _.each(settings, function (conf, iter) {
                        self.save(conf);

                        if (iter === settings.length-1) {
                            self.redirect(['']);
                        }
                    });
                }
                catch (error) {
                    throw new Error('File chould be in json format');
                }
            };

            reader.readAsText(data.files[0]);
        },

        // Save new settings
        // ----------------------
        save: function (setting) {
            var model = this.configs.get(setting.name);

            if (model) {
                model.save({ value : setting.value });
            }
        },

        // Navivate back and reload the page
        // ---------------------------------
        redirect: function (changedSettings) {
            App.modal.close();

            if ( this.isEncryptionChanged(changedSettings) === false) {
                App.navigate(URI.link('/notes'), {trigger : false});

                if (changedSettings && changedSettings.length !== 0) {
                    window.location.reload();
                }
            } else {
                App.log('One of encryption\'s settings is changed');
                App.navigate(URI.link('/encrypt/all'), true);
            }
        },

        // Check is any of encryption settings is changed
        // -------------------------------
        isEncryptionChanged: function (changedSettings) {
            var encrSet = ['encrypt', 'encryptPass', 'encryptSalt', 'encryptIter', 'encryptTag', 'encryptKeySize'],
                encrypt = parseInt(this.configs.get('encrypt').get('value')),
                changed = false;

            _.each(encrSet, function (set) {
                if (_.contains(changedSettings, set) === true) {
                    changed = true;
                }
            });

            if (encrypt === App.settings.encrypt && encrypt === 0) {
                changed = false;
            }

            return changed;
        }

    });

    return Show.Controller;
});
<commit_msg>Confirm: Profile remove<commit_after>/*global define*/
define([
    'underscore',
    'app',
    'marionette',
    'helpers/uri',
    'collections/configs',
    'models/config',
    'apps/settings/show/showView',
    'fileSaver'
], function (_, App, Marionette, URI, Configs, Config, View, saveAs) {
    'use strict';

    var Show = App.module('AppSettings.Show');

    /**
     * Settings controller
     */
    Show.Controller = Marionette.Controller.extend({
        initialize: function () {
            _.bindAll(this, 'show');

            this.configs = new Configs();
            this.configs.fetch();

            // Events
            this.configs.on('changeSetting', this.save, this);
            this.configs.on('create:profile', this.createProfile, this);
            this.configs.on('remove:profile', this.removeProfile, this);
        },

        // Show settings form in modal window
        // ---------------------------------
        show : function (args) {
            var view = new View({
                collection : this.configs,
                args: args
            });
            App.modal.show(view);

            view.on('redirect', this.redirect, this);
            view.on('import', this.importSettings, this);
            view.on('export', this.exportSettings, this);
        },

        // Create a new profile
        // -------------------
        createProfile: function (profile) {
            this.configs.createProfile(profile);
            App.configs.fetch();
        },

        // Remove a profile
        // -------------------
        removeProfile: function (profile) {
            var result = window.confirm('Are you sure that you want to delete profile ' + profile + '?');
            if (result ) {
                this.configs.removeProfile(profile);
            }
        },

        // Export all settings
        // -------------------
        exportSettings: function () {
            var blob = new Blob(
                [JSON.stringify( this.configs )],
                {type: 'text/plain;charset=utf-8'}
            );

            saveAs(blob, 'laverna-settings.json');
        },

        // Import settings (encryption settings, hashes etc..)
        // -----------------------------
        importSettings: function (data) {
            var reader = new FileReader(),
                self = this,
                settings;

            if (data.length === 0) {
                return;
            }

            reader.onload = function (event) {
                try {
                    settings = JSON.parse(event.target.result);
                    settings = _.extend(settings, {'appVersion' : App.constants.VERSION});

                    _.each(settings, function (conf, iter) {
                        self.save(conf);

                        if (iter === settings.length-1) {
                            self.redirect(['']);
                        }
                    });
                }
                catch (error) {
                    throw new Error('File chould be in json format');
                }
            };

            reader.readAsText(data.files[0]);
        },

        // Save new settings
        // ----------------------
        save: function (setting) {
            var model = this.configs.get(setting.name);

            if (model) {
                model.save({ value : setting.value });
            }
        },

        // Navivate back and reload the page
        // ---------------------------------
        redirect: function (changedSettings) {
            App.modal.close();

            if ( this.isEncryptionChanged(changedSettings) === false) {
                App.navigate(URI.link('/notes'), {trigger : false});

                if (changedSettings && changedSettings.length !== 0) {
                    window.location.reload();
                }
            } else {
                App.log('One of encryption\'s settings is changed');
                App.navigate(URI.link('/encrypt/all'), true);
            }
        },

        // Check is any of encryption settings is changed
        // -------------------------------
        isEncryptionChanged: function (changedSettings) {
            var encrSet = ['encrypt', 'encryptPass', 'encryptSalt', 'encryptIter', 'encryptTag', 'encryptKeySize'],
                encrypt = parseInt(this.configs.get('encrypt').get('value')),
                changed = false;

            _.each(encrSet, function (set) {
                if (_.contains(changedSettings, set) === true) {
                    changed = true;
                }
            });

            if (encrypt === App.settings.encrypt && encrypt === 0) {
                changed = false;
            }

            return changed;
        }

    });

    return Show.Controller;
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
    "dataFolder": "/opt/soajs/soajs.utilities/data/",
    "imagesFolder": "/opt/soajs/soajs.utilities/deployment/",
    "repositoryPrefix" : "soajs"
};
<commit_msg>fixed folder location in config<commit_after>'use strict';

module.exports = {
    "dataFolder": "/opt/soajs/node_modules/soajs.utilities/data/",
    "imagesFolder": "/opt/soajs/node_modules/soajs.utilities/deployment/",
    "repositoryPrefix" : "soajs"
};
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import { Box, Text } from '@hackclub/design-system'
import Post from 'components/challenge/Post'
import LoadingBar from 'components/LoadingBar'
import ErrorPage from 'components/admin/ErrorPage'
import api from 'api'
import { includes, isEmpty, get, orderBy } from 'lodash'

class Posts extends Component {
  state = { posts: [], upvotes: [], status: 'loading', prevUserId: null }

  static getDerivedStateFromProps = (nextProps, prevState) => {
    const { userId } = nextProps
    const { prevUserId } = prevState

    if (userId === prevUserId) {
      return {}
    } else {
      return { prevUserId: userId }
    }
  }

  componentDidMount() {
    this.refreshPosts(this.props.userId)
    this.refreshIntervalId = setInterval(() => {
      this.refreshPosts(this.props.userId, true)
    }, 3000)
  }

  componentWillUnmount() {
    clearInterval(this.refreshIntervalId)
  }

  refreshPosts(newUserId, intervalUpdate = false) {
    const { challengeId, userId: oldUserId } = this.props

    // super hacky, but right now there's a dumb issue where userId is not
    // properly passed & it's set to undefined when we actually have its value
    // stored in localStorage.
    //
    // the real solution is to do proper state management so we don't have to do
    // a full http request to get the current user's info & we can retrieve
    // cached values with confidence, but for the time being i'm going to fall
    // back to the last remembered userId in localStorage. i think this is set
    // by the login component, but not 100% sure.
    const userId = newUserId || oldUserId || localStorage.getItem('userId')

    console.log('refreshing!')

    api
      .get(`v1/challenges/${challengeId}/posts`)
      .then(data => {
        let posts = data || []

        // array of post ids that user has upvoted
        const upvotes = []
        posts.forEach(post => {
          post.upvotesCount = post.upvotes.length
          post.upvotes.forEach(upvote => {
            if (upvote.user.id == userId) {
              upvotes.push(post.id)
            }
          })
        })

        // sort by upvote count
        posts = posts.sort((p1, p2) => {
          return p2.upvotesCount - p1.upvotesCount
        })

        // TODO (later): remove upvotes array for better performace
        this.setState({ upvotes, posts, status: 'success' })
      })
      .catch(err => {
        // A user shouldn't see an update if they lose connection for 3 seconds
        // They'll only see an error for a user-triggered action
        if (!intervalUpdate) {
          this.setState({ status: 'error' })
        }
      })
  }

  onUpvote(e, postId) {
    const { userId: authUser } = this.props
    if (authUser === undefined) return
    let { upvotes, posts } = this.state
    let post = posts.find(post => post.id == postId)
    let postIndex = posts.indexOf(post)
    if (includes(upvotes, postId)) {
      // if this is nil, this means we've ran into a race where this.state.posts
      // hasn't finished updating (probably from a this.refreshPosts call) -
      // just ignore the user action for the time being and let them retry once
      // it actually updates
      const upvote = posts
        .find(post => post.id == postId)
        .upvotes.find(upvote => upvote.user.id == authUser)

      if (!upvote) return

      // immediately increment values so it feels responsive, later refresh
      // posts completely so we have the correct values in state
      // remove post from upvotes array w/ removed postId
      let truncatedUpvotes = upvotes.filter(upvote => upvote !== postId)
      this.setState({ upvotes: truncatedUpvotes })

      // decrement upvotesCount //
      post.upvotesCount--
      posts[postIndex] = post
      this.setState({ posts })

      // actually make requests & refresh
      api.delete(`v1/upvotes/${upvote.id}`).then(() => this.refreshPosts())
    } else {
      // add post to upvotes array //
      upvotes.push(postId)
      this.setState({ upvotes })

      // increment upvotesCount //
      post.upvotesCount++
      posts[postIndex] = post
      this.setState({ posts })

      // actually make requests & refresh
      api.post(`v1/posts/${postId}/upvotes`).then(() => this.refreshPosts())
    }
  }

  render() {
    const { posts, upvotes, status } = this.state
    const { userId } = this.props
    switch (status) {
      case 'loading':
        return <LoadingBar />
      case 'success':
        return (
          <Fragment>
            {isEmpty(posts) && (
              <Text f={3} color="muted" py={4} align="center">
                No submissions yet!
              </Text>
            )}
            {orderBy(posts, 'rank_score', 'desc').map(post => (
              <Post
                id={post.id}
                name={post.name}
                url={post.url_redirect}
                description={post.description}
                createdAt={post.created_at}
                mine={get(post, 'creator.id') === userId}
                commentsCount={post.comment_count}
                upvotesCount={post.upvotesCount}
                upvoted={includes(upvotes, post.id)}
                onUpvote={e => this.onUpvote(e, post.id)}
                disabled={userId === undefined}
                key={post.id}
              />
            ))}
          </Fragment>
        )
      default:
        return <ErrorPage />
    }
  }
}

export default Posts
<commit_msg>Revert "Use cached userId value when available"<commit_after>import React, { Component, Fragment } from 'react'
import { Box, Text } from '@hackclub/design-system'
import Post from 'components/challenge/Post'
import LoadingBar from 'components/LoadingBar'
import ErrorPage from 'components/admin/ErrorPage'
import api from 'api'
import { includes, isEmpty, get, orderBy } from 'lodash'

class Posts extends Component {
  state = { posts: [], upvotes: [], status: 'loading', prevUserId: null }

  static getDerivedStateFromProps = (nextProps, prevState) => {
    const { userId } = nextProps
    const { prevUserId } = prevState

    if (userId === prevUserId) {
      return {}
    } else {
      return { prevUserId: userId }
    }
  }

  componentDidMount() {
    this.refreshPosts(this.props.userId)
    this.refreshIntervalId = setInterval(() => {
      this.refreshPosts(this.props.userId, true)
    }, 3000)
  }

  componentWillUnmount() {
    clearInterval(this.refreshIntervalId)
  }

  refreshPosts(newUserId, intervalUpdate = false) {
    const { challengeId, userId: oldUserId } = this.props
    const userId = newUserId || oldUserId
    console.log('refreshing!')

    api
      .get(`v1/challenges/${challengeId}/posts`)
      .then(data => {
        let posts = data || []

        // array of post ids that user has upvoted
        const upvotes = []
        posts.forEach(post => {
          post.upvotesCount = post.upvotes.length
          post.upvotes.forEach(upvote => {
            if (upvote.user.id == userId) {
              upvotes.push(post.id)
            }
          })
        })

        // sort by upvote count
        posts = posts.sort((p1, p2) => {
          return p2.upvotesCount - p1.upvotesCount
        })

        // TODO (later): remove upvotes array for better performace
        this.setState({ upvotes, posts, status: 'success' })
      })
      .catch(err => {
        // A user shouldn't see an update if they lose connection for 3 seconds
        // They'll only see an error for a user-triggered action
        if (!intervalUpdate) {
          this.setState({ status: 'error' })
        }
      })
  }

  onUpvote(e, postId) {
    const { userId: authUser } = this.props
    if (authUser === undefined) return
    let { upvotes, posts } = this.state
    let post = posts.find(post => post.id == postId)
    let postIndex = posts.indexOf(post)
    if (includes(upvotes, postId)) {
      // if this is nil, this means we've ran into a race where this.state.posts
      // hasn't finished updating (probably from a this.refreshPosts call) -
      // just ignore the user action for the time being and let them retry once
      // it actually updates
      const upvote = posts
        .find(post => post.id == postId)
        .upvotes.find(upvote => upvote.user.id == authUser)

      if (!upvote) return

      // immediately increment values so it feels responsive, later refresh
      // posts completely so we have the correct values in state
      // remove post from upvotes array w/ removed postId
      let truncatedUpvotes = upvotes.filter(upvote => upvote !== postId)
      this.setState({ upvotes: truncatedUpvotes })

      // decrement upvotesCount //
      post.upvotesCount--
      posts[postIndex] = post
      this.setState({ posts })

      // actually make requests & refresh
      api.delete(`v1/upvotes/${upvote.id}`).then(() => this.refreshPosts())
    } else {
      // add post to upvotes array //
      upvotes.push(postId)
      this.setState({ upvotes })

      // increment upvotesCount //
      post.upvotesCount++
      posts[postIndex] = post
      this.setState({ posts })

      // actually make requests & refresh
      api.post(`v1/posts/${postId}/upvotes`).then(() => this.refreshPosts())
    }
  }

  render() {
    const { posts, upvotes, status } = this.state
    const { userId } = this.props
    switch (status) {
      case 'loading':
        return <LoadingBar />
      case 'success':
        return (
          <Fragment>
            {isEmpty(posts) && (
              <Text f={3} color="muted" py={4} align="center">
                No submissions yet!
              </Text>
            )}
            {orderBy(posts, 'rank_score', 'desc').map(post => (
              <Post
                id={post.id}
                name={post.name}
                url={post.url_redirect}
                description={post.description}
                createdAt={post.created_at}
                mine={get(post, 'creator.id') === userId}
                commentsCount={post.comment_count}
                upvotesCount={post.upvotesCount}
                upvoted={includes(upvotes, post.id)}
                onUpvote={e => this.onUpvote(e, post.id)}
                disabled={userId === undefined}
                key={post.id}
              />
            ))}
          </Fragment>
        )
      default:
        return <ErrorPage />
    }
  }
}

export default Posts
<|endoftext|>