<commit_before>exports.excelFileParser = function(fileName, tabName, metadataFields){
    var assert = require('assert');
    console.log(metadataFields);
    if(typeof require !== 'undefined') XLSX = require('xlsx');

    var mongodb = require('mongodb');
    var _ = require('underscore');

    var fs = require('fs');

    var Converter=require("csvtojson").core.Converter;

    var  distinctDataFields;

    var collectionFields = [];

    var MongoClient = mongodb.MongoClient;

    var url = 'mongodb://localhost:27017/datamanager-03-test';

    var workbook = XLSX.readFile('./uploads/' + fileName);

    var worksheet = workbook.Sheets[tabName];

    var metaFields = metadataFields;

    var json_conversion = XLSX.utils.sheet_to_json(worksheet);

    var csv_conversion = XLSX.utils.sheet_to_csv(worksheet);

    var csv_lines = csv_conversion.split('\n');

    var dirtyHeaderLine = csv_lines[0];

    var cleanHeaderLine = dirtyHeaderLine.replace(/\./g,"")
        .replace(/ /g,"")
        .replace(/"'"/g,"")
        .replace(/","/g,"")
        .replace(/"\/"/g,"")
        .replace(/"#"/g,"")
        .replace(/\(/g,"")
        .replace(/\)/g,"");

    cleanHeaderLine = cleanHeaderLine.replace(/,+$/, "");

    console.log(cleanHeaderLine);

    csv_lines[0] = cleanHeaderLine;

    var newCsvLines = [];

    csv_lines.forEach(function(line){
        if (line.charAt(0) == ',') { line = line.slice(1); }
        var currentIndex = csv_lines.indexOf(line);
        if(currentIndex === 0){
            newCsvLines.push(line);
        }
        else {
            newCsvLines.push("\n" + line);
        }
    });

    var csvFileName = './uploads/' + fileName + '_clean.csv';

    fs.writeFileSync(csvFileName, newCsvLines);

    var fileStream=fs.createReadStream(csvFileName);
//new converter instance
    var csvConverter=new Converter({constructResult:true});

    //end_parsed will be emitted once parsing finished
    csvConverter.on("end_parsed",function(jsonObj){
        //console.log(jsonObj); //here is your result json object
        makeRecords(jsonObj);

    });

    //read from file
    fileStream.pipe(csvConverter);

    function makeRecords(result){
        console.log(result.length);

        MongoClient.connect(url, function (err, db) {
            if (err) {
                console.log('Unable to connect to the mongoDB server. Error:', err);
            } else {
                console.log('Connection established to', url);

                var categoryName = metaFields.subjectCategory;

                var collectionName = categoryName.replace(/ /g,"_");

                db.open(function(err, client){
                    client.createCollection(collectionName, function(err, col) {
                    });

                    var collection = db.collection(collectionName);

                    for(var i =0;i < result.length; i++){
                        //console.log(result[i]);

                        var inserted = 0;

                        for(var i = 0;i < result.length; i++) {
                            var dataRecord = result[i];

                                dataRecord.OriginalDocumentName = fileName;
                                dataRecord.DocumentAuthor = metaFields.documentAuthor;
                                dataRecord.TabName = tabName;
                                dataRecord.SubjectCategory = metaFields.subjectCategory;
                                dataRecord.Subject = metaFields.subject;
                                dataRecord.DateDocumentProduced = metaFields.dateDocumentProduced;
                                dataRecord.DateDocumentReceived = metaFields.dateDocumentReceived;
                                dataRecord.DocumentSubmitter = metaFields.documentSubmitter;
                                dataRecord.DocumentReviewer = metaFields.documentReviewer;
                                dataRecord.OriginalSource = metadataFields.originalSource,
                                dataRecord.DataVersion = metadataFields.dataVersion,
                                dataRecord.DataFields = newCsvLines[0];

                            collection.insert(dataRecord, function (err) {
                                if (++inserted == result.length) {
                                    console.log("finished");
                                    db.close();
                                }
                            });
                        }
                    }
                });

                /////////////////////////////////////

                db.open(function(err, client) {
                    client.createCollection(collectionName, function (err, col) {
                    });

                    client.collection(collectionName, function (err, col) {

                        var dataVersions = [];

                        col.distinct("DataFields", (function (err, docs) {
                            console.log('Distinct Data Fields:  ' + docs);

                            var fields = [];

                            if(!docs[0]){
                                docs[0] = collectionFields;

                            }

                            if (docs[0]) {
                                fields = docs[0].split(',');
                            }
                            else {
                                fields = collectionFields;
                                dataVersions = ['1'];
                            }

                            db.collection('collections_metadata').update(
                                {
                                    collectionName: collectionName
                                },
                                {
                                    $set: {
                                        collectionName: collectionName,
                                        dataFields: fields,
                                        dataVersions: dataVersions
                                    }
                                },
                                {upsert: true}
                            )
                        }));
                    })

                    ///////////////////////////

                    db.collection('collections_metadata')
                        .find({collectionName: collectionName}).toArray(function (err, docs) {

                            if (docs[0]) {
                                dataVersions = docs[0].dataVersions;
                                if (!_.contains(dataVersions, metadataFields.dataVersion)) {

                                    dataVersions.push(metadataFields.dataVersion);

                                    db.collection('collections_metadata').update(
                                        {
                                            collectionName: collectionName
                                        },
                                        {
                                            $set: {
                                                dataVersions: dataVersions
                                            }
                                        }
                                        ,
                                        {upsert: true}
                                    )
                                }
                                else {
                                    db.collection('collections_metadata').update(
                                        {
                                            collectionName: collectionName
                                        },
                                        {
                                            $set: {
                                                dataVersions: dataVersions
                                            }
                                        }
                                        ,
                                        {upsert: true}
                                    )
                                }

                            }
                            else {
                                dataVersions = ['1'];
                                db.collection('collections_metadata').update(
                                    {
                                        collectionName: collectionName
                                    },
                                    {
                                        $set: {
                                            dataVersions: dataVersions
                                        }
                                    }
                                    ,
                                    {upsert: true}
                                )
                            }

                            //////////////////////////////

                        });
                })

                /////////////////////////////////
            }
        });

    }
};
<commit_msg>add handling of collection dataFields not being present to excel parser<commit_after>exports.excelFileParser = function(fileName, tabName, metadataFields){
    var assert = require('assert');
    console.log(metadataFields);
    if(typeof require !== 'undefined') XLSX = require('xlsx');

    var mongodb = require('mongodb');
    var _ = require('underscore');

    var fs = require('fs');

    var Converter=require("csvtojson").core.Converter;

    var  distinctDataFields;

    var collectionFields = [];

    var MongoClient = mongodb.MongoClient;

    var url = 'mongodb://localhost:27017/datamanager-03-test';

    var workbook = XLSX.readFile('./uploads/' + fileName);

    var worksheet = workbook.Sheets[tabName];

    var metaFields = metadataFields;

    var json_conversion = XLSX.utils.sheet_to_json(worksheet);

    var csv_conversion = XLSX.utils.sheet_to_csv(worksheet);

    var csv_lines = csv_conversion.split('\n');

    var dirtyHeaderLine = csv_lines[0];

    var cleanHeaderLine = dirtyHeaderLine.replace(/\./g,"")
        .replace(/ /g,"")
        .replace(/"'"/g,"")
        .replace(/","/g,"")
        .replace(/"\/"/g,"")
        .replace(/"#"/g,"")
        .replace(/\(/g,"")
        .replace(/\)/g,"");

    cleanHeaderLine = cleanHeaderLine.replace(/,+$/, "");

    console.log(cleanHeaderLine);

    csv_lines[0] = cleanHeaderLine;

    var newCsvLines = [];

    csv_lines.forEach(function(line){
        if (line.charAt(0) == ',') { line = line.slice(1); }
        var currentIndex = csv_lines.indexOf(line);
        if(currentIndex === 0){
            newCsvLines.push(line);
        }
        else {
            newCsvLines.push("\n" + line);
        }
    });

    var csvFileName = './uploads/' + fileName + '_clean.csv';

    fs.writeFileSync(csvFileName, newCsvLines);

    var fileStream=fs.createReadStream(csvFileName);
//new converter instance
    var csvConverter=new Converter({constructResult:true});

    //end_parsed will be emitted once parsing finished
    csvConverter.on("end_parsed",function(jsonObj){
        //console.log(jsonObj); //here is your result json object
        makeRecords(jsonObj);

    });

    //read from file
    fileStream.pipe(csvConverter);

    function makeRecords(result){
        console.log(result.length);

        MongoClient.connect(url, function (err, db) {
            if (err) {
                console.log('Unable to connect to the mongoDB server. Error:', err);
            } else {
                console.log('Connection established to', url);

                var categoryName = metaFields.subjectCategory;

                var collectionName = categoryName.replace(/ /g,"_");

                db.open(function(err, client){
                    client.createCollection(collectionName, function(err, col) {
                    });

                    var collection = db.collection(collectionName);

                    for(var i =0;i < result.length; i++){
                        //console.log(result[i]);

                        var inserted = 0;

                        for(var i = 0;i < result.length; i++) {
                            var dataRecord = result[i];

                                dataRecord.OriginalDocumentName = fileName;
                                dataRecord.DocumentAuthor = metaFields.documentAuthor;
                                dataRecord.TabName = tabName;
                                dataRecord.SubjectCategory = metaFields.subjectCategory;
                                dataRecord.Subject = metaFields.subject;
                                dataRecord.DateDocumentProduced = metaFields.dateDocumentProduced;
                                dataRecord.DateDocumentReceived = metaFields.dateDocumentReceived;
                                dataRecord.DocumentSubmitter = metaFields.documentSubmitter;
                                dataRecord.DocumentReviewer = metaFields.documentReviewer;
                                dataRecord.OriginalSource = metadataFields.originalSource,
                                dataRecord.DataVersion = metadataFields.dataVersion,
                                dataRecord.DataFields = newCsvLines[0];

                            collection.insert(dataRecord, function (err) {
                                if (++inserted == result.length) {
                                    console.log("finished");
                                    db.close();
                                }
                            });
                        }
                    }
                });

                /////////////////////////////////////

                db.open(function(err, client) {
                    client.createCollection(collectionName, function (err, col) {
                    });

                    client.collection(collectionName, function (err, col) {

                        var dataVersions = [];

                        col.distinct("DataFields", (function (err, docs) {
                            console.log('Distinct Data Fields:  ' + docs);

                            var fields = [];

                            if(!docs[0]){
                                docs[0] = collectionFields;

                            }

                            if (docs[0]) {
                                fields = docs[0].split(',');
                            }
                            else {
                                fields = collectionFields;
                                dataVersions = ['1'];
                            }

                            db.collection('collections_metadata').update(
                                {
                                    collectionName: collectionName
                                },
                                {
                                    $set: {
                                        collectionName: collectionName,
                                        dataFields: fields,
                                        dataVersions: dataVersions
                                    }
                                },
                                {upsert: true}
                            )
                        }));
                    })

                    ///////////////////////////

                    db.collection('collections_metadata')
                        .find({collectionName: collectionName}).toArray(function (err, docs) {
                            if(!docs[0]){
                                docs[0] = [];
                            }
                            if (docs[0]) {
                                dataVersions = docs[0].dataVersions;
                                if (!_.contains(dataVersions, metadataFields.dataVersion)) {

                                    dataVersions.push(metadataFields.dataVersion);

                                    db.collection('collections_metadata').update(
                                        {
                                            collectionName: collectionName
                                        },
                                        {
                                            $set: {
                                                dataVersions: dataVersions
                                            }
                                        }
                                        ,
                                        {upsert: true}
                                    )
                                }
                                else {
                                    db.collection('collections_metadata').update(
                                        {
                                            collectionName: collectionName
                                        },
                                        {
                                            $set: {
                                                dataVersions: dataVersions
                                            }
                                        }
                                        ,
                                        {upsert: true}
                                    )
                                }

                            }
                            else {
                                dataVersions = ['1'];
                                db.collection('collections_metadata').update(
                                    {
                                        collectionName: collectionName
                                    },
                                    {
                                        $set: {
                                            dataVersions: dataVersions
                                        }
                                    }
                                    ,
                                    {upsert: true}
                                )
                            }

                            //////////////////////////////

                        });
                })

                /////////////////////////////////
            }
        });

    }
};
<|endoftext|>


