<commit_before><commit_msg>remove extra jquery<commit_after><|endoftext|>


<commit_before>(function(){
    jQuery.fn.tap = function(fn /* [callback_arg], [callback_arg], [...] */) {
        var args = jQuery.makeArray(arguments);
        args.unshift();

        fn.apply(this, args);
        return this;
    };
}());<commit_msg>license notice<commit_after>/*!
 * ©2009 Jiayong Ou
 *
 * License (MIT):
 * http://github.com/jou/jquery.tap.js/blob/master/LICENSE
 */
(function(){
    jQuery.fn.tap = function(fn /* [callback_arg], [callback_arg], [...] */) {
        var args = jQuery.makeArray(arguments);
        args.unshift();

        fn.apply(this, args);
        return this;
    };
}());<|endoftext|>


<commit_before>'use strict';

function draw_logic(){
    for(var vertex in vertices){
        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertex]['dy']
        );
        buffer.lineTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();

        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.lineTo(
          vertices[vertices[vertex]['parent']]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();
    }

    // Draw vertices_amount and lineWidth.
    buffer.fillText(
      vertices_amount,
      0,
      25
    );
    buffer.fillText(
      buffer.lineWidth,
      0,
      50
    );
}

function logic(){
    for(var vertex in vertices){
        vertices[vertex]['loop'] += vertices[vertex]['speed'];
        if(vertices[vertex]['loop'] > 359){
            vertices[vertex]['loop'] -= 360;

        }else if(vertices[vertex]['loop'] < 0){
            vertices[vertex]['loop'] += 360;
        }

        var rotation = vertices[vertex]['loop'] * degree;

        vertices[vertex]['dx'] =
          x
          + vertices[vertex]['x']
          + vertices[vertex]['radius']
          * Math.cos(rotation);
        vertices[vertex]['dy'] =
          y
          + vertices[vertex]['y']
          + vertices[vertex]['radius']
          * Math.sin(rotation);
    }
}

function randomize(){
    vertices.length = 0;

    var loop_counter = vertices_amount - 1;
    do{
        vertices.push({
          'dx': 0,
          'dy': 0,
          'loop': Math.floor(Math.random() * 50) + 50,
          'parent': vertices_amount - loop_counter,
          'radius': Math.random() * 20 + 5,
          'speed': Math.floor(Math.random() * 10) - 5 * 2,
          'x': Math.floor(Math.random() * 500) - 250,
          'y': Math.floor(Math.random() * 500) - 250,
        });
    }while(loop_counter--);

    vertices[vertices_amount - 1]['parent'] = 0;
}

function resize_logic(){
    buffer.fillStyle = '#fff';
    buffer.font = '23pt sans-serif';
    buffer.lineJoin = 'round';
    buffer.strokeStyle = '#fff';

    randomize();
}

var degree = Math.PI / 180;
var vertices = [];
var vertices_amount = 23;

window.onload = function(e){
    init_canvas();
    init_input(
      {
        27: {
          'todo': function(){
              buffer.lineWidth = 1;
              vertices_amount = 23;
              randomize();
          },
        },
        65: {
          'todo': function(){
              buffer.lineWidth = Math.max(
                buffer.lineWidth - 1,
                1
              );
          },
        },
        68: {
          'todo': function(){
              buffer.lineWidth += 1;
          },
        },
        83: {
          'todo': function(){
              vertices_amount = Math.max(
                vertices_amount - 1,
                2
              );
              randomize();
          },
        },
        87: {
          'todo': function(){
              vertices_amount += 1;
              randomize();
          },
        },
      },
      {
        'mousedown': {
          'todo': function(){
              randomize();
          },
        },
      }
    );
};
<commit_msg>simplified input init<commit_after>'use strict';

function draw_logic(){
    for(var vertex in vertices){
        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertex]['dy']
        );
        buffer.lineTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();

        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.lineTo(
          vertices[vertices[vertex]['parent']]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();
    }

    // Draw vertices_amount and lineWidth.
    buffer.fillText(
      vertices_amount,
      0,
      25
    );
    buffer.fillText(
      buffer.lineWidth,
      0,
      50
    );
}

function logic(){
    for(var vertex in vertices){
        vertices[vertex]['loop'] += vertices[vertex]['speed'];
        if(vertices[vertex]['loop'] > 359){
            vertices[vertex]['loop'] -= 360;

        }else if(vertices[vertex]['loop'] < 0){
            vertices[vertex]['loop'] += 360;
        }

        var rotation = vertices[vertex]['loop'] * degree;

        vertices[vertex]['dx'] =
          x
          + vertices[vertex]['x']
          + vertices[vertex]['radius']
          * Math.cos(rotation);
        vertices[vertex]['dy'] =
          y
          + vertices[vertex]['y']
          + vertices[vertex]['radius']
          * Math.sin(rotation);
    }
}

function randomize(){
    vertices.length = 0;

    var loop_counter = vertices_amount - 1;
    do{
        vertices.push({
          'dx': 0,
          'dy': 0,
          'loop': Math.floor(Math.random() * 50) + 50,
          'parent': vertices_amount - loop_counter,
          'radius': Math.random() * 20 + 5,
          'speed': Math.floor(Math.random() * 10) - 5 * 2,
          'x': Math.floor(Math.random() * 500) - 250,
          'y': Math.floor(Math.random() * 500) - 250,
        });
    }while(loop_counter--);

    vertices[vertices_amount - 1]['parent'] = 0;
}

function resize_logic(){
    buffer.fillStyle = '#fff';
    buffer.font = '23pt sans-serif';
    buffer.lineJoin = 'round';
    buffer.strokeStyle = '#fff';

    randomize();
}

var degree = Math.PI / 180;
var vertices = [];
var vertices_amount = 23;

window.onload = function(e){
    init_canvas();
    init_input(
      {
        27: {
          'todo': function(){
              buffer.lineWidth = 1;
              vertices_amount = 23;
              randomize();
          },
        },
        65: {
          'todo': function(){
              buffer.lineWidth = Math.max(
                buffer.lineWidth - 1,
                1
              );
          },
        },
        68: {
          'todo': function(){
              buffer.lineWidth += 1;
          },
        },
        83: {
          'todo': function(){
              vertices_amount = Math.max(
                vertices_amount - 1,
                2
              );
              randomize();
          },
        },
        87: {
          'todo': function(){
              vertices_amount += 1;
              randomize();
          },
        },
      },
      {
        'mousedown': {
          'todo': randomize,
        },
      }
    );
};
<|endoftext|>


<commit_before>import React from 'react';
import {
  Text,
  Clipboard,
  StyleSheet,
  TouchableWithoutFeedback,
  View,
} from 'react-native';

import MessageText from './MessageText';
import MessageImage from './MessageImage';
import Time from './Time';

export default class Bubble extends React.Component {
  constructor(props) {
    super(props);
    this.onLongPress = this.onLongPress.bind(this);
  }

  handleBubbleToNext() {
    if (this.props.isSameUser(this.props.currentMessage, this.props.nextMessage) && this.props.isSameDay(this.props.currentMessage, this.props.nextMessage)) {
      return StyleSheet.flatten([styles[this.props.position].containerToNext, this.props.containerToNextStyle[this.props.position]]);
    }
    return null;
  }

  handleBubbleToPrevious() {
    if (this.props.isSameUser(this.props.currentMessage, this.props.previousMessage) && this.props.isSameDay(this.props.currentMessage, this.props.previousMessage)) {
      return StyleSheet.flatten([styles[this.props.position].containerToPrevious, this.props.containerToPreviousStyle[this.props.position]]);
    }
    return null;
  }

  renderMessageText() {
    if (this.props.currentMessage.text) {
      const {containerStyle, wrapperStyle, ...messageTextProps} = this.props;
      if (this.props.renderMessageText) {
        return this.props.renderMessageText(messageTextProps);
      }
      return <MessageText {...messageTextProps}/>;
    }
    return null;
  }

  renderMessageImage() {
    if (this.props.currentMessage.image) {
      const {containerStyle, wrapperStyle, ...messageImageProps} = this.props;
      if (this.props.renderMessageImage) {
        return this.props.renderMessageImage(messageImageProps);
      }
      return <MessageImage {...messageImageProps}/>;
    }
    return null;
  }

  renderTicks() {
    if (this.props.renderTicks) {
        return this.props.renderTicks(currentMessage);
    }

    const {currentMessage} = this.props;
    if (currentMessage.user._id !== this.props.user._id) {
        return;
    }
    if (currentMessage.sent || currentMessage.received) {
      return (
        <View style={styles.tickView}>
          {currentMessage.sent && <Text style={styles.tick}>✓</Text>}
          {currentMessage.received && <Text style={styles.tick}>✓</Text>}
        </View>
      )
    }
  }

  renderTime() {
    if (this.props.currentMessage.createdAt) {
      const {containerStyle, wrapperStyle, ...timeProps} = this.props;
      if (this.props.renderTime) {
        return this.props.renderTime(timeProps);
      }
      return <Time {...timeProps}/>;
    }
    return null;
  }

  renderCustomView() {
    if (this.props.renderCustomView) {
      return this.props.renderCustomView(this.props);
    }
    return null;
  }

  onLongPress() {
    if (this.props.onLongPress) {
      this.props.onLongPress(this.context);
    } else {
      if (this.props.currentMessage.text) {
        const options = [
          'Copy Text',
          'Cancel',
        ];
        const cancelButtonIndex = options.length - 1;
        this.context.actionSheet().showActionSheetWithOptions({
          options,
          cancelButtonIndex,
        },
        (buttonIndex) => {
          switch (buttonIndex) {
            case 0:
              Clipboard.setString(this.props.currentMessage.text);
              break;
          }
        });
      }
    }
  }

  render() {
    return (
      <View style={[styles[this.props.position].container, this.props.containerStyle[this.props.position]]}>
        <View style={[styles[this.props.position].wrapper, this.props.wrapperStyle[this.props.position], this.handleBubbleToNext(), this.handleBubbleToPrevious()]}>
          <TouchableWithoutFeedback
            onLongPress={this.onLongPress}
            accessibilityTraits="text"
            {...this.props.touchableProps}
          >
            <View>
              {this.renderCustomView()}
              {this.renderMessageImage()}
              {this.renderMessageText()}
              <View style={styles.bottom}>
                {this.renderTime()}
                {this.renderTicks()}
              </View>
            </View>
          </TouchableWithoutFeedback>
        </View>
      </View>
    );
  }
}

