<commit_before>// make sure we have otp.config and otp.config.locale defined
if(typeof(otp) == "undefined" || otp == null) otp = {};
if(typeof(otp.config) == "undefined" || otp.config == null) otp.config = {};
if(typeof(otp.locale) == "undefined" || otp.locale == null) otp.locale = {};
if(typeof(otp.locale.English) == "undefined" || otp.locale.English == null) otp.locale.English = {};

otp_consts = {
    /**
     * The OTP web service locations
     */
    hostname       : "",
    restService    : "otp_test",
    //solrService    : "http://maps.trimet.org/solr/select",
    solrService    : "http://localhost:45454/solr/select",
    center         : new L.LatLng(45.494833,-122.670376),
    maxWalk        : 804.672, // 1/2 mile walk
    //maxWalk        : 1207.008, // 3/4 mile walk
    //maxWalk        : 1609.344, // 1 mile walk
    attribution    : 'Map data © 2018 Oregon Metro and OpenStreetMap and contributors.'
};

otp.config = {

    debug: false,
    debug_layers: false,
    locale: otp.locale.English,

    //All avalible locales
    //key is translation name. Must be the same as po file or .json file
    //value is name of settings file for localization in locale subfolder
    //File should be loaded in index.html
    locales : {
        'en': otp.locale.English,
        'de': otp.locale.German,
        'pl': otp.locale.Polish,
        'sl': otp.locale.Slovenian,
        'fr': otp.locale.French,
        'it': otp.locale.Italian,
        'ca_ES': otp.locale.Catalan
    },

    languageChooser : function() {
        var active_locales = _.values(otp.config.locales);
        var str = "
";
        var localesLength = active_locales.length;
        var param_name = i18n.options.detectLngQS;
        for (var i = 0; i < localesLength; i++) {
            var current_locale = active_locales[i];
            var url_param = {};
            url_param[param_name] = current_locale.config.locale_short;
            str += '
' + current_locale.config.name + ' (' + current_locale.config.locale_short + ')
';
        }
        str += "
";
        return str;
    },

    /**
     * The OTP web service locations
     */
    hostname       : otp_consts.hostname,
    restService    : otp_consts.restService,
    datastoreUrl   : otp_consts.datastoreUrl,

    /**
     * Base layers: the base map tile layers available for use by all modules.
     * Expressed as an array of objects, where each object has the following
     * fields:
     *   - name: <string> a unique name for this layer, used for both display
     *       and internal reference purposes
     *   - tileUrl: <string> the map tile service address (typically of the
     *       format 'http://{s}.yourdomain.com/.../{z}/{x}/{y}.png')
     *   - attribution: <string> the attribution text for the map tile data
     *   - [subdomains]: <array of strings> a list of tileUrl subdomains, if
     *       applicable
     *
     */
    baseLayers: [
        {
            name: 'TriMet Map',
            tileUrl: 'http://{s}.trimet.org/tilecache/tilecache.py/1.0.0/currentOSM/{z}/{x}/{y}',
            subdomains : ["tilea","tileb","tilec","tiled"],
            attribution : otp_consts.attribution
        },
        {
            name: 'TriMet Aerials',
            tileUrl: 'http://{s}.trimet.org/tilecache/tilecache.py/1.0.0/hybridOSM/{z}/{x}/{y}',
            subdomains : ["tilea","tileb","tilec","tiled"],
            attribution : otp_consts.attribution
        },
        {
            name: 'OSM Tiles',
            tileUrl: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            subdomains : ['a','b'],
            attribution : 'Map data © OpenStreetMap contributors'
        }
    ],

    /**
     * Map start location and zoom settings: by default, the client uses the
     * OTP routerInfo API call to center and zoom the map. The following
     * properties, when set, override that behavioir.
     */
    initLatLng : otp_consts.center,
    initZoom : 11,
    minZoom : 10,
    maxZoom : 22,
    zoomToFitResults    : true,

    /**
     * Site name / description / branding display options
     */
    siteName            : "TEST Multi Agency OTP",
    siteDescription     : "TEST Multi Agency OTP",
    logoGraphic         : 'images/agency_logo.png',
    agencyStopLinkText  : "Real Time Arrivals",
    fareDisplayOverride : "$2.50 (A), $1.25 (H), $1.25 (Y)",
    bikeshareName       : "BIKETOWN",

    showLogo            : true,
    showTitle           : true,
    showModuleSelector  : true,
    showWheelchairOption: false,
    infoWidgets         : [],     // turns off about and contact top-nav items

    metric              : false,

    /**
     * Modules: a list of the client modules to be loaded at startup. Expressed
     * as an array of objects, where each object has the following fields:
     *   - id: <string> a unique identifier for this module
     *   - className: <string> the name of the main class for this module; class
     *       must extend otp.modules.Module
     *   - [defaultBaseLayer] : <string> the name of the map tile base layer to
     *       used by default for this module
     *   - [isDefault]: <boolean> whether this module is shown by default;
     *       should only be 'true' for one module
     *
     * @see: http://trimet.dev.conveyal.com/js/otp/config.js
     */
    modules : [
        {
            id : 'planner',
            defaultBaseLayer : 'TriMet Map',
            isDefault: true,
            className : 'otp.modules.multimodal.MultimodalPlannerModule'
        },
        {
            id : 'analyst',
            defaultBaseLayer : 'TriMet Map',
            className : 'otp.modules.analyst.AnalystModule'
        }
    ],

    /**
     * Geocoders: a list of supported geocoding services available for use in
     * address resolution. Expressed as an array of objects, where each object
     * has the following fields:
     *   - name: <string> the name of the service to be displayed to the user
     *   - className: <string> the name of the class that implements this service
     *   - url: <string> the location of the service's API endpoint
     *   - addressParam: <string> the name of the API parameter used to pass in
     *       the user-specifed address string
     */
    geocoders : [
        {
            name : 'SOLR',
            className    : 'otp.core.SOLRGeocoder',
            url          : otp_consts.solrService,
            addressParam : 'q'
        }
    ],

    //This is shown if showLanguageChooser is true
    infoWidgetLangChooser : {
        title: '',
        languages: true
    },

    /**
     * Support for the "AddThis" display for sharing to social media sites, etc.
     */
    showAddThis     : false,
    //addThisPubId    : 'your-addthis-id',
    //addThisTitle    : 'Your title for AddThis sharing messages',

    /**
     * Formats to use for date and time displays, expressed as ISO-8601 strings.
     */
    timeFormat  : "h:mma",
    dateFormat  : "MMM Do YYYY"
};
var options = {
    resGetPath: 'js/otp/locale/__lng__.json',
    fallbackLng: 'en',
    nsseparator: ';;', //Fixes problem when : is in translation text
    keyseparator: '_|_',
    preload: ['en'],
    //TODO: Language choosing works only with this disabled
    /*lng: otp.config.locale_short,*/
    /*postProcess: 'add_nekaj', //Adds | around every string that is translated*/
    /*shortcutFunction: 'sprintf',*/
    /*postProcess: 'sprintf',*/
    debug: true,
    getAsync: false, //TODO: make async
    fallbackOnEmpty: true
};
var _tr = null; //key
var ngettext = null; // singular, plural, value
var pgettext = null; // context, key
var npgettext = null; // context, singular, plural, value

i18n.addPostProcessor('add_nekaj', function(val, key, opts) {
    return "|"+val+"|";
});

i18n.init(options, function(t) {
    //Sets locale and metric based on currently selected/detected language
    if (i18n.lng() in otp.config.locales) {
        otp.config.locale = otp.config.locales[i18n.lng()];
        otp.config.metric = otp.config.locale.config.metric;
        //Conditionally load datepicker-lang.js?
    }

    //Use infoWidgets from locale
    //Default locale is English which has infoWidgets
    if ("infoWidgets" in otp.config.locale) {
        otp.config.infoWidgets=otp.config.locale.infoWidgets;
    } else {
        otp.config.infoWidgets=otp.locale.English.infoWidgets;
    }

    if (otp.config.showLanguageChooser) {
        otp.config.infoWidgets.push(otp.config.infoWidgetLangChooser);
    }
    //Accepts Key, value or key, value1 ... valuen
    //Key is string to be translated
    //Value is used for sprintf parameter values
    //http://www.diveintojavascript.com/projects/javascript-sprintf
    //Value is optional and can be one parameter as javascript object if key
    //has named parameters
    //Or can be multiple parameters if used as positional sprintf parameters
    _tr = function() {
        var arg_length = arguments.length;
        //Only key
        if (arg_length == 1) {
            key = arguments[0];
            return t(key);
        //key with sprintf values
        } else if (arg_length > 1) {
            key = arguments[0];
            values = [];
            for(var i = 1; i < arg_length; i++) {
                values.push(arguments[i]);
            }
            return t(key, {postProcess: 'sprintf', sprintf: values});
        } else {
            console.error("_tr function doesn't have an argument");
            return "";
        }
    };
    ngettext = function(singular, plural, value) {
        return t(singular, {count: value, postProcess: 'sprintf', sprintf: [value]});
    };
    pgettext = function(context, key) {
        return t(key, {context: context});
    };
    npgettext = function(context, singular, plural, value) {
        return t(singular, {context: context,
                 count: value,
                 postProcess: 'sprintf',
                 sprintf: [value]});
    };

});

otp.config.modes = {
    "TRANSIT,WALK"              : _tr("Transit"),
    "BUS,WALK"                  : _tr("Bus Only"),
    "TRAM,RAIL,GONDOLA,WALK"    : _tr("Rail Only"),
    "BICYCLE"                   : _tr('Bicycle Only'),
    "TRANSIT,BICYCLE"           : _tr("Bicycle & Transit"),
    //"AIRPLANE,WALK"           : _tr("Airplane Only"),
    "CAR_PARK,WALK,TRANSIT"     : _tr('Park and Ride'),
    "CAR,WALK,TRANSIT"          : _tr('Kiss and Ride'),
    "BICYCLE_PARK,WALK,TRANSIT" : _tr('Bike and Ride'),
    // uncomment only if bike rental exists in a map
    // TODO: remove this hack, and provide code that allows the mode array to be configured with different transit modes.
    //'WALK,BICYCLE_RENT'       :_tr('Rented Bicycle'),
    'TRANSIT,WALK,BICYCLE_RENT' : _tr('Transit & Rented Bicycle'),
    "CAR"                       : _tr('Drive Only'),
    "WALK"                      : _tr('Walk Only')
};
<commit_msg>add solrwrap proxy path on maps6<commit_after>// make sure we have otp.config and otp.config.locale defined
if(typeof(otp) == "undefined" || otp == null) otp = {};
if(typeof(otp.config) == "undefined" || otp.config == null) otp.config = {};
if(typeof(otp.locale) == "undefined" || otp.locale == null) otp.locale = {};
if(typeof(otp.locale.English) == "undefined" || otp.locale.English == null) otp.locale.English = {};