<commit_before>(function(root) {

    (function(_, $) {

        var _createOffscreenElem = function() {
            var elem = $('<div/>');
            elem.css({
                // Remove from normal flow to avoid causing any page flicker
                position: 'absolute',
                top: '-999em',
                left: '-999em'
            });
            return elem;
        };

        var _EVENT_NAMES = document.createElement('input').oninput === null ? 'input' : 'keydown keyup change',
            _DEFAULTS = {
                numPaddingChars: 1,
                numPaddingLines: 0
            },
            _BUGS = {
                /** @type {boolean} */
                textareaWidth: (function() {
                    var testElem = _createOffscreenElem();

                    testElem.css({
                        width: '235px',
                        'font-size': '24px',
                        'font-family': 'Arial',
                        'overflow-y': 'scroll'
                    });

                    testElem.appendTo(document.body);

                    // Dummy text that is known to trigger the bug in IE8 w/ Arial @ 24px
                    var testText = 'asd asdlkjasd asdlk';

                    testElem.text(testText);
                    var preHeight = testElem.height();

                    testElem.text(testText + '.');
                    var postHeight = testElem.height();

                    testElem.remove();

                    return (postHeight > preHeight);
                }())
            };

        /**
         * Generates a padding string with the specified number of characters.  The returned string can be used to
         * pad an input's value to ensure it is wide enough to fix all of the user's input.
         * @param {Number} numPaddingChars Number of characters to pad.
         * @param {String} [ch='W'] Character to use for padding.
         * @returns {string}
         * @private
         */
        var _createPaddingString = function(numPaddingChars, ch) {
            return new Array(numPaddingChars + 1).join(ch || 'W');
        };

        /**
         * Merges the given user-specified configuration object with the default values and returns the combined result.
         * @param {Object} [config] User-specified config object that overrides the default settings.
         * @returns {Object} A new object containing the merged config values.
         * @private
         */
        var _mergeConfig = function(config) {
            return _.extend({}, _DEFAULTS, config);
        };

        /**
         * Gets the computed CSS styles for the given element.
         * @param {Element|jQuery} el DOM element or jQuery object to retrieve computed styles for.
         * @returns {Object} Hash object containing key-value pairs, where the key is a CSS property name
         * 					 and the value is the computed value of that property.
         * @private
         */
        var _getComputedStyles = function(el) {
            if (el instanceof jQuery)
                el = el[0];

            // IE
            if (el.currentStyle) {
                return el.currentStyle;
            }

            // Firefox
            if (document.defaultView && document.defaultView.getComputedStyle) {
                var computed = document.defaultView.getComputedStyle(el, null);

                if (!computed.hasOwnProperty('length')) {
                    return computed;
                }

                var retStyles = {},
                    idx = computed.length,
                    propName;

                while (idx--) {
                    propName = computed[idx];
                    retStyles[propName] = computed[propName];
                }

                return retStyles;
            }

            // Get inline style
            return el.style;
        };

        /**
         * Copies the computed style properties and their values from srcEl and sets them on destEl.
         * @param {jQuery} destEl
         * @param {jQuery} srcEl
         * @private
         */
        var _setComputedStylesFrom = function(destEl, srcEl) {
            var computedStyles = _getComputedStyles(srcEl);
            var styles = {};
            for (var prop in computedStyles) {
                styles[prop] = computedStyles[prop];
            }
            destEl.css(styles);
        };

        /**
         * Automatically resizes the given input so that it is wide enough to fit its input value plus the given padding string.
         * @param {jQuery} input
         * @param {string} paddingStr
         * @private
         */
        var _autoResizeWidth = function(input, paddingStr) {
            var offscreen = _createOffscreenElem();

            _setComputedStylesFrom(offscreen, input);

            offscreen.css({
                width: 'auto',
                'white-space': 'pre'
            });

            offscreen.text(input.val() + paddingStr);
            offscreen.appendTo(document.body);

            input.width(offscreen.width());

            offscreen.remove();
        };

        $.fn.autoWidth = function(config) {
            config = _mergeConfig(config);

            var paddingStr = _createPaddingString(config.numPaddingChars);

            this.each(function() {
                var input = $(this),
                    resize = function() {
                        _autoResizeWidth(input, paddingStr);
                    };

                input.on(_EVENT_NAMES, function(e) {
                    // Modern browsers
                    if (e.type === 'input') {
                        resize();
                    }
                    // Legacy browsers
                    else {
                        // IE needs a small delay to resize properly
                        _.defer(resize);
                    }
                });

                resize();
            });
        };

        /**
         * Automatically resizes the given input so that it is tall enough to fit its input value.
         * @param {jQuery} input
         * @param {string} paddingStr
         * @private
         */
        var _autoResizeHeight = function(input, paddingStr) {
            var offscreen = _createOffscreenElem();

            _setComputedStylesFrom(offscreen, input);

            // If the last line of input ends with a newline, browsers will ignore it and our calculation will be off
            // (one line too short).
            var normVal = (input.val() + paddingStr).replace(/\n$/g, '\n.');

            offscreen.css({
                // Allow height to expand as needed
                height: 'auto',

                // Force <div> to behave like a <textarea>
                'white-space': 'pre-wrap',
                'word-wrap': 'break-word'
            });

            offscreen.appendTo(document.body);
            offscreen.text(normVal);

            // Prevents vertical scrollbars from goofing shit up.
            // IE8 (naturally) treats textareas and divs differently when calculating width and scrollbar allowance,
            // so we have to subtract 2px from the offscreen element's width in IE8 to make up for Microsoft's incompetence.
            offscreen.width(input.width() - (_BUGS.textareaWidth ? 2 : 0));

            // Force browsers to hide scrollbars by resizing the textarea much taller than it needs to be
            // and _then_ resizing it to its actual ideal height.
            // NOTE: DO NOT VARIABLE-IZE offscreen.height()!!  It needs to be queried twice to force the browser to
            // recalculate sizes and positions (at least in Chrome 33).
            input.height(offscreen.height() * 2);
            input.height(offscreen.height());

            offscreen.remove();
        };

        $.fn.autoHeight = function(config) {
            config = _mergeConfig(config);

            var paddingStr = _createPaddingString(config.numPaddingLines, '\n');

            this.each(function() {
                var input = $(this),
                    resize = function() {
                        _autoResizeHeight(input, paddingStr);
                    };

                input.on(_EVENT_NAMES, function(e) {
                    // Modern browsers
                    if (e.type === 'input') {
                        resize();
                    }
                    // Legacy browsers
                    else {
                        // IE needs a small delay to resize properly
                        _.defer(resize);
                    }
                });

                resize();
            });
        };

    }(root._, root.jQuery || root.Zepto));

}(window));
<commit_msg>Fixed derp: Positioning of offscreen elements<commit_after>(function(root) {

    (function(_, $) {

        var _createOffscreenElem = function() {
            var elem = $('<div/>');
            _positionOffscreen(elem);
            return elem;
        };

        var _positionOffscreen = function(elem) {
            elem.css({
                // Remove from normal flow to avoid causing any page flicker
                position: 'absolute',
                top: '-999em',
                left: '-999em'
            });
        };

        var _EVENT_NAMES = document.createElement('input').oninput === null ? 'input' : 'keydown keyup change',
            _DEFAULTS = {
                numPaddingChars: 1,
                numPaddingLines: 0
            },
            _BUGS = {
                /** @type {boolean} */
                textareaWidth: (function() {
                    var testElem = _createOffscreenElem();

                    testElem.css({
                        width: '235px',
                        'font-size': '24px',
                        'font-family': 'Arial',
                        'overflow-y': 'scroll'
                    });

                    testElem.appendTo(document.body);

                    // Dummy text that is known to trigger the bug in IE8 w/ Arial @ 24px
                    var testText = 'asd asdlkjasd asdlk';

                    testElem.text(testText);
                    var preHeight = testElem.height();

                    testElem.text(testText + '.');
                    var postHeight = testElem.height();

                    testElem.remove();

                    return (postHeight > preHeight);
                }())
            };

        /**
         * Generates a padding string with the specified number of characters.  The returned string can be used to
         * pad an input's value to ensure it is wide enough to fix all of the user's input.
         * @param {Number} numPaddingChars Number of characters to pad.
         * @param {String} [ch='W'] Character to use for padding.
         * @returns {string}
         * @private
         */
        var _createPaddingString = function(numPaddingChars, ch) {
            return new Array(numPaddingChars + 1).join(ch || 'W');
        };

        /**
         * Merges the given user-specified configuration object with the default values and returns the combined result.
         * @param {Object} [config] User-specified config object that overrides the default settings.
         * @returns {Object} A new object containing the merged config values.
         * @private
         */
        var _mergeConfig = function(config) {
            return _.extend({}, _DEFAULTS, config);
        };

        /**
         * Gets the computed CSS styles for the given element.
         * @param {Element|jQuery} el DOM element or jQuery object to retrieve computed styles for.
         * @returns {Object} Hash object containing key-value pairs, where the key is a CSS property name
         * 					 and the value is the computed value of that property.
         * @private
         */
        var _getComputedStyles = function(el) {
            if (el instanceof jQuery)
                el = el[0];

            // IE
            if (el.currentStyle) {
                return el.currentStyle;
            }

            // Firefox
            if (document.defaultView && document.defaultView.getComputedStyle) {
                var computed = document.defaultView.getComputedStyle(el, null);

                if (!computed.hasOwnProperty('length')) {
                    return computed;
                }

                var retStyles = {},
                    idx = computed.length,
                    propName;

                while (idx--) {
                    propName = computed[idx];
                    retStyles[propName] = computed[propName];
                }

                return retStyles;
            }

            // Get inline style
            return el.style;
        };

        /**
         * Copies the computed style properties and their values from srcEl and sets them on destEl.
         * @param {jQuery} destEl
         * @param {jQuery} srcEl
         * @private
         */
        var _setComputedStylesFrom = function(destEl, srcEl) {
            var computedStyles = _getComputedStyles(srcEl);
            var styles = {};
            for (var prop in computedStyles) {
                styles[prop] = computedStyles[prop];
            }
            destEl.css(styles);
        };

        /**
         * Automatically resizes the given input so that it is wide enough to fit its input value plus the given padding string.
         * @param {jQuery} input
         * @param {string} paddingStr
         * @private
         */
        var _autoResizeWidth = function(input, paddingStr) {
            var offscreen = _createOffscreenElem();

            _setComputedStylesFrom(offscreen, input);

            _positionOffscreen(offscreen);

            offscreen.css({
                width: 'auto',
                'white-space': 'pre'
            });

            offscreen.text(input.val() + paddingStr);
            offscreen.appendTo(document.body);

            input.width(offscreen.width());

            offscreen.remove();
        };

        $.fn.autoWidth = function(config) {
            config = _mergeConfig(config);

            var paddingStr = _createPaddingString(config.numPaddingChars);

            this.each(function() {
                var input = $(this),
                    resize = function() {
                        _autoResizeWidth(input, paddingStr);
                    };

                input.on(_EVENT_NAMES, function(e) {
                    // Modern browsers
                    if (e.type === 'input') {
                        resize();
                    }
                    // Legacy browsers
                    else {
                        // IE needs a small delay to resize properly
                        _.defer(resize);
                    }
                });

                resize();
            });
        };

        /**
         * Automatically resizes the given input so that it is tall enough to fit its input value.
         * @param {jQuery} input
         * @param {string} paddingStr
         * @private
         */
        var _autoResizeHeight = function(input, paddingStr) {
            var offscreen = _createOffscreenElem();

            _setComputedStylesFrom(offscreen, input);

            _positionOffscreen(offscreen);

            // If the last line of input ends with a newline, browsers will ignore it and our calculation will be off
            // (one line too short).
            var normVal = (input.val() + paddingStr).replace(/\n$/g, '\n.');

            offscreen.css({
                // Allow height to expand as needed
                height: 'auto',

                // Force <div> to behave like a <textarea>
                'white-space': 'pre-wrap',
                'word-wrap': 'break-word'
            });

            offscreen.appendTo(document.body);
            offscreen.text(normVal);

            // Prevents vertical scrollbars from goofing shit up.
            // IE8 (naturally) treats textareas and divs differently when calculating width and scrollbar allowance,
            // so we have to subtract 2px from the offscreen element's width in IE8 to make up for Microsoft's incompetence.
            offscreen.width(input.width() - (_BUGS.textareaWidth ? 2 : 0));

            // Force browsers to hide scrollbars by resizing the textarea much taller than it needs to be
            // and _then_ resizing it to its actual ideal height.
            // NOTE: DO NOT VARIABLE-IZE offscreen.height()!!  It needs to be queried twice to force the browser to
            // recalculate sizes and positions (at least in Chrome 33).
            input.height(offscreen.height() * 2);
            input.height(offscreen.height());

            offscreen.remove();
        };

        $.fn.autoHeight = function(config) {
            config = _mergeConfig(config);

            var paddingStr = _createPaddingString(config.numPaddingLines, '\n');

            this.each(function() {
                var input = $(this),
                    resize = function() {
                        _autoResizeHeight(input, paddingStr);
                    };

                input.on(_EVENT_NAMES, function(e) {
                    // Modern browsers
                    if (e.type === 'input') {
                        resize();
                    }
                    // Legacy browsers
                    else {
                        // IE needs a small delay to resize properly
                        _.defer(resize);
                    }
                });

                resize();
            });
        };

    }(root._, root.jQuery || root.Zepto));

}(window));
<|endoftext|>


