<commit_before>// Copyright 2016, University of Colorado Boulder

/**
 * A flexible keypad that looks somewhat like a calculator or keyboard keypad.
 *
 * @author Aadish Gupta
 * @author John Blanco
 */

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

  // modules
  var BackspaceIcon = require( 'SCENERY_PHET/BackspaceIcon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var NumberAccumulator = require( 'SCENERY_PHET/keypad/NumberAccumulator' );
  var Key = require( 'SCENERY_PHET/keypad/Key' );
  var Keys = require( 'SCENERY_PHET/keypad/Keys' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Tandem = require( 'TANDEM/Tandem' );

  // constants
  var DEFAULT_BUTTON_WIDTH = 35;
  var DEFAULT_BUTTON_HEIGHT = 35;
  var DEFAULT_BUTTON_FONT = new PhetFont( { size: 20 } );
  var PLUS_CHAR = '\u002b';
  var MINUS_CHAR = '\u2212';

  /**
   * @param {Array.<Key>} layout - an array that specifies the keys and the layout, see static instance below for
   * example usage
   * @param {Object} [options]
   * @constructor
   */
  function Keypad( layout, options ) {
    Tandem.indicateUninstrumentedCode();

    options = _.extend( {
      buttonWidth: DEFAULT_BUTTON_WIDTH,
      buttonHeight: DEFAULT_BUTTON_HEIGHT,
      xSpacing: 10,
      ySpacing: 10,
      buttonColor: 'white',
      buttonFont: DEFAULT_BUTTON_FONT,
      accumulator: null
    }, options );

    Node.call( this );
    var self = this;

    // @private
    if ( options.accumulator ) {
      this.keyAccumulator = options.accumulator;
    }
    else {
      this.keyAccumulator = new NumberAccumulator( options );
    }

    this.stringProperty = this.keyAccumulator.stringProperty; // @public (read-only)
    this.valueProperty = this.keyAccumulator.valueProperty; // @public (read-only)
    this.accumulatedKeysProperty = this.keyAccumulator.accumulatedKeysProperty; // @public (read-only)

    // determine number of rows and columns from the input layout
    var numRows = layout.length;
    var numColumns = 0;
    var i;

    for ( i = 0; i < numRows; i++ ) {
      if ( layout[ i ].length > numColumns ) {
        numColumns = layout[ i ].length;
      }
    }
    // check last row to see if any button has vertical span more than 1
    var maxVerticalSpan = 1;
    for ( i = 0; i < layout[ numRows - 1 ].length; i++ ) {
      if ( layout[ numRows - 1 ][ i ] && layout[ numRows - 1 ][ i ].verticalSpan > maxVerticalSpan ) {
        maxVerticalSpan = layout[ numRows - 1 ][ i ].verticalSpan;
      }
    }
    numRows += maxVerticalSpan - 1;
    var occupiedLayoutGrid = [];

    for ( i = 0; i < numRows; i++ ) {
      occupiedLayoutGrid[ i ] = [];
      for ( var j = 0; j < numColumns; j++ ) {
        occupiedLayoutGrid[ i ][ j ] = 0;
      }
    }

    // interpret the layout specification
    var x;
    var y;
    for ( i = 0; i < layout.length; i++ ) {
      var startRow = i;
      for ( j = 0; j < layout[ i ].length; j++ ) {
        var button = layout[ i ][ j ];
        if ( button ) {
          var startColumn = j + ( j > 0 && layout[ i ][ j - 1 ] ? layout[ i ][ j - 1 ].horizontalSpan - 1 : 0 );
          var verticalSpan = button.verticalSpan;
          var horizontalSpan = button.horizontalSpan;
          // check for overlap between the buttons
          for ( x = startRow; x < ( startRow + verticalSpan ); x++ ) {
            for ( y = startColumn; y < ( startColumn + horizontalSpan ); y++ ) {
              assert && assert( !occupiedLayoutGrid[ x ][ y ], 'keys overlap in the layout' );
              occupiedLayoutGrid[ x ][ y ] = true;
            }
          }

          // create and add the buttons
          var buttonWidth = button.horizontalSpan * options.buttonWidth + ( button.horizontalSpan - 1 ) * options.xSpacing;
          var buttonHeight = button.verticalSpan * options.buttonHeight + ( button.verticalSpan - 1 ) * options.ySpacing;
          var buttonNode = createKeyNode( button, self.keyAccumulator, buttonWidth, buttonHeight, options );
          buttonNode.left = startColumn * options.buttonWidth + startColumn * options.xSpacing;
          buttonNode.top = startRow * options.buttonHeight + startRow * options.ySpacing;
          self.addChild( buttonNode );
        }
      }
    }

    this.mutate( options );
  }

  sceneryPhet.register( 'Keypad', Keypad );

  /**
   * Helper function to create the display key node for the provided key object
   *
   * @param {Key} keyObject
   * @param {AbstractKeyAccumulator} keyAccumulator
   * @param {number} width
   * @param {number} height
   * @param {Object} [options]
   * @returns {RectangularPushButton} keyNode
   */
  function createKeyNode( keyObject, keyAccumulator, width, height, options ) {

    options = _.extend( {
      buttonColor: 'white',
      buttonFont: DEFAULT_BUTTON_FONT
    }, options );

    var content = ( keyObject.displayNode instanceof Node ) ?
                  keyObject.displayNode :
                  new Text( keyObject.displayNode, { font: options.buttonFont } );

    var keyNode = new RectangularPushButton( {
      content: content,
      baseColor: options.buttonColor,
      minWidth: width,
      minHeight: height,
      xMargin: 5,
      yMargin: 5,
      listener: function() {
        keyAccumulator.handleKeyPressed( keyObject.identifier );
      }
    } );
    keyNode.scale( width / keyNode.width, height / keyNode.height );
    return keyNode;
  }

  return inherit( Node, Keypad, {

    /**
     * Calls the clear function for the given accumulator
     */
    clear: function() {
      this.keyAccumulator.clear();
    },

    /**
     * Determines whether pressing a key (except for backspace) will clear the existing value.
     * @param {boolean} clearOnNextKeyPress
     * @public
     */
    setClearOnNextKeyPress: function( clearOnNextKeyPress ) {
      this.keyAccumulator.setClearOnNextKeyPress( clearOnNextKeyPress );
    },

    /**
     * Will pressing a key (except for backspace) clear the existing value?
     * @returns {boolean}
     * @public
     */
    getClearOnNextKeyPress: function() {
      return this.keyAccumulator.getClearOnNextKeyPress();
    }

  }, {

    // -------------------- static common layouts -------------------------

    /**
     * Layout Specifications for creating your custom layout
     * If Vertical Span greater than 1 is provided the column in the next rows has to be null else it will hit assertion
     * for overlap
     * If Horizontal Span greater than 1 is provided the next key in that row will not overlap and start after the row
     * If you want blank spaces in the row you would need to provide null
     */

    PositiveIntegerLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '0', Keys.ZERO, { horizontalSpan: 2 } ), new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    PositiveAndNegativeIntegerLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ), new Key( '0', Keys.ZERO ), new Key( PLUS_CHAR + '/' + MINUS_CHAR, Keys.PLUSMINUS ) ]
    ],

    PositiveFloatingPointLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '.', Keys.DECIMAL ), new Key( '0', Keys.ZERO ), new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    PositiveAndNegativeFloatingPointLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '0', Keys.ZERO, { horizontalSpan: 2 } ), new Key( PLUS_CHAR + '/' + MINUS_CHAR, Keys.PLUSMINUS ) ],
      [ new Key( '.', Keys.DECIMAL ), null, new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    // Weird Layout is created for testing purposes to test the edge cases and layout capabilities
    WeirdLayout: [
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE, { horizontalSpan: 3 } ) ],
      [ null, new Key( '4', Keys.FOUR ) ],
      [ new Key( '5', Keys.FIVE, { verticalSpan: 2 } ), new Key( '6', Keys.SIX ), new Key( '7', Keys.SEVEN ) ],
      [ null, new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ null, new Key( '0', Keys.ZERO, { horizontalSpan: 2, verticalSpan: 2 } ) ]
    ]

  } );
} );<commit_msg>simplify variable names<commit_after>// Copyright 2016, University of Colorado Boulder

