<commit_before>/**
 * Created by lukaszmroz on 23.10.2016.
 */

module.exports = function(request, router, PAGE_ACCESS_TOKEN, VERIFY_TOKEN) {

    function receivedAuthentication(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfAuth = event.timestamp;

        // The 'ref' field is set in the 'Send to Messenger' plugin, in the 'data-ref'
        // The developer can set this to an arbitrary value to associate the
        // authentication callback with the 'Send to Messenger' click event. This is
        // a way to do account linking when the user clicks the 'Send to Messenger'
        // plugin.
        var passThroughParam = event.optin.ref;

        // console.log("Received authentication for user %d and page %d with pass " +
        //     "through param '%s' at %d", senderID, recipientID, passThroughParam,
        //     timeOfAuth);

        // When an authentication is received, we'll send a message back to the sender
        // to let them know it was successful.
        sendTextMessage(senderID, "Authentication successful");
    }

    function sendTextMessage(recipientId, messageText) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                text: messageText
            }
        };

        callSendAPI(messageData);
    }

    function sendButtonMessage(recipientId) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                attachment: {
                    type: "template",
                    payload: {
                        template_type: "button",
                        text: "W czym Ci mogę pomóć? Wybierz opcję",
                        buttons:[{
                            type: "postback",
                            title: "Chciałbyś sprzedać",
                            payload: "{operation: SELL}"
                        }, {
                            type: "postback",
                            title: "Chciałbyś kupić",
                            payload: "{operation: BUY}"
                        }]
                    }
                }
            }
        };

        callSendAPI(messageData);
    }

    function sendGenericMessage(recipientId) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                attachment: {
                    type: "template",
                    payload: {
                        template_type: "generic",
                        elements: [{
                            title: "rift",
                            subtitle: "Next-generation virtual reality",
                            item_url: "https://www.oculus.com/en-us/rift/",
                            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
                            buttons: [{
                                type: "web_url",
                                url: "https://www.oculus.com/en-us/rift/",
                                title: "Open Web URL"
                            }, {
                                type: "postback",
                                title: "Call Postback",
                                payload: "Payload for first bubble",
                            }]
                        }, {
                            title: "touch",
                            subtitle: "Your Hands, Now in VR",
                            item_url: "https://www.oculus.com/en-us/touch/",
                            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
                            buttons: [{
                                type: "web_url",
                                url: "https://www.oculus.com/en-us/touch/",
                                title: "Open Web URL"
                            }, {
                                type: "postback",
                                title: "Call Postback",
                                payload: "Payload for second bubble",
                            }]
                        }]
                    }
                }
            }
        };

        callSendAPI(messageData);
    }

    function callSendAPI(messageData) {
        request({
            uri: 'https://graph.facebook.com/v2.6/me/messages',
            qs: { access_token: PAGE_ACCESS_TOKEN },
            method: 'POST',
            json: messageData

        }, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var recipientId = body.recipient_id;
                var messageId = body.message_id;

                console.log("Successfully sent generic message with id %s to recipient %s",
                    messageId, recipientId);
            } else {
                console.error("Unable to send message.");
                // console.error(response);
                // console.error(error);
            }
        });
    }

    function receivedDeliveryConfirmation(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var delivery = event.delivery;
        var messageIDs = delivery.mids;
        var watermark = delivery.watermark;
        var sequenceNumber = delivery.seq;

        if (messageIDs) {
            messageIDs.forEach(function(messageID) {
                console.log("Received delivery confirmation for message ID: %s",
                    messageID);
            });
        }

        console.log("All message before %d were delivered.", watermark);
    }

    function receivedMessage(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfMessage = event.timestamp;
        var message = event.message;

        console.log("Received message for user %d and page %d at %d with message:",
            senderID, recipientID, timeOfMessage);
        console.log(JSON.stringify(message));

        var messageId = message.mid;

        // You may get a text or attachment but not both
        var messageText = message.text;
        var messageAttachments = message.attachments;

        if (messageText) {

            // If we receive a text message, check to see if it matches any special
            // keywords and send back the corresponding example. Otherwise, just echo
            // the text we received.
            switch (messageText) {
                case 'image':
                    // sendImageMessage(senderID);
                    break;

                case 'button':
                    sendButtonMessage(senderID);
                    break;

                case 'generic':
                    sendGenericMessage(senderID);
                    break;

                default:
                    sendTextMessage(senderID, messageText);
            }
        } else if (messageAttachments) {
            sendTextMessage(senderID, "Message with attachment received");
        }
    }

    function receivedPostback(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfPostback = event.timestamp;

        // The 'payload' param is a developer-defined field which is set in a postback
        // button for Structured Messages.
        var payload = event.postback.payload;

        console.log("Received postback for user %d and page %d with payload '%s' " +
            "at %d", senderID, recipientID, payload, timeOfPostback);

        if (payload) {
            if (payload == '{operation: SELL}') {
                sendTextMessage(senderID, "Co chciałbyś sprzedać?");
            } else if (payload == '{operation: BUY}') {
                sendTextMessage(senderID, "Co chciałbyś kupić?");
            }

        } else {
            // When a postback is called, we'll send a message back to the sender to
            // let them know it was successful
            sendTextMessage(senderID, "Przepraszam, ale nie rozumiem. Możesz wyjaśnić jeszcze raz?");
        }

    }

// for Facebook verification
    router
        .get('/', function(req, res) {
            if (req.query['hub.mode'] === 'subscribe' &&
                req.query['hub.verify_token'] === VERIFY_TOKEN) {
                // console.log("Validating webhook");
                res.status(200).send(req.query['hub.challenge']);
            } else {
                // console.error("Failed validation. Make sure the validation tokens match.");
                res.sendStatus(403);
            }
        })
// for sending messages
        .post('/', function (req, res) {
            var data = req.body;

            // Make sure this is a page subscription
            if (data.object == 'page') {
                // Iterate over each entry
                // There may be multiple if batched
                data.entry.forEach(function(pageEntry) {
                    var pageID = pageEntry.id;
                    var timeOfEvent = pageEntry.time;

                    // Iterate over each messaging event
                    pageEntry.messaging.forEach(function(messagingEvent) {
                        if (messagingEvent.optin) {
                            receivedAuthentication(messagingEvent);
                        } else if (messagingEvent.message) {
                            receivedMessage(messagingEvent);
                        } else if (messagingEvent.delivery) {
                            receivedDeliveryConfirmation(messagingEvent);
                        } else if (messagingEvent.postback) {
                            receivedPostback(messagingEvent);
                        } else {
                            console.log("Webhook received unknown messagingEvent: ", messagingEvent);
                        }
                    });
                });

                // Assume all went well.
                //
                // You must send back a 200, within 20 seconds, to let us know you've
                // successfully received the callback. Otherwise, the request will time out.
                res.sendStatus(200);
            }
        });
    return router;
};<commit_msg>APteki<commit_after>/**
 * Created by lukaszmroz on 23.10.2016.
 */