const styles = {
  left: StyleSheet.create({
    container: {
      flex: 1,
      alignItems: 'flex-start',
    },
    wrapper: {
      borderRadius: 15,
      backgroundColor: '#f0f0f0',
      marginRight: 60,
      minHeight: 20,
      justifyContent: 'flex-end',
    },
    containerToNext: {
      borderBottomLeftRadius: 3,
    },
    containerToPrevious: {
      borderTopLeftRadius: 3,
    },
  }),
  right: StyleSheet.create({
    container: {
      flex: 1,
      alignItems: 'flex-end',
    },
    wrapper: {
      borderRadius: 15,
      backgroundColor: '#0084ff',
      marginLeft: 60,
      minHeight: 20,
      justifyContent: 'flex-end',
    },
    containerToNext: {
      borderBottomRightRadius: 3,
    },
    containerToPrevious: {
      borderTopRightRadius: 3,
    },
  }),
  bottom: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  tick: {
    fontSize: 10,
    backgroundColor: 'transparent',
    color: 'white',
  },
  tickView: {
    flexDirection: 'row',
    marginRight: 10,
  }
};

Bubble.contextTypes = {
  actionSheet: React.PropTypes.func,
};

Bubble.defaultProps = {
  touchableProps: {},
  onLongPress: null,
  renderMessageImage: null,
  renderMessageText: null,
  renderCustomView: null,
  renderTime: null,
  isSameUser: () => {},
  isSameDay: () => {},
  position: 'left',
  currentMessage: {
    text: null,
    createdAt: null,
    image: null,
  },
  nextMessage: {},
  previousMessage: {},
  containerStyle: {},
  wrapperStyle: {},
  containerToNextStyle: {},
  containerToPreviousStyle: {},
};

Bubble.propTypes = {
  touchableProps: React.PropTypes.object,
  onLongPress: React.PropTypes.func,
  renderMessageImage: React.PropTypes.func,
  renderMessageText: React.PropTypes.func,
  renderCustomView: React.PropTypes.func,
  renderTime: React.PropTypes.func,
  isSameUser: React.PropTypes.func,
  isSameDay: React.PropTypes.func,
  position: React.PropTypes.oneOf(['left', 'right']),
  currentMessage: React.PropTypes.object,
  nextMessage: React.PropTypes.object,
  previousMessage: React.PropTypes.object,
  containerStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  wrapperStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  containerToNextStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  containerToPreviousStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
};
<commit_msg>renderTick: Move the constant declaration to the top of the function<commit_after>import React from 'react';
import {
  Text,
  Clipboard,
  StyleSheet,
  TouchableWithoutFeedback,
  View,
} from 'react-native';

import MessageText from './MessageText';
import MessageImage from './MessageImage';
import Time from './Time';

export default class Bubble extends React.Component {
  constructor(props) {
    super(props);
    this.onLongPress = this.onLongPress.bind(this);
  }

  handleBubbleToNext() {
    if (this.props.isSameUser(this.props.currentMessage, this.props.nextMessage) && this.props.isSameDay(this.props.currentMessage, this.props.nextMessage)) {
      return StyleSheet.flatten([styles[this.props.position].containerToNext, this.props.containerToNextStyle[this.props.position]]);
    }
    return null;
  }

  handleBubbleToPrevious() {
    if (this.props.isSameUser(this.props.currentMessage, this.props.previousMessage) && this.props.isSameDay(this.props.currentMessage, this.props.previousMessage)) {
      return StyleSheet.flatten([styles[this.props.position].containerToPrevious, this.props.containerToPreviousStyle[this.props.position]]);
    }
    return null;
  }

  renderMessageText() {
    if (this.props.currentMessage.text) {
      const {containerStyle, wrapperStyle, ...messageTextProps} = this.props;
      if (this.props.renderMessageText) {
        return this.props.renderMessageText(messageTextProps);
      }
      return <MessageText {...messageTextProps}/>;
    }
    return null;
  }

  renderMessageImage() {
    if (this.props.currentMessage.image) {
      const {containerStyle, wrapperStyle, ...messageImageProps} = this.props;
      if (this.props.renderMessageImage) {
        return this.props.renderMessageImage(messageImageProps);
      }
      return <MessageImage {...messageImageProps}/>;
    }
    return null;
  }

  renderTicks() {
    const {currentMessage} = this.props;
    if (this.props.renderTicks) {
        return this.props.renderTicks(currentMessage);
    }
    if (currentMessage.user._id !== this.props.user._id) {
        return;
    }
    if (currentMessage.sent || currentMessage.received) {
      return (
        <View style={styles.tickView}>
          {currentMessage.sent && <Text style={styles.tick}>✓</Text>}
          {currentMessage.received && <Text style={styles.tick}>✓</Text>}
        </View>
      )
    }
  }

  renderTime() {
    if (this.props.currentMessage.createdAt) {
      const {containerStyle, wrapperStyle, ...timeProps} = this.props;
      if (this.props.renderTime) {
        return this.props.renderTime(timeProps);
      }
      return <Time {...timeProps}/>;
    }
    return null;
  }

  renderCustomView() {
    if (this.props.renderCustomView) {
      return this.props.renderCustomView(this.props);
    }
    return null;
  }

  onLongPress() {
    if (this.props.onLongPress) {
      this.props.onLongPress(this.context);
    } else {
      if (this.props.currentMessage.text) {
        const options = [
          'Copy Text',
          'Cancel',
        ];
        const cancelButtonIndex = options.length - 1;
        this.context.actionSheet().showActionSheetWithOptions({
          options,
          cancelButtonIndex,
        },
        (buttonIndex) => {
          switch (buttonIndex) {
            case 0:
              Clipboard.setString(this.props.currentMessage.text);
              break;
          }
        });
      }
    }
  }

  render() {
    return (
      <View style={[styles[this.props.position].container, this.props.containerStyle[this.props.position]]}>
        <View style={[styles[this.props.position].wrapper, this.props.wrapperStyle[this.props.position], this.handleBubbleToNext(), this.handleBubbleToPrevious()]}>
          <TouchableWithoutFeedback
            onLongPress={this.onLongPress}
            accessibilityTraits="text"
            {...this.props.touchableProps}
          >
            <View>
              {this.renderCustomView()}
              {this.renderMessageImage()}
              {this.renderMessageText()}
              <View style={styles.bottom}>
                {this.renderTime()}
                {this.renderTicks()}
              </View>
            </View>
          </TouchableWithoutFeedback>
        </View>
      </View>
    );
  }
}

const styles = {
  left: StyleSheet.create({
    container: {
      flex: 1,
      alignItems: 'flex-start',
    },
    wrapper: {
      borderRadius: 15,
      backgroundColor: '#f0f0f0',
      marginRight: 60,
      minHeight: 20,
      justifyContent: 'flex-end',
    },
    containerToNext: {
      borderBottomLeftRadius: 3,
    },
    containerToPrevious: {
      borderTopLeftRadius: 3,
    },
  }),
  right: StyleSheet.create({
    container: {
      flex: 1,
      alignItems: 'flex-end',
    },
    wrapper: {
      borderRadius: 15,
      backgroundColor: '#0084ff',
      marginLeft: 60,
      minHeight: 20,
      justifyContent: 'flex-end',
    },
    containerToNext: {
      borderBottomRightRadius: 3,
    },
    containerToPrevious: {
      borderTopRightRadius: 3,
    },
  }),
  bottom: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  tick: {
    fontSize: 10,
    backgroundColor: 'transparent',
    color: 'white',
  },
  tickView: {
    flexDirection: 'row',
    marginRight: 10,
  }
};

Bubble.contextTypes = {
  actionSheet: React.PropTypes.func,
};

Bubble.defaultProps = {
  touchableProps: {},
  onLongPress: null,
  renderMessageImage: null,
  renderMessageText: null,
  renderCustomView: null,
  renderTime: null,
  isSameUser: () => {},
  isSameDay: () => {},
  position: 'left',
  currentMessage: {
    text: null,
    createdAt: null,
    image: null,
  },
  nextMessage: {},
  previousMessage: {},
  containerStyle: {},
  wrapperStyle: {},
  containerToNextStyle: {},
  containerToPreviousStyle: {},
};

Bubble.propTypes = {
  touchableProps: React.PropTypes.object,
  onLongPress: React.PropTypes.func,
  renderMessageImage: React.PropTypes.func,
  renderMessageText: React.PropTypes.func,
  renderCustomView: React.PropTypes.func,
  renderTime: React.PropTypes.func,
  isSameUser: React.PropTypes.func,
  isSameDay: React.PropTypes.func,
  position: React.PropTypes.oneOf(['left', 'right']),
  currentMessage: React.PropTypes.object,
  nextMessage: React.PropTypes.object,
  previousMessage: React.PropTypes.object,
  containerStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  wrapperStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  containerToNextStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
  containerToPreviousStyle: React.PropTypes.shape({
    left: View.propTypes.style,
    right: View.propTypes.style,
  }),
};
<|endoftext|>


<commit_before>/**
 * Manager.js
 *
 * @param  {Object} window    Window
 * @param  {[type]} factory [description]
 */