/**
 * A flexible keypad that looks somewhat like a calculator or keyboard keypad.
 *
 * @author Aadish Gupta
 * @author John Blanco
 */

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

  // modules
  var BackspaceIcon = require( 'SCENERY_PHET/BackspaceIcon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var NumberAccumulator = require( 'SCENERY_PHET/keypad/NumberAccumulator' );
  var Key = require( 'SCENERY_PHET/keypad/Key' );
  var Keys = require( 'SCENERY_PHET/keypad/Keys' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Tandem = require( 'TANDEM/Tandem' );

  // constants
  var DEFAULT_BUTTON_WIDTH = 35;
  var DEFAULT_BUTTON_HEIGHT = 35;
  var DEFAULT_BUTTON_FONT = new PhetFont( { size: 20 } );
  var DEFAULT_BUTTON_COLOR = 'white';
  var PLUS_CHAR = '\u002b';
  var MINUS_CHAR = '\u2212';

  /**
   * @param {Array.<Key>} layout - an array that specifies the keys and the layout, see static instance below for
   * example usage
   * @param {Object} [options]
   * @constructor
   */
  function Keypad( layout, options ) {
    Tandem.indicateUninstrumentedCode();

    options = _.extend( {
      buttonWidth: DEFAULT_BUTTON_WIDTH,
      buttonHeight: DEFAULT_BUTTON_HEIGHT,
      xSpacing: 10,
      ySpacing: 10,
      buttonColor: DEFAULT_BUTTON_COLOR,
      buttonFont: DEFAULT_BUTTON_FONT,
      accumulator: null
    }, options );

    Node.call( this );
    var self = this;

    // @private
    if ( options.accumulator ) {
      this.keyAccumulator = options.accumulator;
    }
    else {
      this.keyAccumulator = new NumberAccumulator( options );
    }

    this.stringProperty = this.keyAccumulator.stringProperty; // @public (read-only)
    this.valueProperty = this.keyAccumulator.valueProperty; // @public (read-only)
    this.accumulatedKeysProperty = this.keyAccumulator.accumulatedKeysProperty; // @public (read-only)

    // determine number of rows and columns from the input layout
    var numRows = layout.length;
    var numColumns = 0;
    var row;
    var column;

    for ( row = 0; row < numRows; row++ ) {
      if ( layout[ row ].length > numColumns ) {
        numColumns = layout[ row ].length;
      }
    }
    // check last row to see if any button has vertical span more than 1
    var maxVerticalSpan = 1;
    for ( column = 0; column < layout[ numRows - 1 ].length; column++ ) {
      if ( layout[ numRows - 1 ][ column ] && layout[ numRows - 1 ][ column ].verticalSpan > maxVerticalSpan ) {
        maxVerticalSpan = layout[ numRows - 1 ][ column ].verticalSpan;
      }
    }
    numRows += maxVerticalSpan - 1;

    // 2D grid to check for the overlap
    var occupiedLayoutGrid = [];

    for ( row = 0; row < numRows; row++ ) {
      occupiedLayoutGrid[ row ] = [];
      for ( column = 0; column < numColumns; column++ ) {
        occupiedLayoutGrid[ row ][ column ] = 0;
      }
    }

    // interpret the layout specification
    var x;
    var y;
    for ( row = 0; row < layout.length; row++ ) {
      var startRow = row;
      for ( column = 0; column < layout[ row ].length; column++ ) {
        var button = layout[ row ][ column ];
        if ( button ) {
          var startColumn = column +
                            ( column > 0 && layout[ row ][ column - 1 ] ?
                              layout[ row ][ column - 1 ].horizontalSpan - 1 : 0 );
          var verticalSpan = button.verticalSpan;
          var horizontalSpan = button.horizontalSpan;
          // check for overlap between the buttons
          for ( x = startRow; x < ( startRow + verticalSpan ); x++ ) {
            for ( y = startColumn; y < ( startColumn + horizontalSpan ); y++ ) {
              assert && assert( !occupiedLayoutGrid[ x ][ y ], 'keys overlap in the layout' );
              occupiedLayoutGrid[ x ][ y ] = true;
            }
          }

          // create and add the buttons
          var buttonWidth = button.horizontalSpan * options.buttonWidth + ( button.horizontalSpan - 1 ) * options.xSpacing;
          var buttonHeight = button.verticalSpan * options.buttonHeight + ( button.verticalSpan - 1 ) * options.ySpacing;
          var buttonNode = createKeyNode( button, self.keyAccumulator, buttonWidth, buttonHeight, options );
          buttonNode.left = startColumn * options.buttonWidth + startColumn * options.xSpacing;
          buttonNode.top = startRow * options.buttonHeight + startRow * options.ySpacing;
          self.addChild( buttonNode );
        }
      }
    }

    this.mutate( options );
  }

  sceneryPhet.register( 'Keypad', Keypad );

  /**
   * Helper function to create the display key node for the provided key object
   *
   * @param {Key} keyObject
   * @param {AbstractKeyAccumulator} keyAccumulator
   * @param {number} width
   * @param {number} height
   * @param {Object} [options]
   * @returns {RectangularPushButton} keyNode
   */
  function createKeyNode( keyObject, keyAccumulator, width, height, options ) {

    options = _.extend( {
      buttonColor: 'white',
      buttonFont: DEFAULT_BUTTON_FONT
    }, options );

    var content = ( keyObject.displayNode instanceof Node ) ?
                  keyObject.displayNode :
                  new Text( keyObject.displayNode, { font: options.buttonFont } );

    var keyNode = new RectangularPushButton( {
      content: content,
      baseColor: options.buttonColor,
      minWidth: width,
      minHeight: height,
      xMargin: 5,
      yMargin: 5,
      listener: function() {
        keyAccumulator.handleKeyPressed( keyObject.identifier );
      }
    } );
    keyNode.scale( width / keyNode.width, height / keyNode.height );
    return keyNode;
  }

  return inherit( Node, Keypad, {

    /**
     * Calls the clear function for the given accumulator
     */
    clear: function() {
      this.keyAccumulator.clear();
    },

    /**
     * Determines whether pressing a key (except for backspace) will clear the existing value.
     * @param {boolean} clearOnNextKeyPress
     * @public
     */
    setClearOnNextKeyPress: function( clearOnNextKeyPress ) {
      this.keyAccumulator.setClearOnNextKeyPress( clearOnNextKeyPress );
    },

    /**
     * Will pressing a key (except for backspace) clear the existing value?
     * @returns {boolean}
     * @public
     */
    getClearOnNextKeyPress: function() {
      return this.keyAccumulator.getClearOnNextKeyPress();
    }

  }, {

    // -------------------- static common layouts -------------------------

    /**
     * Layout Specifications for creating your custom layout
     * If Vertical Span greater than 1 is provided the column in the next rows has to be null else it will hit assertion
     * for overlap
     * If Horizontal Span greater than 1 is provided the next key in that row will not overlap and start after the row
     * If you want blank spaces in the row you would need to provide null
     */

    PositiveIntegerLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '0', Keys.ZERO, { horizontalSpan: 2 } ), new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    PositiveAndNegativeIntegerLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ), new Key( '0', Keys.ZERO ), new Key( PLUS_CHAR + '/' + MINUS_CHAR, Keys.PLUSMINUS ) ]
    ],

    PositiveFloatingPointLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '.', Keys.DECIMAL ), new Key( '0', Keys.ZERO ), new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    PositiveAndNegativeFloatingPointLayout: [
      [ new Key( '7', Keys.SEVEN ), new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ new Key( '4', Keys.FOUR ), new Key( '5', Keys.FIVE ), new Key( '6', Keys.SIX ) ],
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE ) ],
      [ new Key( '0', Keys.ZERO, { horizontalSpan: 2 } ), new Key( PLUS_CHAR + '/' + MINUS_CHAR, Keys.PLUSMINUS ) ],
      [ new Key( '.', Keys.DECIMAL ), null, new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), Keys.BACKSPACE ) ]
    ],

    // Weird Layout is created for testing purposes to test the edge cases and layout capabilities
    WeirdLayout: [
      [ new Key( '1', Keys.ONE ), new Key( '2', Keys.TWO ), new Key( '3', Keys.THREE, { horizontalSpan: 3 } ) ],
      [ null, new Key( '4', Keys.FOUR, { horizontalSpan: 5 } ) ],
      [ new Key( '5', Keys.FIVE, { verticalSpan: 2 } ), new Key( '6', Keys.SIX ), new Key( '7', Keys.SEVEN ) ],
      [ null, new Key( '8', Keys.EIGHT ), new Key( '9', Keys.NINE ) ],
      [ null, new Key( '0', Keys.ZERO, { horizontalSpan: 2, verticalSpan: 2 } ) ]
    ]

  } );
} );<|endoftext|>


<commit_before>define([],function(){

    var round = Math.round;

    Math.round = function(value, digits){
        if(digits){
            var pow = Math.pow(10, digits);
            return round(value * pow) / pow;
        }
        return round(value);
    };

    return true;
});<commit_msg>added polyfill for Date.now()<commit_after>define([],function(){

    var round = Math.round;

    Math.round = function(value, digits){
        if(digits){
            var pow = Math.pow(10, digits);
            return round(value * pow) / pow;
        }
        return round(value);
    };

    if (!Date.now) {
        Date.now = function now() {
            return +(new Date);
        };
    }

    return true;
});<|endoftext|>


<commit_before>var config = {
    link: "http://example.com"
}

initMiniChat(config);

function initMiniChat(config) {
    var head = document.head;
    var externalCss = document.createElement('link');

    externalCss.href = 'http://localhost:3000/public/css/chat.css';
    externalCss.rel = 'stylesheet';

    // Then bind the event to the activate function.
    // There are several events for cross browser compatibility.
    var callback = activate(config);

    externalCss.onreadystatechange = callback;
    externalCss.onload = callback;

    head.appendChild(externalCss);
}

function activate(config) {
    var miniChatContainer = document.createElement('div');
    miniChatContainer.setAttribute('class', 'mini-chat-container');

    var miniChatHtml =
    '
' +
      '' +
    '
' +
    '
' +
      '' +
    '
';

    miniChatContainer.innerHTML = miniChatHtml;

    var icon = 'mini-chat-life-ring-icon';
    var iconReplace = 'mini-chat-times-icon';
    var miniChatMenuIconClass = document.getElementsByClassName('mini-chat-menu-icon');
    var miniChatPaneClass = document.getElementsByClassName('mini-chat-pane');
    var miniChatMenuClass = document.getElementsByClassName('mini-chat-menu');

    var lifeRing = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04OTYgMHExODIgMCAzNDggNzF0Mjg2IDE5MSAxOTEgMjg2IDcxIDM0OC03MSAzNDgtMTkxIDI4Ni0yODYgMTkxLTM0OCA3MS0zNDgtNzEtMjg2LTE5MS0xOTEtMjg2LTcxLTM0OCA3MS0zNDggMTkxLTI4NiAyODYtMTkxIDM0OC03MXptMCAxMjhxLTE5MCAwLTM2MSA5MGwxOTQgMTk0cTgyLTI4IDE2Ny0yOHQxNjcgMjhsMTk0LTE5NHEtMTcxLTkwLTM2MS05MHptLTY3OCAxMTI5bDE5NC0xOTRxLTI4LTgyLTI4LTE2N3QyOC0xNjdsLTE5NC0xOTRxLTkwIDE3MS05MCAzNjF0OTAgMzYxem02NzggNDA3cTE5MCAwIDM2MS05MGwtMTk0LTE5NHEtODIgMjgtMTY3IDI4dC0xNjctMjhsLTE5NCAxOTRxMTcxIDkwIDM2MSA5MHptMC0zODRxMTU5IDAgMjcxLjUtMTEyLjV0MTEyLjUtMjcxLjUtMTEyLjUtMjcxLjUtMjcxLjUtMTEyLjUtMjcxLjUgMTEyLjUtMTEyLjUgMjcxLjUgMTEyLjUgMjcxLjUgMjcxLjUgMTEyLjV6bTQ4NC0yMTdsMTk0IDE5NHE5MC0xNzEgOTAtMzYxdC05MC0zNjFsLTE5NCAxOTRxMjggODIgMjggMTY3dC0yOCAxNjd6IiBmaWxsPSIjZmZmIi8+PC9zdmc+";
    var times = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDkwIDEzMjJxMCA0MC0yOCA2OGwtMTM2IDEzNnEtMjggMjgtNjggMjh0LTY4LTI4bC0yOTQtMjk0LTI5NCAyOTRxLTI4IDI4LTY4IDI4dC02OC0yOGwtMTM2LTEzNnEtMjgtMjgtMjgtNjh0MjgtNjhsMjk0LTI5NC0yOTQtMjk0cS0yOC0yOC0yOC02OHQyOC02OGwxMzYtMTM2cTI4LTI4IDY4LTI4dDY4IDI4bDI5NCAyOTQgMjk0LTI5NHEyOC0yOCA2OC0yOHQ2OCAyOGwxMzYgMTM2cTI4IDI4IDI4IDY4dC0yOCA2OGwtMjk0IDI5NCAyOTQgMjk0cTI4IDI4IDI4IDY4eiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==";

    document.body.appendChild(miniChatContainer);

    // Set life ring icon
    miniChatMenuIconClass[0].setAttribute('src', lifeRing);

    document.getElementById('mini-chat-menu').addEventListener('click', function () {
        miniChatMenuIconClass[0].classList.value = miniChatMenuIconClass[0].classList.value.replace(icon, iconReplace);
        miniChatPaneClass[0].classList.toggle('show');
        miniChatPaneClass[0].children[0].classList.toggle('show');
        miniChatMenuClass[0].classList.toggle('show');

        if (miniChatMenuIconClass[0].classList.contains('mini-chat-times-icon')) {
            miniChatMenuIconClass[0].setAttribute('src', times);
        } else {
            miniChatMenuIconClass[0].setAttribute('src', lifeRing);
        }

        var temp = iconReplace;
        iconReplace = icon;
        icon = temp;
    });

    document.getElementById('mini-chat-menu').addEventListener('mousedown', function(event) {
        event.preventDefault();
    }, false);
}
<commit_msg>fix ie support<commit_after>var config = {
    link: "http://example.com"
}