module.exports = function(request, router, PAGE_ACCESS_TOKEN, VERIFY_TOKEN) {

    function receivedAuthentication(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfAuth = event.timestamp;

        // The 'ref' field is set in the 'Send to Messenger' plugin, in the 'data-ref'
        // The developer can set this to an arbitrary value to associate the
        // authentication callback with the 'Send to Messenger' click event. This is
        // a way to do account linking when the user clicks the 'Send to Messenger'
        // plugin.
        var passThroughParam = event.optin.ref;

        // console.log("Received authentication for user %d and page %d with pass " +
        //     "through param '%s' at %d", senderID, recipientID, passThroughParam,
        //     timeOfAuth);

        // When an authentication is received, we'll send a message back to the sender
        // to let them know it was successful.
        sendTextMessage(senderID, "Authentication successful");
    }

    function sendTextMessage(recipientId, messageText) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                text: messageText
            }
        };

        callSendAPI(messageData);
    }

    function sendButtonMessage(recipientId) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                attachment: {
                    type: "template",
                    payload: {
                        template_type: "button",
                        text: "W czym Ci mogę pomóć? Wybierz opcję",
                        buttons:[{
                            type: "postback",
                            title: "Chciałbyś sprzedać",
                            payload: "{operation: SELL}"
                        }, {
                            type: "postback",
                            title: "Chciałbyś kupić",
                            payload: "{operation: BUY}"
                        }]
                    }
                }
            }
        };

        callSendAPI(messageData);
    }

    function sendPharmacies(recipientId) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                attachment: {
                    type: "template",
                    payload: {
                        template_type: "generic",
                        elements: [{
                            title: "Ziko Apteka",
                            subtitle: "Targowa 39, 03-729 Warszawa",
                            item_url: "https://goo.gl/maps/5phMpL6KRMt",
                            image_url: "https://lh3.googleusercontent.com/-231glKPCWuY/VgKvzi7tiHI/AAAAAAAAAAw/q57wqYerKQYjmQESO72U1jGGKHted2wRQ/s408-k-no/",
                            buttons: [{
                                type: "web_url",
                                url: "https://goo.gl/maps/5phMpL6KRMt",
                                title: "Otwórz w Mapach Google"
                            }]
                        }, {
                            title: "Apteka Przy Wileńskiej",
                            subtitle: "Wileńska 9, 03-001 Warszawa",
                            item_url: "https://goo.gl/maps/RN13omtnA4u",
                            image_url: "https://geo1.ggpht.com/cbk?panoid=ONUmYhFtpKUTsZaBrhSIcQ&output=thumbnail&cb_client=search.TACTILE.gps&thumb=2&w=408&h=256&yaw=322.52&pitch=0&thumbfov=100",
                            buttons: [{
                                type: "web_url",
                                url: "https://goo.gl/maps/RN13omtnA4u",
                                title: "Otwórz w Mapach Google"
                            }]
                        }]
                    }
                }
            }
        };

        callSendAPI(messageData);
    }

    function sendGenericMessage(recipientId) {
        var messageData = {
            recipient: {
                id: recipientId
            },
            message: {
                attachment: {
                    type: "template",
                    payload: {
                        template_type: "generic",
                        elements: [{
                            title: "rift",
                            subtitle: "Next-generation virtual reality",
                            item_url: "https://www.oculus.com/en-us/rift/",
                            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
                            buttons: [{
                                type: "web_url",
                                url: "https://www.oculus.com/en-us/rift/",
                                title: "Open Web URL"
                            }, {
                                type: "postback",
                                title: "Call Postback",
                                payload: "Payload for first bubble",
                            }]
                        }, {
                            title: "touch",
                            subtitle: "Your Hands, Now in VR",
                            item_url: "https://www.oculus.com/en-us/touch/",
                            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
                            buttons: [{
                                type: "web_url",
                                url: "https://www.oculus.com/en-us/touch/",
                                title: "Open Web URL"
                            }, {
                                type: "postback",
                                title: "Call Postback",
                                payload: "Payload for second bubble",
                            }]
                        }]
                    }
                }
            }
        };

        callSendAPI(messageData);
    }

    function callSendAPI(messageData) {
        request({
            uri: 'https://graph.facebook.com/v2.6/me/messages',
            qs: { access_token: PAGE_ACCESS_TOKEN },
            method: 'POST',
            json: messageData

        }, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var recipientId = body.recipient_id;
                var messageId = body.message_id;

                console.log("Successfully sent generic message with id %s to recipient %s",
                    messageId, recipientId);
            } else {
                console.error("Unable to send message.");
                // console.error(response);
                // console.error(error);
            }
        });
    }

    function receivedDeliveryConfirmation(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var delivery = event.delivery;
        var messageIDs = delivery.mids;
        var watermark = delivery.watermark;
        var sequenceNumber = delivery.seq;

        if (messageIDs) {
            messageIDs.forEach(function(messageID) {
                console.log("Received delivery confirmation for message ID: %s",
                    messageID);
            });
        }

        console.log("All message before %d were delivered.", watermark);
    }

    function receivedMessage(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfMessage = event.timestamp;
        var message = event.message;

        console.log("Received message for user %d and page %d at %d with message:",
            senderID, recipientID, timeOfMessage);
        console.log(JSON.stringify(message));

        var messageId = message.mid;

        // You may get a text or attachment but not both
        var messageText = message.text;
        var messageAttachments = message.attachments;

        if (messageText) {

            // If we receive a text message, check to see if it matches any special
            // keywords and send back the corresponding example. Otherwise, just echo
            // the text we received.
            switch (messageText) {
                case 'image':
                    // sendImageMessage(senderID);
                    break;

                case 'button':
                    sendButtonMessage(senderID);
                    break;

                case 'generic':
                    sendGenericMessage(senderID);
                    break;

                default:
                    sendTextMessage(senderID, messageText);
            }
        } else if (messageAttachments) {
            sendTextMessage(senderID, "Message with attachment received");
        }
    }

    function receivedPostback(event) {
        var senderID = event.sender.id;
        var recipientID = event.recipient.id;
        var timeOfPostback = event.timestamp;

        // The 'payload' param is a developer-defined field which is set in a postback
        // button for Structured Messages.
        var payload = event.postback.payload;

        console.log("Received postback for user %d and page %d with payload '%s' " +
            "at %d", senderID, recipientID, payload, timeOfPostback);

        if (payload) {
            if (payload == 'SELL') {
                sendTextMessage(senderID, "Co chciałbyś sprzedać?");
            } else if (payload == 'BUY') {
                sendTextMessage(senderID, "Co chciałbyś kupić?");
            } else if (payload == 'HELP') {
                sendButtonMessage(senderID);
            } else if (payload == 'PHARMACY') {
                sendPharmacies(senderID);
            }

        } else {
            // When a postback is called, we'll send a message back to the sender to
            // let them know it was successful
            sendTextMessage(senderID, "Przepraszam, ale nie rozumiem. Możesz wyjaśnić jeszcze raz?");
        }

    }

// for Facebook verification
    router
        .get('/', function(req, res) {
            if (req.query['hub.mode'] === 'subscribe' &&
                req.query['hub.verify_token'] === VERIFY_TOKEN) {
                // console.log("Validating webhook");
                res.status(200).send(req.query['hub.challenge']);
            } else {
                // console.error("Failed validation. Make sure the validation tokens match.");
                res.sendStatus(403);
            }
        })
// for sending messages
        .post('/', function (req, res) {
            var data = req.body;

            // Make sure this is a page subscription
            if (data.object == 'page') {
                // Iterate over each entry
                // There may be multiple if batched
                data.entry.forEach(function(pageEntry) {
                    var pageID = pageEntry.id;
                    var timeOfEvent = pageEntry.time;

                    // Iterate over each messaging event
                    pageEntry.messaging.forEach(function(messagingEvent) {
                        if (messagingEvent.optin) {
                            receivedAuthentication(messagingEvent);
                        } else if (messagingEvent.message) {
                            receivedMessage(messagingEvent);
                        } else if (messagingEvent.delivery) {
                            receivedDeliveryConfirmation(messagingEvent);
                        } else if (messagingEvent.postback) {
                            receivedPostback(messagingEvent);
                        } else {
                            console.log("Webhook received unknown messagingEvent: ", messagingEvent);
                        }
                    });
                });

                // Assume all went well.
                //
                // You must send back a 200, within 20 seconds, to let us know you've
                // successfully received the callback. Otherwise, the request will time out.
                res.sendStatus(200);
            }
        });
    return router;
};<|endoftext|>


<commit_before>/*!
 * Bar chart
 */
import Enums from './Enums';
import BaseChart from './BaseChart';
import BarAxis from './BarAxis';
import {select as d3_select,
        max as d3_max,
        sum as d3_sum,
        scaleBand,
        scaleLinear} from 'd3';

import {isFunction as _isFunction, isArray as _isArray } from 'lodash';

export default class BarChart extends BaseChart {
  //
  // ENUMISH
  //

  constructor (selection) {
    super();
    //
    // Create initial object
    //
    this.defaultOptions = {
        padding: 2,
        anchor: 'bottom',
        chartClass: 'chart-bar',
        labelPosition: 'none',
        labelAlign: 'left',
        labelColor: '#000',
        labelFormat: null,
        valuesPosition: 'fit',
        valuesAlign: 'left',
        valuesColor: '#000',
        // valuesFormat: null,
        idPrefix: 'bar-',
        barLayout: 'grouped',
        axis: {

        }
    }

    this.options = Object.assign({}, this.baseOptions, this.defaultOptions);

    if (selection) {
      this.draw(selection);
    }
  }

  draw (selection) {
    const that = this
        , textColor = this.options.theme.textColor
    ;

    this.selection = selection || this.selection;
    if (!this.selection) {
      console.warn('Could not find a HTML node to write to, please check your code');
      return;
    }
    if (!_isFunction(this.selection.node)) {
      this.selection = d3_select(this.selection);
    }

    // force a value for the dataType if there is a multi dimensional dataset
    this.options.barLayout = (this.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) ? this.options.barLayout || Enums.BARLAYOUT_GROUPED : null;

    this.maxValue = this._maxValue;
    this.xScale = this._xScale;
    this.yScale = this._yScale;

    //
    // Create a grouped breadth scale and update the dataset
    //
    if (this.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
      this.groupedXScale = this._groupedXScale;

      if (this.options.barLayout === Enums.BARLAYOUT_STACKED) {
        var lpos;
        for(let data of this.options.data) {
          lpos = 0;
          data.values = data.values.map(function (d) {
                          lpos +=  d.values;
                          d.lpos = lpos;
                          return d;
                        });
        };
      }
    }

    // this.selection.each(function() {

      // var dom = d3_select(this)
      //   , baritems
    const mouseEvents = that.getEventsOfType(['mouse', 'click'])
      // ;

      // remove old
      if (that.svg) {
        that.svg.remove();
      }

      //
      // The main svg element
      //
      this.svg = this.selection.append('svg')
          .attr('class', 'chart barchart')
          .attr('height', this.options.height)
          .attr('width', this.options.width);

      // The actual bars
      var bars = that.svg.selectAll('rect.chart__bar')
                      .data(that.options.data)
                      .enter()
                      .append('g')
                      .attr("id", function(d){return d.id;})
                      .attr('transform', function (d) {
                        var _x = that.options.margin.left + (that.isVertical ? that.xScale(d.label) : 0)
                          , _y = that.isVertical ? that.options.margin.top : that.xScale(d.label)
                        ;
                        return 'translate(' + _x + ',' + _y +')';
                      });

      //
      // Supply additional data if multi dimensional
      //
      if (that.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
        baritems = bars.selectAll("rect")
                        .data(function(d) {
                          return d.values;
                        })
                      .enter()
                        .append('g')
                        .attr('class', 'barItem')
                        .append("rect")
                        .attr('width', this.barWidth)
                        .attr('height', this.barHeight)
                        .attr('x', this.barXPos)
                        .attr('y', this.barYPos)
                          return that.options.color(d.label);
                        });