otp_consts = {
    /**
     * The OTP web service locations
     */
    hostname       : "",
    restService    : "otp_test",
    solrService    : "http://maps.trimet.org/solr/select",
    solrService    : "http://localhost:45454/solr/select",
    solrService    : "http://maps6.trimet.org/solrwrap/solr/select",
    center         : new L.LatLng(45.494833,-122.670376),
    maxWalk        : 804.672, // 1/2 mile walk
    //maxWalk        : 1207.008, // 3/4 mile walk
    //maxWalk        : 1609.344, // 1 mile walk
    attribution    : 'Map data © 2018 Oregon Metro and OpenStreetMap and contributors.'
};

otp.config = {

    debug: false,
    debug_layers: false,
    locale: otp.locale.English,

    //All avalible locales
    //key is translation name. Must be the same as po file or .json file
    //value is name of settings file for localization in locale subfolder
    //File should be loaded in index.html
    locales : {
        'en': otp.locale.English,
        'de': otp.locale.German,
        'pl': otp.locale.Polish,
        'sl': otp.locale.Slovenian,
        'fr': otp.locale.French,
        'it': otp.locale.Italian,
        'ca_ES': otp.locale.Catalan
    },

    languageChooser : function() {
        var active_locales = _.values(otp.config.locales);
        var str = "
";
        var localesLength = active_locales.length;
        var param_name = i18n.options.detectLngQS;
        for (var i = 0; i < localesLength; i++) {
            var current_locale = active_locales[i];
            var url_param = {};
            url_param[param_name] = current_locale.config.locale_short;
            str += '
' + current_locale.config.name + ' (' + current_locale.config.locale_short + ')
';
        }
        str += "
";
        return str;
    },

    /**
     * The OTP web service locations
     */
    hostname       : otp_consts.hostname,
    restService    : otp_consts.restService,
    datastoreUrl   : otp_consts.datastoreUrl,

    /**
     * Base layers: the base map tile layers available for use by all modules.
     * Expressed as an array of objects, where each object has the following
     * fields:
     *   - name: <string> a unique name for this layer, used for both display
     *       and internal reference purposes
     *   - tileUrl: <string> the map tile service address (typically of the
     *       format 'http://{s}.yourdomain.com/.../{z}/{x}/{y}.png')
     *   - attribution: <string> the attribution text for the map tile data
     *   - [subdomains]: <array of strings> a list of tileUrl subdomains, if
     *       applicable
     *
     */
    baseLayers: [
        {
            name: 'TriMet Map',
            tileUrl: 'http://{s}.trimet.org/tilecache/tilecache.py/1.0.0/currentOSM/{z}/{x}/{y}',
            subdomains : ["tilea","tileb","tilec","tiled"],
            attribution : otp_consts.attribution
        },
        {
            name: 'TriMet Aerials',
            tileUrl: 'http://{s}.trimet.org/tilecache/tilecache.py/1.0.0/hybridOSM/{z}/{x}/{y}',
            subdomains : ["tilea","tileb","tilec","tiled"],
            attribution : otp_consts.attribution
        },
        {
            name: 'OSM Tiles',
            tileUrl: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            subdomains : ['a','b'],
            attribution : 'Map data © OpenStreetMap contributors'
        }
    ],

    /**
     * Map start location and zoom settings: by default, the client uses the
     * OTP routerInfo API call to center and zoom the map. The following
     * properties, when set, override that behavioir.
     */
    initLatLng : otp_consts.center,
    initZoom : 11,
    minZoom : 10,
    maxZoom : 22,
    zoomToFitResults    : true,

    /**
     * Site name / description / branding display options
     */
    siteName            : "TEST Multi Agency OTP",
    siteDescription     : "TEST Multi Agency OTP",
    logoGraphic         : 'images/agency_logo.png',
    agencyStopLinkText  : "Real Time Arrivals",
    fareDisplayOverride : "$2.50 (A), $1.25 (H), $1.25 (Y)",
    bikeshareName       : "BIKETOWN",

    showLogo            : true,
    showTitle           : true,
    showModuleSelector  : true,
    showWheelchairOption: false,
    infoWidgets         : [],     // turns off about and contact top-nav items

    metric              : false,

    /**
     * Modules: a list of the client modules to be loaded at startup. Expressed
     * as an array of objects, where each object has the following fields:
     *   - id: <string> a unique identifier for this module
     *   - className: <string> the name of the main class for this module; class
     *       must extend otp.modules.Module
     *   - [defaultBaseLayer] : <string> the name of the map tile base layer to
     *       used by default for this module
     *   - [isDefault]: <boolean> whether this module is shown by default;
     *       should only be 'true' for one module
     *
     * @see: http://trimet.dev.conveyal.com/js/otp/config.js
     */
    modules : [
        {
            id : 'planner',
            defaultBaseLayer : 'TriMet Map',
            isDefault: true,
            className : 'otp.modules.multimodal.MultimodalPlannerModule'
        },
        {
            id : 'analyst',
            defaultBaseLayer : 'TriMet Map',
            className : 'otp.modules.analyst.AnalystModule'
        }
    ],

    /**
     * Geocoders: a list of supported geocoding services available for use in
     * address resolution. Expressed as an array of objects, where each object
     * has the following fields:
     *   - name: <string> the name of the service to be displayed to the user
     *   - className: <string> the name of the class that implements this service
     *   - url: <string> the location of the service's API endpoint
     *   - addressParam: <string> the name of the API parameter used to pass in
     *       the user-specifed address string
     */
    geocoders : [
        {
            name : 'SOLR',
            className    : 'otp.core.SOLRGeocoder',
            url          : otp_consts.solrService,
            addressParam : 'q'
        }
    ],

    //This is shown if showLanguageChooser is true
    infoWidgetLangChooser : {
        title: '',
        languages: true
    },

    /**
     * Support for the "AddThis" display for sharing to social media sites, etc.
     */
    showAddThis     : false,
    //addThisPubId    : 'your-addthis-id',
    //addThisTitle    : 'Your title for AddThis sharing messages',

    /**
     * Formats to use for date and time displays, expressed as ISO-8601 strings.
     */
    timeFormat  : "h:mma",
    dateFormat  : "MMM Do YYYY"
};
var options = {
    resGetPath: 'js/otp/locale/__lng__.json',
    fallbackLng: 'en',
    nsseparator: ';;', //Fixes problem when : is in translation text
    keyseparator: '_|_',
    preload: ['en'],
    //TODO: Language choosing works only with this disabled
    /*lng: otp.config.locale_short,*/
    /*postProcess: 'add_nekaj', //Adds | around every string that is translated*/
    /*shortcutFunction: 'sprintf',*/
    /*postProcess: 'sprintf',*/
    debug: true,
    getAsync: false, //TODO: make async
    fallbackOnEmpty: true
};
var _tr = null; //key
var ngettext = null; // singular, plural, value
var pgettext = null; // context, key
var npgettext = null; // context, singular, plural, value

i18n.addPostProcessor('add_nekaj', function(val, key, opts) {
    return "|"+val+"|";
});

i18n.init(options, function(t) {
    //Sets locale and metric based on currently selected/detected language
    if (i18n.lng() in otp.config.locales) {
        otp.config.locale = otp.config.locales[i18n.lng()];
        otp.config.metric = otp.config.locale.config.metric;
        //Conditionally load datepicker-lang.js?
    }

    //Use infoWidgets from locale
    //Default locale is English which has infoWidgets
    if ("infoWidgets" in otp.config.locale) {
        otp.config.infoWidgets=otp.config.locale.infoWidgets;
    } else {
        otp.config.infoWidgets=otp.locale.English.infoWidgets;
    }

    if (otp.config.showLanguageChooser) {
        otp.config.infoWidgets.push(otp.config.infoWidgetLangChooser);
    }
    //Accepts Key, value or key, value1 ... valuen
    //Key is string to be translated
    //Value is used for sprintf parameter values
    //http://www.diveintojavascript.com/projects/javascript-sprintf
    //Value is optional and can be one parameter as javascript object if key
    //has named parameters
    //Or can be multiple parameters if used as positional sprintf parameters
    _tr = function() {
        var arg_length = arguments.length;
        //Only key
        if (arg_length == 1) {
            key = arguments[0];
            return t(key);
        //key with sprintf values
        } else if (arg_length > 1) {
            key = arguments[0];
            values = [];
            for(var i = 1; i < arg_length; i++) {
                values.push(arguments[i]);
            }
            return t(key, {postProcess: 'sprintf', sprintf: values});
        } else {
            console.error("_tr function doesn't have an argument");
            return "";
        }
    };
    ngettext = function(singular, plural, value) {
        return t(singular, {count: value, postProcess: 'sprintf', sprintf: [value]});
    };
    pgettext = function(context, key) {
        return t(key, {context: context});
    };
    npgettext = function(context, singular, plural, value) {
        return t(singular, {context: context,
                 count: value,
                 postProcess: 'sprintf',
                 sprintf: [value]});
    };

});

otp.config.modes = {
    "TRANSIT,WALK"              : _tr("Transit"),
    "BUS,WALK"                  : _tr("Bus Only"),
    "TRAM,RAIL,GONDOLA,WALK"    : _tr("Rail Only"),
    "BICYCLE"                   : _tr('Bicycle Only'),
    "TRANSIT,BICYCLE"           : _tr("Bicycle & Transit"),
    //"AIRPLANE,WALK"           : _tr("Airplane Only"),
    "CAR_PARK,WALK,TRANSIT"     : _tr('Park and Ride'),
    "CAR,WALK,TRANSIT"          : _tr('Kiss and Ride'),
    "BICYCLE_PARK,WALK,TRANSIT" : _tr('Bike and Ride'),
    // uncomment only if bike rental exists in a map
    // TODO: remove this hack, and provide code that allows the mode array to be configured with different transit modes.
    //'WALK,BICYCLE_RENT'       :_tr('Rented Bicycle'),
    'TRANSIT,WALK,BICYCLE_RENT' : _tr('Transit & Rented Bicycle'),
    "CAR"                       : _tr('Drive Only'),
    "WALK"                      : _tr('Walk Only')
};
<|endoftext|>