<commit_before>'use strict';

import forEachHtmlNodes from '@inc2734/for-each-html-nodes';

export default class BasisPageEffect {
  constructor(args = {}) {
    this.args = args;
    this.args.pageEffect = this.args.pageEffect || '.c-page-effect';
    this.args.duration = this.args.duration || 0 === this.args.duration ? this.args.duration : 200;

    const container      = document.querySelector(this.args.pageEffect);
    const pageOutObjects = document.querySelectorAll('[data-page-effect-link="true"], a[href]:not([target="_blank"]):not([href^="#"]):not([href*="javascript"]):not([href*=".jpg"]):not([href*=".jpeg"]):not([href*=".gif"]):not([href*=".png"]):not([href*=".mov"]):not([href*=".swf"]):not([href*=".mp4"]):not([href*=".flv"]):not([href*=".avi"]):not([href*=".mp3"]):not([href*=".pdf"]):not([href*=".zip"]):not([href^="mailto:"]):not([data-page-effect-link="false"])');

    if (! container) {
      return;
    }

    const fadeInPage = () => {
      container.setAttribute('aria-hidden', 'true');
      container.setAttribute('data-page-effect', 'fadein');
    }

    const fadeOutPage = () => {
      container.setAttribute('aria-hidden', 'false');
      container.setAttribute('data-page-effect', 'fadeout');
    }

    const moveLocation = (url) => {
      setTimeout(() => window.location.href = url, this.args['duration']);
    }

    window.addEventListener('load', () => fadeInPage(), false);

    forEachHtmlNodes(
      pageOutObjects,
      (link) => {
        link.addEventListener(
          'click',
          (event) => {
            if (event.shiftKey || event.ctrlKey || event.metaKey) {
              return;
            }

            event.preventDefault();
            fadeOutPage();
            moveLocation(link.getAttribute('href'));
          },
          false
        );
      }
    );
  }
}
<commit_msg>Fix #116<commit_after>'use strict';