          for(let e of mouseEvents) {
            baritems.on(e.action, e.method);
          };
      }
      //
      // Single dimension just sets the bars
      //
      else {
        bars.append("rect")
            .attr(dimensions)
            .attr(positions)
            .style("fill", function(d) {
              return that.options.color(d.label);
            });

      }

      // Draw labels if required
      if (that.options.labelPosition !== Enums.LABEL_NONE || that.options.valuesPosition !== 'none') {
        // that.drawLabels();
      }

      if (that.axis.y && that.axis.y.show()) {
        if (!that.yaxis) {
          that.yaxis = new BarAxis('y');
        }
        that.yaxis.anchor(that.options.anchor)
                  .margin(that.options.margin)
                  .height(that.options.height)
                  .scale(that.yScale)
                  .draw(this.svg);
      }

      if (that.axis.x && that.axis.x.show()) {
        if (!that.xaxis) {
          that.xaxis = new BarAxis('x');
        }
        that.xaxis.anchor(that.options.anchor)
                  .margin(that.options.margin)
                  .height(that.options.height)
                  .scale(that.xScale)
                  .draw(this.svg);
      }
      // //
      // Add the draw event to bar charts
      //
      if (_isArray(that.options.on) && that.options.on.length) {
        drawEvent = that.options.on.find(function (o) {
          return o.action === 'draw';
        });

        if (drawEvent && _isFunction (drawEvent.method)) {
          drawEvent.method.call(that);
        }
      }

    this.svg.selectAll(".axis text")
          .attr('fill', textColor)
          .style('font-size', '0.875rem');

    this.svg.selectAll('.axis line, .axis path')
          .attr('fill', 'none')
          .attr('stroke', textColor)
          .attr('stroke-width', '1');
    }

    /**
     * Sets the layout of the chart
     * @param  {String} value - the type of layout to use allowed values = BARLAYOUT_GROUPED | BARLAYOUT_STACKED
     * @return {Mixed}        - the value or chart
     */
    barLayout(value) {
      if (!arguments.length) return this.options.barLayout;
      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== Enums.BARLAYOUT_GROUPED &&
          value !== Enums.BARLAYOUT_STACKED) {
        console.error(value, 'is invalid. Only ', Enums.BARLAYOUT_STACKED, ' OR ', Enums.BARLAYOUT_GROUPED, ' allowed');

        return this;
      }
      this.options.barLayout = value;

      return this;
    }

    /**
     * Sets the direction of the graph
     * @param  {String} value - the direction used, allowed values = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_BOTTOM | ANCHOR_TOP
     * @return {Mixed}        - the value or chart
     */
    anchor (value) {
      if (!arguments.length) return this.options.anchor;
      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== Enums.ANCHOR_LEFT &&
          value !== Enums.ANCHOR_RIGHT &&
          value !== Enums.ANCHOR_TOP &&
          value !== Enums.ANCHOR_BOTTOM) {
        console.error(value, 'is invalid. Only ', Enums.ANCHOR_LEFT, ', ', Enums.ANCHOR_RIGHT, ', ', Enums.ANCHOR_TOP, ' or ', Enums.ANCHOR_BOTTOM, 'allowed');
        return this;
      }
      this.options.anchor = value;

      return this;
    }

    /**
     * Accessor for the axis
     * @return {Object} - returns an object with x and y, each an instance of the BarAxis class
     */
    get axis() {
      let ax = {};
      this.xaxis = this.xaxis || new BarAxis('x', this);
      this.yaxis = this.yaxis || new BarAxis('y', this);
      ax.x = this.xaxis;
      ax.y = this.yaxis;

      return ax;
    }

    //
    //
    // Getters, should have been subclassed maybe?
    //
    //
      /**
   * creates and returns a breadth scale, this is the scale that handles the wideness of a bar, irrespective of dimension
   *
   * @return {scaleBand} - an ordinal scale to calculate the wideness of a bar
   */
  get _xScale () {
    let bRange;

    //
    // Range
    //
    if (this.isVertical) {
      bRange = [0 , this.calculatedWidth];
    } else {
      bRange = [(this.options.margin.top, this.options.height) - this.options.margin.bottom]; // if horisontal the "breadth" equals the height and must be caluculad from vertical dimensions
    }

    //
    // Domain
    //
    const bDomain = this.options.data.map(d => {
                return d.label;
              });

    //
    // Scale
    //
    return scaleBand()
                    .domain(bDomain)
                    .range(bRange, 0.1);
  }

  /**
   * creates and returns a length scale, this is the scale that handles the length of a bar, irrespective of dimension
   *
   * @return {scaleLinear} - a linear scale to calculate the length of a bar
   */
  get _yScale () {
    let lRange;

    //
    // Range
    //
    if (this.isVertical) {
      lRange = [0, this.calculatedHeight];
    } else {
      lRange = [0, this.calculatedWidth];
    }

    //
    // Scale
    //
    return scaleLinear()
                    .domain([0, this._maxValue])
                    .range(lRange);
  }

  /**
   * creates and returns a breadth scale for grouped data, this is the scale that handles the wideness of a bar within a group, irrespective of dimension
   *
   * @return {scaleBand} - an ordinal scale to calculate the wideness of grouped bar
   */
  get _groupedXScale() {
    let bDomain;
    const data = this.options.data[0];

    //
    // Domain
    //
    bDomain = data.values.map(d => {
                return d.label;
              });

    //
    // Scale
    //
    return scaleBand()
                    .domain(bDomain)
                    .range([this.xScale.bandwidth(), 0]);
  }

  /**
   * Returns the max value for a dataset
   * if the dataset is a stacked dataset, the max will then be the max of the sum of all values for a node
   *
   * @return {Number} - The highest value or sum found in the dataset
   */
  get _maxValue () {
    const that = this;
    return d3_max(this.options.data, function (d) {
            // multi dimensional data
            if (that.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
              // grouped data
              if (that.options.barLayout === Enums.BARLAYOUT_GROUPED) {
                return d3_max(d.values, function (d2) {
                  return d2.values;
                });
              }
              // stacked data
              else {
                return d3_sum(d.values, function (d2) {
                  return d2.values;
                });
              }
            }
            // uni dimensional data
            return d.values;
        });
  }

  //
  // MOve to base
  //

    /**
     * returns the width of the bar (could be breadth or length depending on dimension)
     * @return {function} - a function to get the width of a bar
     */
    get barWidth() {
      if (this.isVertical) {
        return this.barBreadth;
      }
      return this.barLength;
    }

    /**
     * returns the height of the bar (could be breadth or length depending on dimension)
     * @return {function} - a function to get the height of a bar
     */
    get barHeight() {
      if (this.isVertical) {
        return this.barLength;
      }
      return this.barBreadth;
    }

    /**
     * Returns a function to get the wideness of a bar
     * @return {function} - function to calculate the wideness of a bar
     */
    get barBreadth() {
      const that = this
          , useGroupedData = (this.options.barLayout === Enums.BARLAYOUT_GROUPED);

      return function (d) {
        let value = useGroupedData ? that.groupedXScale.bandwidth() : that.xScale.bandwidth();
        return Math.max(value, 1);
      };
    }

    /**
     * Returns a function to get the length of a bar
     * @return {function} - function to calculate the length of a bar
     */
    get barLength() {
      const that = this;

      return function(d) {
        return that.yScale(d.values);
      };
    }

    /**
     * [barXPos description]
     * @return {[type]} [description]
     */
    get barXPos() {
      if (this.isVertical) {
        return this.getBreadthPos();
      }
      return this.getLengthPos();
    }

    /**
     * [barYPos description]
     * @return {[type]} [description]
     */
    get barYPos() {
      if (this.isVertical) {
        return  this.getLengthPos();
      }
      return this.getBreadthPos();
    }

    /**
     * Returns a function used to calculate the length of a bar
     * @return {function} - the length pos function
     */
    getBreadthPos() {
      const that = this
          , useGroupedData = (this.options.barLayout === Enums.BARLAYOUT_GROUPED);

      return function(d) {
        return useGroupedData ? that.groupedXScale(d.label) : 0 ;
      };
    }

    /**
     * Returns a function used to calculate the length of a bar
     * @return {function} - the length pos function
     */
    getLengthPos() {
      const that = this
          , useStackedData = (this.options.barLayout === Enums.BARLAYOUT_STACKED);

      return function(d) {
        // stacked data needs to use cummulative sizes
        if (useStackedData) {
          if (that.options.anchor === Enums.ANCHOR_LEFT || that.options.anchor === Enums.ANCHOR_TOP ) {
            return that.yScale(d.lpos) - that.yScale(d.values);
          }
          return that.calculatedHeight - that.yScale(d.lpos);
        }

        if (that.options.anchor === Enums.ANCHOR_TOP || that.options.anchor === Enums.ANCHOR_LEFT ) {
          return 0;
        }

        if (that.options.anchor === Enums.ANCHOR_RIGHT ) {
          return that.calculatedWidth - that.yScale(d.values);
        }

        return that.calculatedHeight - that.yScale(d.values);
      };
    }

}<commit_msg>cleanup code<commit_after>/*!
 * Bar chart
 */
import Enums from './Enums';
import BaseChart from './BaseChart';
import BarAxis from './BarAxis';
import {select as d3_select,
        max as d3_max,
        sum as d3_sum,
        scaleBand,
        scaleLinear} from 'd3';