( function ( window, factory ) {

    'use strict';

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config',
            './Ajax',
        ], function ( $, Util, Config, Ajax ) {
            return factory( window, $, Util, Config, Ajax );
        } );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            window,
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' ),
            require( './Ajax' )
        );

    } else {

        window.StateManager = window.StateManager || {};

        window.StateManager.Manager = factory(
            window,
            window.jQuery,
            window.StateManager.Util,
            window.StateManager.Config,
            window.StateManager.Ajax
        );

    }

} ( window, function ( window, $, Util, Config, Ajax ) {

    'use strict';

    // define any private variables
    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        initialized = false,
        history = null,
        mode = 'traditional', // will either be 'dynamic' or 'traditional'
        $contentHolder,
        $window = $( window ),
        $body = $( 'body' ),
        currentPageConfig = {},
        prefetchCache = {
            list: [],
            limit: null
        },
        ajaxCache = {
            list: [],
            limit: null
        },
        ajaxQueue = []
    ;

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

    function init() {

        if ( initialized ) {
            return;
        }

        prefetchCache.limit = Config.get( 'prefetchCacheLimit' );
        ajaxCache.limit = Config.get( 'ajaxCacheLimit' );

        $contentHolder = $( Config.get( 'content' ) ).first();

        if ( $contentHolder.length < 1 ) {
            return;
        }

        initHistory();

        initState({
            isInitLoad: true
        });

        ajaxEventListener();

        initialized = true;

    }

    /**
     * Set up history object and add popstate event listener
     */
    function initHistory() {

        // set up our private alias to the history.js adapter
        history = window.history;

        mode = Util.getMode();

        if ( mode === 'traditional' ) {
            return;
        }

        Ajax.ajaxifyLinks( $body );

        // bind to statechanges
        $window.on( 'popstate', function () {

            gotoUrl(
                history.state.url,
                {}
            );

            $window.trigger( 'StateManager.StateChange' );

        } );

    }

    /**
     * Initializes whatever state has been loaded
     * (either on page load or on a history push.)
     *
     * @param  {Object} options
     */
    function initState( options ) {

        var isInitLoad = false,
            stateInfo = history.state,
            // pageConfig = $( Config.get( 'content' ) ).data( 'config' ),
            pageTitle = 'title' in options ? options.title : document.title
        ;

        if ( typeof options !== 'undefined' && typeof options.isInitLoad !== 'undefined' ) {
            isInitLoad = options.isInitLoad
        }

        // if ( typeof pageConfig === 'undefined' ) {
        //     return;
        // }

        // if ( pageConfig === null ) {
        //     return;
        // }

        // check to see if this page relies on any custom body classes
        // if ( typeof pageConfig.bodyClass !== 'undefined' ) {
        //     $body.addClass( pageConfig.bodyClass );
        // }

        Util.setDocumentTitle( pageTitle );

        if ( ! isInitLoad ) {
            $window.trigger(
                'EventTrackAjax.RecordPageview',
                {
                    url: stateInfo.url,
                    title: pageTitle
                }
            );
        }

        // update currentPageConfig
        // currentPageConfig = pageConfig;

        prefetchUpcomingUrls();

        $window.trigger( 'StateManager.AfterInitState' );

    }

    /**
     * Loop through anchor tags with data-prefetch added
     */
    function prefetchUpcomingUrls() {

        if ( mode !== 'dynamic' ) {
            return false;
        }

        var currentState = history.state;

        // stagger prefetch of additional URLs
        $( 'a[data-prefetch]' ).each( function ( index ) {

            // if ( index > prefetchCache.limit ) {
            //     return false;
            // }

            var thisHref = Util.fullyQualifyUrl( $( this ).attr( 'href' ) );

            // make sure we don't reload the page we're on
            if ( thisHref !== currentState.url ) {
                setTimeout( function () {
                    prefetchUrl( thisHref );
                }, 50 * ( index + 1 ) );
            }

        });
    }

    /**
     * Inserts url data to the DOM. Called by gotoUrl().
     *
     * @param  {Object} data
     * @param  {Object} options
     */
    function renderUrl( data ) {

        // drop in image_box HTML
        $contentHolder.html( data.data );

        Ajax.ajaxifyLinks( $contentHolder );

        initState( data );

    }

    /**
     * Toggles whether we're waiting for content to load.
     *
     * @param  {Boolean} isLoading
     */
    function toggleLoading( isLoading ) {

        if ( isLoading ) {

            // reveal loading state
            $window.trigger( 'StateManager.LoadingReveal' );

        } else {

            // hide loading state
            $window.trigger( 'StateManager.LoadingComplete' );

        }

    }

    /**
     * Handles loading of a particular url, then moves along to rendering.
     *
     * @param  {String} url
     * @param  {Object} options
     */
    function gotoUrl( url, options ) {

        // set empty options if they don't exist
        if ( typeof options === 'undefined' ) {
            options = {};
        }

        var thisUrl = url,
            thisOptions = options,
            data = getUrlData({
                url: thisUrl,
                afterAjaxLoad: function ( data ) {
                    // this function only fires if we need to wait for
                    // an ajax load.

                    // record what the last url had been
                    var lastUrlInQueue = ajaxQueue[ ajaxQueue.length - 1 ].url;

                    removeUrlFromAjaxQueue( thisUrl );

                    // save the new data to the cache
                    saveCacheData( ajaxCache, thisUrl, data );

                    // only proceed if this was the last url in the queue
                    if ( lastUrlInQueue === thisUrl ) {

                        // now that we have the data, recall gotoUrl
                        gotoUrl( thisUrl, thisOptions );

                    } else {
                        debug( '******* still loading: ' + lastUrlInQueue );
                    }
                }
            } )
        ;

        // did we get data back, or are we waiting on an ajax request to be completed?
        if ( typeof data.loading !== 'undefined' && data.loading ) {

            toggleLoading( true );

            return false;

        }

        // if we reached this point, we have the data we need and can proceed.
        toggleLoading( false );

        renderUrl( data );

    }

    /**
     * Returns html data for a particular url. This data may be cached already. If not
     *       we make an AJAX call to load the data.
     *
     * @param  {Object} options Includes: url, isPrefetch
     * @return {Object}         Trigger the loading function
     */
    function getUrlData( options ) {

        var thisUrl = options.url,
            trackProgress = true,
            data
        ;

        if ( data = checkCacheForData( ajaxCache.list, options.url ) ) {
            return data;
        }

        if ( data = checkCacheForData( prefetchCache.list, options.url ) ) {
            return data;
        }

        if ( data = checkCacheForData( ajaxQueue, options.url ) ) {
            return {
                loading: true
            };
        }

        // Disable tracking progress if this is a prefetch request
        if ( options.isPrefetch ) {
            trackProgress = false;
        }

        ajaxQueue.push({ url: thisUrl });

        // if we reach this point, the data wasn't in the cache. make ajax request.
        Ajax.loadAjax({
            url: thisUrl,
            dataType: 'html',
            trackProgress: trackProgress,
            success: options.afterAjaxLoad,
            error: function () {

            }
        });

        return {
            loading: true
        };

    }

    /**
     * Prefetches URLs we think the user is likely to load in the cache.
     *
     * @param {String} url Marker to decide which pages should be cached
     */
    function prefetchUrl( url ) {

        if ( mode === 'traditional' ) {
            return;
        }

        // no, let's make the request
        getUrlData({
            url: url,
            isPrefetch: true,
            afterAjaxLoad: function ( data ) {
                removeUrlFromAjaxQueue( url );
                saveCacheData( prefetchCache, url, data );
            }
        });
    }

    /**
     * Check supplied cache array for a match against the URI
     *
     * @param  {Array} cacheList  prefetch, ajax, or ajaxQueue cache
     * @param  {String} url       used to identify the correct data in the loop
     * @return {Object|Boolean}   cacheList data if present or false
     */
    function checkCacheForData( cacheList, url ) {

        if ( cacheList.length > 0 ) {
            for ( var i = 0; i < cacheList.length; i++ ) {
                if ( cacheList[i].url === url ) {
                    return cacheList[i];
                }
            }
        }

        return false;

    }

    /**
     * A function for passing different cache arrays with url and data to save
     *
     * @param  {Array} cacheType  prefetch or ajax cache
     * @param  {String} url       Page URI to cache
     * @param  {String} data      Data from page to be cached, it get's parsed first
     */
    function saveCacheData( cacheType, url, data ) {

        var cacheObj = {
            url: url,
            data: Ajax.parseHtml( data )
        };

        cacheType.list.push( cacheObj );

        // check if cacheType has grown too large
        if ( cacheType.list.length > cacheType.limit ) {
            // remove the oldest data
            debug( 'over cache limit of ' + cacheType.limit + ', removing oldest data' );

            cacheType.list = $.grep( cacheType.list, function ( element, index ) {
                return index !== 0;
            } );

        }

    }

    /**
     * Loop through the queue and remove the url from the array
     *
     * @param  {String} url  string to remove from the queue
     */
    function removeUrlFromAjaxQueue( url ) {

        // remove url from ajaxQueue
        ajaxQueue = $.grep( ajaxQueue, function ( element, index ) {
            return element.url !== url;
        } );

    }

    /**
     * Add any Listeners to avoid exposing functions
     */
    function ajaxEventListener() {

        $window.on( 'StateManager.GotoUrl', function ( event, url ) {

            gotoUrl( url, {} );

        } );

    }

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

    return {
        init: init
    };

} ) );<commit_msg>Add the title to the cached data.<commit_after>/**
 * Manager.js
 *
 * @param  {Object} window    Window
 * @param  {[type]} factory [description]
 */