initMiniChat(config);

function initMiniChat(config) {
    var head = document.head;
    var externalCss = document.createElement('link');

    externalCss.href = 'http://localhost:3000/public/css/chat.css';
    externalCss.rel = 'stylesheet';

    // Then bind the event to the activate function.
    // There are several events for cross browser compatibility.
    var callback = activate(config);

    externalCss.onreadystatechange = callback;
    externalCss.onload = callback;

    head.appendChild(externalCss);
}

function activate(config) {
    var miniChatContainer = document.createElement('div');
    miniChatContainer.setAttribute('class', 'mini-chat-container');

    var miniChatHtml =
    '
' +
      '' +
    '
' +
    '
' +
      '' +
    '
';

    miniChatContainer.innerHTML = miniChatHtml;

    var timesClass = 'mini-chat-times-icon';
    var miniChatMenuIconClass = document.getElementsByClassName('mini-chat-menu-icon');
    var miniChatPaneClass = document.getElementsByClassName('mini-chat-pane');
    var miniChatMenuClass = document.getElementsByClassName('mini-chat-menu');

    var lifeRingImage = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04OTYgMHExODIgMCAzNDggNzF0Mjg2IDE5MSAxOTEgMjg2IDcxIDM0OC03MSAzNDgtMTkxIDI4Ni0yODYgMTkxLTM0OCA3MS0zNDgtNzEtMjg2LTE5MS0xOTEtMjg2LTcxLTM0OCA3MS0zNDggMTkxLTI4NiAyODYtMTkxIDM0OC03MXptMCAxMjhxLTE5MCAwLTM2MSA5MGwxOTQgMTk0cTgyLTI4IDE2Ny0yOHQxNjcgMjhsMTk0LTE5NHEtMTcxLTkwLTM2MS05MHptLTY3OCAxMTI5bDE5NC0xOTRxLTI4LTgyLTI4LTE2N3QyOC0xNjdsLTE5NC0xOTRxLTkwIDE3MS05MCAzNjF0OTAgMzYxem02NzggNDA3cTE5MCAwIDM2MS05MGwtMTk0LTE5NHEtODIgMjgtMTY3IDI4dC0xNjctMjhsLTE5NCAxOTRxMTcxIDkwIDM2MSA5MHptMC0zODRxMTU5IDAgMjcxLjUtMTEyLjV0MTEyLjUtMjcxLjUtMTEyLjUtMjcxLjUtMjcxLjUtMTEyLjUtMjcxLjUgMTEyLjUtMTEyLjUgMjcxLjUgMTEyLjUgMjcxLjUgMjcxLjUgMTEyLjV6bTQ4NC0yMTdsMTk0IDE5NHE5MC0xNzEgOTAtMzYxdC05MC0zNjFsLTE5NCAxOTRxMjggODIgMjggMTY3dC0yOCAxNjd6IiBmaWxsPSIjZmZmIi8+PC9zdmc+";
    var timesImage = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDkwIDEzMjJxMCA0MC0yOCA2OGwtMTM2IDEzNnEtMjggMjgtNjggMjh0LTY4LTI4bC0yOTQtMjk0LTI5NCAyOTRxLTI4IDI4LTY4IDI4dC02OC0yOGwtMTM2LTEzNnEtMjgtMjgtMjgtNjh0MjgtNjhsMjk0LTI5NC0yOTQtMjk0cS0yOC0yOC0yOC02OHQyOC02OGwxMzYtMTM2cTI4LTI4IDY4LTI4dDY4IDI4bDI5NCAyOTQgMjk0LTI5NHEyOC0yOCA2OC0yOHQ2OCAyOGwxMzYgMTM2cTI4IDI4IDI4IDY4dC0yOCA2OGwtMjk0IDI5NCAyOTQgMjk0cTI4IDI4IDI4IDY4eiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==";

    document.body.appendChild(miniChatContainer);

    // Set life ring icon
    miniChatMenuIconClass[0].setAttribute('src', lifeRingImage);

    document.getElementById('mini-chat-menu').addEventListener('click', function () {
        miniChatMenuIconClass[0].classList.toggle(timesClass);
        miniChatPaneClass[0].classList.toggle('show');
        miniChatPaneClass[0].children[0].classList.toggle('show');
        miniChatMenuClass[0].classList.toggle('show');

        if (miniChatMenuIconClass[0].classList.contains(timesClass)) {
            miniChatMenuIconClass[0].setAttribute('src', timesImage);
        } else {
            miniChatMenuIconClass[0].setAttribute('src', lifeRingImage);
        }
    });

    document.getElementById('mini-chat-menu').addEventListener('mousedown', function(event) {
        event.preventDefault();
    }, false);
}
<|endoftext|>


<commit_before>if ( !CONFIG ) CONFIG = {};
CONFIG.COMPAREPHOTO_ID = "gsi-compare-photo";
CONFIG.COMPAREPHOTO_COCOTILEURL = "https://cyberjapandata.gsi.go.jp/xyz/cocotile_ap/{z}/{x}/{y}.csv";
CONFIG.COMPAREPHOTO_PHOTOLIST = [
  {
    "id": "ort_1928",
    "url": "https://maps.gsi.go.jp/xyz/ort_1928/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 18,
    "year": {
      "from": 1928
    }
  },
  {
    "id": "ort_riku10",
    "url": "https://maps.gsi.go.jp/xyz/ort_riku10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 18,
    "year": {
      "from": 1936,
      "to": 1942
    }
  },
  {
    "id": "ort_USA10",
    "url": "https://maps.gsi.go.jp/xyz/ort_USA10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1945,
      "to": 1950
    }
  },
  {
    "id": "ort_old10",
    "url": "https://maps.gsi.go.jp/xyz/ort_old10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1961,
      "to": 1969
    }
  },
  {
    "id": "gazo1",
    "url": "https://maps.gsi.go.jp/xyz/gazo1/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1974,
      "to": 1978
    }
  },
  {
    "id": "gazo2",
    "url": "https://maps.gsi.go.jp/xyz/gazo2/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1979,
      "to": 1983
    }
  },
  {
    "id": "gazo3",
    "url": "https://maps.gsi.go.jp/xyz/gazo3/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1984,
      "to": 1986
    }
  },
  {
    "id": "gazo4",
    "url": "https://maps.gsi.go.jp/xyz/gazo4/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1988,
      "to": 1990
    }
  },
  {
    "id": "nendophoto2007",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2007/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2007
    }
  },
  {
    "id": "nendophoto2008",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2008/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2008
    }
  },
  {
    "id": "nendophoto2009",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2009/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2009
    }
  },
  {
    "id": "nendophoto2010",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2010/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2010
    }
  },
  {
    "id": "nendophoto2011",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2011/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2011
    }
  },
  {
    "id": "nendophoto2012",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2012/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2012
    }
  },
  {
    "id": "nendophoto2013",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2013/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2013
    }
  },
  {
    "id": "nendophoto2014",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2014/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2014
    }
  },
  {
    "id": "nendophoto2015",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2015/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2015
    }
  },
  {
    "id": "nendophoto2016",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2016/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2016
    }
  },
  {
    "id": "nendophoto2017",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2017/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2017
    }
  },
  {
    "id": "nendophoto2018",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2018/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2018
    }
  },
  {
    "id": "nendophoto2019",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2019/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2019
    }
  },
  {
    "id": "nendophoto2020",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2020/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2020
    }
  }
];
<commit_msg>Update photo.js<commit_after>if ( !CONFIG ) CONFIG = {};
CONFIG.COMPAREPHOTO_ID = "gsi-compare-photo";
CONFIG.COMPAREPHOTO_COCOTILEURL = "https://cyberjapandata.gsi.go.jp/xyz/cocotile_ap/{z}/{x}/{y}.csv";
CONFIG.COMPAREPHOTO_PHOTOLIST = [
  {
    "id": "ort_1928",
    "url": "https://maps.gsi.go.jp/xyz/ort_1928/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 18,
    "year": {
      "from": 1928
    }
  },
  {
    "id": "ort_riku10",
    "url": "https://maps.gsi.go.jp/xyz/ort_riku10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 18,
    "year": {
      "from": 1936,
      "to": 1942
    }
  },
  {
    "id": "ort_USA10",
    "url": "https://maps.gsi.go.jp/xyz/ort_USA10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1945,
      "to": 1950
    }
  },
  {
    "id": "ort_old10",
    "url": "https://maps.gsi.go.jp/xyz/ort_old10/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1961,
      "to": 1969
    }
  },
  {
    "id": "gazo1",
    "url": "https://maps.gsi.go.jp/xyz/gazo1/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1974,
      "to": 1978
    }
  },
  {
    "id": "gazo2",
    "url": "https://maps.gsi.go.jp/xyz/gazo2/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1979,
      "to": 1983
    }
  },
  {
    "id": "gazo3",
    "url": "https://maps.gsi.go.jp/xyz/gazo3/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1984,
      "to": 1986
    }
  },
  {
    "id": "gazo4",
    "url": "https://maps.gsi.go.jp/xyz/gazo4/{z}/{x}/{y}.jpg",
    "minZoom": 14,
    "maxZoom": 18,
    "maxNativeZoom": 17,
    "year": {
      "from": 1987,
      "to": 1990
    }
  },
  {
    "id": "nendophoto2007",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2007/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2007
    }
  },
  {
    "id": "nendophoto2008",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2008/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2008
    }
  },
  {
    "id": "nendophoto2009",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2009/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2009
    }
  },
  {
    "id": "nendophoto2010",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2010/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2010
    }
  },
  {
    "id": "nendophoto2011",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2011/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2011
    }
  },
  {
    "id": "nendophoto2012",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2012/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2012
    }
  },
  {
    "id": "nendophoto2013",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2013/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2013
    }
  },
  {
    "id": "nendophoto2014",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2014/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2014
    }
  },
  {
    "id": "nendophoto2015",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2015/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2015
    }
  },
  {
    "id": "nendophoto2016",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2016/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2016
    }
  },
  {
    "id": "nendophoto2017",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2017/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2017
    }
  },
  {
    "id": "nendophoto2018",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2018/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2018
    }
  },
  {
    "id": "nendophoto2019",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2019/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2019
    }
  },
  {
    "id": "nendophoto2020",
    "url": "https://maps.gsi.go.jp/xyz/nendophoto2020/{z}/{x}/{y}.png",
    "minZoom": 14,
    "maxZoom": 18,
    "year": {
      "from": 2020
    }
  }
];
<|endoftext|>


<commit_before>/* global this, ItemClass, GgpkEntry */