import {isFunction as _isFunction, isArray as _isArray } from 'lodash';

export default class BarChart extends BaseChart {
  //
  // ENUMISH
  //

  constructor (selection) {
    super();
    //
    // Create initial object
    //
    this.defaultOptions = {
        padding: 2,
        anchor: 'bottom',
        chartClass: 'chart-bar',
        labelPosition: 'none',
        labelAlign: 'left',
        labelColor: '#000',
        labelFormat: null,
        valuesPosition: 'fit',
        valuesAlign: 'left',
        valuesColor: '#000',
        // valuesFormat: null,
        idPrefix: 'bar-',
        barLayout: 'grouped',
        axis: {

        }
    }

    this.options = Object.assign({}, this.baseOptions, this.defaultOptions);

    if (selection) {
      this.draw(selection);
    }
  }

  draw (selection) {
    const that = this
        , textColor = this.options.theme.textColor
    ;

    this.selection = selection || this.selection;
    if (!this.selection) {
      console.warn('Could not find a HTML node to write to, please check your code');
      return;
    }
    if (!_isFunction(this.selection.node)) {
      this.selection = d3_select(this.selection);
    }

    // force a value for the dataType if there is a multi dimensional dataset
    this.options.barLayout = (this.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) ? this.options.barLayout || Enums.BARLAYOUT_GROUPED : null;

    this.maxValue = this._maxValue;
    this.xScale = this._xScale;
    this.yScale = this._yScale;

    //
    // Create a grouped breadth scale and update the dataset
    //
    if (this.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
      this.groupedXScale = this._groupedXScale;

      if (this.options.barLayout === Enums.BARLAYOUT_STACKED) {
        var lpos;
        for(let data of this.options.data) {
          lpos = 0;
          data.values = data.values.map(function (d) {
                          lpos +=  d.values;
                          d.lpos = lpos;
                          return d;
                        });
        };
      }
    }

    // this.selection.each(function() {

      // var dom = d3_select(this)
      //   , baritems
    const mouseEvents = that.getEventsOfType(['mouse', 'click'])
      // ;

      // remove old
      if (this.svg) {
        this.svg.remove();
      }

      //
      // The main svg element
      //
      this.svg = this.selection.append('svg')
          .attr('class', 'chart barchart')
          .attr('height', this.options.height)
          .attr('width', this.options.width);

      // The actual bars
      var bars = this.svg.selectAll('rect.chart__bar')
                      .data(this.options.data)
                      .enter()
                      .append('g')
                      .attr("id", function(d){return d.id;})
                      .attr('transform', function (d) {
                        let _x = that.options.margin.left + (that.isVertical ? that.xScale(d.label) : 0)
                          , _y = that.isVertical ? that.options.margin.top : that.xScale(d.label)
                        ;
                        return 'translate(' + _x + ',' + _y +')';
                      });

      //
      // Supply additional data if multi dimensional
      //
      if (that.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
        let baritems = bars.selectAll("rect")
                        .data(function(d) {
                          return d.values;
                        })
                      .enter()
                        .append('g')
                        .attr('class', 'barItem')
                        .append("rect")
                        .attr('width', this.barWidth)
                        .attr('height', this.barHeight)
                        .attr('x', this.barXPos)
                        .attr('y', this.barYPos)
                        .attr("fill", function(d) {
                          return that.options.color(d.label);
                        });

          for(let e of mouseEvents) {
            baritems.on(e.action, e.method);
          };
      }
      //
      // Single dimension just sets the bars
      //
      else {
        bars.append("rect")
            .attr(dimensions)
            .attr(positions)
            .style("fill", function(d) {
              return that.options.color(d.label);
            });

      }

    //   // Draw labels if required
    //   if (that.options.labelPosition !== Enums.LABEL_NONE || that.options.valuesPosition !== 'none') {
    //     // that.drawLabels();
    //   }

      if (that.axis.y && that.axis.y.show()) {
        if (!that.yaxis) {
          that.yaxis = new BarAxis('y');
        }
        that.yaxis.anchor(that.options.anchor)
                  .margin(that.options.margin)
                  .height(that.options.height)
                  .scale(that.yScale)
                  .draw(this.svg);

        // const yaxis = that.yaxis.get();
      }

      if (that.axis.x && that.axis.x.show()) {
        if (!that.xaxis) {
          that.xaxis = new BarAxis('x');
        }
        that.xaxis.anchor(that.options.anchor)
                  .margin(that.options.margin)
                  .height(that.options.height)
                  .scale(that.xScale)
                  .draw(this.svg);
      }
    //   // //
    //   // Add the draw event to bar charts
    //   //
      if (_isArray(that.options.on) && that.options.on.length) {
        drawEvent = that.options.on.find(function (o) {
          return o.action === 'draw';
        });

        if (drawEvent && _isFunction (drawEvent.method)) {
          drawEvent.method.call(that);
        }
      }

    this.svg.selectAll(".axis text")
          .attr('fill', textColor)
          .style('font-size', '0.875rem');

    this.svg.selectAll('.axis line, .axis path')
          .attr('fill', 'none')
          .attr('stroke', textColor)
          .attr('stroke-width', '1');
    }

    /**
     * Sets the layout of the chart
     * @param  {String} value - the type of layout to use allowed values = BARLAYOUT_GROUPED | BARLAYOUT_STACKED
     * @return {Mixed}        - the value or chart
     */
    barLayout(value) {
      if (!arguments.length) return this.options.barLayout;
      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== Enums.BARLAYOUT_GROUPED &&
          value !== Enums.BARLAYOUT_STACKED) {
        console.error(value, 'is invalid. Only ', Enums.BARLAYOUT_STACKED, ' OR ', Enums.BARLAYOUT_GROUPED, ' allowed');

        return this;
      }
      this.options.barLayout = value;

      return this;
    }

    /**
     * Sets the direction of the graph
     * @param  {String} value - the direction used, allowed values = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_BOTTOM | ANCHOR_TOP
     * @return {Mixed}        - the value or chart
     */
    anchor (value) {
      if (!arguments.length) return this.options.anchor;
      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== Enums.ANCHOR_LEFT &&
          value !== Enums.ANCHOR_RIGHT &&
          value !== Enums.ANCHOR_TOP &&
          value !== Enums.ANCHOR_BOTTOM) {
        console.error(value, 'is invalid. Only ', Enums.ANCHOR_LEFT, ', ', Enums.ANCHOR_RIGHT, ', ', Enums.ANCHOR_TOP, ' or ', Enums.ANCHOR_BOTTOM, 'allowed');
        return this;
      }
      this.options.anchor = value;

      return this;
    }

    /**
     * Accessor for the axis
     * @return {Object} - returns an object with x and y, each an instance of the BarAxis class
     */
    get axis() {
      let ax = {};
      this.xaxis = this.xaxis || new BarAxis('x', this);
      this.yaxis = this.yaxis || new BarAxis('y', this);
      ax.x = this.xaxis;
      ax.y = this.yaxis;

      return ax;
    }

    //
    //
    // Getters, should have been subclassed maybe?
    //
    //
      /**
   * creates and returns a breadth scale, this is the scale that handles the wideness of a bar, irrespective of dimension
   *
   * @return {scaleBand} - an ordinal scale to calculate the wideness of a bar
   */
  get _xScale () {
    let bRange;

    //
    // Range
    //
    if (this.isVertical) {
      bRange = [0 , this.calculatedWidth];
    } else {
      bRange = [this.options.margin.top, this.options.height - this.options.margin.bottom]; // if horisontal the "breadth" equals the height and must be caluculad from vertical dimensions
    }

    //
    // Domain
    //
    const bDomain = this.options.data.map(d => {
                return d.label;
              });

    //
    // Scale
    //
    return scaleBand()
                    .domain(bDomain)
                    .range(bRange, 0.1);
  }

  /**
   * creates and returns a length scale, this is the scale that handles the length of a bar, irrespective of dimension
   *
   * @return {scaleLinear} - a linear scale to calculate the length of a bar
   */
  get _yScale () {
    let lRange;

    //
    // Range
    //
    if (this.isVertical) {
      lRange = [0, this.calculatedHeight];
    } else {
      lRange = [0, this.calculatedWidth];
    }

    //
    // Scale
    //
    return scaleLinear()
                    .domain([0, this._maxValue])
                    .range(lRange);
  }

  /**
   * creates and returns a breadth scale for grouped data, this is the scale that handles the wideness of a bar within a group, irrespective of dimension
   *
   * @return {scaleBand} - an ordinal scale to calculate the wideness of grouped bar
   */
  get _groupedXScale() {
    let bDomain;
    const data = this.options.data[0];

    //
    // Domain
    //
    bDomain = data.values.map(d => {
                return d.label;
              });

    //
    // Scale
    //
    return scaleBand()
                    .domain(bDomain)
                    .range([this.xScale.bandwidth(), 0]);
  }

  /**
   * Returns the max value for a dataset
   * if the dataset is a stacked dataset, the max will then be the max of the sum of all values for a node
   *
   * @return {Number} - The highest value or sum found in the dataset
   */
  get _maxValue () {
    const that = this;
    return d3_max(this.options.data, function (d) {
            // multi dimensional data
            if (that.options.dataType === Enums.DATATYPE_MULTIDIMENSIONAL) {
              // grouped data
              if (that.options.barLayout === Enums.BARLAYOUT_GROUPED) {
                return d3_max(d.values, function (d2) {
                  return d2.values;
                });
              }
              // stacked data
              else {
                return d3_sum(d.values, function (d2) {
                  return d2.values;
                });
              }
            }
            // uni dimensional data
            return d.values;
        });
  }

  //
  // MOve to base
  //

    /**
     * returns the width of the bar (could be breadth or length depending on dimension)
     * @return {function} - a function to get the width of a bar
     */
    get barWidth() {
      if (this.isVertical) {
        return this.barBreadth;
      }
      return this.barLength;
    }

    /**
     * returns the height of the bar (could be breadth or length depending on dimension)
     * @return {function} - a function to get the height of a bar
     */
    get barHeight() {
      if (this.isVertical) {
        return this.barLength;
      }
      return this.barBreadth;
    }

    /**
     * Returns a function to get the wideness of a bar
     * @return {function} - function to calculate the wideness of a bar
     */
    get barBreadth() {
      const that = this
          , useGroupedData = (this.options.barLayout === Enums.BARLAYOUT_GROUPED);

      return function (d) {
        let value = useGroupedData ? that.groupedXScale.bandwidth() : that.xScale.bandwidth();
        return Math.max(value, 1);
      };
    }

    /**
     * Returns a function to get the length of a bar
     * @return {function} - function to calculate the length of a bar
     */
    get barLength() {
      const that = this;

      return function(d) {
        return that.yScale(d.values);
      };
    }

    /**
     * [barXPos description]
     * @return {[type]} [description]
     */
    get barXPos() {
      if (this.isVertical) {
        return this.getBreadthPos();
      }
      return this.getLengthPos();
    }

    /**
     * [barYPos description]
     * @return {[type]} [description]
     */
    get barYPos() {
      if (this.isVertical) {
        return  this.getLengthPos();
      }
      return this.getBreadthPos();
    }

    /**
     * Returns a function used to calculate the length of a bar
     * @return {function} - the length pos function
     */
    getBreadthPos() {
      const that = this
          , useGroupedData = (this.options.barLayout === Enums.BARLAYOUT_GROUPED);

      return function(d) {
        return useGroupedData ? that.groupedXScale(d.label) : 0 ;
      };
    }

    /**
     * Returns a function used to calculate the length of a bar
     * @return {function} - the length pos function
     */
    getLengthPos() {
      const that = this
          , useStackedData = (this.options.barLayout === Enums.BARLAYOUT_STACKED);

      return function(d) {
        // stacked data needs to use cummulative sizes
        if (useStackedData) {
          if (that.options.anchor === Enums.ANCHOR_LEFT || that.options.anchor === Enums.ANCHOR_TOP ) {
            return that.yScale(d.lpos) - that.yScale(d.values);
          }
          return that.calculatedHeight - that.yScale(d.lpos);
        }

        if (that.options.anchor === Enums.ANCHOR_TOP || that.options.anchor === Enums.ANCHOR_LEFT ) {
          return 0;
        }

        if (that.options.anchor === Enums.ANCHOR_RIGHT ) {
          return that.calculatedWidth - that.yScale(d.values);
        }

        return that.calculatedHeight - that.yScale(d.values);
      };
    }

}<|endoftext|>