import forEachHtmlNodes from '@inc2734/for-each-html-nodes';

export default class BasisPageEffect {
  constructor(args = {}) {
    this.args = args;
    this.args.pageEffect = this.args.pageEffect || '.c-page-effect';
    this.args.duration = this.args.duration || 0 === this.args.duration ? this.args.duration : 200;

    const container      = document.querySelector(this.args.pageEffect);
    const pageOutObjects = document.querySelectorAll('[data-page-effect-link="true"], a[href]:not([target="_blank"]):not([href^="#"]):not([href*="javascript"]):not([href*=".jpg"]):not([href*=".jpeg"]):not([href*=".gif"]):not([href*=".png"]):not([href*=".mov"]):not([href*=".swf"]):not([href*=".mp4"]):not([href*=".flv"]):not([href*=".avi"]):not([href*=".mp3"]):not([href*=".pdf"]):not([href*=".zip"]):not([href^="mailto:"]):not([data-page-effect-link="false"])');

    if (! container) {
      return;
    }

    const fadeInPage = () => {
      container.setAttribute('aria-hidden', 'true');
      container.setAttribute('data-page-effect', 'fadein');
    }

    const fadeOutPage = () => {
      container.setAttribute('aria-hidden', 'false');
      container.setAttribute('data-page-effect', 'fadeout');
    }

    const moveLocation = (url) => {
      setTimeout(() => window.location.href = url, this.args['duration']);
    }

    window.addEventListener('load', () => fadeInPage(), false);
    window.addEventListener('pageshow', () => fadeInPage(), false);

    forEachHtmlNodes(
      pageOutObjects,
      (link) => {
        link.addEventListener(
          'click',
          (event) => {
            if (event.shiftKey || event.ctrlKey || event.metaKey) {
              return;
            }

            event.preventDefault();
            fadeOutPage();
            moveLocation(link.getAttribute('href'));
          },
          false
        );
      }
    );
  }
}
<|endoftext|>


<commit_before>import * as THREE from 'three';

import GP           from '../GameProperties.js';
import Player       from './Player.js';
import Port         from './Port.js';
import WaterSurface from './WaterSurface.js';
import WaterTrail   from './WaterTrail.js';

class Game {
    /**
     * Initialize the game
     */
    constructor(controls) {
        this.controls = controls;

        this.ratio = 1;
        this.gameContainer = document.getElementById('g');
        let rect = this.gameContainer.getBoundingClientRect();
        this.width  = rect.width;
        this.height = rect.height;

        this.scene = new THREE.Scene();

        this.waterSurface = new WaterSurface(this);
        this.scene.add(this.waterSurface);

        this.player = new Player(this);
        this.scene.add(this.player);

        this.waterTrail = new WaterTrail(this);
        this.scene.add(this.waterTrail);

        this.port = new Port(this);
        this.port.position.set(-100, 0, 0);
        this.scene.add(this.port);

        this.sun = new THREE.DirectionalLight(0xffffff, .5);
        this.sun.position.set(GP.SunPosition.x, GP.SunPosition.y, GP.SunPosition.z);
        this.sun.castShadow = true;
        this.sun.shadow.camera.near = 0.5;
        this.sun.shadow.camera.far  = 1000;

        this.sun.shadow.camera.top    = 300;
        this.sun.shadow.camera.right  = 200;
        this.sun.shadow.camera.bottom = -200;
        this.sun.shadow.camera.left   = -200;
        this.sun.target = this.player;
        this.scene.add(this.sun);

        let hemiLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
        this.scene.add(hemiLight);

        this.runTime = 0.0;
        this.buttonTimeout = 0.0;
        this.isRecording = false;

        this.camera = new THREE.PerspectiveCamera(75, this.ratio, 0.1, 20000);
        this.camera.name = 'main cam';
        this.camera.position.set(GP.CameraOffset.x, GP.CameraOffset.y, GP.CameraOffset.z);
        this.camera.lookAt(new THREE.Vector3(0))

        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(GP.GameSize, GP.GameSize);
        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type    = THREE.PCFShadowMap;
        this.gameContainer.appendChild(this.renderer.domElement);
        this.renderer.domElement.style = null;
    }

    /**
     * Draw a single frame
     * @returns {void}
     */
    draw() {
        this.renderer.render(this.scene, this.camera);
    }

    /**
     * Update all game objects, handle input, etc.
     * @param {number} dt Delta Time in s
     * @returns {void}
     */
    update(dt) {
        this.runTime += dt;

        if(this.buttonTimeout >= 0.0) {
            this.buttonTimeout -= dt;
        }

        let oldPlayerPosition = new THREE.Vector3(this.player.position.x, 0, this.player.position.z);

        this.controls.update();
        this.handleControls(this.controls.states, dt);
        this.player.update(dt);

        let playerPositionDelta = oldPlayerPosition.sub(new THREE.Vector3(this.player.position.x, 0, this.player.position.z));
        this.camera.position.sub(playerPositionDelta);

        // Move water with camera
        this.waterSurface.position.set(
            this.camera.position.x - GP.CameraOffset.x,
            this.camera.position.y - GP.CameraOffset.y,
            this.camera.position.z - GP.CameraOffset.z
        );

        // Move sun with player
        this.sun.position.set(
            this.player.position.x + GP.SunPosition.x,
            this.player.position.y + GP.SunPosition.y,
            this.player.position.z + GP.SunPosition.z
        );

        this.waterSurface.offset.x -= playerPositionDelta.x / 800;
        this.waterSurface.offset.y += playerPositionDelta.z / 800;

        this.waterSurface.update(dt);
        this.waterTrail.update(dt);
    }

    handleControls(states, dt) {
        if(states.toggleRecord && this.buttonTimeout <= 0.0) {
            this.buttonTimeout += GP.ButtonTimeout;
            this.isRecording = !this.isRecording;

            if(this.isRecording) {
                console.log('Start recording');
                this.stream = this.renderer.domElement.captureStream();
                this.recordedBlobs = [];

                let options = { mimeType: 'video/webm' };
                try {
                    this.mediaRecorder = new MediaRecorder(this.stream, options);

                    this.mediaRecorder.ondataavailable = (event) => {
                        if (event.data && event.data.size > 0) {
                            this.recordedBlobs.push(event.data);
                        }
                    };
                    this.mediaRecorder.start(100); // collect 100ms of data
                } catch(e) {
                    console.warn('Unable to create MediaRecorder with options Object: ', e);
                }
            } else {
                console.log('Stop recording');
                this.mediaRecorder.stop();
                let blob = new Blob(this.recordedBlobs, { type: 'video/webm' });
                let url  = window.URL.createObjectURL(blob);
                let a    = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = 'output.webm';
                document.body.appendChild(a);
                a.click();
                setTimeout(function() {
                    document.body.removeChild(a);
                    window.URL.revokeObjectURL(url);
                }, 100);
            }
        }
    }
}

export default Game;<commit_msg>Added function for debug messages that don't spam the console<commit_after>import * as THREE from 'three';

import GP           from '../GameProperties.js';
import Player       from './Player.js';
import Port         from './Port.js';
import WaterSurface from './WaterSurface.js';
import WaterTrail   from './WaterTrail.js';

class Game {
    /**
     * Initialize the game
     */
    constructor(controls) {
        this.controls = controls;

        this.ratio = 1;
        this.gameContainer = document.getElementById('g');
        let rect = this.gameContainer.getBoundingClientRect();
        this.width  = rect.width;
        this.height = rect.height;

        this.debugElement = document.getElementById('d');

        this.scene = new THREE.Scene();

        this.waterSurface = new WaterSurface(this);
        this.scene.add(this.waterSurface);

        this.player = new Player(this);
        this.scene.add(this.player);

        this.waterTrail = new WaterTrail(this);
        this.scene.add(this.waterTrail);

        this.port = new Port(this);
        this.port.position.set(-100, 0, 0);
        this.scene.add(this.port);

        this.sun = new THREE.DirectionalLight(0xffffff, .5);
        this.sun.position.set(GP.SunPosition.x, GP.SunPosition.y, GP.SunPosition.z);
        this.sun.castShadow = true;
        this.sun.shadow.camera.near = 0.5;
        this.sun.shadow.camera.far  = 1000;

        this.sun.shadow.camera.top    = 300;
        this.sun.shadow.camera.right  = 200;
        this.sun.shadow.camera.bottom = -200;
        this.sun.shadow.camera.left   = -200;
        this.sun.target = this.player;
        this.scene.add(this.sun);

        let hemiLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
        this.scene.add(hemiLight);

        this.runTime = 0.0;
        this.buttonTimeout = 0.0;
        this.isRecording = false;

        this.camera = new THREE.PerspectiveCamera(75, this.ratio, 0.1, 20000);
        this.camera.name = 'main cam';
        this.camera.position.set(GP.CameraOffset.x, GP.CameraOffset.y, GP.CameraOffset.z);
        this.camera.lookAt(new THREE.Vector3(0))

        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(GP.GameSize, GP.GameSize);
        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type    = THREE.PCFShadowMap;
        this.gameContainer.appendChild(this.renderer.domElement);
        this.renderer.domElement.style = null;
    }