(function (__undefined) {
    /**
     * extends GgpkEntry implements Localizeable
     */
    this.Mod = GgpkEntry.extend({
        init: function (props) {
            this._super(props);
        },
        isPrefix: function () {
            return +this.getProp("GenerationType") === Mod.MOD_TYPE.PREFIX;
        },
        isSuffix: function () {
            return +this.getProp("GenerationType") === Mod.MOD_TYPE.SUFFIX;
        },
        isImplicit: function () {
            return +this.getProp("GenerationType") === Mod.MOD_TYPE.VAAL;
            return [Mod.MOD_TYPE.IMPLICIT, Mod.MOD_TYPE.VAAL].indexOf(+this.getProp("GenerationType")) !== -1;
        },
        isAffix: function () {
            return this.isPrefix() || this.isSuffix();
        },
        /**
         * @returns {Array<Stat>} all stats from this mod
         */
        statsJoined: function () {
            var that = this;
            return $.map(this.valueAsArray("Stats"), function (row, i) {
                if (row.toString().toLowerCase() === 'null') {
                    // continue
                    return null;
                }

                var stat = new Stat(Mod.all_stats[row]);
                stat.values = new ValueRange(+that.getProp("Stat" + (i + 1) + "Min"),
                                             +that.getProp("Stat" + (i + 1) + "Max"));

                return stat;
            });
        },
        /**
         * translates the stats
         * @returns {String}
         */
        t: function () {
            var stats = this.statsJoined();
            // TODO maybe check before localizing cause unique on long strings might
            // be inefficient. on the other hand we almost always handle < 10 mods
            return $.unique($.map(stats, function (stat) {
                return stat.t(stats, Mod.localization);
            })).join("\n");
        },
        /**
         * translates the correct group
         * @returns {String}
         */
        correctGroupTranslated: function () {
            var correct_group = this.getProp("CorrectGroup");
            var translated = Mod.correct_group_localization[correct_group];

            if (translated === __undefined || translated === "") {
                // DeCamelize
                return correct_group
                        // insert a space before all caps
                        .replace(/([A-Z])/g, ' $1');
            }

            return translated;
        },
        /**
         * string identifier of the generation type
         * @returns {String}
         */
        modType: function () {
            var that = this;
            return $.map(Mod.MOD_TYPE, function (mod_type, type_name) {
                if (mod_type === +that.getProp("GenerationType")) {
                    return type_name.toLowerCase();
                }

                return null;
            })[0];
        },
        name: function () {
            return this.getProp("Name");
        },
        /**
         * unique id for dom
         * @returns {String}
         */
        domId: function () {
            return Mod.domId(this.getProp("Rows"));
        }
    });

    this.Mod.domId = function (id) {
        return "mod_" + id;
    };

    this.Mod.MOD_TYPE = {
        PREFIX: 1,
        SUFFIX: 2,
        IMPLICIT: 3,
        NEMESIS: 4,
        VAAL: 5,
        BLOODLINES: 6,
        TORMENT: 7,
        TEMPEST: 8
    };

    this.Mod.DOMAIN = {
        ITEM: 1,
        FLASK: 2,
        MONSTER: 3,
        STRONGBOX: 4,
        MAP: 5,
        STANCE: 9,
        MASTER: 10,
        JEWEL: 11
    };

    this.Mod.localization = null;
    this.Mod.correct_group_localization;
    this.Mod.all_stats = null;

    // table `mods`
    this.mods = null;
})();

<commit_msg>fixed implicit characterization<commit_after>/* global this, ItemClass, GgpkEntry */

(function (__undefined) {
    /**
     * extends GgpkEntry implements Localizeable
     */
    this.Mod = GgpkEntry.extend({
        init: function (props) {
            this._super(props);
        },
        isPrefix: function () {
            return +this.getProp("GenerationType") === Mod.MOD_TYPE.PREFIX;
        },
        isSuffix: function () {
            return +this.getProp("GenerationType") === Mod.MOD_TYPE.SUFFIX;
        },
        isImplicit: function () {
            return [Mod.MOD_TYPE.IMPLICIT, Mod.MOD_TYPE.VAAL].indexOf(+this.getProp("GenerationType")) !== -1;
        },
        isAffix: function () {
            return this.isPrefix() || this.isSuffix();
        },
        /**
         * @returns {Array<Stat>} all stats from this mod
         */
        statsJoined: function () {
            var that = this;
            return $.map(this.valueAsArray("Stats"), function (row, i) {
                if (row.toString().toLowerCase() === 'null') {
                    // continue
                    return null;
                }

                var stat = new Stat(Mod.all_stats[row]);
                stat.values = new ValueRange(+that.getProp("Stat" + (i + 1) + "Min"),
                                             +that.getProp("Stat" + (i + 1) + "Max"));

                return stat;
            });
        },
        /**
         * translates the stats
         * @returns {String}
         */
        t: function () {
            var stats = this.statsJoined();
            // TODO maybe check before localizing cause unique on long strings might
            // be inefficient. on the other hand we almost always handle < 10 mods
            return $.unique($.map(stats, function (stat) {
                return stat.t(stats, Mod.localization);
            })).join("\n");
        },
        /**
         * translates the correct group
         * @returns {String}
         */
        correctGroupTranslated: function () {
            var correct_group = this.getProp("CorrectGroup");
            var translated = Mod.correct_group_localization[correct_group];

            if (translated === __undefined || translated === "") {
                // DeCamelize
                return correct_group
                        // insert a space before all caps
                        .replace(/([A-Z])/g, ' $1');
            }

            return translated;
        },
        /**
         * string identifier of the generation type
         * @returns {String}
         */
        modType: function () {
            var that = this;
            return $.map(Mod.MOD_TYPE, function (mod_type, type_name) {
                if (mod_type === +that.getProp("GenerationType")) {
                    return type_name.toLowerCase();
                }

                return null;
            })[0];
        },
        name: function () {
            return this.getProp("Name");
        },
        /**
         * unique id for dom
         * @returns {String}
         */
        domId: function () {
            return Mod.domId(this.getProp("Rows"));
        }
    });

    this.Mod.domId = function (id) {
        return "mod_" + id;
    };

    this.Mod.MOD_TYPE = {
        PREFIX: 1,
        SUFFIX: 2,
        IMPLICIT: 3,
        NEMESIS: 4,
        VAAL: 5,
        BLOODLINES: 6,
        TORMENT: 7,
        TEMPEST: 8
    };

    this.Mod.DOMAIN = {
        ITEM: 1,
        FLASK: 2,
        MONSTER: 3,
        STRONGBOX: 4,
        MAP: 5,
        STANCE: 9,
        MASTER: 10,
        JEWEL: 11
    };

    this.Mod.localization = null;
    this.Mod.correct_group_localization;
    this.Mod.all_stats = null;

    // table `mods`
    this.mods = null;
})();

<|endoftext|>


<commit_before>// Types of events:
// 1 = Sosialt
// 2 = Bedriftspresentasjon
// 3 = Kurs
// 4 = Utflukt
// 5 = Ekskursjon
// 6 = Internt
// 7 = Annet

var UserList = React.createClass({
    getInitialState: function () {
        return {
            data: [],
            selectedIndex: null
        };
    },

    componentDidMount: function () {
        $.get("js/data.json", function (data) {
            var events = data.events.filter(function (event) {
                return (event.attendance_event != null)
            });

            if (this.isMounted()) {
                this.setState({
                    data: events,
                    selectedIndex: 0
                });
            }
        }.bind(this));
    },

    render: function () {
        var eventNames = this.state.data.map(function (event, index) {
            return React.createElement('div',
                                      { className: "event-name", listItem: index, key: index },
                                      event.title)
        });

        var usersData = [];

        if (this.state.selectedIndex) {
            usersData = this.state.data.events[0].attendance_event.users.map(function (user) {
                return React.createElement('li',
                                           { className: "user-item", key: user.id },
                                           "noe"/*"User: " + user.user.first_name + ", rfid: " + user.user.rfid*/)
            });
        }

        return
{ eventNames }
{ usersData }

;
    }
});

ReactDOM.render(
    React.createElement(UserList, null),
    document.querySelector('#options')
);<commit_msg>Adding API-methods and async-functions<commit_after>// Types of events:
// 1 = Sosialt
// 2 = Bedriftspresentasjon
// 3 = Kurs
// 4 = Utflukt
// 5 = Ekskursjon
// 6 = Internt
// 7 = Annet

var API_KEY = "derp",
    API_BASE_URL = "http://localhost";

// API module, has a private doRequest method, and public get and set methods

/*
TABLE OF CONTENTS
=================

Private:
-----------------
- doRequest

Public:
-----------------
- get_events
- get_user_by_username
- get_user_by_rfid
- set_attended
- patch_user
- update_event

*/