( function ( window, factory ) {

    'use strict';

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config',
            './Ajax',
        ], function ( $, Util, Config, Ajax ) {
            return factory( window, $, Util, Config, Ajax );
        } );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            window,
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' ),
            require( './Ajax' )
        );

    } else {

        window.StateManager = window.StateManager || {};

        window.StateManager.Manager = factory(
            window,
            window.jQuery,
            window.StateManager.Util,
            window.StateManager.Config,
            window.StateManager.Ajax
        );

    }

} ( window, function ( window, $, Util, Config, Ajax ) {

    'use strict';

    // define any private variables
    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        initialized = false,
        history = null,
        mode = 'traditional', // will either be 'dynamic' or 'traditional'
        $contentHolder,
        $window = $( window ),
        $body = $( 'body' ),
        currentPageConfig = {},
        prefetchCache = {
            list: [],
            limit: null
        },
        ajaxCache = {
            list: [],
            limit: null
        },
        ajaxQueue = []
    ;

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

    function init() {

        if ( initialized ) {
            return;
        }

        prefetchCache.limit = Config.get( 'prefetchCacheLimit' );
        ajaxCache.limit = Config.get( 'ajaxCacheLimit' );

        $contentHolder = $( Config.get( 'content' ) ).first();

        if ( $contentHolder.length < 1 ) {
            return;
        }

        initHistory();

        initState({
            isInitLoad: true
        });

        ajaxEventListener();

        initialized = true;

    }

    /**
     * Set up history object and add popstate event listener
     */
    function initHistory() {

        // set up our private alias to the history.js adapter
        history = window.history;

        mode = Util.getMode();

        if ( mode === 'traditional' ) {
            return;
        }

        Ajax.ajaxifyLinks( $body );

        // bind to statechanges
        $window.on( 'popstate', function () {

            gotoUrl(
                history.state.url,
                {}
            );

            $window.trigger( 'StateManager.StateChange' );

        } );

    }

    /**
     * Initializes whatever state has been loaded
     * (either on page load or on a history push.)
     *
     * @param  {Object} options
     */
    function initState( options ) {

        var isInitLoad = false,
            stateInfo = history.state,
            // pageConfig = $( Config.get( 'content' ) ).data( 'config' ),
            pageTitle = 'title' in options ? options.title : document.title
        ;

        if ( typeof options !== 'undefined' && typeof options.isInitLoad !== 'undefined' ) {
            isInitLoad = options.isInitLoad
        }

        // if ( typeof pageConfig === 'undefined' ) {
        //     return;
        // }

        // if ( pageConfig === null ) {
        //     return;
        // }

        // check to see if this page relies on any custom body classes
        // if ( typeof pageConfig.bodyClass !== 'undefined' ) {
        //     $body.addClass( pageConfig.bodyClass );
        // }

        Util.setDocumentTitle( pageTitle );

        if ( ! isInitLoad ) {
            $window.trigger(
                'EventTrackAjax.RecordPageview',
                {
                    url: stateInfo.url,
                    title: pageTitle
                }
            );
        }

        // update currentPageConfig
        // currentPageConfig = pageConfig;

        prefetchUpcomingUrls();

        $window.trigger( 'StateManager.AfterInitState' );

    }

    /**
     * Loop through anchor tags with data-prefetch added
     */
    function prefetchUpcomingUrls() {

        if ( mode !== 'dynamic' ) {
            return false;
        }

        var currentState = history.state;

        // stagger prefetch of additional URLs
        $( 'a[data-prefetch]' ).each( function ( index ) {

            // if ( index > prefetchCache.limit ) {
            //     return false;
            // }

            var thisHref = Util.fullyQualifyUrl( $( this ).attr( 'href' ) );

            // make sure we don't reload the page we're on
            if ( thisHref !== currentState.url ) {
                setTimeout( function () {
                    prefetchUrl( thisHref );
                }, 50 * ( index + 1 ) );
            }

        });
    }

    /**
     * Inserts url data to the DOM. Called by gotoUrl().
     *
     * @param  {Object} data
     * @param  {Object} options
     */
    function renderUrl( data ) {

        // drop in image_box HTML
        $contentHolder.html( data.data );

        Ajax.ajaxifyLinks( $contentHolder );

        initState( data );

    }

    /**
     * Toggles whether we're waiting for content to load.
     *
     * @param  {Boolean} isLoading
     */
    function toggleLoading( isLoading ) {

        if ( isLoading ) {

            // reveal loading state
            $window.trigger( 'StateManager.LoadingReveal' );

        } else {

            // hide loading state
            $window.trigger( 'StateManager.LoadingComplete' );

        }

    }

    /**
     * Handles loading of a particular url, then moves along to rendering.
     *
     * @param  {String} url
     * @param  {Object} options
     */
    function gotoUrl( url, options ) {

        // set empty options if they don't exist
        if ( typeof options === 'undefined' ) {
            options = {};
        }

        var thisUrl = url,
            thisOptions = options,
            data = getUrlData({
                url: thisUrl,
                afterAjaxLoad: function ( data ) {
                    // this function only fires if we need to wait for
                    // an ajax load.

                    // record what the last url had been
                    var lastUrlInQueue = ajaxQueue[ ajaxQueue.length - 1 ].url;

                    removeUrlFromAjaxQueue( thisUrl );

                    // save the new data to the cache
                    saveCacheData( ajaxCache, thisUrl, data );

                    // only proceed if this was the last url in the queue
                    if ( lastUrlInQueue === thisUrl ) {

                        // now that we have the data, recall gotoUrl
                        gotoUrl( thisUrl, thisOptions );

                    } else {
                        debug( '******* still loading: ' + lastUrlInQueue );
                    }
                }
            } )
        ;

        // did we get data back, or are we waiting on an ajax request to be completed?
        if ( typeof data.loading !== 'undefined' && data.loading ) {

            toggleLoading( true );

            return false;

        }

        // if we reached this point, we have the data we need and can proceed.
        toggleLoading( false );

        renderUrl( data );

    }

    /**
     * Returns html data for a particular url. This data may be cached already. If not
     *       we make an AJAX call to load the data.
     *
     * @param  {Object} options Includes: url, isPrefetch
     * @return {Object}         Trigger the loading function
     */
    function getUrlData( options ) {

        var thisUrl = options.url,
            trackProgress = true,
            data
        ;

        if ( data = checkCacheForData( ajaxCache.list, options.url ) ) {
            return data;
        }

        if ( data = checkCacheForData( prefetchCache.list, options.url ) ) {
            return data;
        }

        if ( data = checkCacheForData( ajaxQueue, options.url ) ) {
            return {
                loading: true
            };
        }

        // Disable tracking progress if this is a prefetch request
        if ( options.isPrefetch ) {
            trackProgress = false;
        }

        ajaxQueue.push({ url: thisUrl });

        // if we reach this point, the data wasn't in the cache. make ajax request.
        Ajax.loadAjax({
            url: thisUrl,
            dataType: 'html',
            trackProgress: trackProgress,
            success: options.afterAjaxLoad,
            error: function () {

            }
        });

        return {
            loading: true
        };

    }

    /**
     * Prefetches URLs we think the user is likely to load in the cache.
     *
     * @param {String} url Marker to decide which pages should be cached
     */
    function prefetchUrl( url ) {

        if ( mode === 'traditional' ) {
            return;
        }

        // no, let's make the request
        getUrlData({
            url: url,
            isPrefetch: true,
            afterAjaxLoad: function ( data ) {
                removeUrlFromAjaxQueue( url );
                saveCacheData( prefetchCache, url, data );
            }
        });
    }

    /**
     * Check supplied cache array for a match against the URI
     *
     * @param  {Array} cacheList  prefetch, ajax, or ajaxQueue cache
     * @param  {String} url       used to identify the correct data in the loop
     * @return {Object|Boolean}   cacheList data if present or false
     */
    function checkCacheForData( cacheList, url ) {

        if ( cacheList.length > 0 ) {
            for ( var i = 0; i < cacheList.length; i++ ) {
                if ( cacheList[i].url === url ) {
                    return cacheList[i];
                }
            }
        }

        return false;

    }

    /**
     * A function for passing different cache arrays with url and data to save
     *
     * @param  {Array} cacheType  prefetch or ajax cache
     * @param  {String} url       Page URI to cache
     * @param  {String} data      Data from page to be cached, it get's parsed first
     */
    function saveCacheData( cacheType, url, data ) {

        var cacheObj = {
            url: url,
            title: Ajax.parseTitle( data ),
            data: Ajax.parseHtml( data )
        };

        cacheType.list.push( cacheObj );

        // check if cacheType has grown too large
        if ( cacheType.list.length > cacheType.limit ) {
            // remove the oldest data
            debug( 'over cache limit of ' + cacheType.limit + ', removing oldest data' );

            cacheType.list = $.grep( cacheType.list, function ( element, index ) {
                return index !== 0;
            } );

        }

    }

    /**
     * Loop through the queue and remove the url from the array
     *
     * @param  {String} url  string to remove from the queue
     */
    function removeUrlFromAjaxQueue( url ) {

        // remove url from ajaxQueue
        ajaxQueue = $.grep( ajaxQueue, function ( element, index ) {
            return element.url !== url;
        } );

    }

    /**
     * Add any Listeners to avoid exposing functions
     */
    function ajaxEventListener() {

        $window.on( 'StateManager.GotoUrl', function ( event, url ) {

            gotoUrl( url, {} );

        } );

    }

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

    return {
        init: init
    };

} ) );<|endoftext|>