<commit_before>var Tournament = require('./Tournament');
var Entity = require('../entity');

function HungerGames() {
    Tournament.apply(this, Array.prototype.slice.call(arguments));

    this.ID = 11;
    this.name = "Hunger Games";

    // Gamemode Specific Variables
    this.maxContenders = 12;
    this.baseSpawnPoints = [{
            x: 1600,
            y: 200
        }, {
            x: 3200,
            y: 200
        }, {
            x: 4800,
            y: 200
        }, // Top
        {
            x: 200,
            y: 1600
        }, {
            x: 200,
            y: 3200
        }, {
            x: 200,
            y: 4800
        }, // Left
        {
            x: 6200,
            y: 1600
        }, {
            x: 6200,
            y: 3200
        }, {
            x: 6200,
            y: 4800
        }, // Right
        {
            x: 1600,
            y: 6200
        }, {
            x: 3200,
            y: 6200
        }, {
            x: 4800,
            y: 6200
        } // Bottom
    ];
    this.contenderSpawnPoints;
    this.borderDec = 100; // Border shrinks by this size everytime someone dies
}

module.exports = HungerGames;
HungerGames.prototype = new Tournament();

// Gamemode Specific Functions

HungerGames.prototype.getPos = function() {
    var pos = {
        x: 0,
        y: 0
    };

    // Random Position
    if (this.contenderSpawnPoints.length > 0) {
        var index = Math.floor(Math.random() * this.contenderSpawnPoints.length);
        pos = this.contenderSpawnPoints[index];
        this.contenderSpawnPoints.splice(index, 1);
    }

    return {
        x: pos.x,
        y: pos.y
    };
};

HungerGames.prototype.spawnFood = function(gameServer, mass, pos) {
    var f = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer);
    f.setColor(gameServer.getRandomColor());
    gameServer.addNode(f);
    gameServer.currentFood++;
};

HungerGames.prototype.spawnVirus = function(gameServer, pos) {
    var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, gameServer.config.virusStartMass);
    gameServer.addNode(v);
};

HungerGames.prototype.onPlayerDeath = function(gameServer) {
    var config = gameServer.config;
    config.borderLeft += this.borderDec;
    config.borderRight -= this.borderDec;
    config.borderTop += this.borderDec;
    config.borderBottom -= this.borderDec;

    // Remove all cells
    var len = gameServer.nodes.length;
    for (var i = 0; i < len; i++) {
        var node = gameServer.nodes[i];

        if ((!node) || (node.getType() == 0)) {
            continue;
        }

        // Move
        if (node.position.x < config.borderLeft) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.x > config.borderRight) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.y < config.borderTop) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.y > config.borderBottom) {
            gameServer.removeNode(node);
            i--;
        }
    }
};

// Override

HungerGames.prototype.onServerInit = function(gameServer) {
    // Prepare
    this.prepare(gameServer);

    // Resets spawn points
    this.contenderSpawnPoints = this.baseSpawnPoints.slice();

    // Override config values
    if (gameServer.config.serverBots > this.maxContenders) {
        // The number of bots cannot exceed the maximum amount of contenders
        gameServer.config.serverBots = this.maxContenders;
    }
    gameServer.config.spawnInterval = 20;
    gameServer.config.borderLeft = 0;
    gameServer.config.borderRight = 6400;
    gameServer.config.borderTop = 0;
    gameServer.config.borderBottom = 6400;
    gameServer.config.foodSpawnAmount = 5; // This is hunger games
    gameServer.config.foodStartAmount = 100;
    gameServer.config.foodMaxAmount = 200;
    gameServer.config.foodMass = 2; // Food is scarce, but its worth more
    gameServer.config.virusMinAmount = 10; // We need to spawn some viruses in case someone eats them all
    gameServer.config.virusMaxAmount = 100;
    gameServer.config.ejectSpawnPlayer = 0;
    gameServer.config.playerDisconnectTime = 10; // So that people dont disconnect and stall the game for too long

    // Spawn Initial Virus/Large food
    var mapWidth = gameServer.config.borderRight - gameServer.config.borderLeft;
    var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop;

    // Food
    this.spawnFood(gameServer, 200, {
        x: mapWidth * .5,
        y: mapHeight * .5
    }); // Center
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .4,
        y: mapHeight * .6
    }); //
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .6,
        y: mapHeight * .6
    });
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .4,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .6,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .7,
        y: mapHeight * .5
    }); //
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .3,
        y: mapHeight * .5
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .5,
        y: mapHeight * .7
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .5,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .7,
        y: mapHeight * .625
    }); // Corner
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .625,
        y: mapHeight * .7
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .3,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .4,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .6,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .7,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .3,
        y: mapHeight * .6
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .4,
        y: mapHeight * .7
    });

    // Virus
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .5
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .5
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .5,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .5,
        y: mapHeight * .6
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .3,
        y: mapHeight * .3
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .3,
        y: mapHeight * .7
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .7,
        y: mapHeight * .3
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .7,
        y: mapHeight * .7
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .25,
        y: mapHeight * .6
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .25,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .75,
        y: mapHeight * .6
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .75,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .25
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .25
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .75
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .75
    });
};

HungerGames.prototype.onPlayerSpawn = function(gameServer, player) {
    // Only spawn players if the game hasnt started yet
    if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) {
        player.color = gameServer.getRandomColor(); // Random color
        this.contenders.push(player); // Add to contenders list
        gameServer.spawnPlayer(player, this.getPos());

        if (this.contenders.length == this.maxContenders) {
            // Start the game once there is enough players
            this.startGamePrep(gameServer);
        }
    }
};
<commit_msg>Update HungerGames.js<commit_after>var Tournament = require('./Tournament');
var Entity = require('../entity');

function HungerGames() {
    Tournament.apply(this, Array.prototype.slice.call(arguments));

    this.ID = 11;
    this.name = "Hunger Games";

    // Gamemode Specific Variables
    this.maxContenders = 12;
    this.baseSpawnPoints = [{
            x: 1600,
            y: 200
        }, {
            x: 3200,
            y: 200
        }, {
            x: 4800,
            y: 200
        }, // Top
        {
            x: 200,
            y: 1600
        }, {
            x: 200,
            y: 3200
        }, {
            x: 200,
            y: 4800
        }, // Left
        {
            x: 6200,
            y: 1600
        }, {
            x: 6200,
            y: 3200
        }, {
            x: 6200,
            y: 4800
        }, // Right
        {
            x: 1600,
            y: 6200
        }, {
            x: 3200,
            y: 6200
        }, {
            x: 4800,
            y: 6200
        } // Bottom
    ];
    this.contenderSpawnPoints;
    this.borderDec = 100; // Border shrinks by this size everytime someone dies
}