    /**
     * Draw a single frame
     * @returns {void}
     */
    draw() {
        this.renderer.render(this.scene, this.camera);
    }

    /**
     * Update all game objects, handle input, etc.
     * @param {number} dt Delta Time in s
     * @returns {void}
     */
    update(dt) {
        this.debugElement.innerText = '';

        this.runTime += dt;

        if(this.buttonTimeout >= 0.0) {
            this.buttonTimeout -= dt;
        }

        let oldPlayerPosition = new THREE.Vector3(this.player.position.x, 0, this.player.position.z);

        this.controls.update();
        this.handleControls(this.controls.states, dt);
        this.player.update(dt);

        let playerPositionDelta = oldPlayerPosition.sub(new THREE.Vector3(this.player.position.x, 0, this.player.position.z));
        this.camera.position.sub(playerPositionDelta);

        // Move water with camera
        this.waterSurface.position.set(
            this.camera.position.x - GP.CameraOffset.x,
            this.camera.position.y - GP.CameraOffset.y,
            this.camera.position.z - GP.CameraOffset.z
        );

        // Move sun with player
        this.sun.position.set(
            this.player.position.x + GP.SunPosition.x,
            this.player.position.y + GP.SunPosition.y,
            this.player.position.z + GP.SunPosition.z
        );

        this.waterSurface.offset.x -= playerPositionDelta.x / 800;
        this.waterSurface.offset.y += playerPositionDelta.z / 800;

        this.waterSurface.update(dt);
        this.waterTrail.update(dt);
    }

    debug(text) {
        if(typeof(text) == 'object') {
            text = JSON.stringify(text);
        }
        this.debugElement.innerHTML += text + '\n';
    }

    handleControls(states, dt) {
        if(states.toggleRecord && this.buttonTimeout <= 0.0) {
            this.buttonTimeout += GP.ButtonTimeout;
            this.isRecording = !this.isRecording;

            if(this.isRecording) {
                console.log('Start recording');
                this.stream = this.renderer.domElement.captureStream();
                this.recordedBlobs = [];

                let options = { mimeType: 'video/webm' };
                try {
                    this.mediaRecorder = new MediaRecorder(this.stream, options);

                    this.mediaRecorder.ondataavailable = (event) => {
                        if (event.data && event.data.size > 0) {
                            this.recordedBlobs.push(event.data);
                        }
                    };
                    this.mediaRecorder.start(100); // collect 100ms of data
                } catch(e) {
                    console.warn('Unable to create MediaRecorder with options Object: ', e);
                }
            } else {
                console.log('Stop recording');
                this.mediaRecorder.stop();
                let blob = new Blob(this.recordedBlobs, { type: 'video/webm' });
                let url  = window.URL.createObjectURL(blob);
                let a    = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = 'output.webm';
                document.body.appendChild(a);
                a.click();
                setTimeout(function() {
                    document.body.removeChild(a);
                    window.URL.revokeObjectURL(url);
                }, 100);
            }
        }
    }
}

export default Game;<|endoftext|>


<commit_before>/**
 * Created by kdbanman on 2/29/16.
 */
var lodash = require('lodash');

var Logger = require('polyball/shared/Logger');
var Server = require('socket.io');
var Util = require('polyball/shared/Util');
var Client = require('polyball/shared/model/Client');
var CommsEvents = require('polyball/shared/CommsEvents');

/**
 *
 * @param {{
 *   httpServer: node.http.Server,
 *   model: Model
 * }} config
 * @constructor
 */
var Comms = function (config) {

    //
    //    ########  ########  #### ##     ##    ###    ######## ########
    //    ##     ## ##     ##  ##  ##     ##   ## ##      ##    ##
    //    ##     ## ##     ##  ##  ##     ##  ##   ##     ##    ##
    //    ########  ########   ##  ##     ## ##     ##    ##    ######
    //    ##        ##   ##    ##   ##   ##  #########    ##    ##
    //    ##        ##    ##   ##    ## ##   ##     ##    ##    ##
    //    ##        ##     ## ####    ###    ##     ##    ##    ########
    //
    ///////////////////////////////////////////////////////////////////////////

    //
    //             INITIALIZATION
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Create an new event subscriber object from CommsEvents.ServerToServer.
     * @returns {Object<string, function>}  Map from event strings to arrays of callbacks.
     */
    var buildEventSubscribers = function () {
        var eventStrings = lodash.map(CommsEvents.ServerToServer.keys, function (key) {
            return CommsEvents.ServerToServer[key];
        });

        var eventSubscribers = {};
        eventStrings.forEach(function (eventString) {
            eventSubscribers[eventString] = [];
        });

        return eventSubscribers;
    };

    //
    //             PRIVATE STATE
    //
    ///////////////////////////////////////////////////////////////////////////

    var io = new Server(config.httpServer, {serveClient: true});
    /**
     * @type {Model}
     */
    var model = config.model;
    /**
     * For server side event subscribers, like the Engine.
     * @type {Object.<string, Function>}
     */
    var eventSubscribers = buildEventSubscribers();

    //
    //             CLIENT EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    io.on('connection', function (clientSocket) {
        Logger.info('New client connected.');

        // create a spectator with the socket in the model
        var client = new Client({
            name: Util.randomUsername(),
            socket: clientSocket
        });

        // NOTE: Do NOT store spectator - model or engine could convert it to a player or remove
        //       the spectator from under the Comms feet.  Query for IDs when necessary.
        model.addSpectator(client);

        clientSocket.on('disconnect', function () {
            Logger.info('Client disconnected');
        });

        clientSocket.on(CommsEvents.ClientToServer.queueToPlay, function () {
            var spectatorID = getPlayerOrSpectator(clientSocket).client.id;
            fireNewPlayerQueued({spectatorID: spectatorID});
        });

        clientSocket.on(CommsEvents.ClientToServer.vote, function () {
            //broadcast to listeners (like the engine).
            //listeners.notify('vote', voteData);
        });

        clientSocket.emit('logLevel', Logger.getLevel());
    });

    //
    //             SERVER TO SERVER EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Fire an event by name with a parameters object.
     * @param {String} eventName
     * @param {Object} params
     */
    var fireEvent = function (eventName, params) {
        if (eventSubscribers[eventName] == null) {
            Logger.warn('Comms#fire called with unrecognized event: ' + eventName);
        }

        eventSubscribers[eventName].forEach(function (callback) {
            callback(params);
        });
    };

    /**
     * Notify subscribers that a spectator has queued to be a player.
     *
     * @param params
     * @param {Number} params.spectatorID
     */
    var fireNewPlayerQueued = function (params) {
        fireEvent(CommsEvents.ServerToServer.newPlayerQueued, params);
    };

    //
    //             UTILITY METHODS AND QUERIES
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @param socket
     * @returns {Player|Spectator}
     */
    var getPlayerOrSpectator = function (socket) {
        var candidates = model.getPlayers(function (player) {
            return player.client.socket === socket;
        });

        if (candidates.length == 0) {
            candidates = model.getSpectators(function (spectator) {
                return spectator.client.socket === socket;
            });
        }

        if (candidates.length == 0) {
            Logger.error('Comms could not find player or spectator with a socket.');
            return;
        } else if (candidates.length > 1) {
            Logger.error('Comms found more than one player or spectator with a socket.');
            return;
        }

        return candidates[0];
    };

    //
    //    ########  ##     ## ########  ##       ####  ######
    //    ##     ## ##     ## ##     ## ##        ##  ##    ##
    //    ##     ## ##     ## ##     ## ##        ##  ##
    //    ########  ##     ## ########  ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##    ##
    //    ##         #######  ########  ######## ####  ######
    //
    ///////////////////////////////////////////////////////////////////////////

    this.broadcastSnapshot = function (snapshot) {
        Logger.debug("Comms broadcasting snapshot.");
        io.sockets.emit('snapshot', snapshot);
    };

    //
    //             EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Register a callback with an event.
     * @param {String} eventName
     * @param {Function} callback
     */
    this.on = function (eventName, callback) {
        if (eventSubscribers[eventName] == null) {
            Logger.warn('Comms#on called with unrecognized event: ' + eventName);
        }

        eventSubscribers[eventName].push(callback);
    };

};

module.exports = Comms;<commit_msg>appease linter with Comms.<commit_after>/**
 * Created by kdbanman on 2/29/16.
 */
var lodash = require('lodash');

var Logger = require('polyball/shared/Logger');
var Server = require('socket.io');
var Util = require('polyball/shared/Util');
var Client = require('polyball/shared/model/Client');
var CommsEvents = require('polyball/shared/CommsEvents');

/**
 *
 * @param {{
 *   httpServer: node.http.Server,
 *   model: Model
 * }} config
 * @constructor
 */