<commit_before>/*jslint browser: true, devel: true, node: true, ass: true, nomen: true, unparam: true, indent: 4 */

/**
 * @license
 * Copyright (c) 2015 The ExpandJS authors. All rights reserved.
 * This code may only be used under the BSD style license found at https://expandjs.github.io/LICENSE.txt
 * The complete set of authors may be found at https://expandjs.github.io/AUTHORS.txt
 * The complete set of contributors may be found at https://expandjs.github.io/CONTRIBUTORS.txt
 */
(function (global) {
    "use strict";

    // Vars
    var http      = require('http'),
        https     = require('https'),
        load      = require('xp-load'),
        XP        = load(require('expandjs'), 'XP'),
        XPEmitter = load(require('xp-emitter'), 'XPEmitter');

    /*********************************************************************/

    /**
     * This class is used to perform XHR requests.
     *
     * @class XPRequest
     * @description This class is used to perform XHR requests.
     * @extends XPEmitter
     */
    module.exports = new XP.Class('XPRequest', {

        // EXTENDS
        extends: XPEmitter,

        // OPTIONS
        options: {
            dataType: 'text',
            headers: null,
            keepAlive: 0,
            method: 'GET',
            url: ''
        },

        /*********************************************************************/

        /**
         * Emitted when a chunk of data is received.
         *
         * @event chunk
         * @param {Buffer | string} chunk
         * @param {Object} emitter
         */

        /**
         * Emitted when the entire data is received.
         *
         * @event data
         * @param {Buffer | string} data
         * @param {Object} emitter
         */

        /**
         * Emitted when an error is received.
         *
         * @event error
         * @param {string} error
         * @param {Object} emitter
         */

        /**
         * Emitted when a response is received.
         *
         * @event response
         * @param {Object} response
         * @param {Object} emitter
         */

        /**
         * Emitted when the request state changes.
         *
         * @event state
         * @param {string} state
         * @param {Object} emitter
         */

        /*********************************************************************/

        /**
         * @constructs
         * @param {Object | string} opt The request url or options.
         *   @param {string} [opt.dataType = "text"] The type of data expected back from the server.
         *   @param {Object} [opt.headers] An object containing request headers.
         *   @param {number} [opt.keepAlive = 0] How often to send TCP KeepAlive packets over sockets being kept alive.
         *   @param {string} [opt.method = "GET"] A string specifying the HTTP request method.
         *   @param {string} opt.url The request url.
         */
        initialize: {
            promise: true,
            value: function (opt, resolver) {

                // Asserting
                XP.assertArgument(XP.isObject(opt) || XP.isString(opt, true), 1, 'Object or string');

                // Vars
                var self = this;

                // Super
                XPEmitter.call(self);

                // Setting
                self.chunks    = [];
                self.options   = XP.isString(opt) ? {} : opt;
                self.dataType  = self.options.dataType;
                self.headers   = self.options.headers || {};
                self.keepAlive = self.options.keepAlive;
                self.method    = self.options.method;
                self.url       = self.options.url;
                self.resolver  = resolver;
                self.state     = 'idle';

                // Adapting
                self.adapt();

                // Listening
                self.adaptee.on('error', self.handleError.bind(self));
                self.adaptee.on('response', self.handleResponse.bind(self));
            }
        },

        /*********************************************************************/

        /**
         * Aborts the request.
         *
         * @method abort
         * @returns {Object}
         */
        abort: function () {

            // Vars
            var self = this;

            // Checking
            if (self.timeAbort) { return self; }

            // Aborting
            self.adaptee.abort();

            // Setting
            self.state     = 'aborted';
            self.timeAbort = Date.now();

            return self;
        },

        /**
         * Sends the request, appending data to the request body.
         *
         * @method send
         * @param {Buffer | string} [data]
         * @returns {Object}
         */
        send: function (data) {

            // Asserting
            XP.assertArgument(XP.isVoid(data) || Buffer.isBuffer(data) || XP.isString(data, true), 1, 'Buffer or string');

            // Vars
            var self = this;

            // Checking
            if (self.timeSend) { return self; }

            // Ending
            self.adaptee.end(data);

            // Setting
            self.state    = 'pending';
            self.timeSend = Date.now();

            return self;
        },

        /*********************************************************************/

        /**
         * Creates the adpatee
         *
         * @method adapt
         * @returns {Object}
         * @private
         */
        adapt: {
            enumerable: false,
            value: function () {

                // Vars
                var parsed   = XP.parseURL(this.url),
                    self     = XP.assign(this, {secure: (parsed.protocol || global.location.protocol) === 'https:'}),
                    port     = self.secure ? 443 : 80,
                    protocol = self.secure ? https : http;

                // Adapting
                self.adaptee = protocol.request({
                    auth: parsed.auth,
                    headers: self.headers,
                    hostname: parsed.hostname || global.location.hostname,
                    keepAlive: self.keepAlive > 0,
                    keepAliveMsecs: Math.max(self.keepAlive, 0),
                    method: self.method,
                    path: parsed.path + (parsed.hash || ''),
                    port: parsed.port || (parsed.hostname && port) || global.location.port
                });

                return self;
            }
        },

        /**
         * Parses the data
         *
         * @method parse
         * @param {Buffer | string} data
         * @returns {*}
         * @private
         */
        parse: {
            enumerable: true,
            value: function (data) {
                var self = this;
                if (self.dataType === 'json') { return JSON.parse(data); }
                return data;
            }
        },

        /*********************************************************************/

        /**
         * TODO DOC
         *
         * @property adaptee
         * @type Object
         * @private
         */
        adaptee: {
            enumerable: false,
            set: function (val) { return this.request || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property chunks
         * @type Array
         * @readonly
         * @private
         */
        chunks: {
            set: function (val) { return this.chunks || val; },
            validate: function (val) { return XP.isArray(val); }
        },

        /**
         * TODO DOC
         *
         * @property data
         * @type Buffer | string
         * @readonly
         */
        data: {
            validate: function (val) { return XP.isString(val) || Buffer.isBuffer(val); }
        },

        /**
         * TODO DOC
         *
         * @property dataType
         * @type string
         * @default "text"
         */
        dataType: {
            set: function (val) { return this.dataType || val; },
            validate: function (val) { return XP.includes(this.dataTypes, val); }
        },

        /**
         * TODO DOC
         *
         * @property dataTypes
         * @type Array
         * @default ["json", "text"]
         * @readonly
         */
        dataTypes: {
            frozen: true,
            writable: false,
            value: ['json', 'text']
        },

        /**
         * TODO DOC
         *
         * @property header
         * @type Object
         */
        headers: {
            set: function (val) { return this.headers || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property keepAlive
         * @type number
         * @default 0
         */
        keepAlive: {
            set: function (val) { return this.keepAlive >= 0 ? this.keepAlive : val; },
            validate: function (val) { return XP.isInt(val, true); }
        },

        /**
         * TODO DOC
         * @property method
         * @type string
         * @default "GET"
         */
        method: {
            set: function (val) { return this.method || XP.upperCase(val); },
            validate: function (val) { return XP.isString(val, true); }
        },

        /**
         * TODO DOC
         * @property response
         * @type Object
         * @readonly
         */
        response: {
            set: function (val) { return this.response || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property secure
         * @type boolean
         * @readonly
         */
        secure: {
            set: function (val) { return XP.isBoolean(this.secure) ? this.secure : !!val; }
        },

        /**
         * TODO DOC
         *
         * @property state
         * @type string
         * @readonly
         */
        state: {
            then: function (post) { this.emit('state', post, this); },
            validate: function (val) { return XP.includes(this.states, val); }
        },

        /**
         * TODO DOC
         *
         * @property states
         * @type Array
         * @default ["aborted", "failed", "idle", "pending", "received", "receiving"]
         * @readonly
         */
        states: {
            frozen: true,
            writable: false,
            value: ['aborted', 'failed', 'idle', 'pending', 'received', 'receiving']
        },

        /**
         * TODO DOC
         *
         * @property timeAbort
         * @type number
         * @readonly
         */
        timeAbort: {
            set: function (val) { return this.timeAbort || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeData
         * @type number
         * @readonly
         */
        timeData: {
            set: function (val) { return this.timeData || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeResponse
         * @type number
         * @readonly
         */
        timeResponse: {
            set: function (val) { return this.timeResponse || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeSend
         * @type number
         * @readonly
         */
        timeSend: {
            set: function (val) { return this.timeSend || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property url
         * @type string
         */
        url: {
            set: function (val) { return this.url || val; },
            validate: function (val) { return XP.isString(val); }
        },

        /*********************************************************************/

        // HANDLER
        handleData: function (chunk) {

            // Vars
            var self = this;

            // Setting
            self.chunks.push(chunk);

            // Emitting
            self.emit('chunk', chunk, self);
        },

        // HANDLER
        handleEnd: function () {

            // Vars
            var self = this;

            // Trying
            try {

                // Setting
                self.data     = self.parse(Buffer.isBuffer(self.chunks[0]) ? Buffer.concat(self.chunks) : self.chunks.join(''));
                self.state    = 'received';
                self.timeData = Date.now();

            } catch (exc) {

                // Handling
                return self.handleError(exc.message);
            }

            // Resolving
            self.resolver(null, self.data);

            // Emitting
            self.emit('data', self.data, self);
        },

        // HANDLER
        handleError: function (error) {

            // Vars
            var self = this;

            // Setting
            self.state        = 'failed';
            self.timeResponse = Date.now();
            self.timeData     = self.timeResponse;

            // Rejecting
            self.resolver(error);

            // Emitting
            self.emit('response', null, self);
            self.emit('error', error, self);
        },

        // HANDLER
        handleResponse: function (response) {

            // Vars
            var self = this;

            // Setting
            self.response     = response;
            self.state        = 'receiving';
            self.timeResponse = Date.now();

            // Listening
            response.on('data', self.handleData.bind(self));
            response.on('end', self.handleEnd.bind(self));

            // Emitting
            self.emit('response', response, self);
        }
    });

    /*********************************************************************/

    // Browserify
    XP.browserify(module.exports, 'XPRequest');

}(typeof window !== "undefined" ? window : global));<commit_msg>First release<commit_after>/*jslint browser: true, devel: true, node: true, ass: true, nomen: true, unparam: true, indent: 4 */

/**
 * @license
 * Copyright (c) 2015 The ExpandJS authors. All rights reserved.
 * This code may only be used under the BSD style license found at https://expandjs.github.io/LICENSE.txt
 * The complete set of authors may be found at https://expandjs.github.io/AUTHORS.txt
 * The complete set of contributors may be found at https://expandjs.github.io/CONTRIBUTORS.txt
 */
(function (global) {
    "use strict";

    // Vars
    var http      = require('http'),
        https     = require('https'),
        load      = require('xp-load'),
        XP        = load(require('expandjs'), 'XP'),
        XPEmitter = load(require('xp-emitter'), 'XPEmitter');

    /*********************************************************************/

    /**
     * This class is used to perform XHR requests.
     *
     * @class XPRequest
     * @description This class is used to perform XHR requests.
     * @extends XPEmitter
     */
    module.exports = new XP.Class('XPRequest', {

        // EXTENDS
        extends: XPEmitter,

        // OPTIONS
        options: {
            dataType: 'text',
            headers: null,
            keepAlive: 0,
            method: 'GET',
            url: ''
        },

        /*********************************************************************/

        /**
         * Emitted when a chunk of data is received.
         *
         * @event chunk
         * @param {Buffer | string} chunk
         * @param {Object} emitter
         */

        /**
         * Emitted when the entire data is received.
         *
         * @event data
         * @param {Buffer | string} data
         * @param {Object} emitter
         */

        /**
         * Emitted when an error is received.
         *
         * @event error
         * @param {string} error
         * @param {Object} emitter
         */

        /**
         * Emitted when a response is received.
         *
         * @event response
         * @param {Object} response
         * @param {Object} emitter
         */

        /**
         * Emitted when the request state changes.
         *
         * @event state
         * @param {string} state
         * @param {Object} emitter
         */

        /*********************************************************************/

        /**
         * @constructs
         * @param {Object | string} opt The request url or options.
         *   @param {string} [opt.dataType = "text"] The type of data expected back from the server.
         *   @param {Object} [opt.headers] An object containing request headers.
         *   @param {number} [opt.keepAlive = 0] How often to send TCP KeepAlive packets over sockets being kept alive.
         *   @param {string} [opt.method = "GET"] A string specifying the HTTP request method.
         *   @param {string} opt.url The request url.
         */
        initialize: {
            promise: true,
            value: function (opt, resolver) {

                // Asserting
                XP.assertArgument(XP.isObject(opt) || XP.isString(opt, true), 1, 'Object or string');

                // Vars
                var self = this;

                // Super
                XPEmitter.call(self);

                // Setting
                self.chunks    = [];
                self.options   = XP.isString(opt) ? {} : opt;
                self.dataType  = self.options.dataType;
                self.headers   = self.options.headers || {};
                self.keepAlive = self.options.keepAlive;
                self.method    = self.options.method;
                self.url       = self.options.url;
                self.resolver  = resolver;
                self.state     = 'idle';

                // Adapting
                self.adapt();

                // Listening
                self.adaptee.on('error', self.handleError.bind(self));
                self.adaptee.on('response', self.handleResponse.bind(self));
            }
        },

        /*********************************************************************/

        /**
         * Aborts the request.
         *
         * @method abort
         * @returns {Object}
         */
        abort: function () {

            // Vars
            var self = this;

            // Checking
            if (self.timeAbort) { return self; }

            // Aborting
            self.adaptee.abort();

            // Setting
            self.state     = 'aborted';
            self.timeAbort = Date.now();

            return self;
        },

        /**
         * Sends the request, appending data to the request body.
         *
         * @method send
         * @param {Buffer | string} [data]
         * @returns {Object}
         */
        send: function (data) {

            // Asserting
            XP.assertArgument(XP.isVoid(data) || Buffer.isBuffer(data) || XP.isString(data, true), 1, 'Buffer or string');

            // Vars
            var self = this;

            // Checking
            if (self.timeSend) { return self; }

            // Ending
            self.adaptee.end(data);

            // Setting
            self.state    = 'pending';
            self.timeSend = Date.now();

            return self;
        },

        /*********************************************************************/

        /**
         * Creates the adpatee
         *
         * @method adapt
         * @returns {Object}
         * @private
         */
        adapt: {
            enumerable: false,
            value: function () {

                // Checking
                if (!this.url) { return this; }

                // Vars
                var parsed   = XP.parseURL(this.url),
                    self     = XP.assign(this, {secure: (parsed.protocol || global.location.protocol) === 'https:'}),
                    port     = self.secure ? 443 : 80,
                    protocol = self.secure ? https : http;

                // Adapting
                self.adaptee = protocol.request({
                    auth: parsed.auth,
                    headers: self.headers,
                    hostname: parsed.hostname || global.location.hostname,
                    keepAlive: self.keepAlive > 0,
                    keepAliveMsecs: Math.max(self.keepAlive, 0),
                    method: self.method,
                    path: parsed.path + (parsed.hash || ''),
                    port: parsed.port || (parsed.hostname && port) || global.location.port
                });

                return self;
            }
        },

        /**
         * Parses the data
         *
         * @method parse
         * @param {Buffer | string} data
         * @returns {*}
         * @private
         */
        parse: {
            enumerable: true,
            value: function (data) {
                var self = this;
                if (self.dataType === 'json') { return JSON.parse(data); }
                return data;
            }
        },

        /*********************************************************************/

        /**
         * TODO DOC
         *
         * @property adaptee
         * @type Object
         * @private
         */
        adaptee: {
            enumerable: false,
            set: function (val) { return this.request || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property chunks
         * @type Array
         * @readonly
         * @private
         */
        chunks: {
            set: function (val) { return this.chunks || val; },
            validate: function (val) { return XP.isArray(val); }
        },

        /**
         * TODO DOC
         *
         * @property data
         * @type Buffer | string
         * @readonly
         */
        data: {
            validate: function (val) { return XP.isString(val) || Buffer.isBuffer(val); }
        },

        /**
         * TODO DOC
         *
         * @property dataType
         * @type string
         * @default "text"
         */
        dataType: {
            set: function (val) { return this.dataType || val; },
            validate: function (val) { return XP.includes(this.dataTypes, val); }
        },

        /**
         * TODO DOC
         *
         * @property dataTypes
         * @type Array
         * @default ["json", "text"]
         * @readonly
         */
        dataTypes: {
            frozen: true,
            writable: false,
            value: ['json', 'text']
        },

        /**
         * TODO DOC
         *
         * @property header
         * @type Object
         */
        headers: {
            set: function (val) { return this.headers || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property keepAlive
         * @type number
         * @default 0
         */
        keepAlive: {
            set: function (val) { return this.keepAlive >= 0 ? this.keepAlive : val; },
            validate: function (val) { return XP.isInt(val, true); }
        },

        /**
         * TODO DOC
         * @property method
         * @type string
         * @default "GET"
         */
        method: {
            set: function (val) { return this.method || XP.upperCase(val); },
            validate: function (val) { return XP.isString(val, true); }
        },

        /**
         * TODO DOC
         * @property response
         * @type Object
         * @readonly
         */
        response: {
            set: function (val) { return this.response || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property secure
         * @type boolean
         * @readonly
         */
        secure: {
            set: function (val) { return XP.isBoolean(this.secure) ? this.secure : !!val; }
        },

        /**
         * TODO DOC
         *
         * @property state
         * @type string
         * @readonly
         */
        state: {
            then: function (post) { this.emit('state', post, this); },
            validate: function (val) { return XP.includes(this.states, val); }
        },

        /**
         * TODO DOC
         *
         * @property states
         * @type Array
         * @default ["aborted", "failed", "idle", "pending", "received", "receiving"]
         * @readonly
         */
        states: {
            frozen: true,
            writable: false,
            value: ['aborted', 'failed', 'idle', 'pending', 'received', 'receiving']
        },

        /**
         * TODO DOC
         *
         * @property timeAbort
         * @type number
         * @readonly
         */
        timeAbort: {
            set: function (val) { return this.timeAbort || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeData
         * @type number
         * @readonly
         */
        timeData: {
            set: function (val) { return this.timeData || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeResponse
         * @type number
         * @readonly
         */
        timeResponse: {
            set: function (val) { return this.timeResponse || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeSend
         * @type number
         * @readonly
         */
        timeSend: {
            set: function (val) { return this.timeSend || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property url
         * @type string
         */
        url: {
            set: function (val) { return this.url || val; },
            validate: function (val) { return XP.isString(val); }
        },

        /*********************************************************************/

        // HANDLER
        handleData: function (chunk) {

            // Vars
            var self = this;

            // Setting
            self.chunks.push(chunk);

            // Emitting
            self.emit('chunk', chunk, self);
        },

        // HANDLER
        handleEnd: function () {

            // Vars
            var self = this;

            // Trying
            try {

                // Setting
                self.data     = self.parse(Buffer.isBuffer(self.chunks[0]) ? Buffer.concat(self.chunks) : self.chunks.join(''));
                self.state    = 'received';
                self.timeData = Date.now();

            } catch (exc) {

                // Handling
                return self.handleError(exc.message);
            }

            // Resolving
            self.resolver(null, self.data);

            // Emitting
            self.emit('data', self.data, self);
        },

        // HANDLER
        handleError: function (error) {

            // Vars
            var self = this;

            // Setting
            self.state        = 'failed';
            self.timeResponse = Date.now();
            self.timeData     = self.timeResponse;

            // Rejecting
            self.resolver(error);

            // Emitting
            self.emit('response', null, self);
            self.emit('error', error, self);
        },

        // HANDLER
        handleResponse: function (response) {

            // Vars
            var self = this;

            // Setting
            self.response     = response;
            self.state        = 'receiving';
            self.timeResponse = Date.now();

            // Listening
            response.on('data', self.handleData.bind(self));
            response.on('end', self.handleEnd.bind(self));

            // Emitting
            self.emit('response', response, self);
        }
    });

    /*********************************************************************/

    // Browserify
    XP.browserify(module.exports, 'XPRequest');

}(typeof window !== "undefined" ? window : global));<|endoftext|>


<commit_before>'use strict';

exports.getRandomPhago = phagoFactory();
//FIXME I need some cleaning

exports.move = function(player, move, grid) {
  for (var i = 0; i < move.length; i++) {
    grid[move[i].y][move[i].x].player = player;
    move[i].position = grid[move[i].y][move[i].x].position;
  }

  var phago = phagocyte(player, grid, move[0]);

  var totalMove = move.concat(phago);

  return totalMove;
}

exports.createGame = function() {
  var game = {
    id: 1,
    sideSize: 20,
    phagoQuantity: 7,
    level: 10,
    activePlayer: 'p1'
  }

  game.grid = createNewGrid(game.sideSize);

  return game;
}

function phagocyte(player, grid, pontoInicial) {
  function pontosAoRedor(ponto) {
    var pontos = [];

    if(ponto.x > 1) {
      pontos.push({x: ponto.x - 1, y: ponto.y});
    }
    if(ponto.x < grid.length - 1) {
      pontos.push({x: ponto.x + 1, y: ponto.y});
    }
    if(ponto.y > 1) {
      pontos.push({x: ponto.x, y: ponto.y - 1});
    }
    if(ponto.y < grid.length - 1) {
      pontos.push({x: ponto.x, y: ponto.y + 1});
    }
    return pontos;
  }

  Array.prototype.pIndexOf = function(p) {
    for (var i = 0; i < this.length; i++) {
      if (this[i].x == p.x && this[i].y == p.y) {
        return i;
      }
    }
    return -1;
  }

  var figura = [pontoInicial];

  for (var i = 0; i < figura.length; i++) {
    var ponto = figura[i];
    var proximosPontos = pontosAoRedor(ponto);

    proximosPontos.forEach((ponto) => {
      if(figura.pIndexOf(ponto) === -1 && grid[ponto.y][ponto.x].player === player) {
        figura.push(ponto);
      }
    });
  }

  var esquerdoAcima = cantoEsquerdoAcima(figura);
  var direitoAbaixo = cantoDireitoAbaixo(figura);

  var encontrouQuadradoCinza = false;
  for (var y = esquerdoAcima.y; y <= direitoAbaixo.y; y++) {
    for (var x = esquerdoAcima.x; x <= direitoAbaixo.x; x++) {
      if(!grid[y][x].player) {
        encontrouQuadradoCinza = true;
        break;
      };
    }
    if(encontrouQuadradoCinza) {
      break;
    }
  }

  var phagocyte = []
  if(!encontrouQuadradoCinza) {
    for (var y = esquerdoAcima.y; y <= direitoAbaixo.y; y++) {
      for (var x = esquerdoAcima.x; x <= direitoAbaixo.x; x++) {
        grid[y][x].player = player;
        phagocyte.push({x: x, y: y, position: grid[y][x].position});
      }
    }
  }

  return phagocyte;
}

function cantoDireitoAbaixo(figura) {
  return figura.reduce((canto, ponto) => {
    return {
      x: canto.x > ponto.x ? canto.x : ponto.x,
      y: canto.y > ponto.y ? canto.y : ponto.y
    };
  }, {x: 0, y: 0});
}

function cantoEsquerdoAcima(figura) {
  return figura.reduce((canto, ponto) => {
    return {
      x: canto.x < ponto.x ? canto.x : ponto.x,
      y: canto.y < ponto.y ? canto.y : ponto.y
    };
  }, {x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY});
}

function createNewGrid(size) {
  var grid = [];

  var pos = 1;
  for (var y = 0; y < size + 1; y++) {
    grid.push([]);
    for (var x = 0; x < size + 1; x++) {
      if(x > 0 && y > 0) {
        grid[y][x] = {position: pos++};
      }
    }
  }

  return grid;
}

function phagoFactory() {
  var phagos = [];
  for(var i = 1; i < 10; i++) {
    for(var j = 1; j < 10; j++) {
      var produto = i * j;
      if(phagos.indexOf(produto) === -1) {
        phagos.push(produto);
      }
    }
  }

  var phagoFactory = [];
  phagoFactory = phagos.sort((a, b) => {
    if(a === b) { return 0; }

    return a < b ? -1 : 1;
  }).map((phago) => {
    return {phago: phago, peso: 90 - phago};
  });

  var dificuldade = 19;
  var getRandomPhago = (function () {
    var pesoTotal = phagoFactory.slice(0, dificuldade).reduce((acc, phago) => {
          return acc += phago.peso;
        }, 0);

    function obterNumeroAleatorio(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    return function() {
      var pesoEscolhido = obterNumeroAleatorio(0, pesoTotal - 1);
      var phagoEscolhido = 0;
      for (var i = 0; i < phagoFactory.length; i++) {
        pesoEscolhido -= phagoFactory[i].peso;
        if(pesoEscolhido < 0) {
          phagoEscolhido = phagoFactory[i].phago;
          break;
        }
      }
      return phagoEscolhido;
    }
  })();

  return getRandomPhago;
}
<commit_msg>removing unnecessary IIFE<commit_after>'use strict';

exports.getRandomPhago = phagoFactory();
//FIXME I need some cleaning

exports.move = function(player, move, grid) {
  for (var i = 0; i < move.length; i++) {
    grid[move[i].y][move[i].x].player = player;
    move[i].position = grid[move[i].y][move[i].x].position;
  }

  var phago = phagocyte(player, grid, move[0]);

  var totalMove = move.concat(phago);

  return totalMove;
}

exports.createGame = function() {
  var game = {
    id: 1,
    sideSize: 20,
    phagoQuantity: 7,
    level: 10,
    activePlayer: 'p1'
  }

  game.grid = createNewGrid(game.sideSize);

  return game;
}

function phagocyte(player, grid, pontoInicial) {
  function pontosAoRedor(ponto) {
    var pontos = [];

    if(ponto.x > 1) {
      pontos.push({x: ponto.x - 1, y: ponto.y});
    }
    if(ponto.x < grid.length - 1) {
      pontos.push({x: ponto.x + 1, y: ponto.y});
    }
    if(ponto.y > 1) {
      pontos.push({x: ponto.x, y: ponto.y - 1});
    }
    if(ponto.y < grid.length - 1) {
      pontos.push({x: ponto.x, y: ponto.y + 1});
    }
    return pontos;
  }

  Array.prototype.pIndexOf = function(p) {
    for (var i = 0; i < this.length; i++) {
      if (this[i].x == p.x && this[i].y == p.y) {
        return i;
      }
    }
    return -1;
  }

  var figura = [pontoInicial];

  for (var i = 0; i < figura.length; i++) {
    var ponto = figura[i];
    var proximosPontos = pontosAoRedor(ponto);

    proximosPontos.forEach((ponto) => {
      if(figura.pIndexOf(ponto) === -1 && grid[ponto.y][ponto.x].player === player) {
        figura.push(ponto);
      }
    });
  }

  var esquerdoAcima = cantoEsquerdoAcima(figura);
  var direitoAbaixo = cantoDireitoAbaixo(figura);

  var encontrouQuadradoCinza = false;
  for (var y = esquerdoAcima.y; y <= direitoAbaixo.y; y++) {
    for (var x = esquerdoAcima.x; x <= direitoAbaixo.x; x++) {
      if(!grid[y][x].player) {
        encontrouQuadradoCinza = true;
        break;
      };
    }
    if(encontrouQuadradoCinza) {
      break;
    }
  }

  var phagocyte = []
  if(!encontrouQuadradoCinza) {
    for (var y = esquerdoAcima.y; y <= direitoAbaixo.y; y++) {
      for (var x = esquerdoAcima.x; x <= direitoAbaixo.x; x++) {
        grid[y][x].player = player;
        phagocyte.push({x: x, y: y, position: grid[y][x].position});
      }
    }
  }

  return phagocyte;
}

function cantoDireitoAbaixo(figura) {
  return figura.reduce((canto, ponto) => {
    return {
      x: canto.x > ponto.x ? canto.x : ponto.x,
      y: canto.y > ponto.y ? canto.y : ponto.y
    };
  }, {x: 0, y: 0});
}

function cantoEsquerdoAcima(figura) {
  return figura.reduce((canto, ponto) => {
    return {
      x: canto.x < ponto.x ? canto.x : ponto.x,
      y: canto.y < ponto.y ? canto.y : ponto.y
    };
  }, {x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY});
}

function createNewGrid(size) {
  var grid = [];

  var pos = 1;
  for (var y = 0; y < size + 1; y++) {
    grid.push([]);
    for (var x = 0; x < size + 1; x++) {
      if(x > 0 && y > 0) {
        grid[y][x] = {position: pos++};
      }
    }
  }

  return grid;
}

function phagoFactory() {
  var phagos = [];
  for(var i = 1; i < 10; i++) {
    for(var j = 1; j < 10; j++) {
      var produto = i * j;
      if(phagos.indexOf(produto) === -1) {
        phagos.push(produto);
      }
    }
  }

  var phagoFactory = [];
  phagoFactory = phagos.sort((a, b) => {
    if(a === b) { return 0; }

    return a < b ? -1 : 1;
  }).map((phago) => {
    return {phago: phago, peso: 90 - phago};
  });

  var dificuldade = 19;
  var pesoTotal = phagoFactory.slice(0, dificuldade).reduce((acc, phago) => {
    return acc += phago.peso;
  }, 0);

  return function() {
    var pesoEscolhido = obterNumeroAleatorio(0, pesoTotal - 1);
    var phagoEscolhido = 0;
    for (var i = 0; i < phagoFactory.length; i++) {
      pesoEscolhido -= phagoFactory[i].peso;
      if(pesoEscolhido < 0) {
        phagoEscolhido = phagoFactory[i].phago;
        break;
      }
    }
    return phagoEscolhido;
  }

  function obterNumeroAleatorio(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}
<|endoftext|>


<commit_before>import sourcemap from 'source-map';
import coffee from 'coffee-script';
import { BabelCompiler } from 'meteor/babel-compiler';

// The coffee-script compiler overrides Error.prepareStackTrace, mostly for the
// use of coffee.run which we don't use.  This conflicts with the tool's use of
// Error.prepareStackTrace to properly show error messages in linked code.
// Restore the tool's one after coffee-script clobbers it at import time.
if (Error.METEOR_prepareStackTrace) {
  Error.prepareStackTrace = Error.METEOR_prepareStackTrace;
}

Plugin.registerCompiler({
  extensions: ['coffee', 'litcoffee', 'coffee.md']
}, () => new CoffeeCompiler());

// The CompileResult for this CachingCompiler is a {source, sourceMap} object.

export class CoffeeCompiler extends CachingCompiler {
  constructor() {
    super({
      compilerName: 'coffeescript',
      defaultCacheSize: 1024*1024*10,
    });
    this.babelCompiler = new BabelCompiler();
  }

  _getCompileOptions(inputFile) {
    return {
      bare: true,
      filename: inputFile.getPathInPackage(),
      literate: inputFile.getExtension() !== 'coffee',
      // Return a source map.
      sourceMap: true,
      // Include the original source in the source map (sourcesContent field).
      inline: true,
      // This becomes the "file" field of the source map.
      generatedFile: '/' + this._outputFilePath(inputFile),
      // This becomes the "sources" field of the source map.
      sourceFiles: [inputFile.getDisplayPath()],
    };
  }

  _outputFilePath(inputFile) {
    return inputFile.getPathInPackage() + '.js';
  }

  getCacheKey(inputFile) {
    return [
      inputFile.getSourceHash(),
      inputFile.getDeclaredExports(),
      this._getCompileOptions(inputFile),
    ];
  }

  compileOneFile(inputFile) {
    const source = inputFile.getContentsAsString();
    const compileOptions = this._getCompileOptions(inputFile);

    let output;
    try {
      output = coffee.compile(source, compileOptions);
    } catch (e) {
      inputFile.error({
        message: e.message,
        line: e.location && (e.location.first_line + 1),
        column: e.location && (e.location.first_column + 1)
      });
      return null;
    }

    if (source.indexOf('`') !== -1) {
      // If source contains backticks, pass the coffee output through babel-compiler
      const doubleRoastedCoffee = this.babelCompiler.processOneFileForTarget(inputFile, output.js);
      if (doubleRoastedCoffee != null && doubleRoastedCoffee.data != null) {
        output.js = doubleRoastedCoffee.data;
      }
    }

    const stripped = stripExportedVars(
      output.js,
      inputFile.getDeclaredExports().map(e => e.name));
    const sourceWithMap = addSharedHeader(
      stripped, JSON.parse(output.v3SourceMap));
    return sourceWithMap;
  }

  addCompileResult(inputFile, sourceWithMap) {
    inputFile.addJavaScript({
      path: this._outputFilePath(inputFile),
      sourcePath: inputFile.getPathInPackage(),
      data: sourceWithMap.source,
      sourceMap: sourceWithMap.sourceMap,
      bare: inputFile.getFileOptions().bare
    });
  }

  compileResultSize(sourceWithMap) {
    return sourceWithMap.source.length +
      this.sourceMapSize(sourceWithMap.sourceMap);
  }
}

function stripExportedVars(source, exports) {
  if (!exports || !exports.length)
    return source;
  const lines = source.split("\n");

  // We make the following assumptions, based on the output of CoffeeScript
  // 1.7.1.
  //   - The var declaration in question is not indented and is the first such
  //     var declaration.  (CoffeeScript only produces one var line at each
  //     scope and there's only one top-level scope.)  All relevant variables
  //     are actually on this line.
  //   - The user hasn't used a ###-comment containing a line that looks like
  //     a var line, to produce something like
  //        /* bla
  //        var foo;
  //        */
  //     before an actual var line.  (ie, we do NOT attempt to figure out if
  //     we're inside a /**/ comment, which is produced by ### comments.)
  //   - The var in question is not assigned to in the declaration, nor are any
  //     other vars on this line. (CoffeeScript does produce some assignments
  //     but only for internal helpers generated by CoffeeScript, and they end
  //     up on subsequent lines.)
  // XXX relax these assumptions by doing actual JS parsing (eg with jsparse).
  //     I'd do this now, but there's no easy way to "unparse" a jsparse AST.
  //     Or alternatively, hack the compiler to allow us to specify unbound
  //     symbols directly.

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const match = /^var (.+)([,;])$/.exec(line);
    if (!match)
      continue;

    // If there's an assignment on this line, we assume that there are ONLY
    // assignments and that the var we are looking for is not declared. (Part
    // of our strong assumption about the layout of this code.)
    if (match[1].indexOf('=') !== -1)
      continue;

    // We want to replace the line with something no shorter, so that all
    // records in the source map continue to point at valid
    // characters.
    function replaceLine(x) {
      if (x.length >= lines[i].length) {
        lines[i] = x;
      } else {
        lines[i] = x + new Array(1 + (lines[i].length - x.length)).join(' ');
      }
    }

    let vars = match[1].split(', ').filter(v => exports.indexOf(v) === -1);
    if (vars.length) {
      replaceLine('var ' + vars.join(', ') + match[2]);
    } else {
      // We got rid of all the vars on this line. Drop the whole line if this
      // didn't continue to the next line, otherwise keep just the 'var '.
      if (match[2] === ';')
        replaceLine('');
      else
        replaceLine('var');
    }
    break;
  }

  return lines.join('\n');
}

function addSharedHeader(source, sourceMap) {
  // We want the symbol "share" to be visible to all CoffeeScript files in the
  // package (and shared between them), but not visible to JavaScript
  // files. (That's because we don't want to introduce two competing ways to
  // make package-local variables into JS ("share" vs assigning to non-var
  // variables).) The following hack accomplishes that: "__coffeescriptShare"
  // will be visible at the package level and "share" at the file level.  This
  // should work both in "package" mode where __coffeescriptShare will be added
  // as a var in the package closure, and in "app" mode where it will end up as
  // a global.
  //
  // This ends in a newline to make the source map easier to adjust.
  const header = ("__coffeescriptShare = typeof __coffeescriptShare === 'object' " +
                  "? __coffeescriptShare : {}; " +
                  "var share = __coffeescriptShare;\n");

  // If the file begins with "use strict", we need to keep that as the first
  // statement.
  const processedSource = source.replace(/^(?:((['"])use strict\2;)\n)?/, (match, useStrict) => {
    if (match) {
      // There's a "use strict"; we keep this as the first statement and insert
      // our header at the end of the line that it's on. This doesn't change
      // line numbers or the part of the line that previous may have been
      // annotated, so we don't need to update the source map.
      return useStrict + '  ' + header;
    } else {
      // There's no use strict, so we can just add the header at the very
      // beginning. This adds a line to the file, so we update the source map to
      // add a single un-annotated line to the beginning.
      sourceMap.mappings = ';' + sourceMap.mappings;
      return header;
    }
  });
  return {
    source: processedSource,
    sourceMap: sourceMap
  };
}
<commit_msg>Combine CoffeeScript and BabelCompiler source maps.<commit_after>import {
  SourceMapConsumer,
  SourceMapGenerator,
} from 'source-map';
import coffee from 'coffee-script';
import { BabelCompiler } from 'meteor/babel-compiler';

// The coffee-script compiler overrides Error.prepareStackTrace, mostly for the
// use of coffee.run which we don't use.  This conflicts with the tool's use of
// Error.prepareStackTrace to properly show error messages in linked code.
// Restore the tool's one after coffee-script clobbers it at import time.
if (Error.METEOR_prepareStackTrace) {
  Error.prepareStackTrace = Error.METEOR_prepareStackTrace;
}

Plugin.registerCompiler({
  extensions: ['coffee', 'litcoffee', 'coffee.md']
}, () => new CoffeeCompiler());

// The CompileResult for this CachingCompiler is a {source, sourceMap} object.

export class CoffeeCompiler extends CachingCompiler {
  constructor() {
    super({
      compilerName: 'coffeescript',
      defaultCacheSize: 1024*1024*10,
    });
    this.babelCompiler = new BabelCompiler();
  }

  _getCompileOptions(inputFile) {
    return {
      bare: true,
      filename: inputFile.getPathInPackage(),
      literate: inputFile.getExtension() !== 'coffee',
      // Return a source map.
      sourceMap: true,
      // Include the original source in the source map (sourcesContent field).
      inline: true,
      // This becomes the "file" field of the source map.
      generatedFile: '/' + this._outputFilePath(inputFile),
      // This becomes the "sources" field of the source map.
      sourceFiles: [inputFile.getDisplayPath()],
    };
  }

  _outputFilePath(inputFile) {
    return inputFile.getPathInPackage() + '.js';
  }

  getCacheKey(inputFile) {
    return [
      inputFile.getSourceHash(),
      inputFile.getDeclaredExports(),
      this._getCompileOptions(inputFile),
    ];
  }

  compileOneFile(inputFile) {
    const source = inputFile.getContentsAsString();
    const compileOptions = this._getCompileOptions(inputFile);

    let output;
    try {
      output = coffee.compile(source, compileOptions);
    } catch (e) {
      inputFile.error({
        message: e.message,
        line: e.location && (e.location.first_line + 1),
        column: e.location && (e.location.first_column + 1)
      });
      return null;
    }

    let sourceMap = JSON.parse(output.v3SourceMap);

    if (source.indexOf('`') !== -1) {
      // If source contains backticks, pass the coffee output through babel-compiler
      const doubleRoastedCoffee =
        this.babelCompiler.processOneFileForTarget(inputFile, output.js);

      if (doubleRoastedCoffee != null &&
          doubleRoastedCoffee.data != null) {
        output.js = doubleRoastedCoffee.data;

        // Combine the original CoffeeScript source map with the one
        // produced by this.babelCompiler.processOneFileForTarget.
        const smg = new SourceMapGenerator(
          new SourceMapConsumer(doubleRoastedCoffee.sourceMap));
        smg.applySourceMap(new SourceMapConsumer(sourceMap));
        sourceMap = smg.toJSON();
      }
    }

    return addSharedHeader(stripExportedVars(
      output.js,
      inputFile.getDeclaredExports().map(e => e.name)
    ), sourceMap);
  }

  addCompileResult(inputFile, sourceWithMap) {
    inputFile.addJavaScript({
      path: this._outputFilePath(inputFile),
      sourcePath: inputFile.getPathInPackage(),
      data: sourceWithMap.source,
      sourceMap: sourceWithMap.sourceMap,
      bare: inputFile.getFileOptions().bare
    });
  }

  compileResultSize(sourceWithMap) {
    return sourceWithMap.source.length +
      this.sourceMapSize(sourceWithMap.sourceMap);
  }
}

function stripExportedVars(source, exports) {
  if (!exports || !exports.length)
    return source;
  const lines = source.split("\n");

  // We make the following assumptions, based on the output of CoffeeScript
  // 1.7.1.
  //   - The var declaration in question is not indented and is the first such
  //     var declaration.  (CoffeeScript only produces one var line at each
  //     scope and there's only one top-level scope.)  All relevant variables
  //     are actually on this line.
  //   - The user hasn't used a ###-comment containing a line that looks like
  //     a var line, to produce something like
  //        /* bla
  //        var foo;
  //        */
  //     before an actual var line.  (ie, we do NOT attempt to figure out if
  //     we're inside a /**/ comment, which is produced by ### comments.)
  //   - The var in question is not assigned to in the declaration, nor are any
  //     other vars on this line. (CoffeeScript does produce some assignments
  //     but only for internal helpers generated by CoffeeScript, and they end
  //     up on subsequent lines.)
  // XXX relax these assumptions by doing actual JS parsing (eg with jsparse).
  //     I'd do this now, but there's no easy way to "unparse" a jsparse AST.
  //     Or alternatively, hack the compiler to allow us to specify unbound
  //     symbols directly.

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const match = /^var (.+)([,;])$/.exec(line);
    if (!match)
      continue;

    // If there's an assignment on this line, we assume that there are ONLY
    // assignments and that the var we are looking for is not declared. (Part
    // of our strong assumption about the layout of this code.)
    if (match[1].indexOf('=') !== -1)
      continue;

    // We want to replace the line with something no shorter, so that all
    // records in the source map continue to point at valid
    // characters.
    function replaceLine(x) {
      if (x.length >= lines[i].length) {
        lines[i] = x;
      } else {
        lines[i] = x + new Array(1 + (lines[i].length - x.length)).join(' ');
      }
    }

    let vars = match[1].split(', ').filter(v => exports.indexOf(v) === -1);
    if (vars.length) {
      replaceLine('var ' + vars.join(', ') + match[2]);
    } else {
      // We got rid of all the vars on this line. Drop the whole line if this
      // didn't continue to the next line, otherwise keep just the 'var '.
      if (match[2] === ';')
        replaceLine('');
      else
        replaceLine('var');
    }
    break;
  }

  return lines.join('\n');
}

function addSharedHeader(source, sourceMap) {
  // We want the symbol "share" to be visible to all CoffeeScript files in the
  // package (and shared between them), but not visible to JavaScript
  // files. (That's because we don't want to introduce two competing ways to
  // make package-local variables into JS ("share" vs assigning to non-var
  // variables).) The following hack accomplishes that: "__coffeescriptShare"
  // will be visible at the package level and "share" at the file level.  This
  // should work both in "package" mode where __coffeescriptShare will be added
  // as a var in the package closure, and in "app" mode where it will end up as
  // a global.
  //
  // This ends in a newline to make the source map easier to adjust.
  const header = ("__coffeescriptShare = typeof __coffeescriptShare === 'object' " +
                  "? __coffeescriptShare : {}; " +
                  "var share = __coffeescriptShare;\n");

  // If the file begins with "use strict", we need to keep that as the first
  // statement.
  const processedSource = source.replace(/^(?:((['"])use strict\2;)\n)?/, (match, useStrict) => {
    if (match) {
      // There's a "use strict"; we keep this as the first statement and insert
      // our header at the end of the line that it's on. This doesn't change
      // line numbers or the part of the line that previous may have been
      // annotated, so we don't need to update the source map.
      return useStrict + '  ' + header;
    } else {
      // There's no use strict, so we can just add the header at the very
      // beginning. This adds a line to the file, so we update the source map to
      // add a single un-annotated line to the beginning.
      sourceMap.mappings = ';' + sourceMap.mappings;
      return header;
    }
  });
  return {
    source: processedSource,
    sourceMap: sourceMap
  };
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var path = require('path');
var mongoose = require('mongoose');
var timestamps = require('mongoose-timestamp');
var shortId = require('shortid');
var is = require('is');
var semver = require('semver');
var compareSemver = require('compare-semver');
var slug = require('cozy-slug');
var randomName = require('yay');
var Version = require('./version');
var Github = require('./github');
var Schema = mongoose.Schema;
var fs = require('fs');

var Package = new Schema({
  _id: {type: String, unique: true, default: shortId.generate},
  user_id: {type: String, default: null},
  name: {type: String, default: null, index: true},
  gist_id: {type: String, default: null},
  versions: {type: [Version.schema], default: []},
  files: {type: Schema.Types.Mixed, default: []}
}, {
  'collection': 'packages',
  '_id': false,
  'versionKey': false
});

// Instance methods

Package.methods.latestVersion = function () {
  var versionNumbers = _(this.versions)
    .filter(function (version) {
      // Ignore yanked versions
      return !version.yanked;
    })
    .pluck('number')
    .value();

  return compareSemver.max(versionNumbers);
};

Package.methods.saveIfValid = function (done) {
  var package = this;

  PackageModel.isNameUnique(package.id, package.name, package.user_id, function (err, isUnique) {
    if (err) return done(err);
    if (!isUnique) return done(null, false);

    package.save(function (err) {
      if (err) return done(err);
      done(err, true);
    });
  });
};

Package.methods.updateFilenamesWithPackageName = function (name) {
  var package = this;
  var newName = this.name;

  if (newName === name) return package;
  if (!package.files.length) return package;

  package.files = _.map(package.files, function (file) {
    var ext = path.extname(file.name);
    var truncFilename = path.basename(file.name, ext);

    if (truncFilename === name) file.name = newName + ext;

    return file;
  });

  package.markModified('files');

  return package;
};

Package.methods.setFileContent = function (filename, content) {
  var existing = _.find(this.files, {name: filename});

  if (!existing) {
    this.files.push({
      name: filename,
      content: content
    });
  }
  else {
    existing.content = content;
  }

  this.markModified('files');

  return this;
};

Package.methods.toResponse = function() {
  return {
    id: this._id,
    name: this.name,
    gist_id: this.gist_id,
    files: parseFileList(this.files),
    versions: this.versions
  }
};

// Static methods

Package.statics.findForUserId = function (userId, done) {
  this.find({user_id: userId}, function (err, packages) {
    if (err) return done(err);

    var sortedPackages = _(packages)
      .map(function (package) {
        var latestNumber = package.latestVersion();
        var latestReleaseDate;

        if (latestNumber) {
          latestReleaseDate = _.find(package.versions, {number: latestNumber}).created_at;
        }

        return {
          name: package.name,
          id: package._id,
          version: latestNumber,
          released_at: latestReleaseDate
        };
      })
      .sortBy('name')
      .value();

    done(err, sortedPackages);
  });
};

Package.statics.findByIdOrName = function (packageId, userId, done, raw) {
  this.findOne({
    '$or': [
      {_id: packageId},
      {name: packageId}
    ],
    '$and': [
      {user_id: userId}
    ]
  }, done);
};

Package.statics.findFileContentsByPackageIdOrName = function (filename, packageId, userId, done) {
  PackageModel.findByIdOrName(packageId, userId, function (err, package) {
    if (err) return done(err);
    if (!package) return done({noPackage: true});

    done(null, package.files[filename]);
  });
};

Package.statics.findFileContentsFromVersion = function (options, done) {
  if (options.number === 'dev') {
    return PackageModel.findFileContentsByPackageIdOrName(
      options.filename,
      options.packageId,
      options.userId,
      done
    );
  }

  PackageModel.getVersion(options, function (err, version, package) {
    if (err) return done(err);
    if (!version) return done();
    if (!package || !package.gist_id) return done();

    var gh = new Github(options.accessToken);
    var gistId = package.gist_id;
    var sha = version.sha;

    gh.getGistFileContentsForCommit(gistId, sha, function (err, files) {
      if (err) return done(err);
      if (!files || !files[options.filename]) return done();

      done(null, files[options.filename].content);
    });
  });
};

Package.statics.getVersion = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    if (!package) return done(err, null, null);

    var where = {
      number: options.number,
      yanked: false
    };

    if (options.number === 'latest') where.number = package.latestVersion();

    done(err, _.find(package.versions, where), package);
  });
};

Package.statics.updateByIdOrName = function(options, done) {
  var packageId = options.packageId;
  var userId = options.userId;
  var data = options.data;

  PackageModel.findByIdOrName(packageId, userId, function (err, package) {
    if (!package) return done();

    var originalName = package.name;

    // Set default Polymer file contents
    if (data.source) package.setFileContent(package.name + '.html', data.source);
    if (data.demo) package.setFileContent('demo.html', data.demo);

    _.extend(package, _.omit(data, ['source', 'demo']));

    // Rename any file that has the same name as the package
    // (so they match, like best friends)
    package
      .updateFilenamesWithPackageName(originalName)
      .saveIfValid(function (err, saved) {
        done(err, saved, package);
      });
  }, true);
};

Package.statics.updateFileContents = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    package.files = _.map(package.files, function (file, idx) {
      if (file.name === options.filename) file.content = options.content;
      return file;
    });

    package.markModified('files');
    package.save(done);
  }, true);
};

Package.statics.isVersionUniqueAndGreatest = function (options, done) {

  // NOTE: this might be a bottle neck as the versions list grows
  // QUESTION: Should we cache the latest version number as a value on the package object?

  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    var versions = package.versions;
    var versionNumber = options.number;

    done(
      err,
      compareSemver.unique(versionNumber, _.pluck(versions, 'number')),
      compareSemver.gt(versionNumber, _.pluck(versions, 'number')),
      package
    );
  }, true);
};

Package.statics.isNameUnique = function (packageId, name, userId, done) {
  if (!name) return done(null, true);

  PackageModel.findByIdOrName(name, userId, function (err, package) {
    if (err) return done(err);
    if (package && package._id !== packageId) return done(null, false); // if the name matches the current package, don't do this

    done(null, true);
  }, true);
};

Package.statics.nameToSlug = function (str) {
  return str ? slug(str) : null;
};

Package.statics.yankVersion = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    var alreadyYanked = false;

    package.versions = _.map(package.versions, function (version) {
      if (version.number === options.number) {
        if (version.yanked) alreadyYanked = true;
        version.yanked = true;
      }
      return version;
    });

    if (alreadyYanked) return done({alreadyYanked: true});

    package.markModified('versions');
    package.save(done);
  });
};

Package.statics.filesFromObjectToArray = function (files) {
  var _filesList = [];

  Object.keys(files).forEach(function (filename) {
    _filesList.push({
      name: filename,
      content: files[filename]
    });
  });

  return _filesList;
};

Package.statics.parseFilelistForGist = function (files) {
  return _(files)
    .map(function (file) {
      return [file.name, {content: file.content}];
    })
    .zipObject()
    .value();
};

Package.pre('save', function formatFileList (next) {
  if (is.object(this.files)) this.files = PackageModel.filesFromObjectToArray(this.files);
  next();
});

Package.pre('save', function ensureName (next) {
  if (!this.name || this.name.length === 0) this.name = randomName();
  if (this.files.length === 0) this.files = defaultFiles(this);
  next();
});

Package.plugin(timestamps, {
  createdAt: 'created_at',
  updatedAt: 'updated_at'
});

var PackageModel = module.exports = mongoose.model('Package', Package);

function parseFileList (packageFiles) {
  var files = {};

  packageFiles.forEach(function (file) {
    files[file.name] = file.content;
  });

  return files;
}

var defaultSource = fs.readFileSync(__dirname + '/../../templates/default-source.html').toString();
var defaultDemo = fs.readFileSync(__dirname + '/../../templates/default-demo.html').toString();
function defaultFiles(package) {
  return [
    {name: package.name + ".html", content: defaultSource.replace(/{ELEMENT_NAME}/g, package.name)},
    {name: 'demo.html', content: defaultSource.replace(/{ELEMENT_NAME}/g, package.name)}
  ]
}<commit_msg>Typo.<commit_after>var _ = require('lodash');
var path = require('path');
var mongoose = require('mongoose');
var timestamps = require('mongoose-timestamp');
var shortId = require('shortid');
var is = require('is');
var semver = require('semver');
var compareSemver = require('compare-semver');
var slug = require('cozy-slug');
var randomName = require('yay');
var Version = require('./version');
var Github = require('./github');
var Schema = mongoose.Schema;
var fs = require('fs');

var Package = new Schema({
  _id: {type: String, unique: true, default: shortId.generate},
  user_id: {type: String, default: null},
  name: {type: String, default: null, index: true},
  gist_id: {type: String, default: null},
  versions: {type: [Version.schema], default: []},
  files: {type: Schema.Types.Mixed, default: []}
}, {
  'collection': 'packages',
  '_id': false,
  'versionKey': false
});

// Instance methods

Package.methods.latestVersion = function () {
  var versionNumbers = _(this.versions)
    .filter(function (version) {
      // Ignore yanked versions
      return !version.yanked;
    })
    .pluck('number')
    .value();

  return compareSemver.max(versionNumbers);
};

Package.methods.saveIfValid = function (done) {
  var package = this;

  PackageModel.isNameUnique(package.id, package.name, package.user_id, function (err, isUnique) {
    if (err) return done(err);
    if (!isUnique) return done(null, false);

    package.save(function (err) {
      if (err) return done(err);
      done(err, true);
    });
  });
};

Package.methods.updateFilenamesWithPackageName = function (name) {
  var package = this;
  var newName = this.name;

  if (newName === name) return package;
  if (!package.files.length) return package;

  package.files = _.map(package.files, function (file) {
    var ext = path.extname(file.name);
    var truncFilename = path.basename(file.name, ext);

    if (truncFilename === name) file.name = newName + ext;

    return file;
  });

  package.markModified('files');

  return package;
};

Package.methods.setFileContent = function (filename, content) {
  var existing = _.find(this.files, {name: filename});

  if (!existing) {
    this.files.push({
      name: filename,
      content: content
    });
  }
  else {
    existing.content = content;
  }

  this.markModified('files');

  return this;
};

Package.methods.toResponse = function() {
  return {
    id: this._id,
    name: this.name,
    gist_id: this.gist_id,
    files: parseFileList(this.files),
    versions: this.versions
  }
};

// Static methods

Package.statics.findForUserId = function (userId, done) {
  this.find({user_id: userId}, function (err, packages) {
    if (err) return done(err);

    var sortedPackages = _(packages)
      .map(function (package) {
        var latestNumber = package.latestVersion();
        var latestReleaseDate;

        if (latestNumber) {
          latestReleaseDate = _.find(package.versions, {number: latestNumber}).created_at;
        }

        return {
          name: package.name,
          id: package._id,
          version: latestNumber,
          released_at: latestReleaseDate
        };
      })
      .sortBy('name')
      .value();

    done(err, sortedPackages);
  });
};

Package.statics.findByIdOrName = function (packageId, userId, done, raw) {
  this.findOne({
    '$or': [
      {_id: packageId},
      {name: packageId}
    ],
    '$and': [
      {user_id: userId}
    ]
  }, done);
};

Package.statics.findFileContentsByPackageIdOrName = function (filename, packageId, userId, done) {
  PackageModel.findByIdOrName(packageId, userId, function (err, package) {
    if (err) return done(err);
    if (!package) return done({noPackage: true});

    done(null, package.files[filename]);
  });
};

Package.statics.findFileContentsFromVersion = function (options, done) {
  if (options.number === 'dev') {
    return PackageModel.findFileContentsByPackageIdOrName(
      options.filename,
      options.packageId,
      options.userId,
      done
    );
  }

  PackageModel.getVersion(options, function (err, version, package) {
    if (err) return done(err);
    if (!version) return done();
    if (!package || !package.gist_id) return done();

    var gh = new Github(options.accessToken);
    var gistId = package.gist_id;
    var sha = version.sha;

    gh.getGistFileContentsForCommit(gistId, sha, function (err, files) {
      if (err) return done(err);
      if (!files || !files[options.filename]) return done();

      done(null, files[options.filename].content);
    });
  });
};

Package.statics.getVersion = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    if (!package) return done(err, null, null);

    var where = {
      number: options.number,
      yanked: false
    };

    if (options.number === 'latest') where.number = package.latestVersion();

    done(err, _.find(package.versions, where), package);
  });
};

Package.statics.updateByIdOrName = function(options, done) {
  var packageId = options.packageId;
  var userId = options.userId;
  var data = options.data;

  PackageModel.findByIdOrName(packageId, userId, function (err, package) {
    if (!package) return done();

    var originalName = package.name;

    // Set default Polymer file contents
    if (data.source) package.setFileContent(package.name + '.html', data.source);
    if (data.demo) package.setFileContent('demo.html', data.demo);

    _.extend(package, _.omit(data, ['source', 'demo']));

    // Rename any file that has the same name as the package
    // (so they match, like best friends)
    package
      .updateFilenamesWithPackageName(originalName)
      .saveIfValid(function (err, saved) {
        done(err, saved, package);
      });
  }, true);
};

Package.statics.updateFileContents = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    package.files = _.map(package.files, function (file, idx) {
      if (file.name === options.filename) file.content = options.content;
      return file;
    });

    package.markModified('files');
    package.save(done);
  }, true);
};

Package.statics.isVersionUniqueAndGreatest = function (options, done) {

  // NOTE: this might be a bottle neck as the versions list grows
  // QUESTION: Should we cache the latest version number as a value on the package object?

  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    var versions = package.versions;
    var versionNumber = options.number;

    done(
      err,
      compareSemver.unique(versionNumber, _.pluck(versions, 'number')),
      compareSemver.gt(versionNumber, _.pluck(versions, 'number')),
      package
    );
  }, true);
};

Package.statics.isNameUnique = function (packageId, name, userId, done) {
  if (!name) return done(null, true);

  PackageModel.findByIdOrName(name, userId, function (err, package) {
    if (err) return done(err);
    if (package && package._id !== packageId) return done(null, false); // if the name matches the current package, don't do this

    done(null, true);
  }, true);
};

Package.statics.nameToSlug = function (str) {
  return str ? slug(str) : null;
};

Package.statics.yankVersion = function (options, done) {
  PackageModel.findByIdOrName(options.packageId, options.userId, function (err, package) {
    var alreadyYanked = false;

    package.versions = _.map(package.versions, function (version) {
      if (version.number === options.number) {
        if (version.yanked) alreadyYanked = true;
        version.yanked = true;
      }
      return version;
    });

    if (alreadyYanked) return done({alreadyYanked: true});

    package.markModified('versions');
    package.save(done);
  });
};

Package.statics.filesFromObjectToArray = function (files) {
  var _filesList = [];

  Object.keys(files).forEach(function (filename) {
    _filesList.push({
      name: filename,
      content: files[filename]
    });
  });

  return _filesList;
};

Package.statics.parseFilelistForGist = function (files) {
  return _(files)
    .map(function (file) {
      return [file.name, {content: file.content}];
    })
    .zipObject()
    .value();
};

Package.pre('save', function formatFileList (next) {
  if (is.object(this.files)) this.files = PackageModel.filesFromObjectToArray(this.files);
  next();
});

Package.pre('save', function ensureName (next) {
  if (!this.name || this.name.length === 0) this.name = randomName();
  if (this.files.length === 0) this.files = defaultFiles(this);
  next();
});

Package.plugin(timestamps, {
  createdAt: 'created_at',
  updatedAt: 'updated_at'
});

var PackageModel = module.exports = mongoose.model('Package', Package);

function parseFileList (packageFiles) {
  var files = {};

  packageFiles.forEach(function (file) {
    files[file.name] = file.content;
  });

  return files;
}

var defaultSource = fs.readFileSync(__dirname + '/../../templates/default-source.html').toString();
var defaultDemo = fs.readFileSync(__dirname + '/../../templates/default-demo.html').toString();
function defaultFiles(package) {
  return [
    {name: package.name + ".html", content: defaultSource.replace(/{ELEMENT_NAME}/g, package.name)},
    {name: 'demo.html', content: defaultDemo.replace(/{ELEMENT_NAME}/g, package.name)}
  ]
}<|endoftext|>