module.exports = HungerGames;
HungerGames.prototype = new Tournament();

// Gamemode Specific Functions

HungerGames.prototype.getPos = function() {
    var pos = {
        x: 0,
        y: 0
    };

    // Random Position
    if (this.contenderSpawnPoints.length > 0) {
        var index = Math.floor(Math.random() * this.contenderSpawnPoints.length);
        pos = this.contenderSpawnPoints[index];
        this.contenderSpawnPoints.splice(index, 1);
    }

    return {
        x: pos.x,
        y: pos.y
    };
};

HungerGames.prototype.spawnFood = function(gameServer, mass, pos) {
    var f = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer);
    f.setColor(gameServer.getRandomColor());

    gameServer.addNode(f);
    gameServer.currentFood++;
};

HungerGames.prototype.spawnVirus = function(gameServer, pos) {
    var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, gameServer.config.virusStartMass);
    gameServer.addNode(v);
};

HungerGames.prototype.onPlayerDeath = function(gameServer) {
    var config = gameServer.config;
    config.borderLeft += this.borderDec;
    config.borderRight -= this.borderDec;
    config.borderTop += this.borderDec;
    config.borderBottom -= this.borderDec;

    // Remove all cells
    var len = gameServer.nodes.length;
    for (var i = 0; i < len; i++) {
        var node = gameServer.nodes[i];

        if ((!node) || (node.getType() == 0)) {
            continue;
        }

        // Move
        if (node.position.x < config.borderLeft) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.x > config.borderRight) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.y < config.borderTop) {
            gameServer.removeNode(node);
            i--;
        } else if (node.position.y > config.borderBottom) {
            gameServer.removeNode(node);
            i--;
        }
    }
};

// Override

HungerGames.prototype.onServerInit = function(gameServer) {
    // Prepare
    this.prepare(gameServer);

    // Resets spawn points
    this.contenderSpawnPoints = this.baseSpawnPoints.slice();

    // Override config values
    if (gameServer.config.serverBots > this.maxContenders) {
        // The number of bots cannot exceed the maximum amount of contenders
        gameServer.config.serverBots = this.maxContenders;
    }
    gameServer.config.spawnInterval = 20;
    gameServer.config.borderLeft = 0;
    gameServer.config.borderRight = 6400;
    gameServer.config.borderTop = 0;
    gameServer.config.borderBottom = 6400;
    gameServer.config.foodSpawnAmount = 5; // This is hunger games
    gameServer.config.foodStartAmount = 100;
    gameServer.config.foodMaxAmount = 200;
    gameServer.config.foodMass = 2; // Food is scarce, but its worth more
    gameServer.config.virusMinAmount = 10; // We need to spawn some viruses in case someone eats them all
    gameServer.config.virusMaxAmount = 100;
    gameServer.config.ejectSpawnPlayer = 0;
    gameServer.config.playerDisconnectTime = 10; // So that people dont disconnect and stall the game for too long

    // Spawn Initial Virus/Large food
    var mapWidth = gameServer.config.borderRight - gameServer.config.borderLeft;
    var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop;

    // Food
    this.spawnFood(gameServer, 200, {
        x: mapWidth * .5,
        y: mapHeight * .5
    }); // Center
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .4,
        y: mapHeight * .6
    }); //
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .6,
        y: mapHeight * .6
    });
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .4,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 80, {
        x: mapWidth * .6,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .7,
        y: mapHeight * .5
    }); //
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .3,
        y: mapHeight * .5
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .5,
        y: mapHeight * .7
    });
    this.spawnFood(gameServer, 50, {
        x: mapWidth * .5,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .7,
        y: mapHeight * .625
    }); // Corner
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .625,
        y: mapHeight * .7
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .3,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .4,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .6,
        y: mapHeight * .3
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .7,
        y: mapHeight * .4
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .3,
        y: mapHeight * .6
    });
    this.spawnFood(gameServer, 30, {
        x: mapWidth * .4,
        y: mapHeight * .7
    });

    // Virus
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .5
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .5
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .5,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .5,
        y: mapHeight * .6
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .3,
        y: mapHeight * .3
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .3,
        y: mapHeight * .7
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .7,
        y: mapHeight * .3
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .7,
        y: mapHeight * .7
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .25,
        y: mapHeight * .6
    }); //
    this.spawnVirus(gameServer, {
        x: mapWidth * .25,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .75,
        y: mapHeight * .6
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .75,
        y: mapHeight * .4
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .25
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .25
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .6,
        y: mapHeight * .75
    });
    this.spawnVirus(gameServer, {
        x: mapWidth * .4,
        y: mapHeight * .75
    });
};

HungerGames.prototype.onPlayerSpawn = function(gameServer, player) {
    // Only spawn players if the game hasnt started yet
    if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) {
        player.color = gameServer.getRandomColor(); // Random color
        this.contenders.push(player); // Add to contenders list
        gameServer.spawnPlayer(player, this.getPos());

        if (this.contenders.length == this.maxContenders) {
            // Start the game once there is enough players
            this.startGamePrep(gameServer);
        }
    }
};
<|endoftext|>


<commit_before><md-card>
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>

  <md-card-content>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio itaque ea, nostrum odio. Dolores, sed accusantium quasi non, voluptas eius illo quas, saepe voluptate pariatur in deleniti minus sint. Excepturi.
  </md-card-content>
</md-card>

<md-card>
  <md-card-header>
    <md-card-header-text>

Title goes here

Subtitle here

    </md-card-header-text>

    <md-menu md-size="4" md-direction="bottom left">
      <md-button class="md-icon-button" md-menu-trigger>
        <md-icon>more_vert</md-icon>
      </md-button>

      <md-menu-content>
        <md-menu-item>
          Call
          <md-icon>phone</md-icon>
        </md-menu-item>

        <md-menu-item>
          Send a message
          <md-icon>message</md-icon>
        </md-menu-item>
      </md-menu-content>
    </md-menu>
  </md-card-header>

  <md-card-media>

  </md-card-media>

  <md-card-content>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio itaque ea, nostrum odio. Dolores, sed accusantium quasi non, voluptas eius illo quas, saepe voluptate pariatur in deleniti minus sint. Excepturi.
  </md-card-content>
</md-card>

<md-card>
  <md-card-media>

  </md-card-media>

  <md-card-content>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio itaque ea nostrum.
  </md-card-content>
</md-card>

<md-card>
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>
</md-card><commit_msg>Added card and ipsum<commit_after>import style from './style.css'

export default {
  render(h) {
return (
<md-card>
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>

  <md-card-content>
  Actually austin tbh 3 wolf moon single-origin coffee farm-to-table bespoke. Everyday carry occupy try-hard pitchfork meggings. Tilde poke hashtag, raclette pour-over semiotics gochujang 8-bit echo park neutra roof party stumptown kitsch microdosing etsy. Roof party next level tofu meh humblebrag banh mi marfa master cleanse four loko small batch normcore gentrify truffaut fanny pack VHS. Tumeric photo booth microdosing, kickstarter copper mug succulents brooklyn godard air plant authentic kale chips single-origin coffee. VHS yuccie master cleanse unicorn
  </md-card-content>
</md-card>

<md-card>
  <md-card-header>
    <md-card-header-text>

Title goes here

Subtitle here

    </md-card-header-text>

    <md-menu md-size="4" md-direction="bottom left">
      <md-button class="md-icon-button" md-menu-trigger>
        <md-icon>more_vert</md-icon>
      </md-button>

      <md-menu-content>
        <md-menu-item>
          Call
          <md-icon>phone</md-icon>
        </md-menu-item>

        <md-menu-item>
          Send a message
          <md-icon>message</md-icon>
        </md-menu-item>
      </md-menu-content>
    </md-menu>
  </md-card-header>

  <md-card-media>

  </md-card-media>

  <md-card-content>
  , quinoa swag af ramps snackwave irony poke. Aesthetic actually XOXO lomo, DIY beard poke vinyl godard trust fund hoodie skateboard seitan hot chicken mixtape. Drinking vinegar organic occupy, truffaut vinyl palo santo yuccie tbh. Seitan small batch unicorn pinterest 90's hoodie hashtag master cleanse dreamcatcher kitsch. Pinterest flexitarian craft beer celiac kogi crucifix kale chips adaptogen ramps synth portland asymmetrical roof party trust fund. Chillwave jean shorts man bun,
</md-card>

<md-card>
  <md-card-media>

  </md-card-media>

  <md-card-content>
   wayfarers deep v vice twee try-hard gochujang hoodie schlitz humblebrag
  </md-card-content>
  </md-card-content>
</md-card>

<md-card>
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>
</md-card>
    )
  }
}
<|endoftext|>


<commit_before>// STATUS BAR ///////////////////////////////////////