// Does a request based in input parameters
var doRequest = function (type, dataType, url, params, data, callback) {
    $.ajax({
        type: type,
        dataType: dataType,
        contentType: "application/json",
        data: data,
        url: API_BASE_URL + url + params,
        success: function (return_data) {
            if (debug) console.log(return_data);
            callback(return_data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            if (xhr.status === 202 || xhr.status === 204 || xhr.status === 304) {
                callback(xhr);
            }
            else {
                callback(null);
            }
        }
    });
}

var apicalls = {
    // Gets event list
    get_events: function () {
        return doRequest("GET", "json", "/api/rfid/events/", "?api_key=" + API_KEY + "&event_end__gte=" + tools.today() + "&order_by=event_start&limit=4", {}, events.events_callback);
    },
    // Gets user object by username
    get_user_by_username: function (username) {
        return doRequest("GET", "json", "/api/rfid/user/", "?username=" + username + "&api_key=" + API_KEY, {}, tools.user_callback);
    },
    // Gets user object by rfid
    get_user_by_rfid: function (rfid) {
        return doRequest("GET", "json", "/api/rfid/user/", "?rfid=" + rfid + "&api_key=" + API_KEY, {}, tools.user_callback);
    },
    // Sets an attendee as attended
    set_attended: function (attendee) {
        return doRequest("PATCH", "json", attendee.resource_uri, "?api_key=" + API_KEY, "{\"attended\": true}", events.attend_callback);
    },
    // Updates the RFID field on a user
    patch_user: function (user, rfid) {
        return doRequest("PATCH", "json", user.resource_uri, "?api_key=" + API_KEY, '{"rfid": "' + rfid + '"}', tools.patch_user_callback);
    },
    // Updates an event with new info
    update_event: function (event) {
        return doRequest("GET", "json", event.resource_uri, "?api_key=" + API_KEY, {}, events.update_event_callback);
    }
}

var UserList = React.createClass({
    getInitialState: function () {
        return {
            data: [],
            selectedIndex: null
        };
    },

    componentDidMount: function () {
        $.get("js/data.json", function (data) {
            var events = data.events.filter(function (event) {
                return (event.attendance_event != null)
            });

            if (this.isMounted()) {
                this.setState({
                    data: events,
                    selectedIndex: 0
                });
            }
        }.bind(this));
    },

    handleClick: function () {
        this.setState({
            selectedIndex: 1
        });
    },

    render: function () {
        var eventNames = this.state.data.map(function (event, index) {
            return
{event.title}

        });

        var usersData = [];

        if (typeof this.state.selectedIndex === "number") {
            usersData = this.state.data[this.state.selectedIndex].attendance_event.users.map(function (user) {
                return React.createElement('li',
                                           { className: "user-item", key: user.id },
                                           "User: " + user.user.first_name + ", rfid: " + user.user.rfid)
            });
        }

        return
{ eventNames }
{ usersData }

;
    }
});

ReactDOM.render(
    React.createElement(UserList, null),
    document.querySelector('#options')
);<|endoftext|>


<commit_before><commit_msg>Download cart now opens when items are added to it per: http://code.google.com/p/relay/issues/detail?id=4<commit_after><|endoftext|>


<commit_before>function checkbox_click(event)
{
	event.stopPropagation();
	do_email(enable_email.url);
	if($(event.target).attr('checked'))
	{
		$(event.target).parent().parent().find("td").addClass('selected').css("backgroundColor","");
	}
	else
	{
		$(event.target).parent().parent().find("td").removeClass();
	}
}

function enable_search(suggest_url,confirm_search_message)
{
	//Keep track of enable_email has been called
	if(!enable_search.enabled)
		enable_search.enabled=true;

	$('#search').click(function()
    {
    	$(this).attr('value','');
    });

    $("#search").autocomplete(suggest_url,{max:100,delay:10, selectFirst: false});
    $("#search").result(function(event, data, formatted)
    {
		do_search(true);
    });

	$('#search_form').submit(function(event)
	{
		event.preventDefault();

		if(get_selected_values().length >0)
		{
			if(!confirm(confirm_search_message))
				return;
		}
		do_search(true);
	});
}
enable_search.enabled=false;

function do_search(show_feedback,on_complete)
{
	//If search is not enabled, don't do anything
	if(!enable_search.enabled)
		return;

	if(show_feedback)
		$('#spinner').show();

	$('#sortable_table tbody').load($('#search_form').attr('action'),{'search':$('#search').val()},function()
	{
		if(typeof on_complete=='function')
			on_complete();

		$('#spinner').hide();
		//re-init elements in new table, as table tbody children were replaced
		tb_init('#sortable_table a.thickbox');
		update_sortable_table();
		enable_row_selection();
		$('#sortable_table tbody :checkbox').click(checkbox_click);
		$("#select_all").attr('checked',false);
	});
}

function enable_email(email_url)
{
	//Keep track of enable_email has been called
	if(!enable_email.enabled)
		enable_email.enabled=true;

	//store url in function cache
	if(!enable_email.url)
	{
		enable_email.url=email_url;
	}

	$('#select_all, #sortable_table tbody :checkbox').click(checkbox_click);
}
enable_email.enabled=false;
enable_email.url=false;

function do_email(url)
{
	//If email is not enabled, don't do anything
	if(!enable_email.enabled)
		return;

	$.post(url, { 'ids[]': get_selected_values() },function(response)
	{
		$('#email').attr('href',response);
	});

}

function enable_checkboxes()
{
	$('#sortable_table tbody :checkbox').click(checkbox_click);
}

function enable_delete(confirm_message,none_selected_message)
{
	//Keep track of enable_delete has been called
	if(!enable_delete.enabled)
		enable_delete.enabled=true;

	$('#delete').click(function(event)
	{
		event.preventDefault();
		if($("#sortable_table tbody :checkbox:checked").length >0)
		{
			if(confirm(confirm_message))
			{
				do_delete($(this).attr('href'));
			}
		}
		else
		{
			alert(none_selected_message);
		}
	});
}
enable_delete.enabled=false;

function do_delete(url)
{
	//If delete is not enabled, don't do anything
	if(!enable_delete.enabled)
		return;

	var row_ids = get_selected_values();
	var selected_rows = get_selected_rows();
	$.post(url, { 'ids[]': row_ids },function(response)
	{
		//delete was successful, remove checkbox rows
		if(response.success)
		{
			$(selected_rows).each(function(index, dom)
			{
				$(this).find("td").animate({backgroundColor:"green"},1200,"linear")
				.end().animate({opacity:0},1200,"linear",function()
				{
					$(this).remove();
					//Re-init sortable table as we removed a row
					update_sortable_table();

				});
			});
			// update rows that were affected by this delete
			for(index in response.ids) {
				update_row(response.ids[index],url.replace(/[^\/]+$/,'get_row'));
			}

			set_feedback(response.message,'success_message',false);
		}
		else
		{
			set_feedback(response.message,'error_message',true);
		}

	},"json");
}

function enable_bulk_edit(none_selected_message)
{
	//Keep track of enable_bulk_edit has been called
	if(!enable_bulk_edit.enabled)
		enable_bulk_edit.enabled=true;

	$('#bulk_edit').click(function(event)
	{
		event.preventDefault();
		if($("#sortable_table tbody :checkbox:checked").length >0)
		{
			tb_show($(this).attr('title'),$(this).attr('href'),false);
			$(this).blur();
		}
		else
		{
			alert(none_selected_message);
		}
	});
}
enable_bulk_edit.enabled=false;

function enable_select_all()
{
	//Keep track of enable_select_all has been called
	if(!enable_select_all.enabled)
		enable_select_all.enabled=true;

	$('#select_all').click(function()
	{
		if($(this).attr('checked'))
		{
			$("#sortable_table tbody :checkbox").each(function()
			{
				$(this).attr('checked',true);
				$(this).parent().parent().find("td").addClass('selected').css("backgroundColor","");

			});
		}
		else
		{
			$("#sortable_table tbody :checkbox").each(function()
			{
				$(this).attr('checked',false);
				$(this).parent().parent().find("td").removeClass();
			});
		}
	 });
}
enable_select_all.enabled=false;

function enable_row_selection(rows)
{
	//Keep track of enable_row_selection has been called
	if(!enable_row_selection.enabled)
		enable_row_selection.enabled=true;

	if(typeof rows =="undefined")
		rows=$("#sortable_table tbody tr");

	rows.hover(
		function row_over()
		{
			$(this).find("td").addClass('over').css("backgroundColor","");
			$(this).css("cursor","pointer");
		},

		function row_out()
		{
			if(!$(this).find("td").hasClass("selected"))
			{
				$(this).find("td").removeClass();
			}
		}
	);

	rows.click(function row_click(event)
	{

		var checkbox = $(this).find(":checkbox");
		checkbox.attr('checked',!checkbox.attr('checked'));
		do_email(enable_email.url);

		if(checkbox.attr('checked'))
		{
			$(this).find("td").addClass('selected').css("backgroundColor","");
		}
		else
		{
			$(this).find("td").removeClass();
		}
	});
}
enable_row_selection.enabled=false;

function update_sortable_table()
{
	//let tablesorter know we changed <tbody> and then triger a resort
	$("#sortable_table").trigger("update");

	if(typeof $("#sortable_table")[0].config!="undefined")
	{
		var sorting = $("#sortable_table")[0].config.sortList;
		$("#sortable_table").trigger("sorton",[sorting]);
	}
}

function get_table_row(id) {
	id = id || $("input[name='sale_id']").val();
	var $element = $("#sortable_table tbody :checkbox[value='" + id + "']");
	if ($element.index() === -1) {
		$element = $("#sortable_table tbody a[href*='" + id + "']");
	}
	return $element;
}

function update_row(row_id,url)
{
	$.post(url, { 'row_id': row_id },function(response)
	{
		//Replace previous row
		var row_to_update = get_table_row(row_id).parent().parent();
		row_to_update.replaceWith(response);
		reinit_row(row_id);
		hightlight_row(row_id);
	});
}

function reinit_row(checkbox_id)
{
	var new_checkbox = $("#sortable_table tbody tr :checkbox[value="+checkbox_id+"]");
	var new_row = new_checkbox.parent().parent();
	enable_row_selection(new_row);
	//Re-init some stuff as we replaced row
	update_sortable_table();
	tb_init(new_row.find("a.thickbox"));
	//re-enable e-mail
	new_checkbox.click(checkbox_click);
}

function animate_row(row,color)
{
	color = color || "#e1ffdd";
	row.find("td").css("backgroundColor", "#ffffff").animate({backgroundColor:color},"slow","linear")
		.animate({backgroundColor:color},5000)
		.animate({backgroundColor:"#ffffff"},"slow","linear");
}

function hightlight_row(checkbox_id)
{
	var new_checkbox = $("#sortable_table tbody tr :checkbox[value="+checkbox_id+"]");
	var new_row = new_checkbox.parent().parent();

	animate_row(new_row);
}

function get_selected_values()
{
	var selected_values = new Array();
	$("#sortable_table tbody :checkbox:checked").each(function()
	{
		selected_values.push($(this).val());
	});
	return selected_values;
}

function get_selected_rows()
{
	var selected_rows = new Array();
	$("#sortable_table tbody :checkbox:checked").each(function()
	{
		selected_rows.push($(this).parent().parent());
	});
	return selected_rows;
}

function get_visible_checkbox_ids()
{
	var row_ids = new Array();
	$("#sortable_table tbody :checkbox").each(function()
	{
		row_ids.push($(this).val());
	});
	return row_ids;
}<commit_msg>Fix item row refresh in detailed receivings and sales<commit_after>function checkbox_click(event)
{
	event.stopPropagation();
	do_email(enable_email.url);
	if($(event.target).attr('checked'))
	{
		$(event.target).parent().parent().find("td").addClass('selected').css("backgroundColor","");
	}
	else
	{
		$(event.target).parent().parent().find("td").removeClass();
	}
}

function enable_search(suggest_url,confirm_search_message)
{
	//Keep track of enable_email has been called
	if(!enable_search.enabled)
		enable_search.enabled=true;

	$('#search').click(function()
    {
    	$(this).attr('value','');
    });

    $("#search").autocomplete(suggest_url,{max:100,delay:10, selectFirst: false});
    $("#search").result(function(event, data, formatted)
    {
		do_search(true);
    });

	$('#search_form').submit(function(event)
	{
		event.preventDefault();

		if(get_selected_values().length >0)
		{
			if(!confirm(confirm_search_message))
				return;
		}
		do_search(true);
	});
}
enable_search.enabled=false;

function do_search(show_feedback,on_complete)
{
	//If search is not enabled, don't do anything
	if(!enable_search.enabled)
		return;

	if(show_feedback)
		$('#spinner').show();

	$('#sortable_table tbody').load($('#search_form').attr('action'),{'search':$('#search').val()},function()
	{
		if(typeof on_complete=='function')
			on_complete();

		$('#spinner').hide();
		//re-init elements in new table, as table tbody children were replaced
		tb_init('#sortable_table a.thickbox');
		update_sortable_table();
		enable_row_selection();
		$('#sortable_table tbody :checkbox').click(checkbox_click);
		$("#select_all").attr('checked',false);
	});
}

function enable_email(email_url)
{
	//Keep track of enable_email has been called
	if(!enable_email.enabled)
		enable_email.enabled=true;

	//store url in function cache
	if(!enable_email.url)
	{
		enable_email.url=email_url;
	}

	$('#select_all, #sortable_table tbody :checkbox').click(checkbox_click);
}
enable_email.enabled=false;
enable_email.url=false;

function do_email(url)
{
	//If email is not enabled, don't do anything
	if(!enable_email.enabled)
		return;

	$.post(url, { 'ids[]': get_selected_values() },function(response)
	{
		$('#email').attr('href',response);
	});

}

function enable_checkboxes()
{
	$('#sortable_table tbody :checkbox').click(checkbox_click);
}

function enable_delete(confirm_message,none_selected_message)
{
	//Keep track of enable_delete has been called
	if(!enable_delete.enabled)
		enable_delete.enabled=true;

	$('#delete').click(function(event)
	{
		event.preventDefault();
		if($("#sortable_table tbody :checkbox:checked").length >0)
		{
			if(confirm(confirm_message))
			{
				do_delete($(this).attr('href'));
			}
		}
		else
		{
			alert(none_selected_message);
		}
	});
}
enable_delete.enabled=false;

function do_delete(url)
{
	//If delete is not enabled, don't do anything
	if(!enable_delete.enabled)
		return;

	var row_ids = get_selected_values();
	var selected_rows = get_selected_rows();
	$.post(url, { 'ids[]': row_ids },function(response)
	{
		//delete was successful, remove checkbox rows
		if(response.success)
		{
			$(selected_rows).each(function(index, dom)
			{
				$(this).find("td").animate({backgroundColor:"green"},1200,"linear")
				.end().animate({opacity:0},1200,"linear",function()
				{
					$(this).remove();
					//Re-init sortable table as we removed a row
					update_sortable_table();

				});
			});
			// update rows that were affected by this delete
			for(index in response.ids) {
				update_row(response.ids[index],url.replace(/[^\/]+$/,'get_row'));
			}

			set_feedback(response.message,'success_message',false);
		}
		else
		{
			set_feedback(response.message,'error_message',true);
		}

	},"json");
}

function enable_bulk_edit(none_selected_message)
{
	//Keep track of enable_bulk_edit has been called
	if(!enable_bulk_edit.enabled)
		enable_bulk_edit.enabled=true;

	$('#bulk_edit').click(function(event)
	{
		event.preventDefault();
		if($("#sortable_table tbody :checkbox:checked").length >0)
		{
			tb_show($(this).attr('title'),$(this).attr('href'),false);
			$(this).blur();
		}
		else
		{
			alert(none_selected_message);
		}
	});
}
enable_bulk_edit.enabled=false;

function enable_select_all()
{
	//Keep track of enable_select_all has been called
	if(!enable_select_all.enabled)
		enable_select_all.enabled=true;

	$('#select_all').click(function()
	{
		if($(this).attr('checked'))
		{
			$("#sortable_table tbody :checkbox").each(function()
			{
				$(this).attr('checked',true);
				$(this).parent().parent().find("td").addClass('selected').css("backgroundColor","");

			});
		}
		else
		{
			$("#sortable_table tbody :checkbox").each(function()
			{
				$(this).attr('checked',false);
				$(this).parent().parent().find("td").removeClass();
			});
		}
	 });
}
enable_select_all.enabled=false;

function enable_row_selection(rows)
{
	//Keep track of enable_row_selection has been called
	if(!enable_row_selection.enabled)
		enable_row_selection.enabled=true;

	if(typeof rows =="undefined")
		rows=$("#sortable_table tbody tr");

	rows.hover(
		function row_over()
		{
			$(this).find("td").addClass('over').css("backgroundColor","");
			$(this).css("cursor","pointer");
		},

		function row_out()
		{
			if(!$(this).find("td").hasClass("selected"))
			{
				$(this).find("td").removeClass();
			}
		}
	);

	rows.click(function row_click(event)
	{

		var checkbox = $(this).find(":checkbox");
		checkbox.attr('checked',!checkbox.attr('checked'));
		do_email(enable_email.url);

		if(checkbox.attr('checked'))
		{
			$(this).find("td").addClass('selected').css("backgroundColor","");
		}
		else
		{
			$(this).find("td").removeClass();
		}
	});
}
enable_row_selection.enabled=false;

function update_sortable_table()
{
	//let tablesorter know we changed <tbody> and then triger a resort
	$("#sortable_table").trigger("update");

	if(typeof $("#sortable_table")[0].config!="undefined")
	{
		var sorting = $("#sortable_table")[0].config.sortList;
		$("#sortable_table").trigger("sorton",[sorting]);
	}
}

function get_table_row(id) {
	id = id || $("input[name='sale_id']").val();
	var $element = $("#sortable_table tbody :checkbox[value='" + id + "']");
	if ($element.index() === -1) {
		// parse link
		var row_id = $("#sortable_table tbody a").each(function(index, element) {
			var result = $(element).attr("href").match(/\/(\d+)\//);
			$element = result && result[1] == id ? $(element) : $element;
		});
	}
	return $element;
}

function update_row(row_id,url)
{
	$.post(url, { 'row_id': row_id },function(response)
	{
		//Replace previous row
		var row_to_update = get_table_row(row_id).parent().parent();
		row_to_update.replaceWith(response);
		reinit_row(row_id);
		hightlight_row(row_id);
	});
}

function reinit_row(checkbox_id)
{
	var new_checkbox = $("#sortable_table tbody tr :checkbox[value="+checkbox_id+"]");
	var new_row = new_checkbox.parent().parent();
	enable_row_selection(new_row);
	//Re-init some stuff as we replaced row
	update_sortable_table();
	tb_init(new_row.find("a.thickbox"));
	//re-enable e-mail
	new_checkbox.click(checkbox_click);
}

function animate_row(row,color)
{
	color = color || "#e1ffdd";
	row.find("td").css("backgroundColor", "#ffffff").animate({backgroundColor:color},"slow","linear")
		.animate({backgroundColor:color},5000)
		.animate({backgroundColor:"#ffffff"},"slow","linear");
}

function hightlight_row(checkbox_id)
{
	var new_checkbox = $("#sortable_table tbody tr :checkbox[value="+checkbox_id+"]");
	var new_row = new_checkbox.parent().parent();

	animate_row(new_row);
}

function get_selected_values()
{
	var selected_values = new Array();
	$("#sortable_table tbody :checkbox:checked").each(function()
	{
		selected_values.push($(this).val());
	});
	return selected_values;
}

function get_selected_rows()
{
	var selected_rows = new Array();
	$("#sortable_table tbody :checkbox:checked").each(function()
	{
		selected_rows.push($(this).parent().parent());
	});
	return selected_rows;
}

function get_visible_checkbox_ids()
{
	var row_ids = new Array();
	$("#sortable_table tbody :checkbox").each(function()
	{
		row_ids.push($(this).val());
	});
	return row_ids;
}<|endoftext|>


<commit_before>function RegisterError(message) {
    this.name = 'Register Error';
    this.message = message;
}

/**
 * Mips emulator constructor
 * @param  {Object} mipsArgs Arguments to construct the mips emulater.
 * @param mipsArgs.startingCode Set the default code for this emulator to run.
 * @param mipsArgs.debug If debug is set to true, the console will print debug statements
 * @return {mipsEmulator}
 * @member mipsEmulator
 */
function mipsEmulator(mipsArgs){
    mipsArgs = mipsArgs || {};
    mipsArgs = _.defaults(mipsArgs, {
        startingCode: null,
        debug: false,
        onError: function(message, lineNumber){
            alert(message);
            return false;
        },
        onWarning: function(message, lineNumber){
            alert(message);
        },
        onRegisterChange: function(regName, newValue) {

        },
        onFinish: function(){
            alert("Finished running emulation, resetting $sp to line 1");
        }
    });
    var debug = mipsArgs.debug
    //////////////////////////////////
    // Private Variables / Setup
    //////////////////////////////////

   var stack = new Stack();

    /**
     * Hash table of registers
     * @property registers
     * @private
     * @member mipsEmulator
     * @type {Object}
     */
    var registers = {};
    /**
     * Array of read/write registers
     * @property readwriteRegs
     * @private
     * @member mipsEmulator
     * @type {Array}
     */
    var readwriteRegs = [
        '$s0', '$s1', '$s2', '$s3', '$s4', '$s5', '$s6', '$s7',
        '$t0', '$t1', '$t2', '$t3', '$t4', '$t5', '$t6', '$t7', '$t8', '$t9',
        '$v0', '$v1',
        '$a0', '$a1', '$a2', '$a3',
        '$sp'
    ];

    /**
     * Array of read only registers
     * @property readonlyRegs
     * @private
     * @member mipsEmulator
     * @type {Array}
     */
    var readonlyRegs = [
        '$zero', '$at',
        '$k0', '$k1',
        '$gp', '$fp', '$ra'
    ];
    // The intial line where we start the emulation.
    /**
     * The current line the mips emulator is looking at.
     * @property currentLine
     * @private
     * @member mipsEmulator
     * @type {Number}
     */
    var currentLine = 1;
    // populate registers with all the read and write registers and set their inital values to random
    for (var i = 0; i < readwriteRegs.length; i++) {
        registers[readwriteRegs[i]] = createRegister({
            writable: true,
            regName:readwriteRegs[i]
        });
    };

    // populate registers with all the read and write registers and set their inital values to random
    for (var i = 0; i < readonlyRegs.length; i++) {
        registers[readonlyRegs[i]] = createRegister({
            writable: false,
            regName: readonlyRegs[i]
        });
    };
    registers.$zero.val = 0;
    registers.$sp.val = stack.pointerToBottomOfStack();

    // Object that will contain analyzed code information
    /**
     * @class mipsCode
     * @private
     * @member mipsEmulator
     * Object that keeps the code to be executed
     * @type {Object}
     */
    var mipsCode = {
        /**
         * Array of lines that can be exectued
         * @property code
         * @member mipsCode
         * @type {Array}
         */
        code:[null], // Initialize with null in the 0 place, to make line numbers line up.
        /**
         * Hashtable of labels pointing to lines of code
         * @property labels
         * @member mipsCode
         * @type {Object}
         */
        labels: {}
    };
    // Public methods
    /**
     * @class mipsEmulator
     * Mips Emulation engine.
     */
    var ME = {
        /**
         * Returns a specified registers value
         * @member mipsEmulator
         * @param  {String} reg
         * @return {String}
         */
        stack: stack,
        getRegisterVal: function(reg) {
            assert(reg[0] === '$');
            return this.getRegister(reg).val;
        },
        getRegister: function(reg) {
            assert(reg[0] === '$');
            if(!this.isValidRegister(reg)) throw new RegisterError('Non existant register: {0}'.format(reg));
            return registers[reg];
        },
        isValidRegister: function(reg) {
            return typeof registers[reg] !== "undefined";
        },
        isValidWritableRegister: function(reg) {
            return this.isValidRegister(reg) && this.getRegister(reg).writable === true;
        },
        /**
         * Set a register value, and call onChange function for that register
         * @member mipsEmulator
         * @param {String} reg
         * @param {Number} value
         */
        setRegisterVal: function(reg, value, enableCallback){
            enableCallback = enableCallback || true;
            assert(reg[0] === '$');
            if(debug) console.log("Setting register " + reg + " to " + value);

            if(!registers[reg]) return error("Line " + currentLine + " register: '" + reg + "' does not exist", currentLine);
            // TODO: ensure the register name does not exist in readonlyRegs (or better yet, ensure it exists in the readwriteRegs)
            if(registers[reg].onChange && enableCallback) registers[reg].onChange();
                registers[reg].val = value;
            if(mipsArgs.onRegisterChange && enableCallback)
                mipsArgs.onRegisterChange(reg, value);
            if(debug) console.log("----> New value: "+ ME.getRegister(reg));

        },
        /**
         * Set an Onchange function for a register
         * @member mipsEmulator
         * @param  {String} reg
         * @param  {Function} func
         * @return {null}
         */
        onChange: function(reg, func){
            registers[reg].onChange = func;
        },
        /**
         * Set which line to run next.
         * @member mipsEmulator
         * @param {Number} lineNo
         * @return {Number} Returns the number the line was set too.
         */
        setLine: function(lineNo){
            var line = mipsCode.code[lineNo];
            if(debug) console.log("setting line: "+ lineNo + " - " + JSON.stringify(line));
            if(!line) return false;
            currentLine = lineNo;
            if(line.ignore) incrementLine();
            return currentLine;
        },
        /**
         * Checks if a string is a valid mips line
         * @member mipsEmulator
         * @param  {String}  line
         * @return {Boolean}
         */
        isValidLine: function(line){
            return !(new mipsLine(line).error);
        },
        /**
         * Set code to be emulated
         * @member mipsEmulator
         * @param {String} mc
         */
        setCode: function(mc){
            if(debug) console.log("Analyzing...");
            mipsCode.code = [null];
            //currentLine = 1;
            $.each(mc.split('\n'), function(index, val){
                var line = new mipsLine(val, mipsCode.code.length);
                line.lineNo = mipsCode.code.length; // save the line number
                // if(debug) console.log(JSON.stringify(line));
                mipsCode.code.push(line);
            });
            if(mipsCode.code[currentLine] && mipsCode.code[currentLine].ignore){
                incrementLine();
                if(debug) console.log("First line is to be ignored, first line set to: " + currentLine);
            }
        },
        /**
         * Run an individual line
         * @member mipsEmulator
         * @param  {String} inputLine
         * @return {null}
         */
        runLine: function(inputLine) {
            var line = new mipsLine(inputLine);
            runLine(line);
        },
        /**
         * execute the line $sp is pointing at.
         * @member mipsEmulator
         * @return {Object}
         * returns object.lineRan which is the line that was just run
         * and object.nextLine which is the line that is about to be run.
         */
        step: function(){
            // check if we are finished with the emulation
            if(currentLine > mipsCode.code.length - 1) return finishEmulation();
            if(!mipsCode.code[currentLine]) return error("Line " + currentLine + " could not be read", currentLine);
            if(mipsCode.code[currentLine].ignore) incrementLine();
            // we need to check again, because the remainder of the lines could have been comments or blank.
            if(currentLine > mipsCode.code.length - 1) return finishEmulation();
            if(debug) console.log("Running line: " + currentLine + " - " + mipsCode.code[currentLine]);
            var ret = {
                lineRan: Number(currentLine)
            };

            runLine(mipsCode.code[currentLine]);
            ret.nextLine = currentLine;

            return ret;
        },
        /**
         * Returns the current line number (the next to be run)
         * @return {Number}
         */
        getLineNumber: function(){
            return currentLine;
        },
        incerementPC: function() {
            incrementLine();
        }
    };

    ////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////
    function finishEmulation(){
        mipsArgs.onFinish();
        if(debug) console.log("Emulation finished. Returning to line: " + ME.setLine(1));
        else ME.setLine(1);
    };
    function checkRegPlusImm(testString){
        return testString.search(/^\s*\d+\s?\(\s*\$[a-zA-Z]+\d*\s*\)/g) >= 0;
    };

    /**
     * Increments the current line to the next line which is not ignored.
     * @return {null}
     */
    function incrementLine(){
        currentLine++;
        while(mipsCode.code[currentLine]
                && currentLine <= mipsCode.code.length
                && mipsCode.code[currentLine].ignore != false
        ){
            if(debug) console.log("ignoring line: " + currentLine);
            currentLine++;
        }
    }
    /**
     * Run an individual line
     * @member mipsEmulator
     * @private
     * @return {null}
     */
    function runLine(line) {
        if (!line || line.ignore || line.error) {
            if(!line) error("Line is null");
            else error(line.error, currentLine); // returns error if there is one or null if not.
            incrementLine();
            return false;
        }
        // we can assume that we parsed successfully at this point.
        instructionExecutor.runInstruction(line.instruction, line.args);
    };

    /**
     * Create a default register
     * @member mipsEmulator
     * @private
     * @param  {Object} reg
     * @return {register}
     */
    function createRegister(reg){
        /**
         * @class register
         * contains register information.
         */
        var register = {
            /**
             * registers value
             * @property
             * @type {Number}
             */
            val: Math.floor((Math.random()*1000)), // Set the initial register data to garbage
            /**
             * Function that is called when this register is changed.
             * @type {Function}
             */
            onChange: null,
            /**
             * Wether or not this register is writable (false if this register is read only)
             * @type {Boolean}
             */
            writable: true,
            /**
             * This registers name
             * @type {String}
             */
            regName: null
        };
        _.defaults(reg, register);
        return reg;
    };
    // these will be called after the parse method has been called
    // the goal is to make these methods look as close to the MIPS cheat sheet as possible.

    var instructionExecutor = mipsInstructionExecutor(ME);

    /**
     * If the user defined an anError message, use that, if not, alert the message
     * @param  {String} message
     * @param  {Number} lineNo
     * @return {null}
     */
    function error(message, lineNo){
        if(debug) console.error("Error being sent");
        if(debug) console.error("--->" + message);
        lineNo = lineNo || currentLine;
        mipsArgs.onError(message, lineNo);
    }

    /**
     * Turns a string into a mips line object which contains a mips line of code and metadata needed to run it
     * @member mipsEmulator
     * @private
     * @param  {String} line
     * @return {Object}
     */
    function mipsLine(line, lineNo){
        lineNo = lineNo || null

        // Object that will save information about a line of code.
        /**
         * @class line
         * Contains information about a single line of mips code
         * @member mipsEmulator
         * @private
         */
        var LINE = {
            /**
             * Arguments for this line of code ex: [$t0, $s0, $zero]
             * @property
             * @type {Array}
             */
            args: [],
            /**
             * The lines instruction ex: ADD
             * @type {String}
             */
            instruction: null,
            /**
             * flag to indicate weather this line should be ignored (not run).
             * @type {Boolean}
             */
            ignore: true,
            /**
             * The comment (if any) that this line of code contained
             * @type {String}
             */
            comment: '',
            /**
             * The label for this line of code
             * @type {String}
             */
            label: '',
            /**
             * Error when running this line of code (if any)
             * @type {String}
             */
            error: null,
            lineNo: lineNo
        };

        //console.log("--> "+val);
        var regex = /^\s*(?:(\w+)\s*:\s*)?(?:(\w+)\s+([^#]+))?(?:#\s*(.*))?$/;
        var ar = line.match(regex);
        // when matched the array contains the following
        // ----> [0] The entire line
        // ----> [1] The label without the ':'
        // ----> [2] The instruction (e.g. 'ADD', 'LW', etc.)
        // ----> [3] The arguments (e.g. '$rd, $rs, $rt'), this should be trimmed
        // ----> [4] The comment without the '#', this should be trimmed
        // if ar is null, that means the regex didn't match

        if(ar){
            // if we have a label, save it to the hashtable and save it to line
            if(ar[1] && ar[1].length > 0){
                LINE.label = ar[1];
                // TODO: mipsCode.labels[ar[1]] = line;
            }

            // If we got variables back
            if(ar[3]){
                // Split the args by `,`
                LINE.args = ar[3].split(',');

                // Trim the varaibles
                for(var i = 0; i < LINE.args.length; i++){
                   LINE.args[i] = $.trim(LINE.args[i]);
                }
            }

            // The instruction for this code;
            LINE.instruction = $.trim(ar[2]).toUpperCase();

            // If the line has an instruction, we should not ignore it. otherwise it may be a comment or blank
            if(LINE.instruction && LINE.instruction.length > 0) LINE.ignore = false;

            // The comment, obviously
            LINE.comment = $.trim(ar[4]);

            // parse the instruction now, so it can be executed whenever we want
            if (LINE.instruction) {
                // an instruction was found, so try to parse it
                var error = {};
                if (!instructionExecutor.parseInstruction(LINE.instruction, LINE.args, null, error)) {
                    LINE.error = "Error: " + line.lineNo + " " + error.message;
                }
            };

        // In the else case, the regex didn't match, possible error?
        } else {
            // TODO: check for special cases
            LINE.error = "Error parsing line: "+ (index+1);
            if(debug) console.log("----> No matches");
        }
        //if(debug) console.log("Finished parsing line: " + JSON.stringify(LINE));
        return LINE;
    }

    // Set the starting code if there was any.
    if(mipsArgs.startingCode) ME.setCode(mipsArgs.startingCode);
    return ME;
}<commit_msg>Added label functionality<commit_after>function RegisterError(message) {
    this.name = 'Register Error';
    this.message = message;
}

/**
 * Mips emulator constructor
 * @param  {Object} mipsArgs Arguments to construct the mips emulater.
 * @param mipsArgs.startingCode Set the default code for this emulator to run.
 * @param mipsArgs.debug If debug is set to true, the console will print debug statements
 * @return {mipsEmulator}
 * @member mipsEmulator
 */
function mipsEmulator(mipsArgs){
    mipsArgs = mipsArgs || {};
    mipsArgs = _.defaults(mipsArgs, {
        startingCode: null,
        debug: false,
        onError: function(message, lineNumber){
            alert(message);
            return false;
        },
        onWarning: function(message, lineNumber){
            alert(message);
        },
        onRegisterChange: function(regName, newValue) {

        },
        onFinish: function(){
            alert("Finished running emulation, resetting $sp to line 1");
        }
    });
    var debug = mipsArgs.debug
    //////////////////////////////////
    // Private Variables / Setup
    //////////////////////////////////

   var stack = new Stack();

    /**
     * Hash table of registers
     * @property registers
     * @private
     * @member mipsEmulator
     * @type {Object}
     */
    var registers = {};
    /**
     * Array of read/write registers
     * @property readwriteRegs
     * @private
     * @member mipsEmulator
     * @type {Array}
     */
    var readwriteRegs = [
        '$s0', '$s1', '$s2', '$s3', '$s4', '$s5', '$s6', '$s7',
        '$t0', '$t1', '$t2', '$t3', '$t4', '$t5', '$t6', '$t7', '$t8', '$t9',
        '$v0', '$v1',
        '$a0', '$a1', '$a2', '$a3',
        '$sp'
    ];

    /**
     * Array of read only registers
     * @property readonlyRegs
     * @private
     * @member mipsEmulator
     * @type {Array}
     */
    var readonlyRegs = [
        '$zero', '$at',
        '$k0', '$k1',
        '$gp', '$fp', '$ra'
    ];
    // The intial line where we start the emulation.
    /**
     * The current line the mips emulator is looking at.
     * @property currentLine
     * @private
     * @member mipsEmulator
     * @type {Number}
     */
    var currentLine = 1;
    // populate registers with all the read and write registers and set their inital values to random
    for (var i = 0; i < readwriteRegs.length; i++) {
        registers[readwriteRegs[i]] = createRegister({
            writable: true,
            regName:readwriteRegs[i]
        });
    };

    // populate registers with all the read and write registers and set their inital values to random
    for (var i = 0; i < readonlyRegs.length; i++) {
        registers[readonlyRegs[i]] = createRegister({
            writable: false,
            regName: readonlyRegs[i]
        });
    };
    registers.$zero.val = 0;
    registers.$sp.val = stack.pointerToBottomOfStack();

    // Object that will contain analyzed code information
    /**
     * @class mipsCode
     * @private
     * @member mipsEmulator
     * Object that keeps the code to be executed
     * @type {Object}
     */
    var mipsCode = {
        /**
         * Array of lines that can be exectued
         * @property code
         * @member mipsCode
         * @type {Array}
         */
        code:[null], // Initialize with null in the 0 place, to make line numbers line up.
        /**
         * Hashtable of labels pointing to lines of code
         * @property labels
         * @member mipsCode
         * @type {Object}
         */
        labels: {}
    };
    // Public methods
    /**
     * @class mipsEmulator
     * Mips Emulation engine.
     */
    var ME = {
        /**
         * Returns a specified registers value
         * @member mipsEmulator
         * @param  {String} reg
         * @return {String}
         */
        stack: stack,
        getRegisterVal: function(reg) {
            assert(reg[0] === '$');
            return this.getRegister(reg).val;
        },
        getRegister: function(reg) {
            assert(reg[0] === '$');
            if(!this.isValidRegister(reg)) throw new RegisterError('Non existant register: {0}'.format(reg));
            return registers[reg];
        },
        isValidRegister: function(reg) {
            return typeof registers[reg] !== "undefined";
        },
        isValidWritableRegister: function(reg) {
            return this.isValidRegister(reg) && this.getRegister(reg).writable === true;
        },
        /**
         * Set a register value, and call onChange function for that register
         * @member mipsEmulator
         * @param {String} reg
         * @param {Number} value
         */
        setRegisterVal: function(reg, value, enableCallback){
            enableCallback = enableCallback || true;
            assert(reg[0] === '$');
            if(debug) console.log("Setting register " + reg + " to " + value);

            if(!registers[reg]) return error("Line " + currentLine + " register: '" + reg + "' does not exist", currentLine);
            // TODO: ensure the register name does not exist in readonlyRegs (or better yet, ensure it exists in the readwriteRegs)
            if(registers[reg].onChange && enableCallback) registers[reg].onChange();
                registers[reg].val = value;
            if(mipsArgs.onRegisterChange && enableCallback)
                mipsArgs.onRegisterChange(reg, value);
            if(debug) console.log("----> New value: "+ ME.getRegister(reg));

        },
        /**
         * Set an Onchange function for a register
         * @member mipsEmulator
         * @param  {String} reg
         * @param  {Function} func
         * @return {null}
         */
        onChange: function(reg, func){
            registers[reg].onChange = func;
        },
        /**
         * Set which line to run next.
         * @member mipsEmulator
         * @param {Number} lineNo
         * @return {Number} Returns the number the line was set too.
         */
        setLine: function(lineNo){
            var line = mipsCode.code[lineNo];
            if(debug) console.log("setting line: "+ lineNo + " - " + JSON.stringify(line));
            if(!line) return false;
            currentLine = lineNo;
            if(line.ignore) incrementLine();
            return currentLine;
        },
        /**
         * Checks if a string is a valid mips line
         * @member mipsEmulator
         * @param  {String}  line
         * @return {Boolean}
         */
        isValidLine: function(line){
            return !(new mipsLine(line).error);
        },
        /**
         * Set code to be emulated
         * @member mipsEmulator
         * @param {String} mc
         */
        setCode: function(mc){
            if(debug) console.log("Analyzing...");
            mipsCode.code = [null];
            //currentLine = 1;
            $.each(mc.split('\n'), function(index, val){
                var line = new mipsLine(val, mipsCode.code.length);
                line.lineNo = mipsCode.code.length; // save the line number
                // if(debug) console.log(JSON.stringify(line));
                mipsCode.code.push(line);
            });
            if(mipsCode.code[currentLine] && mipsCode.code[currentLine].ignore){
                incrementLine();
                if(debug) console.log("First line is to be ignored, first line set to: " + currentLine);
            }
        },
        /**
         * Run an individual line
         * @member mipsEmulator
         * @param  {String} inputLine
         * @return {null}
         */
        runLine: function(inputLine) {
            var line = new mipsLine(inputLine);
            runLine(line);
        },
        /**
         * execute the line $sp is pointing at.
         * @member mipsEmulator
         * @return {Object}
         * returns object.lineRan which is the line that was just run
         * and object.nextLine which is the line that is about to be run.
         */
        step: function(){
            // check if we are finished with the emulation
            if(currentLine > mipsCode.code.length - 1) return finishEmulation();
            if(!mipsCode.code[currentLine]) return error("Line " + currentLine + " could not be read", currentLine);
            if(mipsCode.code[currentLine].ignore) incrementLine();
            // we need to check again, because the remainder of the lines could have been comments or blank.
            if(currentLine > mipsCode.code.length - 1) return finishEmulation();
            if(debug) console.log("Running line: " + currentLine + " - " + mipsCode.code[currentLine]);
            var ret = {
                lineRan: Number(currentLine)
            };

            runLine(mipsCode.code[currentLine]);
            ret.nextLine = currentLine;

            return ret;
        },
        /**
         * Returns the current line number (the next to be run)
         * @return {Number}
         */
        getLineNumber: function(){
            return currentLine;
        },
        incerementPC: function() {
            incrementLine();
        },
        goToLabel: function(label){
            var line = mipsCode.labels[label];
            console.warn(JSON.stringify(mipsCode.labels));
            if(debug) console.log("Getting label: "+ label + " - " +JSON.stringify(line) );
            if(line){
                ME.setLine(line.lineNo);
                return currentLine;
            }
            return false;
        }
    };

    ////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////
    function finishEmulation(){
        mipsArgs.onFinish();
        if(debug) console.log("Emulation finished. Returning to line: " + ME.setLine(1));
        else ME.setLine(1);
    };
    function checkRegPlusImm(testString){
        return testString.search(/^\s*\d+\s?\(\s*\$[a-zA-Z]+\d*\s*\)/g) >= 0;
    };

    /**
     * Increments the current line to the next line which is not ignored.
     * @return {null}
     */
    function incrementLine(){
        currentLine++;
        while(mipsCode.code[currentLine]
                && currentLine <= mipsCode.code.length
                && mipsCode.code[currentLine].ignore != false
        ){
            if(debug) console.log("ignoring line: " + currentLine);
            currentLine++;
        }
    }
    /**
     * Run an individual line
     * @member mipsEmulator
     * @private
     * @return {null}
     */
    function runLine(line) {
        if (!line || line.ignore || line.error) {
            if(!line) error("Line is null");
            else error(line.error, currentLine); // returns error if there is one or null if not.
            incrementLine();
            return false;
        }
        // we can assume that we parsed successfully at this point.
        instructionExecutor.runInstruction(line.instruction, line.args);
    };

    /**
     * Create a default register
     * @member mipsEmulator
     * @private
     * @param  {Object} reg
     * @return {register}
     */
    function createRegister(reg){
        /**
         * @class register
         * contains register information.
         */
        var register = {
            /**
             * registers value
             * @property
             * @type {Number}
             */
            val: Math.floor((Math.random()*1000)), // Set the initial register data to garbage
            /**
             * Function that is called when this register is changed.
             * @type {Function}
             */
            onChange: null,
            /**
             * Wether or not this register is writable (false if this register is read only)
             * @type {Boolean}
             */
            writable: true,
            /**
             * This registers name
             * @type {String}
             */
            regName: null
        };
        _.defaults(reg, register);
        return reg;
    };
    // these will be called after the parse method has been called
    // the goal is to make these methods look as close to the MIPS cheat sheet as possible.

    var instructionExecutor = mipsInstructionExecutor(ME);

    /**
     * If the user defined an anError message, use that, if not, alert the message
     * @param  {String} message
     * @param  {Number} lineNo
     * @return {null}
     */
    function error(message, lineNo){
        if(debug) console.error("Error being sent");
        if(debug) console.error("--->" + message);
        lineNo = lineNo || currentLine;
        mipsArgs.onError(message, lineNo);
    }

    /**
     * Turns a string into a mips line object which contains a mips line of code and metadata needed to run it
     * @member mipsEmulator
     * @private
     * @param  {String} line
     * @return {Object}
     */
    function mipsLine(line, lineNo){
        lineNo = lineNo || null

        // Object that will save information about a line of code.
        /**
         * @class line
         * Contains information about a single line of mips code
         * @member mipsEmulator
         * @private
         */
        var LINE = {
            /**
             * Arguments for this line of code ex: [$t0, $s0, $zero]
             * @property
             * @type {Array}
             */
            args: [],
            /**
             * The lines instruction ex: ADD
             * @type {String}
             */
            instruction: null,
            /**
             * flag to indicate weather this line should be ignored (not run).
             * @type {Boolean}
             */
            ignore: true,
            /**
             * The comment (if any) that this line of code contained
             * @type {String}
             */
            comment: '',
            /**
             * The label for this line of code
             * @type {String}
             */
            label: '',
            /**
             * Error when running this line of code (if any)
             * @type {String}
             */
            error: null,
            lineNo: lineNo
        };

        //console.log("--> "+val);
        var regex = /^\s*(?:(\w+)\s*:\s*)?(?:(\w+)\s+([^#]+))?(?:#\s*(.*))?$/;
        var ar = line.match(regex);
        // when matched the array contains the following
        // ----> [0] The entire line
        // ----> [1] The label without the ':'
        // ----> [2] The instruction (e.g. 'ADD', 'LW', etc.)
        // ----> [3] The arguments (e.g. '$rd, $rs, $rt'), this should be trimmed
        // ----> [4] The comment without the '#', this should be trimmed
        // if ar is null, that means the regex didn't match

        if(ar){
            // if we have a label, save it to the hashtable and save it to line
            if(ar[1] && ar[1].length > 0){
                LINE.label = ar[1];
                console.log("found label: "+ LINE.label)
                mipsCode.labels[LINE.label] = LINE;
                // TODO: mipsCode.labels[ar[1]] = line;
            }

            // If we got variables back
            if(ar[3]){
                // Split the args by `,`
                LINE.args = ar[3].split(',');

                // Trim the varaibles
                for(var i = 0; i < LINE.args.length; i++){
                   LINE.args[i] = $.trim(LINE.args[i]);
                }
            }

            // The instruction for this code;
            LINE.instruction = $.trim(ar[2]).toUpperCase();

            // If the line has an instruction, we should not ignore it. otherwise it may be a comment or blank
            if(LINE.instruction && LINE.instruction.length > 0) LINE.ignore = false;

            // The comment, obviously
            LINE.comment = $.trim(ar[4]);

            // parse the instruction now, so it can be executed whenever we want
            if (LINE.instruction) {
                // an instruction was found, so try to parse it
                var error = {};
                if (!instructionExecutor.parseInstruction(LINE.instruction, LINE.args, null, error)) {
                    LINE.error = "Error: " + line.lineNo + " " + error.message;
                }
            };

        // In the else case, the regex didn't match, possible error?
        } else {
            // TODO: check for special cases
            LINE.error = "Error parsing line: "+ (index+1);
            if(debug) console.log("----> No matches");
        }
        //if(debug) console.log("Finished parsing line: " + JSON.stringify(LINE));
        return LINE;
    }

    // Set the starting code if there was any.
    if(mipsArgs.startingCode) ME.setCode(mipsArgs.startingCode);
    return ME;
}<|endoftext|>