var Comms = function (config) {

    //
    //    ########  ########  #### ##     ##    ###    ######## ########
    //    ##     ## ##     ##  ##  ##     ##   ## ##      ##    ##
    //    ##     ## ##     ##  ##  ##     ##  ##   ##     ##    ##
    //    ########  ########   ##  ##     ## ##     ##    ##    ######
    //    ##        ##   ##    ##   ##   ##  #########    ##    ##
    //    ##        ##    ##   ##    ## ##   ##     ##    ##    ##
    //    ##        ##     ## ####    ###    ##     ##    ##    ########
    //
    ///////////////////////////////////////////////////////////////////////////

    //
    //             INITIALIZATION
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Create an new event subscriber object from CommsEvents.ServerToServer.
     * @returns {Object<string, function>}  Map from event strings to arrays of callbacks.
     */
    var buildEventSubscribers = function () {
        var eventStrings = lodash.map(CommsEvents.ServerToServer.keys, function (key) {
            return CommsEvents.ServerToServer[key];
        });

        var eventSubscribers = {};
        eventStrings.forEach(function (eventString) {
            eventSubscribers[eventString] = [];
        });

        return eventSubscribers;
    };

    //
    //             PRIVATE STATE
    //
    ///////////////////////////////////////////////////////////////////////////

    var io = new Server(config.httpServer, {serveClient: true});
    /**
     * @type {Model}
     */
    var model = config.model;
    /**
     * For server side event subscribers, like the Engine.
     * @type {Object.<string, Function>}
     */
    var eventSubscribers = buildEventSubscribers();

    //
    //             CLIENT EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    io.on('connection', function (clientSocket) {
        Logger.info('New client connected.');

        // create a spectator with the socket in the model
        var client = new Client({
            name: Util.randomUsername(),
            socket: clientSocket
        });

        // NOTE: Do NOT store spectator - model or engine could convert it to a player or remove
        //       the spectator from under the Comms feet.  Query for IDs when necessary.
        model.addSpectator(client);

        clientSocket.on('disconnect', function () {
            Logger.info('Client disconnected');
        });

        clientSocket.on(CommsEvents.ClientToServer.queueToPlay, function () {
            var spectatorID = getPlayerOrSpectator(clientSocket).client.id;
            fireNewPlayerQueued({spectatorID: spectatorID});
        });

        clientSocket.on(CommsEvents.ClientToServer.vote, function () {
            //broadcast to listeners (like the engine).
            //listeners.notify('vote', voteData);
        });

        clientSocket.emit('logLevel', Logger.getLevel());
    });

    //
    //             SERVER TO SERVER EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Fire an event by name with a parameters object.
     * @param {String} eventName
     * @param {Object} params
     */
    var fireEvent = function (eventName, params) {
        if (eventSubscribers[eventName] == null) {
            Logger.warn('Comms#fire called with unrecognized event: ' + eventName);
        }

        eventSubscribers[eventName].forEach(function (callback) {
            callback(params);
        });
    };

    /**
     * Notify subscribers that a spectator has queued to be a player.
     *
     * @param params
     * @param {Number} params.spectatorID
     */
    var fireNewPlayerQueued = function (params) {
        fireEvent(CommsEvents.ServerToServer.newPlayerQueued, params);
    };

    //
    //             UTILITY METHODS AND QUERIES
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * @param socket
     * @returns {Player|Spectator}
     */
    var getPlayerOrSpectator = function (socket) {
        var candidates = model.getPlayers(function (player) {
            return player.client.socket === socket;
        });

        if (candidates.length === 0) {
            candidates = model.getSpectators(function (spectator) {
                return spectator.client.socket === socket;
            });
        }

        if (candidates.length === 0) {
            Logger.error('Comms could not find player or spectator with a socket.');
            return null;
        } else if (candidates.length > 1) {
            Logger.error('Comms found more than one player or spectator with a socket.');
            return null;
        }

        return candidates[0];
    };

    //
    //    ########  ##     ## ########  ##       ####  ######
    //    ##     ## ##     ## ##     ## ##        ##  ##    ##
    //    ##     ## ##     ## ##     ## ##        ##  ##
    //    ########  ##     ## ########  ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##
    //    ##        ##     ## ##     ## ##        ##  ##    ##
    //    ##         #######  ########  ######## ####  ######
    //
    ///////////////////////////////////////////////////////////////////////////

    this.broadcastSnapshot = function (snapshot) {
        Logger.debug("Comms broadcasting snapshot.");
        io.sockets.emit('snapshot', snapshot);
    };

    //
    //             EVENTS
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Register a callback with an event.
     * @param {String} eventName
     * @param {Function} callback
     */
    this.on = function (eventName, callback) {
        if (eventSubscribers[eventName] == null) {
            Logger.warn('Comms#on called with unrecognized event: ' + eventName);
        }

        eventSubscribers[eventName].push(callback);
    };

};

module.exports = Comms;<|endoftext|>


<commit_before>//
// This is the main Episode class for OPAL.
//
angular.module('opal.services')
    .factory('Episode', function($http, $q, $rootScope, Item) {
        Episode = function(resource, schema) {

	        var episode = this;
	        var column, field, attrs;

            // We would like everything for which we have data that is a field to
            // be an instantiated instance of Item - not just those fields in the
            // currently applicable schema.
            _.each($rootScope.fields, function(field){
                if(resource[field.name]){
                    resource[field.name] = _.map(
                        resource[field.name],
                        function(attrs){ return new Item(attrs, episode, field); });
                    if(field.sort){
                        resource[field.name] = _.sortBy(resource[field.name], field.sort).reverse();
                    }
                }else{ resource[field.name] = []; }
            });

            // Sort a particular column according to schema params.
            this.sortColumn = function(columnName, sortBy){
                episode[columnName] = _.sortBy(episode[columnName], sortBy).reverse();
            }

            //
            // TODO - Pull these from the schema?
            // Note - these are date fields on the episode itself - which is not currently
            // serialised and sent with the schema !
            var date_fields = ['date_of_admission', 'discharge_date'];

            // Constructor to update from attrs and parse datish fields
            this.initialise = function(attrs){
                angular.extend(episode, attrs)
                // Convert string-serialised dates into native JavaScriptz
                _.each(date_fields, function(field){
                    if(attrs[field]){
                        var parsed = moment(attrs[field], 'YYYY-MM-DD');
                        episode[field] = parsed._d;
                    }
                });
            }

	        this.getNumberOfItems = function(columnName) {
	            return episode[columnName].length;
	        };

            // Getter function to return active episode tags.
            // Default implementation just hits location.
            this.getTags = function(){
                if(this.tagging[0].makeCopy){
                    var tags =  this.tagging[0].makeCopy()
                }else{
                    var tags = this.tagging[0]
                }
                delete tags.id
                return _.filter(_.keys(tags),  function(t){return tags[t]})
            };

            this.hasTag = function(tag){
                return this.getTags().indexOf(tag) != -1;
            }

	        this.newItem = function(columnName, opts) {
                var column;

                if(!opts){
                    opts = {};
                }
                if(!opts.schema){
                    opts.schema = schema;
                }
                if(!opts.column){
                    opts.column = $rootScope.fields[columnName];
                }

	            var attrs = {};
                //
	            // TODO: don't hardcode this
                //
                // TODO: For serious, this is a bad place for these to go.
                //
	            if (columnName == 'microbiology_test') {
		            attrs.date_ordered = moment().format('YYYY-MM-DD');
	            }
	            if (columnName == 'general_note') {
		            attrs.date = moment().format('YYYY-MM-DD');
	            }
	            if (columnName == 'diagnosis') {
		            attrs.date_of_diagnosis = moment().format('YYYY-MM-DD');
	            }
                if (columnName == 'microbiology_input'){
                    attrs.initials = window.initials;
		            attrs.date = moment().format('YYYY-MM-DD');
                }
                if (columnName == 'observation'){
                    attrs.datetime = new Date();
                }
                if (columnName == 'line'){
                    attrs.insertion_date = moment().format('YYYY-MM-DD');
                }
                if (columnName == 'opat_review'){
                    attrs.initials = window.initials;
                    attrs.datetime = new Date();
                }
                if (columnName == 'opat_line_assessment'){
                    attrs.assessment_date = moment().format('YYYY-MM-DD');
                }
	            return new Item(attrs, episode, opts.column);
	        };

	        this.getItem = function(columnName, iix) {
	            return episode[columnName][iix];
	        };

            //
            // add an item (e.g. instance of a subfield) to this episode
            //
	        this.addItem = function(item) {
                // Sometimes we add an item from a non-active schema.
                if(!episode[item.columnName]){
                    episode[item.columnName] = [];
                }
	            episode[item.columnName].push(item);
                if(item.sort){
                    this.sortColumn(item.columnName, item.sort);
                }
	        };

	        this.removeItem = function(item) {
	            var items = episode[item.columnName];
	            for (iix = 0; iix < items.length; iix++) {
		            if (item.id == items[iix].id) {
		                items.splice(iix, 1);
		                break;
		            };
	            };
	        };

            this.makeCopy = function(){
                var copy = {
                    id               : episode.id,
                    category         : episode.category,
                    date_of_admission: episode.date_of_admission,
                    discharge_date   : episode.discharge_date,
                    consistency_token: episode.consistency_token
                }
                return copy
            };

	        this.compare = function(other) {
	            var v1, v2;
	            var comparators = [
		            function(p) { return CATEGORIES.indexOf(p.location[0].category) },
		            function(p) { return p.location[0].hospital },
		            function(p) {
		                if (p.location[0].hospital == 'UC4H' &&
                            p.location[0].ward.match(/^T\d+/)) {
			                return parseInt(p.location[0].ward.substring(1));
		                } else {
			                return p.location[0].ward
		                }
		            },
		            function(p) { return parseInt(p.location[0].bed) }
	            ];

	            for (var ix = 0; ix < comparators.length; ix++) {
		            v1 = comparators[ix](episode);
		            v2 = comparators[ix](other);
		            if (v1 < v2) {
		                return -1;
		            } else if (v1 > v2) {
		                return 1;
		            }
	            }

	            return 0;
	        };

            //
            //  Save our Episode.
            //
            //  1. Convert datey values to server-style
            //  2. Send our data to the server
            //  3. Handle the response.
            //
            this.save = function(attrs){
                var value;
                var deferred = $q.defer();
                var url = '/episode/' + attrs.id + '/';
                method = 'put'

                _.each(date_fields, function(field){
                    if(attrs[field]){
                        if(angular.isString(attrs[field])){
                            value = moment(attrs[field], 'DD/MM/YYYY')
                        }else{
                            value = moment(attrs[field])
                        }
                        attrs[field] = value.format('YYYY-MM-DD');
                    }
                });

                $http[method](url, attrs).then(
                    function(response){
                        episode.initialise(response.data);
		                deferred.resolve();
                    },
                    function(response) {
		                // TODO handle error better
		                if (response.status == 409) {
			                alert('Item could not be saved because somebody else has \
recently changed it - refresh the page and try again');
		                } else {
			                alert('Item could not be saved');
		                };
		            }
                );

                return deferred.promise;
            };

            //
            // Predicate to determine whether this episode is discharged or not
            //
            this.isDischarged = function(){
                return episode.location[0].category == 'Discharged' ||
                    moment(episode.discharge_date).isBefore(moment());
            }

            this.initialise(resource)
        };

        //
        // takes two arguments, the hospital number and a hash of callbacks.
        //
        // There are three cases for which we proceed:
        //
        // 1. A new patient
        // 2. An existing patient
        // 3. Failure
        //
        // These should be expressed as { newPatient: ..., newForPatient: ..., error: ... }
        //
        Episode.findByHospitalNumber = function(number, callbacks){
            var deferred = $q.defer();
            var result = {
				patients: [],
				hospitalNumber: number
			};

            deferred.promise.then(function(result){
                if(result.patients.length == 0){
                    callbacks.newPatient(result);
                }else if(result.patients.length == 1){
                    callbacks.newForPatient(result.patients[0])
                }else{
                    callbacks.error();
                }
            })

            if(number){
			    // The user entered a hospital number
			    $http.get('/patient/?hospital_number=' + number)
                    .success(function(response) {
					    // We have retrieved patient records matching the hospital number
					    result.patients = response;
                        deferred.resolve(result);
				    });
            }else{
                deferred.resolve(result);
            }

        }
        return Episode
    });