<commit_before>function Network() {
    // To server functions and constants
    function sendGameRequest(numPlayers) {
        var message = {"NumPlayers": numPlayers};
        sendMessage(NEW_GAME_CMD, message);
    }

    function sendClientToken(token) {
        var message = {"Token": token};
        sendMessage(CLIENT_INFO_CMD, message);
    }

    function sendViewWorld() {
        sendMessage(VIEW_WORLD_CMD, {});
    }

    function sendViewUnits() {
        sendMessage(VIEW_UNITS_CMD, {});
    }

    function sendViewTerrain() {
        sendMessage(VIEW_TERRAIN_CMD, {});
    }

    function sendViewPlayers() {
        sendMessage(VIEW_PLAYERS_CMD, {});
    }

    function sendTurn() {
        sendMessage(END_TURN_CMD, {});
    }

    function sendAttack(source, target, attackId) {
        var message = {
            Attacker: source.pos,
            AttackIndex: attackId,
            Target: target.pos
        };
        sendMessage(ATTACK_CMD, message);
    }

    function sendUnitMove(unit, move) {
        var message = {move: []};
        for (var i = 0; i < move.length; i++) {
            message.move.push(move[i].position);
        }

        sendMessage(MOVE_UNIT_CMD, message);
    }

    function sendChatMessage(message) {
        sendMessage(CHAT_CMD, {"message": message})
    }

    CLIENT_INFO_CMD = "clientInfo";
    // newGame is the packet we send to the proxy to request a new game. Right
    // now we receive nothing back from this request
    NEW_GAME_CMD = "newGame";
    VIEW_WORLD_CMD = "viewWorld";
    VIEW_UNITS_CMD = "viewUnits";
    VIEW_TERRAIN_CMD = "viewTerrain";
    VIEW_PLAYERS_CMD = "viewPlayers";
    END_TURN_CMD = "turn";
    ATTACK_CMD = "attack";
    MOVE_UNIT_CMD = "move";
    CHAT_CMD = "chat";

    host = window.location.host.split(":")[0];
    port = getPortNum();
    ws_addr = "ws://" + host + ":" + port + "/ws";
    conn = new WebSocket(ws_addr);
    DELIM = ":";

    // This function expects the message as a javascript object
    function sendMessage(command, messageObj) {
        var jsonified = JSON.stringify(messageObj);
        packet = command + DELIM + jsonified;
        conn.send(packet);
    }

    // From server functions and constants
    this.packetHandlers = {};
    this.packetHandlers[VIEW_WORLD_CMD] = parseViewWorld.bind(this);
    this.packetHandlers[VIEW_UNITS_CMD] = parseViewUnits.bind(this);
    this.packetHandlers[VIEW_TERRAIN_CMD] = parseViewTerrain.bind(this);
    this.packetHandlers[VIEW_PLAYERS_CMD] = parseViewPlayers.bind(this);
    this.packetHandlers[CLIENT_INFO_CMD] = parseClientInfo.bind(this);
    // new is the response from the game server when a game is ready
    this.packetHandlers["new"] = parseGameRequestSuccess.bind(this);
    this.packetHandlers[MOVE_UNIT_CMD] = parseMoveResponse.bind(this);
    this.packetHandlers[END_TURN_CMD] = parseEndTurnResponse.bind(this);
    this.packetHandlers[ATTACK_CMD] = parseAttackResponse.bind(this);
    this.packetHandlers[CHAT_CMD] = parseChatResponse.bind(this);

    function logTemplateComp(name, realResponse) {
        if (!verifyStructure(responseTemplates[name], realResponse)) {
            console.log("The", name, "template does not match. Proceed with caution");
        }
    }

    function parseViewWorld(status, viewWorld) {
        this.logTemplateComp("viewWorld", viewWorld);
        testies = viewWorld;
        parseTerrain(game, viewWorld.TerrainResponse.terrain);
        parsePlayers(game, viewWorld.PlayersResponse);
        parseUnits(game, viewWorld.UnitsResponse.units);
    }

    function parseViewUnits(status, viewUnits) {
        this.logTemplateComp(VIEW_UNITS_CMD, viewUnits);
        parseUnits(game, viewUnits.units);
    }

    function parseViewTerrain(status, viewTerrain) {
        this.logTemplateComp(VIEW_TERRAIN_CMD, viewTerrain)
        parseTerrain(game, viewTerrain.terrain);
    }

    function parseViewPlayers(status, viewPlayers) {
        this.logTemplateComp(VIEW_PLAYERS_CMD, viewPlayers);
        parsePlayers(viewPlayers.players);
    }

    function parseUnits(game, units) {
        for (var id in units) {
            // this should be tank, but I've only got wizards right now
            // not sent movementType right now
            var unit = units[id];
            var position = unit.position;
            var movement = unit.movement;
            var unitnation = parseInt(unit.nation) + unit.name;
            var spriteName = UnitNationSprite[unitnation];
            var attacks = parseUnitAttacks(unit);
            var armor = parseUnitArmor(unit);
            if (!game.world.findUnit(position.x, position.y)) {
                var player = game.world.getPlayerFromNation(unit.nation);
                game.world.addUnit(id, player.id, spriteName, position,
                                   movement.distance, movement.type,
                                   movement.speeds, unit.health, unit.maxHealth,
                                   unit.nation, unit.name, unit.canMove,
                                   attacks, armor);
            } else {
                console.log("Verifing the unit could be useful here");
            }
        }
    }

    function parseTerrain(game, terrain) {
        if (game.world === undefined) {
            game.world = new World(terrain.length, terrain[0].length);
            game.currentState = "CAMERA_CONTROL";
        }
        for (var x = 0; x < terrain.length; x++) {
            for (var y = 0; y < terrain[x].length; y++) {
                terrain[x][y] = new terrainTable[terrain[x][y]](x, y);
            }
        }
        game.world.initialize(terrain);
    }

    function parsePlayers(game, playersResponse) {
        var players = playersResponse.players;
        for (var id in players) {
            var player = players[id];
            game.world.addOrUpdatePlayer(id, player.nation,
                                         player.team);
        }
        game.world.currentPlayerId = playersResponse.turnOwner;
    }

    function parseUnitAttacks(unit) {
        return unit.attacks;
    }

    function parseUnitArmor(unit) {
        return unit.armor;
    }

    function parseClientInfo(status) {
        console.log("Your client info has been received, sending game request");
        this.sendGameRequest(parseInt(desiredPlayers));
    }

    function parseGameRequestSuccess(status) {
        console.log("Congratulations, you've been matched to a game!");
        this.sendViewWorld();
    }

    function parseMoveResponse(status, response) {
        if (status === 0) {
            var path = response.move;
            var unit = game.world.findUnit(path[0].x, path[0].y);
            if (path) {
                // tell the renderer about the move
                var unitSprite = assets.spriteManager.getSprite(unit.spriteName);
                // unitSprite.movements = unitSprite.movements.concat(translatePathToMoves(path));
                var goal = path[path.length - 1];
                game.world.moveUnit(unit.pos, goal);
                // shim, while the mac tab crash bug is still happening
                unitSprite.drawPos = goal;
                unit.canMove = false;
                unitControlState.reset();
            } else {
                console.log("Your path is null, man");
            }
        } else {
            console.log("Go a bad status from server:", status);
        }
    }

    function parseAttackResponse(status, response) {
        console.log("Not implemented", response);
        var sourceUnit = game.world.findUnit(response.attacker.x,
                                             response.attacker.y);
        var targetUnit = game.world.findUnit(response.target.x,
                                             response.target.y);
        var attackId = response.attackIndex;
        var attack = sourceUnit.attacks[attackId];
        console.log(sourceUnit, "attacks", targetUnit, "for",
                    attack.power);
        targetUnit.health -= attack.power;
    }

    function parseEndTurnResponse(status, response) {
        if (status == 0) {
            console.log("Turn ended successfully. Resetting unit state");
            game.world.resetUnits(response.playerId);
        } else {
            console.log("Bad status ending turn: ", status);
        }
    }

    function parseChatResponse(status, data) {
        console.log("Chat: " + data.message);
    }

    conn.onopen = function () {
        this.sendClientInfo();
    }.bind(this);

    // Log errors
    conn.onerror = function (error) {
        console.log('WebSocket Error ' + error);
    };

    // Log messages from the server
    conn.onmessage = function (e) {
        var cmds = trimSocketChars(e.data.actualSplit(DELIM, 2));
        if (cmds.length == 2) {
            var pkt_type = cmds[0];
            var response = JSON.parse(cmds[1]);
            var dataObj = response.payload;
            var status = response.status;
            // It's likely the individual handlers would be interested in the
            // failure condition
            if (status >= 0) {
                var handler = this.packetHandlers[pkt_type];
                if (handler !== undefined) {
                    handler(status, dataObj);
                } else {
                    console.log("Recieved unknown command:", pkt_type);
                }
            } else {
                console.log("Received failure from server:", cmds);
            }
        } else {
            console.log("Received malformed command:", cmds);
        }
    }.bind(this);

    this.sendGameRequest = function(numPlayers) {
        sendGameRequest(numPlayers);
    };

    this.sendClientInfo = function() {
        var token = $.cookie("token");
        if (token) {
            sendClientToken(token);
        } else {
            alert("You do not have a token, can't start game");
        }
    };

    this.sendViewWorld = function() {
        sendViewWorld();
    };

    this.sendUnitMove = function(unit, move) {
        sendUnitMove(unit, move);
    };

    this.sendViewUnits = function() {
        sendViewUnits();
    };

    this.sendViewTerrain = function() {
        sendViewTerrain();
    };

    this.sendViewPlayers = function() {
        sendViewPlayers();
    };

    this.sendAttack = function(source, target, attackId) {
        sendAttack(source, target, attackId);
    };

    this.sendTurn = function() {
        sendTurn();
    }

    this.logTemplateComp = function(name, realResponse) {
        logTemplateComp(name, realResponse);
    };

    this.sendChatMessage = sendChatMessage;
}

function trimSocketChars(cmds) {
    cmds[cmds.length - 1] = cmds[cmds.length - 1].replace(/\x00/gi, "");
    return cmds;
}

String.prototype.actualSplit = function(sep, maxsplit) {
    var split = this.split(sep);
    var excess = split.slice(maxsplit - 1, split.length).join(":");
    if (excess.length > 0) {
        return maxsplit ? split.slice(0, maxsplit - 1).concat(excess) : split;
    } else {
        return maxsplit ? split.slice(0, maxsplit - 1) : split;
    }
};
<commit_msg>Remove old log message<commit_after>function Network() {
    // To server functions and constants
    function sendGameRequest(numPlayers) {
        var message = {"NumPlayers": numPlayers};
        sendMessage(NEW_GAME_CMD, message);
    }

    function sendClientToken(token) {
        var message = {"Token": token};
        sendMessage(CLIENT_INFO_CMD, message);
    }

    function sendViewWorld() {
        sendMessage(VIEW_WORLD_CMD, {});
    }

    function sendViewUnits() {
        sendMessage(VIEW_UNITS_CMD, {});
    }

    function sendViewTerrain() {
        sendMessage(VIEW_TERRAIN_CMD, {});
    }

    function sendViewPlayers() {
        sendMessage(VIEW_PLAYERS_CMD, {});
    }

    function sendTurn() {
        sendMessage(END_TURN_CMD, {});
    }

    function sendAttack(source, target, attackId) {
        var message = {
            Attacker: source.pos,
            AttackIndex: attackId,
            Target: target.pos
        };
        sendMessage(ATTACK_CMD, message);
    }

    function sendUnitMove(unit, move) {
        var message = {move: []};
        for (var i = 0; i < move.length; i++) {
            message.move.push(move[i].position);
        }

        sendMessage(MOVE_UNIT_CMD, message);
    }

    function sendChatMessage(message) {
        sendMessage(CHAT_CMD, {"message": message})
    }

    CLIENT_INFO_CMD = "clientInfo";
    // newGame is the packet we send to the proxy to request a new game. Right
    // now we receive nothing back from this request
    NEW_GAME_CMD = "newGame";
    VIEW_WORLD_CMD = "viewWorld";
    VIEW_UNITS_CMD = "viewUnits";
    VIEW_TERRAIN_CMD = "viewTerrain";
    VIEW_PLAYERS_CMD = "viewPlayers";
    END_TURN_CMD = "turn";
    ATTACK_CMD = "attack";
    MOVE_UNIT_CMD = "move";
    CHAT_CMD = "chat";

    host = window.location.host.split(":")[0];
    port = getPortNum();
    ws_addr = "ws://" + host + ":" + port + "/ws";
    conn = new WebSocket(ws_addr);
    DELIM = ":";

    // This function expects the message as a javascript object
    function sendMessage(command, messageObj) {
        var jsonified = JSON.stringify(messageObj);
        packet = command + DELIM + jsonified;
        conn.send(packet);
    }

    // From server functions and constants
    this.packetHandlers = {};
    this.packetHandlers[VIEW_WORLD_CMD] = parseViewWorld.bind(this);
    this.packetHandlers[VIEW_UNITS_CMD] = parseViewUnits.bind(this);
    this.packetHandlers[VIEW_TERRAIN_CMD] = parseViewTerrain.bind(this);
    this.packetHandlers[VIEW_PLAYERS_CMD] = parseViewPlayers.bind(this);
    this.packetHandlers[CLIENT_INFO_CMD] = parseClientInfo.bind(this);
    // new is the response from the game server when a game is ready
    this.packetHandlers["new"] = parseGameRequestSuccess.bind(this);
    this.packetHandlers[MOVE_UNIT_CMD] = parseMoveResponse.bind(this);
    this.packetHandlers[END_TURN_CMD] = parseEndTurnResponse.bind(this);
    this.packetHandlers[ATTACK_CMD] = parseAttackResponse.bind(this);
    this.packetHandlers[CHAT_CMD] = parseChatResponse.bind(this);

    function logTemplateComp(name, realResponse) {
        if (!verifyStructure(responseTemplates[name], realResponse)) {
            console.log("The", name, "template does not match. Proceed with caution");
        }
    }

    function parseViewWorld(status, viewWorld) {
        this.logTemplateComp("viewWorld", viewWorld);
        testies = viewWorld;
        parseTerrain(game, viewWorld.TerrainResponse.terrain);
        parsePlayers(game, viewWorld.PlayersResponse);
        parseUnits(game, viewWorld.UnitsResponse.units);
    }

    function parseViewUnits(status, viewUnits) {
        this.logTemplateComp(VIEW_UNITS_CMD, viewUnits);
        parseUnits(game, viewUnits.units);
    }

    function parseViewTerrain(status, viewTerrain) {
        this.logTemplateComp(VIEW_TERRAIN_CMD, viewTerrain)
        parseTerrain(game, viewTerrain.terrain);
    }

    function parseViewPlayers(status, viewPlayers) {
        this.logTemplateComp(VIEW_PLAYERS_CMD, viewPlayers);
        parsePlayers(viewPlayers.players);
    }

    function parseUnits(game, units) {
        for (var id in units) {
            // this should be tank, but I've only got wizards right now
            // not sent movementType right now
            var unit = units[id];
            var position = unit.position;
            var movement = unit.movement;
            var unitnation = parseInt(unit.nation) + unit.name;
            var spriteName = UnitNationSprite[unitnation];
            var attacks = parseUnitAttacks(unit);
            var armor = parseUnitArmor(unit);
            if (!game.world.findUnit(position.x, position.y)) {
                var player = game.world.getPlayerFromNation(unit.nation);
                game.world.addUnit(id, player.id, spriteName, position,
                                   movement.distance, movement.type,
                                   movement.speeds, unit.health, unit.maxHealth,
                                   unit.nation, unit.name, unit.canMove,
                                   attacks, armor);
            } else {
                console.log("Verifing the unit could be useful here");
            }
        }
    }

    function parseTerrain(game, terrain) {
        if (game.world === undefined) {
            game.world = new World(terrain.length, terrain[0].length);
            game.currentState = "CAMERA_CONTROL";
        }
        for (var x = 0; x < terrain.length; x++) {
            for (var y = 0; y < terrain[x].length; y++) {
                terrain[x][y] = new terrainTable[terrain[x][y]](x, y);
            }
        }
        game.world.initialize(terrain);
    }

    function parsePlayers(game, playersResponse) {
        var players = playersResponse.players;
        for (var id in players) {
            var player = players[id];
            game.world.addOrUpdatePlayer(id, player.nation,
                                         player.team);
        }
        game.world.currentPlayerId = playersResponse.turnOwner;
    }

    function parseUnitAttacks(unit) {
        return unit.attacks;
    }

    function parseUnitArmor(unit) {
        return unit.armor;
    }

    function parseClientInfo(status) {
        console.log("Your client info has been received, sending game request");
        this.sendGameRequest(parseInt(desiredPlayers));
    }

    function parseGameRequestSuccess(status) {
        console.log("Congratulations, you've been matched to a game!");
        this.sendViewWorld();
    }

    function parseMoveResponse(status, response) {
        if (status === 0) {
            var path = response.move;
            var unit = game.world.findUnit(path[0].x, path[0].y);
            if (path) {
                // tell the renderer about the move
                var unitSprite = assets.spriteManager.getSprite(unit.spriteName);
                // unitSprite.movements = unitSprite.movements.concat(translatePathToMoves(path));
                var goal = path[path.length - 1];
                game.world.moveUnit(unit.pos, goal);
                // shim, while the mac tab crash bug is still happening
                unitSprite.drawPos = goal;
                unit.canMove = false;
                unitControlState.reset();
            } else {
                console.log("Your path is null, man");
            }
        } else {
            console.log("Go a bad status from server:", status);
        }
    }

    function parseAttackResponse(status, response) {
        var sourceUnit = game.world.findUnit(response.attacker.x,
                                             response.attacker.y);
        var targetUnit = game.world.findUnit(response.target.x,
                                             response.target.y);
        var attackId = response.attackIndex;
        var attack = sourceUnit.attacks[attackId];
        console.log(sourceUnit, "attacks", targetUnit, "for",
                    attack.power);
        targetUnit.health -= attack.power;
    }

    function parseEndTurnResponse(status, response) {
        if (status == 0) {
            console.log("Turn ended successfully. Resetting unit state");
            game.world.resetUnits(response.playerId);
        } else {
            console.log("Bad status ending turn: ", status);
        }
    }

    function parseChatResponse(status, data) {
        console.log("Chat: " + data.message);
    }

    conn.onopen = function () {
        this.sendClientInfo();
    }.bind(this);

    // Log errors
    conn.onerror = function (error) {
        console.log('WebSocket Error ' + error);
    };

    // Log messages from the server
    conn.onmessage = function (e) {
        var cmds = trimSocketChars(e.data.actualSplit(DELIM, 2));
        if (cmds.length == 2) {
            var pkt_type = cmds[0];
            var response = JSON.parse(cmds[1]);
            var dataObj = response.payload;
            var status = response.status;
            // It's likely the individual handlers would be interested in the
            // failure condition
            if (status >= 0) {
                var handler = this.packetHandlers[pkt_type];
                if (handler !== undefined) {
                    handler(status, dataObj);
                } else {
                    console.log("Recieved unknown command:", pkt_type);
                }
            } else {
                console.log("Received failure from server:", cmds);
            }
        } else {
            console.log("Received malformed command:", cmds);
        }
    }.bind(this);

    this.sendGameRequest = function(numPlayers) {
        sendGameRequest(numPlayers);
    };

    this.sendClientInfo = function() {
        var token = $.cookie("token");
        if (token) {
            sendClientToken(token);
        } else {
            alert("You do not have a token, can't start game");
        }
    };

    this.sendViewWorld = function() {
        sendViewWorld();
    };

    this.sendUnitMove = function(unit, move) {
        sendUnitMove(unit, move);
    };

    this.sendViewUnits = function() {
        sendViewUnits();
    };

    this.sendViewTerrain = function() {
        sendViewTerrain();
    };

    this.sendViewPlayers = function() {
        sendViewPlayers();
    };

    this.sendAttack = function(source, target, attackId) {
        sendAttack(source, target, attackId);
    };

    this.sendTurn = function() {
        sendTurn();
    }

    this.logTemplateComp = function(name, realResponse) {
        logTemplateComp(name, realResponse);
    };

    this.sendChatMessage = sendChatMessage;
}