// gives user feedback about pending operations. Draws current status
// to website. Updates info in layer chooser.
window.renderUpdateStatus = function() {
  var progress = 1;

  // portal level display
  var t = '';
  if(!window.isSmartphone()) // space is valuable
    t += 'portals: ';
  var minlvl = getMinPortalLevel();
  if(minlvl === 0)
    t+= 'all';
  else
    t+= 'L'+minlvl+(minlvl<8?'+':'') + '';
  t +='';

  // map status display
  t += ' map: ';

  if (window.mapDataRequest) {
    var status = window.mapDataRequest.getStatus();

    // status.short - short description of status
    // status.long - longer description, for tooltip (optional)
    // status.progress - fractional progress (from 0 to 1; -1 for indeterminate) of current state (optional)
    if (status.long)
      t += ''+status.short+'';
    else
      t += ''+status.short+'';

    if (status.progress !== undefined) {
      if(status.progress !== -1)
        t += ' '+Math.floor(status.progress*100)+'%';
      progress = status.progress;
    }
  } else {
    // no mapDataRequest object - no status known
    t += '...unknown...';
  }

/*
  if(mapRunsUserAction)
    t += 'paused</span';
  else if(isIdle())
    t += 'Idle';
  else if(window.requests._quickRefreshPending)
    t += 'refreshing';
  else if(window.activeRequests.length > 0)
    t += window.activeRequests.length + ' requests';
  else {
    // tooltip with detailed tile counts
    t += '';

    // basic error/out of date/up to date message
    if (window.statusErrorMapTiles) t += 'Errors';
    else if (window.statusStaleMapTiles) t += 'Out of date';
    else t += 'Up to date';

    t += '';

  }
*/
  t += '';

  //request status
  if (window.activeRequests.length > 0)
    t += ' ' + window.activeRequests.length + ' requests';
  if (window.failedRequestCount > 0)
    t += ' ' + window.failedRequestCount + ' failed'

  $('#innerstatus').html(t);
  //$('#updatestatus').click(function() { startRefreshTimeout(10); });
  //. ⟳';

  if(progress == 1 && window.activeRequests.length > 0) {
    // we don't know the exact progress, but we have requests (e.g. chat) running, so show it as indeterminate.
    progress = -1;
  }

  if (typeof android !== 'undefined' && android && android.setProgress)
    android.setProgress(progress);
}
<commit_msg>possible performance improvement - status bar display updated moved to a timer. so 1. it happens after any delays rendering in the browser and 2. performance (no repeated updates one after another - possible browser style recalculations)<commit_after>// STATUS BAR ///////////////////////////////////////

// gives user feedback about pending operations. Draws current status
// to website. Updates info in layer chooser.
window.renderUpdateStatusTimer_ = undefined;

window.renderUpdateStatus = function() {
  var progress = 1;

  // portal level display
  var t = '';
  if(!window.isSmartphone()) // space is valuable
    t += 'portals: ';
  var minlvl = getMinPortalLevel();
  if(minlvl === 0)
    t+= 'all';
  else
    t+= 'L'+minlvl+(minlvl<8?'+':'') + '';
  t +='';

  // map status display
  t += ' map: ';

  if (window.mapDataRequest) {
    var status = window.mapDataRequest.getStatus();

    // status.short - short description of status
    // status.long - longer description, for tooltip (optional)
    // status.progress - fractional progress (from 0 to 1; -1 for indeterminate) of current state (optional)
    if (status.long)
      t += ''+status.short+'';
    else
      t += ''+status.short+'';

    if (status.progress !== undefined) {
      if(status.progress !== -1)
        t += ' '+Math.floor(status.progress*100)+'%';
      progress = status.progress;
    }
  } else {
    // no mapDataRequest object - no status known
    t += '...unknown...';
  }

  t += '';

  //request status
  if (window.activeRequests.length > 0)
    t += ' ' + window.activeRequests.length + ' requests';
  if (window.failedRequestCount > 0)
    t += ' ' + window.failedRequestCount + ' failed'

  //it's possible that updating the status bar excessively causes some performance issues. so rather than doing it
  //immediately, delay it to the next javascript event loop, cancelling any pending update
  // will also cause any browser-related rendering to occur first, before the status actually updates

  if (window.renderUpdateStatusTimer_) clearTimeout(window.renderUpdateStatusTimer_);

  window.renderUpdateStatusTimer_ = setTimeout ( function() {
    window.renderUpdateStatusTimer_ = undefined;

    $('#innerstatus').html(t);
    //$('#updatestatus').click(function() { startRefreshTimeout(10); });
    //. ⟳';

    if(progress == 1 && window.activeRequests.length > 0) {
      // we don't know the exact progress, but we have requests (e.g. chat) running, so show it as indeterminate.
      progress = -1;
    }

    if (typeof android !== 'undefined' && android && android.setProgress)
      android.setProgress(progress);
  }, 0);

}
<|endoftext|>


<commit_before>const data = require('./data');
const constants = require('./constants');
const helpers = require('./helpers');
const TextDecoder = require('../lib/encoding').TextDecoder;

class Blowfish {

    static get MODE() {
        return constants.MODE;
    }

    static get PADDING() {
        return constants.PADDING;
    }

    static get TYPE() {
        return constants.TYPE;
    }

    constructor(key, mode = constants.MODE.ECB, padding = constants.PADDING.PKCS5) {
        if (!helpers.isStringOrBuffer(key)) {
            throw new Error('Key should be a string or an ArrayBuffer / Buffer');
        }
        if (Object.keys(constants.MODE).indexOf(mode) < 0) {
            throw new Error('Unsupported mode');
        }
        if (Object.keys(constants.PADDING).indexOf(padding) < 0) {
            throw new Error('Unsupported padding');
        }

        this.key = helpers.toUint8Array(key);
        this.mode = mode;
        this.padding = padding;
        this.iv = new Uint8Array(0);
        this.p = data.P.slice();
        this.s = [
            data.S0.slice(),
            data.S1.slice(),
            data.S2.slice(),
            data.S3.slice()
        ];
        this._generateSubkeys();
    }

    setIv(iv) {
        if (!helpers.isStringOrBuffer(iv)) {
            throw new Error('IV should be a string or an ArrayBuffer / Buffer');
        }
        iv = helpers.toUint8Array(iv);
        if (iv.length !== 8) {
            throw new Error('IV should be 8 byte length');
        }
        this.iv = iv;
    }

    encode(data, returnType = constants.TYPE.UINT8_ARRAY) {
        if (!helpers.isStringOrBuffer(data)) {
            throw new Error('Encode data should be a string or an ArrayBuffer / Buffer');
        }
        if (this.mode !== constants.MODE.ECB && this.iv.length === 0) {
            throw new Error('IV is not set');
        }

        data = helpers.pad(helpers.toUint8Array(data), this.padding);

        switch (this.mode) {
            case constants.MODE.ECB: {
                data = this._encodeECB(data);
                break;
            }
            case constants.MODE.CBC: {
                data = this._encodeCBC(data);
                break;
            }
        }

        switch (returnType) {
            case constants.TYPE.UINT8_ARRAY: {
                return data;
            }
            case constants.TYPE.STRING: {
                return (new TextDecoder()).decode(data);
            }
            default: {
                throw new Error('Unsupported return type');
            }
        }
    }

    decode(data, returnType = constants.TYPE.STRING) {
        if (!helpers.isStringOrBuffer(data)) {
            throw new Error('Decode data should be a string or an ArrayBuffer / Buffer');
        }
        if (this.mode !== constants.MODE.ECB && this.iv.length === 0) {
            throw new Error('IV is not set');
        }
        data = helpers.toUint8Array(data);

        if (data.length % 8 !== 0) {
            throw new Error('Decoded data should be multiple of 8 bytes');
        }

        switch (this.mode) {
            case constants.MODE.ECB: {
                data = this._decodeECB(data);
                break;
            }
            case constants.MODE.CBC: {
                data = this._decodeCBC(data);
                break;
            }
        }

        data = helpers.unpad(data, this.padding);

        switch (returnType) {
            case constants.TYPE.UINT8_ARRAY: {
                return data;
            }
            case constants.TYPE.STRING: {
                return (new TextDecoder()).decode(data);
            }
            default: {
                throw new Error('Unsupported return type');
            }
        }
    }

    _generateSubkeys() {
        const key = helpers.expandKey(this.key);
        for (let i = 0, j = 0; i < 18; i++, j += 4) {
            const n = helpers.packFourBytes(key[j], key[j + 1], key[j + 2], key[j + 3]);
            this.p[i] = helpers.xor(this.p[i], n);
        }
        let l = 0;
        let r = 0;
        for (let i = 0; i < 18; i += 2) {
            [l, r] = this._encryptBlock(l, r);
            this.p[i] = l;
            this.p[i + 1] = r;
        }
        for (let i = 0; i < 4; i++) {
            for (let j = 0; j < 256; j += 2) {
                [l, r] = this._encryptBlock(l, r);
                this.s[i][j] = l;
                this.s[i][j + 1] = r;
            }
        }
    }

    _encryptBlock(l, r) {
        for (let i = 0; i < 16; i++) {
            l = helpers.xor(l, this.p[i]);
            r = helpers.xor(r, this._f(l));
            [l, r] = [r, l];
        }
        [l, r] = [r, l];
        r = helpers.xor(r, this.p[16]);
        l = helpers.xor(l, this.p[17]);
        return [l, r];
    }

    _decryptBlock(l, r) {
        for (let i = 17; i > 1; i--) {
            l = helpers.xor(l, this.p[i]);
            r = helpers.xor(r, this._f(l));
            [l, r] = [r, l];
        }
        [l, r] = [r, l];
        r = helpers.xor(r, this.p[1]);
        l = helpers.xor(l, this.p[0]);
        return [l, r];
    }