<commit_msg>JS Episodes haven't required a schema argument for quite some time.<commit_after>//
// This is the main Episode class for OPAL.
//
angular.module('opal.services')
    .factory('Episode', function($http, $q, $rootScope, Item) {
        Episode = function(resource) {

	        var episode = this;
	        var column, field, attrs;

            // We would like everything for which we have data that is a field to
            // be an instantiated instance of Item - not just those fields in the
            // currently applicable schema.
            _.each($rootScope.fields, function(field){
                if(resource[field.name]){
                    resource[field.name] = _.map(
                        resource[field.name],
                        function(attrs){ return new Item(attrs, episode, field); });
                    if(field.sort){
                        resource[field.name] = _.sortBy(resource[field.name], field.sort).reverse();
                    }
                }else{ resource[field.name] = []; }
            });

            // Sort a particular column according to schema params.
            this.sortColumn = function(columnName, sortBy){
                episode[columnName] = _.sortBy(episode[columnName], sortBy).reverse();
            }

            //
            // TODO - Pull these from the schema?
            // Note - these are date fields on the episode itself - which is not currently
            // serialised and sent with the schema !
            var date_fields = ['date_of_admission', 'discharge_date'];

            // Constructor to update from attrs and parse datish fields
            this.initialise = function(attrs){
                angular.extend(episode, attrs)
                // Convert string-serialised dates into native JavaScriptz
                _.each(date_fields, function(field){
                    if(attrs[field]){
                        var parsed = moment(attrs[field], 'YYYY-MM-DD');
                        episode[field] = parsed._d;
                    }
                });
            }

	        this.getNumberOfItems = function(columnName) {
	            return episode[columnName].length;
	        };

            // Getter function to return active episode tags.
            // Default implementation just hits location.
            this.getTags = function(){
                if(this.tagging[0].makeCopy){
                    var tags =  this.tagging[0].makeCopy()
                }else{
                    var tags = this.tagging[0]
                }
                delete tags.id
                return _.filter(_.keys(tags),  function(t){return tags[t]})
            };

            this.hasTag = function(tag){
                return this.getTags().indexOf(tag) != -1;
            }

	        this.newItem = function(columnName, opts) {
                var column;

                if(!opts){
                    opts = {};
                }
                if(!opts.schema){
                    opts.schema = schema;
                }
                if(!opts.column){
                    opts.column = $rootScope.fields[columnName];
                }

	            var attrs = {};
                //
	            // TODO: don't hardcode this
                //
                // TODO: For serious, this is a bad place for these to go.
                //
	            if (columnName == 'microbiology_test') {
		            attrs.date_ordered = moment().format('YYYY-MM-DD');
	            }
	            if (columnName == 'general_note') {
		            attrs.date = moment().format('YYYY-MM-DD');
	            }
	            if (columnName == 'diagnosis') {
		            attrs.date_of_diagnosis = moment().format('YYYY-MM-DD');
	            }
                if (columnName == 'microbiology_input'){
                    attrs.initials = window.initials;
		            attrs.date = moment().format('YYYY-MM-DD');
                }
                if (columnName == 'observation'){
                    attrs.datetime = new Date();
                }
                if (columnName == 'line'){
                    attrs.insertion_date = moment().format('YYYY-MM-DD');
                }
                if (columnName == 'opat_review'){
                    attrs.initials = window.initials;
                    attrs.datetime = new Date();
                }
                if (columnName == 'opat_line_assessment'){
                    attrs.assessment_date = moment().format('YYYY-MM-DD');
                }
	            return new Item(attrs, episode, opts.column);
	        };

	        this.getItem = function(columnName, iix) {
	            return episode[columnName][iix];
	        };

            //
            // add an item (e.g. instance of a subfield) to this episode
            //
	        this.addItem = function(item) {
                // Sometimes we add an item from a non-active schema.
                if(!episode[item.columnName]){
                    episode[item.columnName] = [];
                }
	            episode[item.columnName].push(item);
                if(item.sort){
                    this.sortColumn(item.columnName, item.sort);
                }
	        };

	        this.removeItem = function(item) {
	            var items = episode[item.columnName];
	            for (iix = 0; iix < items.length; iix++) {
		            if (item.id == items[iix].id) {
		                items.splice(iix, 1);
		                break;
		            };
	            };
	        };

            this.makeCopy = function(){
                var copy = {
                    id               : episode.id,
                    category         : episode.category,
                    date_of_admission: episode.date_of_admission,
                    discharge_date   : episode.discharge_date,
                    consistency_token: episode.consistency_token
                }
                return copy
            };

	        this.compare = function(other) {
	            var v1, v2;
	            var comparators = [
		            function(p) { return CATEGORIES.indexOf(p.location[0].category) },
		            function(p) { return p.location[0].hospital },
		            function(p) {
		                if (p.location[0].hospital == 'UC4H' &&
                            p.location[0].ward.match(/^T\d+/)) {
			                return parseInt(p.location[0].ward.substring(1));
		                } else {
			                return p.location[0].ward
		                }
		            },
		            function(p) { return parseInt(p.location[0].bed) }
	            ];

	            for (var ix = 0; ix < comparators.length; ix++) {
		            v1 = comparators[ix](episode);
		            v2 = comparators[ix](other);
		            if (v1 < v2) {
		                return -1;
		            } else if (v1 > v2) {
		                return 1;
		            }
	            }

	            return 0;
	        };

            //
            //  Save our Episode.
            //
            //  1. Convert datey values to server-style
            //  2. Send our data to the server
            //  3. Handle the response.
            //
            this.save = function(attrs){
                var value;
                var deferred = $q.defer();
                var url = '/episode/' + attrs.id + '/';
                method = 'put'

                _.each(date_fields, function(field){
                    if(attrs[field]){
                        if(angular.isString(attrs[field])){
                            value = moment(attrs[field], 'DD/MM/YYYY')
                        }else{
                            value = moment(attrs[field])
                        }
                        attrs[field] = value.format('YYYY-MM-DD');
                    }
                });

                $http[method](url, attrs).then(
                    function(response){
                        episode.initialise(response.data);
		                deferred.resolve();
                    },
                    function(response) {
		                // TODO handle error better
		                if (response.status == 409) {
			                alert('Item could not be saved because somebody else has \
recently changed it - refresh the page and try again');
		                } else {
			                alert('Item could not be saved');
		                };
		            }
                );

                return deferred.promise;
            };

            //
            // Predicate to determine whether this episode is discharged or not
            //
            this.isDischarged = function(){
                return episode.location[0].category == 'Discharged' ||
                    moment(episode.discharge_date).isBefore(moment());
            }

            this.initialise(resource)
        };

        //
        // takes two arguments, the hospital number and a hash of callbacks.
        //
        // There are three cases for which we proceed:
        //
        // 1. A new patient
        // 2. An existing patient
        // 3. Failure
        //
        // These should be expressed as { newPatient: ..., newForPatient: ..., error: ... }
        //
        Episode.findByHospitalNumber = function(number, callbacks){
            var deferred = $q.defer();
            var result = {
				patients: [],
				hospitalNumber: number
			};

            deferred.promise.then(function(result){
                if(result.patients.length == 0){
                    callbacks.newPatient(result);
                }else if(result.patients.length == 1){
                    callbacks.newForPatient(result.patients[0])
                }else{
                    callbacks.error();
                }
            })

            if(number){
			    // The user entered a hospital number
			    $http.get('/patient/?hospital_number=' + number)
                    .success(function(response) {
					    // We have retrieved patient records matching the hospital number
					    result.patients = response;
                        deferred.resolve(result);
				    });
            }else{
                deferred.resolve(result);
            }

        }
        return Episode
    });