function trimSocketChars(cmds) {
    cmds[cmds.length - 1] = cmds[cmds.length - 1].replace(/\x00/gi, "");
    return cmds;
}

String.prototype.actualSplit = function(sep, maxsplit) {
    var split = this.split(sep);
    var excess = split.slice(maxsplit - 1, split.length).join(":");
    if (excess.length > 0) {
        return maxsplit ? split.slice(0, maxsplit - 1).concat(excess) : split;
    } else {
        return maxsplit ? split.slice(0, maxsplit - 1) : split;
    }
};
<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * Basic 2-dimensional vector
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  require( 'DOT/Util' );
  // require( 'DOT/Vector3' ); // commented out since Require.js complains about the circular dependency

  dot.Vector2 = function( x, y ) {
    // allow optional parameters
    this.x = x || 0;
    this.y = y || 0;
  };
  var Vector2 = dot.Vector2;

  Vector2.createPolar = function( magnitude, angle ) {
    return new Vector2( Math.cos( angle ), Math.sin( angle ) ).timesScalar( magnitude );
  };

  Vector2.prototype = {
    constructor: Vector2,

    isVector2: true,

    dimension: 2,

    magnitude: function() {
      return Math.sqrt( this.magnitudeSquared() );
    },

    magnitudeSquared: function() {
      return this.dot( this );
    },

    // the distance between this vector (treated as a point) and another point
    distance: function( point ) {
      return this.minus( point ).magnitude();
    },

    // the squared distance between this vector (treated as a point) and another point
    distanceSquared: function( point ) {
      return this.minus( point ).magnitudeSquared();
    },

    dot: function( v ) {
      return this.x * v.x + this.y * v.y;
    },

    equals: function( other ) {
      return this.x === other.x && this.y === other.y;
    },

    equalsEpsilon: function( other, epsilon ) {
      if ( !epsilon ) {
        epsilon = 0;
      }
      return Math.abs( this.x - other.x ) + Math.abs( this.y - other.y ) <= epsilon;
    },

    isFinite: function() {
      return isFinite( this.x ) && isFinite( this.y );
    },

    /*---------------------------------------------------------------------------*
     * Immutables
     *----------------------------------------------------------------------------*/

    copy: function() {
      return new Vector2( this.x, this.y );
    },

    crossScalar: function( v ) {
      return this.magnitude() * v.magnitude() * Math.sin( this.getAngle() - v.getAngle() );
    },

    normalized: function() {
      var mag = this.magnitude();
      if ( mag === 0 ) {
        throw new Error( "Cannot normalize a zero-magnitude vector" );
      }
      else {
        return new Vector2( this.x / mag, this.y / mag );
      }
    },

    timesScalar: function( scalar ) {
      return new Vector2( this.x * scalar, this.y * scalar );
    },

    times: function( scalar ) {
      // make sure it's not a vector!
      assert && assert( scalar.dimension === undefined );
      return this.timesScalar( scalar );
    },

    componentTimes: function( v ) {
      return new Vector2( this.x * v.x, this.y * v.y );
    },

    plus: function( v ) {
      return new Vector2( this.x + v.x, this.y + v.y );
    },

    plusScalar: function( scalar ) {
      return new Vector2( this.x + scalar, this.y + scalar );
    },

    minus: function( v ) {
      return new Vector2( this.x - v.x, this.y - v.y );
    },

    minusScalar: function( scalar ) {
      return new Vector2( this.x - scalar, this.y - scalar );
    },

    dividedScalar: function( scalar ) {
      return new Vector2( this.x / scalar, this.y / scalar );
    },

    negated: function() {
      return new Vector2( -this.x, -this.y );
    },

    angle: function() {
      return Math.atan2( this.y, this.x );
    },

    // equivalent to a -PI/2 rotation (right hand rotation)
    perpendicular: function() {
      return new Vector2( this.y, -this.x );
    },

    angleBetween: function( v ) {
      return Math.acos( dot.clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
    },

    rotated: function( angle ) {
      return Vector2.createPolar( this.magnitude(), this.angle() + angle );
    },

    // linear interpolation from this (ratio=0) to vector (ratio=1)
    blend: function( vector, ratio ) {
      return this.plus( vector.minus( this ).times( ratio ) );
    },

    toString: function() {
      return "Vector2(" + this.x + ", " + this.y + ")";
    },

    toVector3: function() {
      return new dot.Vector3( this.x, this.y );
    },

    /*---------------------------------------------------------------------------*
     * Mutables
     *----------------------------------------------------------------------------*/

    set: function( x, y ) {
      this.x = x;
      this.y = y;
      return this;
    },

    setX: function( x ) {
      this.x = x;
      return this;
    },

    setY: function( y ) {
      this.y = y;
      return this;
    },

    add: function( v ) {
      this.x += v.x;
      this.y += v.y;
      return this;
    },

    addScalar: function( scalar ) {
      this.x += scalar;
      this.y += scalar;
      return this;
    },

    subtract: function( v ) {
      this.x -= v.x;
      this.y -= v.y;
      return this;
    },

    subtractScalar: function( scalar ) {
      this.x -= scalar;
      this.y -= scalar;
      return this;
    },

    componentMultiply: function( v ) {
      this.x *= v.x;
      this.y *= v.y;
      return this;
    },

    divideScalar: function( scalar ) {
      this.x /= scalar;
      this.y /= scalar;
      return this;
    },

    negate: function() {
      this.x = -this.x;
      this.y = -this.y;
      return this;
    }

  };

  /*---------------------------------------------------------------------------*
   * Immutable Vector form
   *----------------------------------------------------------------------------*/
  Vector2.Immutable = function( x, y ) {
    this.x = x || 0;
    this.y = y || 0;
  };
  var Immutable = Vector2.Immutable;

  Immutable.prototype = new Vector2();
  Immutable.prototype.constructor = Immutable;

  // throw errors whenever a mutable method is called on our immutable vector
  Immutable.mutableOverrideHelper = function( mutableFunctionName ) {
    Immutable.prototype[mutableFunctionName] = function() {
      throw new Error( "Cannot call mutable method '" + mutableFunctionName + "' on immutable Vector2" );
    };
  };

  // TODO: better way to handle this list?
  Immutable.mutableOverrideHelper( 'set' );
  Immutable.mutableOverrideHelper( 'setX' );
  Immutable.mutableOverrideHelper( 'setY' );
  Immutable.mutableOverrideHelper( 'copy' );
  Immutable.mutableOverrideHelper( 'add' );
  Immutable.mutableOverrideHelper( 'addScalar' );
  Immutable.mutableOverrideHelper( 'subtract' );
  Immutable.mutableOverrideHelper( 'subtractScalar' );
  Immutable.mutableOverrideHelper( 'componentMultiply' );
  Immutable.mutableOverrideHelper( 'divideScalar' );
  Immutable.mutableOverrideHelper( 'negate' );

  // helpful immutable constants
  Vector2.ZERO = new Immutable( 0, 0 );
  Vector2.X_UNIT = new Immutable( 1, 0 );
  Vector2.Y_UNIT = new Immutable( 0, 1 );

  return Vector2;
} );
<commit_msg>Fixed scalar cross product for Vector2, made it more efficient<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * Basic 2-dimensional vector
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  require( 'DOT/Util' );
  // require( 'DOT/Vector3' ); // commented out since Require.js complains about the circular dependency

  dot.Vector2 = function( x, y ) {
    // allow optional parameters
    this.x = x || 0;
    this.y = y || 0;
  };
  var Vector2 = dot.Vector2;

  Vector2.createPolar = function( magnitude, angle ) {
    return new Vector2( Math.cos( angle ), Math.sin( angle ) ).timesScalar( magnitude );
  };

  Vector2.prototype = {
    constructor: Vector2,

    isVector2: true,

    dimension: 2,

    magnitude: function() {
      return Math.sqrt( this.magnitudeSquared() );
    },

    magnitudeSquared: function() {
      return this.dot( this );
    },

    // the distance between this vector (treated as a point) and another point
    distance: function( point ) {
      return this.minus( point ).magnitude();
    },

    // the squared distance between this vector (treated as a point) and another point
    distanceSquared: function( point ) {
      return this.minus( point ).magnitudeSquared();
    },

    dot: function( v ) {
      return this.x * v.x + this.y * v.y;
    },

    equals: function( other ) {
      return this.x === other.x && this.y === other.y;
    },

    equalsEpsilon: function( other, epsilon ) {
      if ( !epsilon ) {
        epsilon = 0;
      }
      return Math.abs( this.x - other.x ) + Math.abs( this.y - other.y ) <= epsilon;
    },

    isFinite: function() {
      return isFinite( this.x ) && isFinite( this.y );
    },

    /*---------------------------------------------------------------------------*
     * Immutables
     *----------------------------------------------------------------------------*/

    copy: function() {
      return new Vector2( this.x, this.y );
    },

    // z component of the equivalent 3-dimensional cross product (this.x, this.y,0) x (v.x, v.y, 0)
    crossScalar: function( v ) {
      return this.x * v.y  - this.y * v.x;
    },

    normalized: function() {
      var mag = this.magnitude();
      if ( mag === 0 ) {
        throw new Error( "Cannot normalize a zero-magnitude vector" );
      }
      else {
        return new Vector2( this.x / mag, this.y / mag );
      }
    },

    timesScalar: function( scalar ) {
      return new Vector2( this.x * scalar, this.y * scalar );
    },

    times: function( scalar ) {
      // make sure it's not a vector!
      assert && assert( scalar.dimension === undefined );
      return this.timesScalar( scalar );
    },

    componentTimes: function( v ) {
      return new Vector2( this.x * v.x, this.y * v.y );
    },

    plus: function( v ) {
      return new Vector2( this.x + v.x, this.y + v.y );
    },

    plusScalar: function( scalar ) {
      return new Vector2( this.x + scalar, this.y + scalar );
    },

    minus: function( v ) {
      return new Vector2( this.x - v.x, this.y - v.y );
    },

    minusScalar: function( scalar ) {
      return new Vector2( this.x - scalar, this.y - scalar );
    },

    dividedScalar: function( scalar ) {
      return new Vector2( this.x / scalar, this.y / scalar );
    },

    negated: function() {
      return new Vector2( -this.x, -this.y );
    },

    angle: function() {
      return Math.atan2( this.y, this.x );
    },

    // equivalent to a -PI/2 rotation (right hand rotation)
    perpendicular: function() {
      return new Vector2( this.y, -this.x );
    },

    angleBetween: function( v ) {
      return Math.acos( dot.clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
    },

    rotated: function( angle ) {
      return Vector2.createPolar( this.magnitude(), this.angle() + angle );
    },

    // linear interpolation from this (ratio=0) to vector (ratio=1)
    blend: function( vector, ratio ) {
      return this.plus( vector.minus( this ).times( ratio ) );
    },

    toString: function() {
      return "Vector2(" + this.x + ", " + this.y + ")";
    },

    toVector3: function() {
      return new dot.Vector3( this.x, this.y );
    },

    /*---------------------------------------------------------------------------*
     * Mutables
     *----------------------------------------------------------------------------*/

    set: function( x, y ) {
      this.x = x;
      this.y = y;
      return this;
    },

    setX: function( x ) {
      this.x = x;
      return this;
    },

    setY: function( y ) {
      this.y = y;
      return this;
    },

    add: function( v ) {
      this.x += v.x;
      this.y += v.y;
      return this;
    },

    addScalar: function( scalar ) {
      this.x += scalar;
      this.y += scalar;
      return this;
    },

    subtract: function( v ) {
      this.x -= v.x;
      this.y -= v.y;
      return this;
    },

    subtractScalar: function( scalar ) {
      this.x -= scalar;
      this.y -= scalar;
      return this;
    },

    componentMultiply: function( v ) {
      this.x *= v.x;
      this.y *= v.y;
      return this;
    },

    divideScalar: function( scalar ) {
      this.x /= scalar;
      this.y /= scalar;
      return this;
    },

    negate: function() {
      this.x = -this.x;
      this.y = -this.y;
      return this;
    }

  };

  /*---------------------------------------------------------------------------*
   * Immutable Vector form
   *----------------------------------------------------------------------------*/
  Vector2.Immutable = function( x, y ) {
    this.x = x || 0;
    this.y = y || 0;
  };
  var Immutable = Vector2.Immutable;

  Immutable.prototype = new Vector2();
  Immutable.prototype.constructor = Immutable;

  // throw errors whenever a mutable method is called on our immutable vector
  Immutable.mutableOverrideHelper = function( mutableFunctionName ) {
    Immutable.prototype[mutableFunctionName] = function() {
      throw new Error( "Cannot call mutable method '" + mutableFunctionName + "' on immutable Vector2" );
    };
  };

  // TODO: better way to handle this list?
  Immutable.mutableOverrideHelper( 'set' );
  Immutable.mutableOverrideHelper( 'setX' );
  Immutable.mutableOverrideHelper( 'setY' );
  Immutable.mutableOverrideHelper( 'copy' );
  Immutable.mutableOverrideHelper( 'add' );
  Immutable.mutableOverrideHelper( 'addScalar' );
  Immutable.mutableOverrideHelper( 'subtract' );
  Immutable.mutableOverrideHelper( 'subtractScalar' );
  Immutable.mutableOverrideHelper( 'componentMultiply' );
  Immutable.mutableOverrideHelper( 'divideScalar' );
  Immutable.mutableOverrideHelper( 'negate' );

  // helpful immutable constants
  Vector2.ZERO = new Immutable( 0, 0 );
  Vector2.X_UNIT = new Immutable( 1, 0 );
  Vector2.Y_UNIT = new Immutable( 0, 1 );

  return Vector2;
} );
<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * Basic 2-dimensional vector
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  var inherit = require( 'PHET_CORE/inherit' );
  require( 'DOT/Util' );
  // require( 'DOT/Vector3' ); // commented out since Require.js complains about the circular dependency

  dot.Vector2 = function Vector2( x, y ) {
    // allow optional parameters
    this.x = x || 0;
    this.y = y || 0;

    assert && assert( typeof this.x === 'number', 'x needs to be a number' );
    assert && assert( typeof this.y === 'number', 'y needs to be a number' );
  };
  var Vector2 = dot.Vector2;

  Vector2.createPolar = function( magnitude, angle ) {
    return new Vector2( magnitude * Math.cos( angle ), magnitude * Math.sin( angle ) );
  };

  Vector2.prototype = {
    constructor: Vector2,

    isVector2: true,

    dimension: 2,

    magnitude: function() {
      return Math.sqrt( this.magnitudeSquared() );
    },

    magnitudeSquared: function() {
      return this.dot( this );
    },

    // the distance between this vector (treated as a point) and another point
    distance: function( point ) {
      return this.minus( point ).magnitude();
    },

    // the squared distance between this vector (treated as a point) and another point
    distanceSquared: function( point ) {
      return this.minus( point ).magnitudeSquared();
    },

    dot: function( v ) {
      return this.x * v.x + this.y * v.y;
    },

    equals: function( other ) {
      return this.x === other.x && this.y === other.y;
    },

    equalsEpsilon: function( other, epsilon ) {
      if ( !epsilon ) {
        epsilon = 0;
      }
      return Math.max( Math.abs( this.x - other.x ), Math.abs( this.y - other.y ) ) <= epsilon;
    },

    isFinite: function() {
      return isFinite( this.x ) && isFinite( this.y );
    },

    /*---------------------------------------------------------------------------*
     * Immutables
     *----------------------------------------------------------------------------*/

    copy: function() {
      return new this.constructor( this.x, this.y );
    },

    // z component of the equivalent 3-dimensional cross product (this.x, this.y,0) x (v.x, v.y, 0)
    crossScalar: function( v ) {
      return this.x * v.y - this.y * v.x;
    },

    normalized: function() {
      var mag = this.magnitude();
      if ( mag === 0 ) {
        throw new Error( "Cannot normalize a zero-magnitude vector" );
      }
      else {
        return new this.constructor( this.x / mag, this.y / mag );
      }
    },

    timesScalar: function( scalar ) {
      return new this.constructor( this.x * scalar, this.y * scalar );
    },

    times: function( scalar ) {
      // make sure it's not a vector!
      assert && assert( scalar.dimension === undefined );
      return this.timesScalar( scalar );
    },

    componentTimes: function( v ) {
      return new this.constructor( this.x * v.x, this.y * v.y );
    },

    plus: function( v ) {
      return new this.constructor( this.x + v.x, this.y + v.y );
    },

    plusScalar: function( scalar ) {
      return new this.constructor( this.x + scalar, this.y + scalar );
    },

    minus: function( v ) {
      return new this.constructor( this.x - v.x, this.y - v.y );
    },

    minusScalar: function( scalar ) {
      return new this.constructor( this.x - scalar, this.y - scalar );
    },

    dividedScalar: function( scalar ) {
      return new this.constructor( this.x / scalar, this.y / scalar );
    },

    negated: function() {
      return new this.constructor( -this.x, -this.y );
    },

    angle: function() {
      return Math.atan2( this.y, this.x );
    },

    // equivalent to a -PI/2 rotation (right hand rotation)
    perpendicular: function() {
      return new this.constructor( this.y, -this.x );
    },

    angleBetween: function( v ) {
      return Math.acos( dot.clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
    },

    rotated: function( angle ) {
      var newAngle = this.angle() + angle;
      return new this.constructor( Math.cos( newAngle ), Math.sin( newAngle ) ).timesScalar( this.magnitude() );
    },

    // linear interpolation from this (ratio=0) to vector (ratio=1)
    blend: function( vector, ratio ) {
      return this.plus( vector.minus( this ).times( ratio ) );
    },

    toString: function() {
      return "Vector2(" + this.x + ", " + this.y + ")";
    },

    toVector3: function() {
      return new dot.Vector3( this.x, this.y );
    },

    /*---------------------------------------------------------------------------*
     * Mutables
     *----------------------------------------------------------------------------*/

    set: function( x, y ) {
      this.x = x;
      this.y = y;
      return this;
    },

    setX: function( x ) {
      this.x = x;
      return this;
    },

    setY: function( y ) {
      this.y = y;
      return this;
    },

    add: function( v ) {
      this.x += v.x;
      this.y += v.y;
      return this;
    },

    addScalar: function( scalar ) {
      this.x += scalar;
      this.y += scalar;
      return this;
    },

    subtract: function( v ) {
      this.x -= v.x;
      this.y -= v.y;
      return this;
    },

    subtractScalar: function( scalar ) {
      this.x -= scalar;
      this.y -= scalar;
      return this;
    },

    componentMultiply: function( v ) {
      this.x *= v.x;
      this.y *= v.y;
      return this;
    },

    divideScalar: function( scalar ) {
      this.x /= scalar;
      this.y /= scalar;
      return this;
    },

    negate: function() {
      this.x = -this.x;
      this.y = -this.y;
      return this;
    }

  };

  /*---------------------------------------------------------------------------*
   * Immutable Vector form
   *----------------------------------------------------------------------------*/
  Vector2.Immutable = function ImmutableVector2( x, y ) {
    Vector2.call( this, x, y );
  };
  var Immutable = Vector2.Immutable;

  inherit( Immutable, Vector2 );

  // throw errors whenever a mutable method is called on our immutable vector
  Immutable.mutableOverrideHelper = function( mutableFunctionName ) {
    Immutable.prototype[mutableFunctionName] = function() {
      throw new Error( "Cannot call mutable method '" + mutableFunctionName + "' on immutable Vector2" );
    };
  };

  // TODO: better way to handle this list?
  Immutable.mutableOverrideHelper( 'set' );
  Immutable.mutableOverrideHelper( 'setX' );
  Immutable.mutableOverrideHelper( 'setY' );
  Immutable.mutableOverrideHelper( 'copy' );
  Immutable.mutableOverrideHelper( 'add' );
  Immutable.mutableOverrideHelper( 'addScalar' );
  Immutable.mutableOverrideHelper( 'subtract' );
  Immutable.mutableOverrideHelper( 'subtractScalar' );
  Immutable.mutableOverrideHelper( 'componentMultiply' );
  Immutable.mutableOverrideHelper( 'divideScalar' );
  Immutable.mutableOverrideHelper( 'negate' );

  // helpful immutable constants
  Vector2.ZERO = new Immutable( 0, 0 );
  Vector2.X_UNIT = new Immutable( 1, 0 );
  Vector2.Y_UNIT = new Immutable( 0, 1 );

  return Vector2;
} );
<commit_msg>Formatting<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * Basic 2-dimensional vector
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  var inherit = require( 'PHET_CORE/inherit' );
  require( 'DOT/Util' );
  // require( 'DOT/Vector3' ); // commented out since Require.js complains about the circular dependency

  dot.Vector2 = function Vector2( x, y ) {
    // allow optional parameters
    this.x = x || 0;
    this.y = y || 0;

    assert && assert( typeof this.x === 'number', 'x needs to be a number' );
    assert && assert( typeof this.y === 'number', 'y needs to be a number' );
  };
  var Vector2 = dot.Vector2;

  Vector2.createPolar = function( magnitude, angle ) {
    return new Vector2( magnitude * Math.cos( angle ), magnitude * Math.sin( angle ) );
  };

  Vector2.prototype = {
    constructor: Vector2,

    isVector2: true,

    dimension: 2,

    magnitude: function() {
      return Math.sqrt( this.magnitudeSquared() );
    },

    magnitudeSquared: function() {
      return this.dot( this );
    },

    // the distance between this vector (treated as a point) and another point
    distance: function( point ) {
      return this.minus( point ).magnitude();
    },

    // the squared distance between this vector (treated as a point) and another point
    distanceSquared: function( point ) {
      return this.minus( point ).magnitudeSquared();
    },

    dot: function( v ) {
      return this.x * v.x + this.y * v.y;
    },

    equals: function( other ) {
      return this.x === other.x && this.y === other.y;
    },

    equalsEpsilon: function( other, epsilon ) {
      if ( !epsilon ) {
        epsilon = 0;
      }
      return Math.max( Math.abs( this.x - other.x ), Math.abs( this.y - other.y ) ) <= epsilon;
    },

    isFinite: function() {
      return isFinite( this.x ) && isFinite( this.y );
    },

    /*---------------------------------------------------------------------------*
     * Immutables
     *----------------------------------------------------------------------------*/

    copy: function() {
      return new this.constructor( this.x, this.y );
    },

    // z component of the equivalent 3-dimensional cross product (this.x, this.y,0) x (v.x, v.y, 0)
    crossScalar: function( v ) {
      return this.x * v.y - this.y * v.x;
    },

    normalized: function() {
      var mag = this.magnitude();
      if ( mag === 0 ) {
        throw new Error( "Cannot normalize a zero-magnitude vector" );
      }
      else {
        return new this.constructor( this.x / mag, this.y / mag );
      }
    },

    timesScalar: function( scalar ) {
      return new this.constructor( this.x * scalar, this.y * scalar );
    },

    times: function( scalar ) {
      // make sure it's not a vector!
      assert && assert( scalar.dimension === undefined );
      return this.timesScalar( scalar );
    },

    componentTimes: function( v ) {
      return new this.constructor( this.x * v.x, this.y * v.y );
    },

    plus: function( v ) {
      return new this.constructor( this.x + v.x, this.y + v.y );
    },

    plusScalar: function( scalar ) {
      return new this.constructor( this.x + scalar, this.y + scalar );
    },

    minus: function( v ) {
      return new this.constructor( this.x - v.x, this.y - v.y );
    },

    minusScalar: function( scalar ) {
      return new this.constructor( this.x - scalar, this.y - scalar );
    },

    dividedScalar: function( scalar ) {
      return new this.constructor( this.x / scalar, this.y / scalar );
    },

    negated: function() {
      return new this.constructor( -this.x, -this.y );
    },

    angle: function() {
      return Math.atan2( this.y, this.x );
    },

    // equivalent to a -PI/2 rotation (right hand rotation)
    perpendicular: function() {
      return new this.constructor( this.y, -this.x );
    },

    angleBetween: function( v ) {
      return Math.acos( dot.clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
    },

    rotated: function( angle ) {
      var newAngle = this.angle() + angle;
      return new this.constructor( Math.cos( newAngle ), Math.sin( newAngle ) ).timesScalar( this.magnitude() );
    },

    // linear interpolation from this (ratio=0) to vector (ratio=1)
    blend: function( vector, ratio ) {
      return this.plus( vector.minus( this ).times( ratio ) );
    },

    toString: function() {
      return "Vector2(" + this.x + ", " + this.y + ")";
    },

    toVector3: function() {
      return new dot.Vector3( this.x, this.y );
    },

    /*---------------------------------------------------------------------------*
     * Mutables
     *----------------------------------------------------------------------------*/

    set: function( x, y ) {
      this.x = x;
      this.y = y;
      return this;
    },

    setX: function( x ) {
      this.x = x;
      return this;
    },

    setY: function( y ) {
      this.y = y;
      return this;
    },

    add: function( v ) {
      this.x += v.x;
      this.y += v.y;
      return this;
    },

    addScalar: function( scalar ) {
      this.x += scalar;
      this.y += scalar;
      return this;
    },

    subtract: function( v ) {
      this.x -= v.x;
      this.y -= v.y;
      return this;
    },

    subtractScalar: function( scalar ) {
      this.x -= scalar;
      this.y -= scalar;
      return this;
    },

    componentMultiply: function( v ) {
      this.x *= v.x;
      this.y *= v.y;
      return this;
    },

    divideScalar: function( scalar ) {
      this.x /= scalar;
      this.y /= scalar;
      return this;
    },

    negate: function() {
      this.x = -this.x;
      this.y = -this.y;
      return this;
    }

  };

  /*---------------------------------------------------------------------------*
   * Immutable Vector form
   *----------------------------------------------------------------------------*/
  Vector2.Immutable = function ImmutableVector2( x, y ) {
    Vector2.call( this, x, y );
  };
  var Immutable = Vector2.Immutable;

  inherit( Immutable, Vector2 );

  // throw errors whenever a mutable method is called on our immutable vector
  Immutable.mutableOverrideHelper = function( mutableFunctionName ) {
    Immutable.prototype[mutableFunctionName] = function() {
      throw new Error( "Cannot call mutable method '" + mutableFunctionName + "' on immutable Vector2" );
    };
  };

  // TODO: better way to handle this list?
  Immutable.mutableOverrideHelper( 'set' );
  Immutable.mutableOverrideHelper( 'setX' );
  Immutable.mutableOverrideHelper( 'setY' );
  Immutable.mutableOverrideHelper( 'copy' );
  Immutable.mutableOverrideHelper( 'add' );
  Immutable.mutableOverrideHelper( 'addScalar' );
  Immutable.mutableOverrideHelper( 'subtract' );
  Immutable.mutableOverrideHelper( 'subtractScalar' );
  Immutable.mutableOverrideHelper( 'componentMultiply' );
  Immutable.mutableOverrideHelper( 'divideScalar' );
  Immutable.mutableOverrideHelper( 'negate' );

  // helpful immutable constants
  Vector2.ZERO = new Immutable( 0, 0 );
  Vector2.X_UNIT = new Immutable( 1, 0 );
  Vector2.Y_UNIT = new Immutable( 0, 1 );

  return Vector2;
} );
<|endoftext|>