    _f(x) {
        const a = (x >>> 24) & 0xFF;
        const b = (x >>> 16) & 0xFF;
        const c = (x >>> 8) & 0xFF;
        const d = x & 0xFF;

        let res = helpers.sumMod32(this.s[0][a], this.s[1][b]);
        res = helpers.xor(res, this.s[2][c]);
        return helpers.sumMod32(res, this.s[3][d]);
    }

    _encodeECB(bytes) {
        const encoded = new Uint8Array(bytes.length);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = this._encryptBlock(l, r);
            encoded.set(helpers.unpackFourBytes(l), i);
            encoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return encoded;
    }

    _encodeCBC(bytes) {
        const encoded = new Uint8Array(bytes.length);
        let prevL = helpers.packFourBytes(this.iv[0], this.iv[1], this.iv[2], this.iv[3]);
        let prevR = helpers.packFourBytes(this.iv[4], this.iv[5], this.iv[6], this.iv[7]);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = [helpers.xor(prevL, l), helpers.xor(prevR, r)];
            [l, r] = this._encryptBlock(l, r);
            [prevL, prevR] = [l, r];
            encoded.set(helpers.unpackFourBytes(l), i);
            encoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return encoded;
    }

    _decodeECB(bytes) {
        const decoded = new Uint8Array(bytes.length);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = this._decryptBlock(l, r);
            decoded.set(helpers.unpackFourBytes(l), i);
            decoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return decoded;
    }

    _decodeCBC(bytes) {
        const decoded = new Uint8Array(bytes.length);
        let prevL = helpers.packFourBytes(this.iv[0], this.iv[1], this.iv[2], this.iv[3]);
        let prevR = helpers.packFourBytes(this.iv[4], this.iv[5], this.iv[6], this.iv[7]);
        let prevLTmp;
        let prevRTmp;
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [prevLTmp, prevRTmp] = [l, r];
            [l, r] = this._decryptBlock(l, r);
            [l, r] = [helpers.xor(prevL, l), helpers.xor(prevR, r)];
            [prevL, prevR] = [prevLTmp, prevRTmp];
            decoded.set(helpers.unpackFourBytes(l), i);
            decoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return decoded;
    }
}

module.exports = Blowfish;
<commit_msg>remove _generateSubkeys<commit_after>const data = require('./data');
const constants = require('./constants');
const helpers = require('./helpers');
const TextDecoder = require('../lib/encoding').TextDecoder;

class Blowfish {

    static get MODE() {
        return constants.MODE;
    }

    static get PADDING() {
        return constants.PADDING;
    }

    static get TYPE() {
        return constants.TYPE;
    }

    constructor(key, mode = constants.MODE.ECB, padding = constants.PADDING.PKCS5) {
        if (!helpers.isStringOrBuffer(key)) {
            throw new Error('Key should be a string or an ArrayBuffer / Buffer');
        }
        if (Object.keys(constants.MODE).indexOf(mode) < 0) {
            throw new Error('Unsupported mode');
        }
        if (Object.keys(constants.PADDING).indexOf(padding) < 0) {
            throw new Error('Unsupported padding');
        }

        this.mode = mode;
        this.padding = padding;
        this.iv = new Uint8Array(0);
        this.p = data.P.slice();
        this.s = [
            data.S0.slice(),
            data.S1.slice(),
            data.S2.slice(),
            data.S3.slice()
        ];

        key = helpers.expandKey(helpers.toUint8Array(key));
        for (let i = 0, j = 0; i < 18; i++, j += 4) {
            const n = helpers.packFourBytes(key[j], key[j + 1], key[j + 2], key[j + 3]);
            this.p[i] = helpers.xor(this.p[i], n);
        }
        let l = 0;
        let r = 0;
        for (let i = 0; i < 18; i += 2) {
            [l, r] = this._encryptBlock(l, r);
            this.p[i] = l;
            this.p[i + 1] = r;
        }
        for (let i = 0; i < 4; i++) {
            for (let j = 0; j < 256; j += 2) {
                [l, r] = this._encryptBlock(l, r);
                this.s[i][j] = l;
                this.s[i][j + 1] = r;
            }
        }
    }

    setIv(iv) {
        if (!helpers.isStringOrBuffer(iv)) {
            throw new Error('IV should be a string or an ArrayBuffer / Buffer');
        }
        iv = helpers.toUint8Array(iv);
        if (iv.length !== 8) {
            throw new Error('IV should be 8 byte length');
        }
        this.iv = iv;
    }

    encode(data, returnType = constants.TYPE.UINT8_ARRAY) {
        if (!helpers.isStringOrBuffer(data)) {
            throw new Error('Encode data should be a string or an ArrayBuffer / Buffer');
        }
        if (this.mode !== constants.MODE.ECB && this.iv.length === 0) {
            throw new Error('IV is not set');
        }

        data = helpers.pad(helpers.toUint8Array(data), this.padding);

        switch (this.mode) {
            case constants.MODE.ECB: {
                data = this._encodeECB(data);
                break;
            }
            case constants.MODE.CBC: {
                data = this._encodeCBC(data);
                break;
            }
        }

        switch (returnType) {
            case constants.TYPE.UINT8_ARRAY: {
                return data;
            }
            case constants.TYPE.STRING: {
                return (new TextDecoder()).decode(data);
            }
            default: {
                throw new Error('Unsupported return type');
            }
        }
    }

    decode(data, returnType = constants.TYPE.STRING) {
        if (!helpers.isStringOrBuffer(data)) {
            throw new Error('Decode data should be a string or an ArrayBuffer / Buffer');
        }
        if (this.mode !== constants.MODE.ECB && this.iv.length === 0) {
            throw new Error('IV is not set');
        }
        data = helpers.toUint8Array(data);

        if (data.length % 8 !== 0) {
            throw new Error('Decoded data should be multiple of 8 bytes');
        }

        switch (this.mode) {
            case constants.MODE.ECB: {
                data = this._decodeECB(data);
                break;
            }
            case constants.MODE.CBC: {
                data = this._decodeCBC(data);
                break;
            }
        }

        data = helpers.unpad(data, this.padding);

        switch (returnType) {
            case constants.TYPE.UINT8_ARRAY: {
                return data;
            }
            case constants.TYPE.STRING: {
                return (new TextDecoder()).decode(data);
            }
            default: {
                throw new Error('Unsupported return type');
            }
        }
    }

    _encryptBlock(l, r) {
        for (let i = 0; i < 16; i++) {
            l = helpers.xor(l, this.p[i]);
            r = helpers.xor(r, this._f(l));
            [l, r] = [r, l];
        }
        [l, r] = [r, l];
        r = helpers.xor(r, this.p[16]);
        l = helpers.xor(l, this.p[17]);
        return [l, r];
    }

    _decryptBlock(l, r) {
        for (let i = 17; i > 1; i--) {
            l = helpers.xor(l, this.p[i]);
            r = helpers.xor(r, this._f(l));
            [l, r] = [r, l];
        }
        [l, r] = [r, l];
        r = helpers.xor(r, this.p[1]);
        l = helpers.xor(l, this.p[0]);
        return [l, r];
    }

    _f(x) {
        const a = (x >>> 24) & 0xFF;
        const b = (x >>> 16) & 0xFF;
        const c = (x >>> 8) & 0xFF;
        const d = x & 0xFF;

        let res = helpers.sumMod32(this.s[0][a], this.s[1][b]);
        res = helpers.xor(res, this.s[2][c]);
        return helpers.sumMod32(res, this.s[3][d]);
    }

    _encodeECB(bytes) {
        const encoded = new Uint8Array(bytes.length);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = this._encryptBlock(l, r);
            encoded.set(helpers.unpackFourBytes(l), i);
            encoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return encoded;
    }

    _encodeCBC(bytes) {
        const encoded = new Uint8Array(bytes.length);
        let prevL = helpers.packFourBytes(this.iv[0], this.iv[1], this.iv[2], this.iv[3]);
        let prevR = helpers.packFourBytes(this.iv[4], this.iv[5], this.iv[6], this.iv[7]);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = [helpers.xor(prevL, l), helpers.xor(prevR, r)];
            [l, r] = this._encryptBlock(l, r);
            [prevL, prevR] = [l, r];
            encoded.set(helpers.unpackFourBytes(l), i);
            encoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return encoded;
    }

    _decodeECB(bytes) {
        const decoded = new Uint8Array(bytes.length);
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [l, r] = this._decryptBlock(l, r);
            decoded.set(helpers.unpackFourBytes(l), i);
            decoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return decoded;
    }

    _decodeCBC(bytes) {
        const decoded = new Uint8Array(bytes.length);
        let prevL = helpers.packFourBytes(this.iv[0], this.iv[1], this.iv[2], this.iv[3]);
        let prevR = helpers.packFourBytes(this.iv[4], this.iv[5], this.iv[6], this.iv[7]);
        let prevLTmp;
        let prevRTmp;
        for (let i = 0; i < bytes.length; i += 8) {
            let l = helpers.packFourBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            let r = helpers.packFourBytes(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]);
            [prevLTmp, prevRTmp] = [l, r];
            [l, r] = this._decryptBlock(l, r);
            [l, r] = [helpers.xor(prevL, l), helpers.xor(prevR, r)];
            [prevL, prevR] = [prevLTmp, prevRTmp];
            decoded.set(helpers.unpackFourBytes(l), i);
            decoded.set(helpers.unpackFourBytes(r), i + 4);
        }
        return decoded;
    }
}

module.exports = Blowfish;
<|endoftext|>