<|endoftext|>


<commit_before>'use strict';

/**
 * @class Frame
 * @constructor
 */
var Frame = function () {
    this.rolls = [];
    this.score = 0;
    this.current = false;
    this.addNextScore = 0;
    this.nextFrame = null;
};

Frame.prototype = {

    /**
     * Returns the score
     * @method getScore
     * @returns {Number}
     */
    getScore: function () {
        return this.score;
    },

    /**
     * Set flag for whether this is the current frame of the game
     * @method setCurrentFrame
     * @param {Boolean} flag
     */
    setCurrentFrame: function (flag) {
        this.current = flag;
    },

    /**
     * Returns true if frame is the current frame of the game
     * @method isCurrentFrame
     * @returns {Boolean}
     */
    isCurrentFrame: function () {
        return this.current;
    },

    /**
     * Return the next frame in the list
     * @method getNextFrame
     * @returns {Frame|null}
     */
    getNextFrame: function () {
        return this.nextFrame;
    },

    /**
     * Set pointer to the next frame in the list
     * @method setNextFrame
     */
    setNextFrame: function (frame) {
        this.nextFrame = frame;
    },

    /**
     * Return whether this is the last frame in the game
     * @method isLastFrame
     * @returns {Boolean}
     */
    isLastFrame: function () {
        return this.nextFrame === null;
    },

    /**
     * Return whether a strike was rolled in this frame
     * @method isStrike
     * @returns {Boolean}
     */
    isStrike: function () {
        return this.rolls.length >= 1 && this.rolls[0] === 10;
    },

    /**
     * Return whether a spare was rolled in this frame
     * @method isSpare
     * @returns {Boolean}
     */
    isSpare: function () {
        return this.rolls.length >= 2 && (this.rolls[0] + this.rolls[1]) === 10;
    },

    /**
     * Returns true if there are no more rolls left in this frame
     * @method isFinished
     * @returns {Boolean}
     */
    isFinished: function () {
        // @todo: Logic for allowing last frame to have 3 rolls under certain conditions
        return this.isStrike() || this.rolls.length === 2;
    },

    /**
     * Move active state from this frame to the next in the list
     * @method enterNextFrame
     */
    enterNextFrame: function () {
        this.setCurrentFrame(false);
        this.getNextFrame().setCurrentFrame(true);
    },

    /**
     * Pass a roll to (or through) the frame.
     * If this is the current frame of the game, the score is added.
     * If this is not the current frame of the game, the roll is passed to the next frame
     * in the list. The roll will be added to the score of this frame if a strike or spare
     * was previously rolled in this frame and this is a relevant subsequent roll to
     * that strike/spare.
     * @method roll
     * @param {Number} number
     * @returns {Number}
     */
    roll: function (number) {
        // Are we the current frame of the game?
        if (!this.isCurrentFrame()) {
            // Are we adding subsequent rolls to our score? (eg. we are a strike / spare)
            if (this.addNextScore > 0) {
                this.addNextScore--;
                this.score += number;
            }

            // Pass roll to next frame
            return this.getNextFrame().roll(number);
        }

        // Add score
        this.score += number;

        // Add roll
        this.rolls.push(number);

        // Is this frame finished?
        if (this.isFinished()) {
            // Are we a strike or a spare? If so, we need to listen to the next subsequent
            // rolls (2 or 1 respectively)
            this.addNextScore = this.isStrike() ? 2 : this.isSpare() ? 1 : 0;

            // Are we at the last frame?
            if (!this.isLastFrame()) {
                // Move game to next frame
                this.enterNextFrame();
            } else {
                // Returning zero signifies the game is finished
                return 0;
            }
        }

        // Return the number of available pins left in this frame (used for next roll)
        // If a strike was bowled, the available pins are 10 as they belong to the next, fresh frame
        return this.isStrike() ? 10 : (10 - this.getScore());
    }
};

// Export Frame module
module.exports = Frame;
<commit_msg>Return 10 available pins if frame has just finished<commit_after>'use strict';

/**
 * @class Frame
 * @constructor
 */
var Frame = function () {
    this.rolls = [];
    this.score = 0;
    this.current = false;
    this.addNextScore = 0;
    this.nextFrame = null;
};

Frame.prototype = {

    /**
     * Returns the score
     * @method getScore
     * @returns {Number}
     */
    getScore: function () {
        return this.score;
    },

    /**
     * Set flag for whether this is the current frame of the game
     * @method setCurrentFrame
     * @param {Boolean} flag
     */
    setCurrentFrame: function (flag) {
        this.current = flag;
    },

    /**
     * Returns true if frame is the current frame of the game
     * @method isCurrentFrame
     * @returns {Boolean}
     */
    isCurrentFrame: function () {
        return this.current;
    },

    /**
     * Return the next frame in the list
     * @method getNextFrame
     * @returns {Frame|null}
     */
    getNextFrame: function () {
        return this.nextFrame;
    },

    /**
     * Set pointer to the next frame in the list
     * @method setNextFrame
     */
    setNextFrame: function (frame) {
        this.nextFrame = frame;
    },

    /**
     * Return whether this is the last frame in the game
     * @method isLastFrame
     * @returns {Boolean}
     */
    isLastFrame: function () {
        return this.nextFrame === null;
    },

    /**
     * Return whether a strike was rolled in this frame
     * @method isStrike
     * @returns {Boolean}
     */
    isStrike: function () {
        return this.rolls.length >= 1 && this.rolls[0] === 10;
    },

    /**
     * Return whether a spare was rolled in this frame
     * @method isSpare
     * @returns {Boolean}
     */
    isSpare: function () {
        return this.rolls.length >= 2 && (this.rolls[0] + this.rolls[1]) === 10;
    },

    /**
     * Returns true if there are no more rolls left in this frame
     * @method isFinished
     * @returns {Boolean}
     */
    isFinished: function () {
        // @todo: Logic for allowing last frame to have 3 rolls under certain conditions
        return this.isStrike() || this.rolls.length === 2;
    },

    /**
     * Move active state from this frame to the next in the list
     * @method enterNextFrame
     */
    enterNextFrame: function () {
        this.setCurrentFrame(false);
        this.getNextFrame().setCurrentFrame(true);
    },

    /**
     * Pass a roll to (or through) the frame.
     * If this is the current frame of the game, the score is added.
     * If this is not the current frame of the game, the roll is passed to the next frame
     * in the list. The roll will be added to the score of this frame if a strike or spare
     * was previously rolled in this frame and this is a relevant subsequent roll to
     * that strike/spare.
     * @method roll
     * @param {Number} number
     * @returns {Number}
     */
    roll: function (number) {
        // Are we the current frame of the game?
        if (!this.isCurrentFrame()) {
            // Are we adding subsequent rolls to our score? (eg. we are a strike / spare)
            if (this.addNextScore > 0) {
                this.addNextScore--;
                this.score += number;
            }

            // Pass roll to next frame
            return this.getNextFrame().roll(number);
        }

        // Add score
        this.score += number;

        // Add roll
        this.rolls.push(number);

        // Is this frame finished?
        if (this.isFinished()) {
            // Are we a strike or a spare? If so, we need to listen to the next subsequent
            // rolls (2 or 1 respectively)
            this.addNextScore = this.isStrike() ? 2 : this.isSpare() ? 1 : 0;

            // Are we at the last frame?
            if (!this.isLastFrame()) {
                // Move game to next frame
                this.enterNextFrame();
            } else {
                // Returning zero signifies the game is finished
                return 0;
            }
        }

        // Return the number of available pins left in this frame (used for next roll)
        // If a strike was bowled, the available pins are 10 as they belong to the next, fresh frame
        return this.isStrike() || this.isFinished() ? 10 : (10 - this.getScore());
    }
};

// Export Frame module
module.exports = Frame;
<|endoftext|>