<commit_before>// Copyright 2002-2017, University of Colorado Boulder

/**
 * A static object used to send aria-live updates to the screen reader.  The herald simply references 'aria-live'
 * elements in the HTML document and updates their content. ARIA attributes specify the behavior of timing for the
 * alerts. The following alert elements must be in the HTML document, see
 * https://github.com/phetsims/chipper/issues/472.
 * These elements were tested and determined to be the most widely supported and most useful for PhET sims.
 *
 *

 *

 *

 *

 *
 * @author Jesse Greenberg
 * @author John Blanco
 */

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

  // modules
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Property = require( 'AXON/Property' );
  var Tandem = require( 'TANDEM/Tandem' );

  // phet-io modules
  var TString = require( 'ifphetio!PHET_IO/types/TString' );

  // phet-io support
  var tandem = Tandem.createStaticTandem( 'AriaHerald' );

  // constants
  var disabled = false; // flag that enables/disables alerts via AriaHerald

  // ids for each of the aria-live elements
  var ASSERTIVE_ELEMENT_ID = 'assertive';
  var POLITE_ELEMENT_ID = 'polite';
  var ASSERTIVE_ALERT_ELEMENT_ID = 'assertive-alert';
  var POLITE_STATUS_ELEMENT_ID = 'polite-status';
  var ALERT_CONTAINER_ELEMENT_ID = 'aria-live-elements';

  // by default, clear old text so sequential updates with identical text are announced, see updateLiveElement()
  var DEFAULT_WITH_CLEAR = true;

  // DOM elements which will receive the updated content
  var assertiveElement = document.getElementById( ASSERTIVE_ELEMENT_ID );
  var politeElement = document.getElementById( POLITE_ELEMENT_ID );
  var assertiveAlertElement = document.getElementById( ASSERTIVE_ALERT_ELEMENT_ID );
  var politeStatusElement = document.getElementById( POLITE_STATUS_ELEMENT_ID );
  var alertContainer = document.getElementById( ALERT_CONTAINER_ELEMENT_ID );

  // verify that all elements are present
  assert && assert( assertiveElement, 'No assertive element found in document' );
  assert && assert( politeElement, 'No polite element found in document' );
  assert && assert( assertiveAlertElement, 'No assertive alert element found in document' );
  assert && assert( politeStatusElement, 'No polite status element found in document' );
  assert && assert( alertContainer, 'No alert container element found in document' );

  // associate a property with each live region - this is done in support of phet-io, otherwise the region values would
  // simply be set directly
  var assertiveElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'assertiveElementProperty' ),
    phetioValueType: TString
  } );
  assertiveElementProperty.link( function( text ) {
    assertiveElement.textContent = text;
  } );
  var politeElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'politeElementProperty' ),
    phetioValueType: TString
  } );
  politeElementProperty.link( function( text ) {
    politeElement.textContent = text;
  } );
  var assertiveAlertElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'assertiveAlertElementProperty' ),
    phetioValueType: TString
  } );
  assertiveAlertElementProperty.link( function( text ) {
    assertiveAlertElement.textContent = text;
  } );
  var politeStatusElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'politeStatusElementProperty' ),
    phetioValueType: TString
  } );
  politeStatusElementProperty.link( function( text ) {
    politeStatusElement.textContent = text;
  } );

  /**
   * Update an element with the 'aria-live' attribute by setting its text content.
   * If using withClear, old element text content will be explicitly removed before new text content is set.  This will
   * allow sequential alerts with identical text content to be announced multiple times in a row, which some screen
   * readers might have prevented.  Text content is only updated if the alerts are enabled.
   *
   * @param {Property.<string>} elementContentProperty - the property containing text content as a string
   * @param {string} textContent - the content to be announced
   * @param {boolean} [withClear] - optional, whether or not to remove the old text content before updating the element
   */
  function updateLiveElement( elementContentProperty, textContent, withClear ) {
    withClear = ( withClear === 'undefined' ) ? DEFAULT_WITH_CLEAR : withClear;
    assert && assert( typeof withClear === 'boolean', 'withClear must be of type boolean' );

    // only update content if the group of aria-live elements are enabled
    if ( !disabled ) {
      if ( withClear ) { elementContentProperty.reset(); }
      elementContentProperty.set( textContent );
    }
  }

  /**
   * Static object that provides the functions for updating the aria-live regions for screen reader announcements.
   */
  var AriaHerald = {

    /**
     * Announce an assertive alert.  This alert should be announced by the AT immediately, regardless of current user
     * interaction status.
     * @public
     *
     * @param {string} textContent - the alert to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announceAssertive: function( textContent, withClear ) {
      updateLiveElement( assertiveElementProperty, textContent, withClear );
    },

    /**
     * Announce a polite alert.  This alert should be announced when the user has finished their current interaction or
     * after other utterances in the queue are finished.
     * @public
     *
     * @param {string} textContent - the polite content to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePolite: function( textContent, withClear ) {
      updateLiveElement( politeElementProperty, textContent, withClear );
    },

    /**
     * Announce an assertive alert, with ARIA alert behavior.  This behaves similarly to announceAssertive but AT will
     * add extra functionality with the alert role, such as literally saying 'Alert' or providing extra navigation
     * strategies to find this content.
     * @public
     *
     * @param  {string} textContent - the content ot announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announceAssertiveWithAlert: function( textContent, withClear ) {
      updateLiveElement( assertiveAlertElementProperty, textContent, withClear );
    },

    /**
     * Announce polite with ARIA status behavior.  This behaves similarly to announcePolite but AT will add extra
     * functionality with the status role, such as literally saying 'Status' or providing extra navigation strategies
     * to find this content.
     * @public
     *
     * @param {string} textContent - the content ot announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePoliteWithStatus: function( textContent, withClear ) {
      updateLiveElement( politeStatusElementProperty, textContent, withClear );
    },

    /**
     * Clear all alerts by resetting text content with empty strings. A screen reader will not announce anything, but
     * this will remove all text content from the aria-live elements so that it cannot be found by the virtual cursor.
     * @public
     */
    clearAll: function() {
      assertiveElementProperty.reset();
      politeElementProperty.reset();
      assertiveAlertElementProperty.reset();
      politeStatusElementProperty.reset();
    },

    /**
     * Clear the text content from the assertive alert element. AT will not announce anything but this will prevent
     * content from being found in the document with the virtual cursor.
     * @public
     */
    clearAssertive: function() {
      assertiveElementProperty.reset();
    },

    /**
     * Clear the text content from the polite alert element.  AT will not announce anything but this will prevent
     * text content from being found in the document with the virtual cursor.
     * @public
     */
    clearPolite: function() {
      politeElementProperty.reset();
    },

    /**
     * Clear the text content from the assertive alert element.  AT will not announce anything but this will prevent
     * the text content from being found in the document with the virtual cursor.
     * @public
     */
    clearAssertiveWithAlert: function() {
      assertiveAlertElementProperty.reset();
    },

    /**
     * Clear the text content from the polite status element.  AT will not announce anything but this will prevent
     * the text content from being found in the document with the virtual cursor.
     * @public
     */
    clearPoliteWithStatus: function() {
      politeStatusElementProperty.reset();
    },

    /**
     * Disables or enable all aria-live elements. When disabled, the user will hear no alerts.
     * @param {Boolean} isDisabled
     */
    setDisabled: function( isDisabled ) {
      disabled = isDisabled;
    },

    // static constants
    ASSERTIVE_ELEMENT_ID: ASSERTIVE_ELEMENT_ID,
    POLITE_ELEMENT_ID: POLITE_ELEMENT_ID,
    ASSERTIVE_ALERT_ELEMENT_ID: ASSERTIVE_ALERT_ELEMENT_ID,
    POLITE_STATUS_ELEMENT_ID: POLITE_STATUS_ELEMENT_ID,
    ALERT_CONTAINER_ELEMENT_ID: ALERT_CONTAINER_ELEMENT_ID
  };

  sceneryPhet.register( 'AriaHerald', AriaHerald );

  return AriaHerald;
} );<commit_msg>Changed aria herald tandem to lower camelcase<commit_after>// Copyright 2002-2017, University of Colorado Boulder

/**
 * A static object used to send aria-live updates to the screen reader.  The herald simply references 'aria-live'
 * elements in the HTML document and updates their content. ARIA attributes specify the behavior of timing for the
 * alerts. The following alert elements must be in the HTML document, see
 * https://github.com/phetsims/chipper/issues/472.
 * These elements were tested and determined to be the most widely supported and most useful for PhET sims.
 *
 *

 *

 *

 *

 *
 * @author Jesse Greenberg
 * @author John Blanco
 */

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

  // modules
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Property = require( 'AXON/Property' );
  var Tandem = require( 'TANDEM/Tandem' );

  // phet-io modules
  var TString = require( 'ifphetio!PHET_IO/types/TString' );

  // phet-io support
  var tandem = Tandem.createStaticTandem( 'ariaHerald' );

  // constants
  var disabled = false; // flag that enables/disables alerts via AriaHerald

  // ids for each of the aria-live elements
  var ASSERTIVE_ELEMENT_ID = 'assertive';
  var POLITE_ELEMENT_ID = 'polite';
  var ASSERTIVE_ALERT_ELEMENT_ID = 'assertive-alert';
  var POLITE_STATUS_ELEMENT_ID = 'polite-status';
  var ALERT_CONTAINER_ELEMENT_ID = 'aria-live-elements';

  // by default, clear old text so sequential updates with identical text are announced, see updateLiveElement()
  var DEFAULT_WITH_CLEAR = true;

  // DOM elements which will receive the updated content
  var assertiveElement = document.getElementById( ASSERTIVE_ELEMENT_ID );
  var politeElement = document.getElementById( POLITE_ELEMENT_ID );
  var assertiveAlertElement = document.getElementById( ASSERTIVE_ALERT_ELEMENT_ID );
  var politeStatusElement = document.getElementById( POLITE_STATUS_ELEMENT_ID );
  var alertContainer = document.getElementById( ALERT_CONTAINER_ELEMENT_ID );

  // verify that all elements are present
  assert && assert( assertiveElement, 'No assertive element found in document' );
  assert && assert( politeElement, 'No polite element found in document' );
  assert && assert( assertiveAlertElement, 'No assertive alert element found in document' );
  assert && assert( politeStatusElement, 'No polite status element found in document' );
  assert && assert( alertContainer, 'No alert container element found in document' );

  // associate a property with each live region - this is done in support of phet-io, otherwise the region values would
  // simply be set directly
  var assertiveElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'assertiveElementProperty' ),
    phetioValueType: TString
  } );
  assertiveElementProperty.link( function( text ) {
    assertiveElement.textContent = text;
  } );
  var politeElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'politeElementProperty' ),
    phetioValueType: TString
  } );
  politeElementProperty.link( function( text ) {
    politeElement.textContent = text;
  } );
  var assertiveAlertElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'assertiveAlertElementProperty' ),
    phetioValueType: TString
  } );
  assertiveAlertElementProperty.link( function( text ) {
    assertiveAlertElement.textContent = text;
  } );
  var politeStatusElementProperty = new Property( '', {
    tandem: tandem.createTandem( 'politeStatusElementProperty' ),
    phetioValueType: TString
  } );
  politeStatusElementProperty.link( function( text ) {
    politeStatusElement.textContent = text;
  } );

  /**
   * Update an element with the 'aria-live' attribute by setting its text content.
   * If using withClear, old element text content will be explicitly removed before new text content is set.  This will
   * allow sequential alerts with identical text content to be announced multiple times in a row, which some screen
   * readers might have prevented.  Text content is only updated if the alerts are enabled.
   *
   * @param {Property.<string>} elementContentProperty - the property containing text content as a string
   * @param {string} textContent - the content to be announced
   * @param {boolean} [withClear] - optional, whether or not to remove the old text content before updating the element
   */
  function updateLiveElement( elementContentProperty, textContent, withClear ) {
    withClear = ( withClear === 'undefined' ) ? DEFAULT_WITH_CLEAR : withClear;
    assert && assert( typeof withClear === 'boolean', 'withClear must be of type boolean' );

    // only update content if the group of aria-live elements are enabled
    if ( !disabled ) {
      if ( withClear ) { elementContentProperty.reset(); }
      elementContentProperty.set( textContent );
    }
  }

  /**
   * Static object that provides the functions for updating the aria-live regions for screen reader announcements.
   */
  var AriaHerald = {

    /**
     * Announce an assertive alert.  This alert should be announced by the AT immediately, regardless of current user
     * interaction status.
     * @public
     *
     * @param {string} textContent - the alert to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announceAssertive: function( textContent, withClear ) {
      updateLiveElement( assertiveElementProperty, textContent, withClear );
    },

    /**
     * Announce a polite alert.  This alert should be announced when the user has finished their current interaction or
     * after other utterances in the queue are finished.
     * @public
     *
     * @param {string} textContent - the polite content to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePolite: function( textContent, withClear ) {
      updateLiveElement( politeElementProperty, textContent, withClear );
    },

    /**
     * Announce an assertive alert, with ARIA alert behavior.  This behaves similarly to announceAssertive but AT will
     * add extra functionality with the alert role, such as literally saying 'Alert' or providing extra navigation
     * strategies to find this content.
     * @public
     *
     * @param  {string} textContent - the content ot announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announceAssertiveWithAlert: function( textContent, withClear ) {
      updateLiveElement( assertiveAlertElementProperty, textContent, withClear );
    },

    /**
     * Announce polite with ARIA status behavior.  This behaves similarly to announcePolite but AT will add extra
     * functionality with the status role, such as literally saying 'Status' or providing extra navigation strategies
     * to find this content.
     * @public
     *
     * @param {string} textContent - the content ot announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePoliteWithStatus: function( textContent, withClear ) {
      updateLiveElement( politeStatusElementProperty, textContent, withClear );
    },

    /**
     * Clear all alerts by resetting text content with empty strings. A screen reader will not announce anything, but
     * this will remove all text content from the aria-live elements so that it cannot be found by the virtual cursor.
     * @public
     */
    clearAll: function() {
      assertiveElementProperty.reset();
      politeElementProperty.reset();
      assertiveAlertElementProperty.reset();
      politeStatusElementProperty.reset();
    },

    /**
     * Clear the text content from the assertive alert element. AT will not announce anything but this will prevent
     * content from being found in the document with the virtual cursor.
     * @public
     */
    clearAssertive: function() {
      assertiveElementProperty.reset();
    },

    /**
     * Clear the text content from the polite alert element.  AT will not announce anything but this will prevent
     * text content from being found in the document with the virtual cursor.
     * @public
     */
    clearPolite: function() {
      politeElementProperty.reset();
    },

    /**
     * Clear the text content from the assertive alert element.  AT will not announce anything but this will prevent
     * the text content from being found in the document with the virtual cursor.
     * @public
     */
    clearAssertiveWithAlert: function() {
      assertiveAlertElementProperty.reset();
    },

    /**
     * Clear the text content from the polite status element.  AT will not announce anything but this will prevent
     * the text content from being found in the document with the virtual cursor.
     * @public
     */
    clearPoliteWithStatus: function() {
      politeStatusElementProperty.reset();
    },

    /**
     * Disables or enable all aria-live elements. When disabled, the user will hear no alerts.
     * @param {Boolean} isDisabled
     */
    setDisabled: function( isDisabled ) {
      disabled = isDisabled;
    },

    // static constants
    ASSERTIVE_ELEMENT_ID: ASSERTIVE_ELEMENT_ID,
    POLITE_ELEMENT_ID: POLITE_ELEMENT_ID,
    ASSERTIVE_ALERT_ELEMENT_ID: ASSERTIVE_ALERT_ELEMENT_ID,
    POLITE_STATUS_ELEMENT_ID: POLITE_STATUS_ELEMENT_ID,
    ALERT_CONTAINER_ELEMENT_ID: ALERT_CONTAINER_ELEMENT_ID
  };

  sceneryPhet.register( 'AriaHerald', AriaHerald );

  return AriaHerald;
} );<|endoftext|>


<commit_before>/* SIMPLE MOVING AVERAGE INDICATOR AND ALGORITHM */

'use strict';

/* == SMA_var Function ==
 * param: candleStickData
 *    This object consists of date objects (candleStickData[i].date) and closing price objects (candleStickData[i].close).
 *     It is candlestick data for a cryptocurrency over some period with some frequency.
 * param: MAWindowSize
 *    This object is a number defining the amount of days to calculate the SMA over
 * return: MATimeSeries
 *    This object consists of date objects (MATimeSeries[i][0]) and closing price objects (MATimeSeries[i][1]). It represents
 *     the moving average over MAWindowSize of candleStickData.
 *
 * effect: This function calculates the SMA for datapoints that are variable length apart, over MAWindowSize # of days.
 */

var SMA = function(candleStickData, MAWindowSize) {
  // Calcuating interval between the dates, and the amount of values needed each day
  var interval = candleStickData[1].date - candleStickData[0].date;
  var entryPerDay = 86400/interval;
  var MATimeSeries = [];
  var prev = null;
  var avg = 0;

  // Looping through each entry in the candleStickData object
  for (var i = 0; i < candleStickData.length; i++){

    // Initial set-up of the first SMA average that can be calculated
    if (i == MAWindowSize * entryPerDay - 1){
      for (var j = 0; j < MAWindowSize * entryPerDay; j++){
        avg += candleStickData[j].close;
      }
      avg = avg/(MAWindowSize * entryPerDay);
      MATimeSeries.push([candleStickData[i].date, avg]);
      prev = candleStickData[0].close;
    }

    // Each time after the initial set-up, the avg is subtracted by the prev, and is added to the newest value
    if (i > MAWindowSize * entryPerDay - 1){
      avg = avg - prev/(MAWindowSize * entryPerDay) + candleStickData[i].close/(MAWindowSize * entryPerDay);
      MATimeSeries.push([candleStickData[i].date, avg]);
      prev = candleStickData[i - (MAWindowSize * entryPerDay - 1)].close;
    }
  }
  return MATimeSeries;
}

/* NEW SMA TEST
var test = [{date:0, close:1},{date:43200, close:4},{date:86400, close:3},{date:12960, close:6},{date:192800, close:5},{date:192800+43200, close:7}];
var test2 = 2;
var test3 = [];
test3 = SMA_var(test, test2);
if (test3 =  [ [ 12960, 3.5 ], [ 192800, 4.5 ], [ 236000, 5.25 ] ]){
  console.log("good");
}
*/

module.exports = SMA;
<commit_msg>linting<commit_after>/* SIMPLE MOVING AVERAGE INDICATOR AND ALGORITHM */

'use strict';

/* == SMA_var Function ==
 * param: candleStickData
 *    This object consists of date objects (candleStickData[i].date) and closing price objects (candleStickData[i].close).
 *     It is candlestick data for a cryptocurrency over some period with some frequency.
 * param: MAWindowSize
 *    This object is a number defining the amount of days to calculate the SMA over
 * return: MATimeSeries
 *    This object consists of date objects (MATimeSeries[i][0]) and closing price objects (MATimeSeries[i][1]). It represents
 *     the moving average over MAWindowSize of candleStickData.
 *
 * effect: This function calculates the SMA for datapoints that are variable length apart, over MAWindowSize # of days.
 */

var SMA = function (candleStickData, MAWindowSize) {
  // Calcuating interval between the dates, and the amount of values needed each day
  var interval = candleStickData[1].date - candleStickData[0].date;
  var entryPerDay = 86400 / interval;
  var MATimeSeries = [];
  var prev = null;
  var avg = 0;

  // Looping through each entry in the candleStickData object
  for (var i = 0; i < candleStickData.length; i++) {

    // Initial set-up of the first SMA average that can be calculated
    if (i == MAWindowSize * entryPerDay - 1) {
      for (var j = 0; j < MAWindowSize * entryPerDay; j++) {
        avg += candleStickData[j].close;
      }
      avg = avg / (MAWindowSize * entryPerDay);
      MATimeSeries.push([candleStickData[i].date, avg]);
      prev = candleStickData[0].close;
    }

    // Each time after the initial set-up, the avg is subtracted by the prev, and is added to the newest value
    if (i > MAWindowSize * entryPerDay - 1) {
      avg = avg - prev / (MAWindowSize * entryPerDay) + candleStickData[i].close / (MAWindowSize * entryPerDay);
      MATimeSeries.push([candleStickData[i].date, avg]);
      prev = candleStickData[i - (MAWindowSize * entryPerDay - 1)].close;
    }
  }
  return MATimeSeries;
}

/* NEW SMA TEST
var test = [{date:0, close:1},{date:43200, close:4},{date:86400, close:3},{date:12960, close:6},{date:192800, close:5},{date:192800+43200, close:7}];
var test2 = 2;
var test3 = [];
test3 = SMA_var(test, test2);
if (test3 =  [ [ 12960, 3.5 ], [ 192800, 4.5 ], [ 236000, 5.25 ] ]){
  console.log("good");
}
*/

module.exports = SMA;
<|endoftext|>


<commit_before>import elrUtlities from './elr-utilities';
const $ = require('jquery');

let elr = elrUtlities();

const elrSimpleSlider = function(params) {
    const self = {};
    const spec = params || {};
    const sliderClass = spec.sliderClass || 'elr-simple-slider';
    const slideClass = spec.slideClass || 'elr-simple-slide';
    const slideHolderClass = spec.slideHolderClass || 'elr-simple-slide-holder';
    const effect = spec.effect || 'fade';
    const navClass = spec.navClass || 'elr-simple-slider-nav';
    const slideListClass = spec.slideListClass || 'elr-simple-slider-list';
    const speed = spec.speed || 500;
    const interval = spec.interval || 10000;
    const auto = spec.auto || true;
    const $slider = $(`.${sliderClass}`);
    let isAnimating = false;

    const createSlideList = function($slides) {
        let li = '';

        $.each($slides, function(index) {
            if ( index === 0 ) {
                li += `<li><button class="active" data-item-num="${index}"></button></li>`;
            } else {
                li += `<li><button data-item-num="${index}"></button></li>`;
            }
        });

        return elr.createElement('ul', {
            'class': slideListClass,
            'html': li
        });
    };

    const getCurrent = function($slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            return $slides.not(':hidden').index();
        } else if ( effect === 'slide-left' ) {
            return (Math.abs($slideHolder.position().left) / parseInt($slides.first().width(), 10));
        }
    };

    const goToSlide = function(current, slideNum, $slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            $slides.eq(current).fadeOut(speed);
            $slides.eq(slideNum).fadeIn(speed);
        } else if ( effect === 'slide-left' ) {
            const slideWidth = parseInt($slides.first().width(), 10);
            const pos = $slideHolder.position().left;

            if ( current < slideNum ) {
                const slideDiff = current - slideNum;
                const newPos = pos + (slideWidth * slideDiff);
            } else if ( current > slideNum ) {
                const slideDiff = -(current - slideNum);
                const newPos = pos - (slideWidth * slideDiff);
            }

            $slideHolder.stop().animate({
                left: newPos
            }, speed);
        }
    };

    const fadeSlide = function(current, dir, $slides) {
        const lastSlide = $slides.length - 1;

        $slides.eq(current).fadeOut(speed);

        if ( dir === 'next' && current === lastSlide ) {
            $slides.first().fadeIn(speed);

            return 0;
        } else if ( dir === 'next' ) {
            $slides.eq(current + 1).fadeIn(speed);

            return current + 1;
        } else if ( dir === 'prev' && current === 0 ) {
            $slides.eq(lastSlide).fadeIn(speed);

            return lastSlide;
        } else {
            $slides.eq(current - 1).fadeIn(speed);

            return current - 1;
        }
    };

    const slideLeft = function(current, dir, $slides, $slideHolder) {
        const lastSlide = $slides.length - 1;
        const slideWidth = parseInt($slides.first().width(), 10);
        const pos = $slideHolder.position().left;
        // let newPos;
        // let $newSlides;
        // let numSlides;
        // let nextSlide;
        // let width;
        // let $oldSlides;

        isAnimating = true;

        if ( dir === 'next' && current === lastSlide ) {
            let $oldSlides = $slides;
            let $newSlides = $slides.clone();
            let numSlides = $slides.length;

            $slideHolder.css('width', slideWidth * (numSlides * 2));
            $slideHolder.append($newSlides);
            let newPos = pos - slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                $slideHolder.css({
                    'width': slideWidth * numSlides,
                    'left': 0
                });
                $oldSlides.remove();
                isAnimating = false;
            });

            return 0;
        } else if ( dir === 'next' ) {
            let newPos = pos - slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                isAnimating = false;
            });

            return (Math.abs(newPos) / slideWidth);
        } else if ( dir === 'prev' && current === 0 ) {
            let $oldSlides = $slides;
            let $newSlides = $slides.clone();
            let numSlides = $slides.length;
            let width = slideWidth * numSlides;

            $slideHolder.css('width', width * 2);
            $slideHolder.prepend($newSlides);
            $slideHolder.css('left', -width);
            let newPos = -width + slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                $slideHolder.css({
                    'width': width,
                    'left': -(width - slideWidth)
                });
                $oldSlides.remove();
                isAnimating = false;
            });

            return numSlides - 1;
        } else {
            let newPos = pos + slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                isAnimating = false;
            });

            return (Math.abs(newPos) / slideWidth);
        }
    };

    const advanceSlide = function(current, dir, $slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            return fadeSlide(current, dir, $slides);
        } else if ( effect === 'slide-left' ) {
            return slideLeft(current, dir, $slides, $slideHolder);
        }
    };

    const pageSlide = function(e, $slideHolder) {
        const current = getCurrent($slideHolder);
        let dir;

        if (e.which === 37) {
            dir = 'prev';
        } else if (e.which === 39) {
            dir = 'next';
        } else {
            return;
        }

        return advanceSlide(current, dir, $slideHolder);
    };

    const startShow = function(interval, $slideHolder, $slideList) {
        return setInterval(function() {
            // $nextControl.trigger('click');
            const current = getCurrent($slideHolder);
            const nextSlide = advanceSlide(current, 'next', $slideHolder);

            $slideList.find('button').removeClass('active');
            $slideList.find('li').eq(nextSlide).find('button').addClass('active');
        }, interval);
    };

    const pauseShow = function(start) {
        clearInterval(start);
        console.log('slider paused');
    };

    if ( $slider.length ) {

        $.each($slider, function() {
            const $currentSlider = $(this);
            const $slideHolder = $currentSlider.find(`.${slideHolderClass}`);
            const $slides = $slideHolder.find(`.${slideClass}`);
            const $currentSliderControls = $currentSlider.find(`.${navClass}`).find('button');
            const $nextControl = $currentSlider.find(`.${navClass}`).find("button[data-dir='next']");
            const $body = $('body');
            let $slideList;
            let begin;

            // add overflow hidden to make sure only the current slide is visible
            $slideHolder.css({overflow: 'hidden'});

            if ( effect === 'slide-left' ) {
                const slideWidth = $slides.first().width();
                const numSlides = $slides.length;

                $currentSlider.addClass('slide-left');
                $slideHolder.css('width', slideWidth * numSlides);
            } else if ( effect === 'fade' ) {
                $slides.hide().first().show();
            }

            $slideList = createSlideList($slides).appendTo($currentSlider);

            if ( auto ) {
                begin = startShow(interval, $slideHolder, $slideList);

                $slides.on('mouseover', function() {
                    pauseShow(begin);
                });

                $slides.on('mouseout', function() {
                    begin = startShow(interval, $slideHolder, $slideList);
                });
            }

            $currentSliderControls.on('click', function(e) {
                const dir = $(this).data('dir');
                const current = getCurrent($slideHolder);
                const nextSlide = advanceSlide(current, dir, $slideHolder);

                $slideList.find('button').removeClass('active');
                $slideList.find('li').eq(nextSlide).find('button').addClass('active');
                pauseShow(begin);

                e.preventDefault();
                e.stopPropagation();
            });

            $currentSlider.on({
                mouseenter: function () {
                    const $holder = $(this).find(`.${slideHolderClass}`);

                    $body.keydown(function(e) {
                        if( isAnimating ) {
                            e.preventDefault();
                            return false;
                        }

                        const nextSlide = pageSlide(e, $holder);

                        $slideList.find('button').removeClass('active');
                        $slideList.find('li').eq(nextSlide).find('button').addClass('active');
                    });
                },
                mouseleave: function () {
                    $body.off('keydown');
                }
            });

            $currentSlider.on('click', `.${slideListClass} button`, function(e) {
                const $that = $(this);
                const current = getCurrent($slideHolder);
                const slideNum = $that.data('item-num');

                $slideList.find('button').removeClass('active');
                $that.addClass('active');

                goToSlide(current, slideNum, $slideHolder);
                pauseShow(begin);

                e.preventDefault();
                e.stopPropagation();
            });
        });
    }

    return self;
};

export default elrSimpleSlider;<commit_msg>move prevent default to the top of functions<commit_after>import elrUtlities from './elr-utilities';
const $ = require('jquery');

let elr = elrUtlities();

const elrSimpleSlider = function(params) {
    const self = {};
    const spec = params || {};
    const sliderClass = spec.sliderClass || 'elr-simple-slider';
    const slideClass = spec.slideClass || 'elr-simple-slide';
    const slideHolderClass = spec.slideHolderClass || 'elr-simple-slide-holder';
    const effect = spec.effect || 'fade';
    const navClass = spec.navClass || 'elr-simple-slider-nav';
    const slideListClass = spec.slideListClass || 'elr-simple-slider-list';
    const speed = spec.speed || 500;
    const interval = spec.interval || 10000;
    const auto = spec.auto || true;
    const $slider = $(`.${sliderClass}`);
    let isAnimating = false;

    const createSlideList = function($slides) {
        let li = '';

        $.each($slides, function(index) {
            if ( index === 0 ) {
                li += `<li><button class="active" data-item-num="${index}"></button></li>`;
            } else {
                li += `<li><button data-item-num="${index}"></button></li>`;
            }
        });

        return elr.createElement('ul', {
            'class': slideListClass,
            'html': li
        });
    };

    const getCurrent = function($slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            return $slides.not(':hidden').index();
        } else if ( effect === 'slide-left' ) {
            return (Math.abs($slideHolder.position().left) / parseInt($slides.first().width(), 10));
        }
    };

    const goToSlide = function(current, slideNum, $slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            $slides.eq(current).fadeOut(speed);
            $slides.eq(slideNum).fadeIn(speed);
        } else if ( effect === 'slide-left' ) {
            const slideWidth = parseInt($slides.first().width(), 10);
            const pos = $slideHolder.position().left;

            if ( current < slideNum ) {
                const slideDiff = current - slideNum;
                const newPos = pos + (slideWidth * slideDiff);
            } else if ( current > slideNum ) {
                const slideDiff = -(current - slideNum);
                const newPos = pos - (slideWidth * slideDiff);
            }

            $slideHolder.stop().animate({
                left: newPos
            }, speed);
        }
    };

    const fadeSlide = function(current, dir, $slides) {
        const lastSlide = $slides.length - 1;

        $slides.eq(current).fadeOut(speed);

        if ( dir === 'next' && current === lastSlide ) {
            $slides.first().fadeIn(speed);

            return 0;
        } else if ( dir === 'next' ) {
            $slides.eq(current + 1).fadeIn(speed);

            return current + 1;
        } else if ( dir === 'prev' && current === 0 ) {
            $slides.eq(lastSlide).fadeIn(speed);

            return lastSlide;
        } else {
            $slides.eq(current - 1).fadeIn(speed);

            return current - 1;
        }
    };

    const slideLeft = function(current, dir, $slides, $slideHolder) {
        const lastSlide = $slides.length - 1;
        const slideWidth = parseInt($slides.first().width(), 10);
        const pos = $slideHolder.position().left;
        // let newPos;
        // let $newSlides;
        // let numSlides;
        // let nextSlide;
        // let width;
        // let $oldSlides;

        isAnimating = true;

        if ( dir === 'next' && current === lastSlide ) {
            let $oldSlides = $slides;
            let $newSlides = $slides.clone();
            let numSlides = $slides.length;

            $slideHolder.css('width', slideWidth * (numSlides * 2));
            $slideHolder.append($newSlides);
            let newPos = pos - slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                $slideHolder.css({
                    'width': slideWidth * numSlides,
                    'left': 0
                });
                $oldSlides.remove();
                isAnimating = false;
            });

            return 0;
        } else if ( dir === 'next' ) {
            let newPos = pos - slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                isAnimating = false;
            });

            return (Math.abs(newPos) / slideWidth);
        } else if ( dir === 'prev' && current === 0 ) {
            let $oldSlides = $slides;
            let $newSlides = $slides.clone();
            let numSlides = $slides.length;
            let width = slideWidth * numSlides;

            $slideHolder.css('width', width * 2);
            $slideHolder.prepend($newSlides);
            $slideHolder.css('left', -width);
            let newPos = -width + slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                $slideHolder.css({
                    'width': width,
                    'left': -(width - slideWidth)
                });
                $oldSlides.remove();
                isAnimating = false;
            });

            return numSlides - 1;
        } else {
            let newPos = pos + slideWidth;

            $slideHolder.stop().animate({
                left: newPos
            }, speed, 'linear', function() {
                isAnimating = false;
            });

            return (Math.abs(newPos) / slideWidth);
        }
    };

    const advanceSlide = function(current, dir, $slideHolder) {
        const $slides = $slideHolder.find(`.${slideClass}`);

        if ( effect === 'fade' ) {
            return fadeSlide(current, dir, $slides);
        } else if ( effect === 'slide-left' ) {
            return slideLeft(current, dir, $slides, $slideHolder);
        }
    };

    const pageSlide = function(e, $slideHolder) {
        const current = getCurrent($slideHolder);
        let dir;

        if (e.which === 37) {
            dir = 'prev';
        } else if (e.which === 39) {
            dir = 'next';
        } else {
            return;
        }

        return advanceSlide(current, dir, $slideHolder);
    };

    const startShow = function(interval, $slideHolder, $slideList) {
        return setInterval(function() {
            // $nextControl.trigger('click');
            const current = getCurrent($slideHolder);
            const nextSlide = advanceSlide(current, 'next', $slideHolder);

            $slideList.find('button').removeClass('active');
            $slideList.find('li').eq(nextSlide).find('button').addClass('active');
        }, interval);
    };

    const pauseShow = function(start) {
        clearInterval(start);
        console.log('slider paused');
    };

    if ( $slider.length ) {

        $.each($slider, function() {
            const $currentSlider = $(this);
            const $slideHolder = $currentSlider.find(`.${slideHolderClass}`);
            const $slides = $slideHolder.find(`.${slideClass}`);
            const $currentSliderControls = $currentSlider.find(`.${navClass}`).find('button');
            const $nextControl = $currentSlider.find(`.${navClass}`).find("button[data-dir='next']");
            const $body = $('body');
            let $slideList;
            let begin;

            // add overflow hidden to make sure only the current slide is visible
            $slideHolder.css({overflow: 'hidden'});

            if ( effect === 'slide-left' ) {
                const slideWidth = $slides.first().width();
                const numSlides = $slides.length;

                $currentSlider.addClass('slide-left');
                $slideHolder.css('width', slideWidth * numSlides);
            } else if ( effect === 'fade' ) {
                $slides.hide().first().show();
            }

            $slideList = createSlideList($slides).appendTo($currentSlider);

            if ( auto ) {
                begin = startShow(interval, $slideHolder, $slideList);

                $slides.on('mouseover', function() {
                    pauseShow(begin);
                });

                $slides.on('mouseout', function() {
                    begin = startShow(interval, $slideHolder, $slideList);
                });
            }

            $currentSliderControls.on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();

                const dir = $(this).data('dir');
                const current = getCurrent($slideHolder);
                const nextSlide = advanceSlide(current, dir, $slideHolder);

                $slideList.find('button').removeClass('active');
                $slideList.find('li').eq(nextSlide).find('button').addClass('active');
                pauseShow(begin);
            });

            $currentSlider.on({
                mouseenter: function () {
                    const $holder = $(this).find(`.${slideHolderClass}`);

                    $body.keydown(function(e) {
                        if( isAnimating ) {
                            e.preventDefault();
                            return false;
                        }

                        const nextSlide = pageSlide(e, $holder);

                        $slideList.find('button').removeClass('active');
                        $slideList.find('li').eq(nextSlide).find('button').addClass('active');
                    });
                },
                mouseleave: function () {
                    $body.off('keydown');
                }
            });

            $currentSlider.on('click', `.${slideListClass} button`, function(e) {
                e.preventDefault();
                e.stopPropagation();

                const $that = $(this);
                const current = getCurrent($slideHolder);
                const slideNum = $that.data('item-num');

                $slideList.find('button').removeClass('active');
                $that.addClass('active');

                goToSlide(current, slideNum, $slideHolder);
                pauseShow(begin);
            });
        });
    }

    return self;
};

export default elrSimpleSlider;<|endoftext|>


<commit_before>// DOM references for the global modal and inner flag submission form fields
const $modal = $('#ctf-edit-modal')
    , $editor  = $modal.find('textarea')
    , $preview = $modal.find('.preview')
    , $mkdnTabs= $modal.find(".nav-tabs a[data-toggle='tab']");

const $ctfConfig = $('.flag-config-table');

const getCtfRow = ($btn) => $btn.parentsUntil('tr').parent();

/* Display flag */
$ctfConfig.on('click', '.btn-flag', function displayFlag(event) {
    const $btn = $(event.currentTarget);
    const flag = $btn.attr('title');
    const name = getCtfRow($btn).children().eq(1).text();

    // show the flag with the text selected, ready to copy+paste
    window.prompt(`Flag for "${name}"`, flag);
});

/* Show modal editor */
$ctfConfig.on('click', '.btn-edit', function showFlagEditorModal(event) {
    const $row = getCtfRow($(event.currentTarget));
    const flagID = $row.data('flag-id');

    // Very tight DOM coupling
    const $form = $modal.find('form');
    const $cells = $row.children();
    const findInput = (name) => $form.find(`input[name=${name}]`);
    const cellText = (idx) => $cells.eq(idx).text();

    findInput("id").val(cellText(0));
    findInput("name").val(cellText(1));
    findInput("category").val(cellText(2));
    findInput("designer").val(cellText(3));
    findInput("total").val(cellText(4));

    findInput("flag").val($cells.eq(7).find('.btn-flag').attr('title'));
    $modal.find('.modal-title').text(`Edit ${cellText(1)}`);

    const isHidden = $cells.eq(5).children().size() > 0;
    findInput("hidden").prop('checked', isHidden);

    const $mkdnPanel = $form.find(".flag-body-panel");
    $mkdnPanel.toggleClass("hidden", isHidden);

    if(isHidden) {
        $modal.modal('show');
    } else {
        $.get(`/api/blue/challenges/${flagID}`).then(desc => {
            $editor.val(desc);
            $mkdnTabs.eq(0).tab('show');
        }, (xhr) => {
            $editor.val("");
            $mkdnTabs.eq(1).tab('show');

            $preview.html($(`<p class="text-danger" />`)
                           .text(`!! Couldn't fetch description: ${getXhrErr(xhr)}`));
        })
        .always(() => { $modal.modal('show'); });
    }
});

/* Live preview of markdown description */
$mkdnTabs.eq(1).on("show.bs.tab", function(event) {
    const desc = $editor.val();
    $preview.html(marked(desc));
});

/* Submit editted Challenge */
$modal.find('form').on('submit', function updateChallenge(event) {
    event.preventDefault();
    const $form = $(event.delegateTarget);
    const findInput = (name) => $form.find(`input[name=${name}]`);

    // Parse form data into JSON
    const data = {};
    data.body = $editor.val();
    data.hidden = findInput("hidden").prop("checked");
    ["id","name","category","designer","flag","total"].forEach(field => {
        data[field] = findInput(field).val();
    });
    data.id = parseInt(data.id, 10);
    data.total = parseFloat(data.total, 10);

    const url = `/api/ctf/flags/${data.id}`;
    ajaxAndReload('PUT', url, data, `${data.name} updated!`);
});

/* Delete challenge */
$modal.find('form').on('click', '.delete-challenge', function deleteChallenge(event) {
    const $form = $(event.delegateTarget);
    const flagID = $form.find("input[name=id]").val();
    const flagName = $form.find("input[name=name]").val();

    BootstrapDialog.confirm(`Are you sure you want to delete "${flagName}"`, yes => {
        if(!yes) { return; }

        const url = `/api/ctf/flags/${flagID}`;
        ajaxJSON('DELETE', url).done(() => {
            $('.flag-config-table > tbody').find(`[data-flag-id=${flagID}]`).remove();
            $modal.modal('hide');
        }).fail((xhr) => {
            alert(getXhrErr(xhr));
        });
    });
});

/* Upload Challenges via CSV */
const $csvUploadForm = $('.flag-config-csv-upload form');

(function initChallengeCsvEditor() {
    const placeholderFlagCsv = `
     Name,  Category, Designer, Hidden,                   Flag, Points, Description
 crypto-1,       CTF,      Bob,  false,   flag{1337_ch4ll3ng0},    100,  easy peasy
 crypto-2,       CTF,      Bob,   true,  flag{_!@*scr4mbl3r)(},    150,
   prog-1,      Prog,      Bob,  false, flag{why all the work},     50,
  Stage 1,      Wifi,      Dan,  false,          that was easy,     25,
`.slice(1, -1); // trim the newlines at the start and end (but not the spaces)

    $csvUploadForm.find('textarea').val(placeholderFlagCsv);
})();

$csvUploadForm.submit(function addChallengesViaCSV(event) {
    event.preventDefault();
    const rawCSV = $csvUploadForm.find('textarea').val();

    let data;
    try {
        data = $.csv.toObjects(rawCSV, { onParseValue: e => e.trimLeft() });
    } catch (e) {
        alert(`CSV Upload failed: ${e.message}`)
        throw e;
    }

    // Lowercase all keys, cast values to scalars, rename fields as needed
    data = data.map(row => {
        row = Object.assign(...Object.entries(row).map(([k, v]) => ({[k.toLowerCase()]: v })));
        row.hidden = row.hidden === "true";
        row.total = parseFloat(row.points);
        delete row.points;
        return row;
    });

    const url = `/api/ctf/flags/`;
    ajaxAndReload('POST', url, data, `${data.length} challenges created!`);
});
<commit_msg>ctf.js: Always show the flag description, even when hidden<commit_after>// DOM references for the global modal and inner flag submission form fields
const $modal = $('#ctf-edit-modal')
    , $editor  = $modal.find('textarea')
    , $preview = $modal.find('.preview')
    , $mkdnTabs= $modal.find(".nav-tabs a[data-toggle='tab']");

const $ctfConfig = $('.flag-config-table');

const getCtfRow = ($btn) => $btn.parentsUntil('tr').parent();

/* Display flag */
$ctfConfig.on('click', '.btn-flag', function displayFlag(event) {
    const $btn = $(event.currentTarget);
    const flag = $btn.attr('title');
    const name = getCtfRow($btn).children().eq(1).text();

    // show the flag with the text selected, ready to copy+paste
    window.prompt(`Flag for "${name}"`, flag);
});

/* Show modal editor */
$ctfConfig.on('click', '.btn-edit', function showFlagEditorModal(event) {
    const $row = getCtfRow($(event.currentTarget));
    const flagID = $row.data('flag-id');

    // Very tight DOM coupling
    const $form = $modal.find('form');
    const $cells = $row.children();
    const findInput = (name) => $form.find(`input[name=${name}]`);
    const cellText = (idx) => $cells.eq(idx).text();

    findInput("id").val(cellText(0));
    findInput("name").val(cellText(1));
    findInput("category").val(cellText(2));
    findInput("designer").val(cellText(3));
    findInput("total").val(cellText(4));

    findInput("flag").val($cells.eq(7).find('.btn-flag').attr('title'));
    $modal.find('.modal-title').text(`Edit ${cellText(1)}`);

    const isHidden = $cells.eq(5).children().length > 0;
    findInput("hidden").prop('checked', isHidden);

    // TODO: This fetches the entire challenge again, just
    // to get the description. This is a minor waste.
    $.getJSON(`/api/ctf/flags/${flagID}`).then(chal => {
        $editor.val(chal.body);
        $mkdnTabs.eq(0).tab('show');
    }, (xhr) => {
        $editor.val("");
        $mkdnTabs.eq(1).tab('show');

        $preview.html($(`<p class="text-danger" />`)
                       .text(`!! Couldn't fetch description: ${getXhrErr(xhr)}`));
    })
    .always(() => { $modal.modal('show'); });
});

/* Live preview of markdown description */
$mkdnTabs.eq(1).on("show.bs.tab", function(event) {
    const desc = $editor.val();
    $preview.html(marked(desc));
});

/* Submit editted Challenge */
$modal.find('form').on('submit', function updateChallenge(event) {
    event.preventDefault();
    const $form = $(event.delegateTarget);
    const findInput = (name) => $form.find(`input[name=${name}]`);

    // Parse form data into JSON
    const data = {};
    data.body = $editor.val();
    data.hidden = findInput("hidden").prop("checked");
    ["id","name","category","designer","flag","total"].forEach(field => {
        data[field] = findInput(field).val();
    });
    data.id = parseInt(data.id, 10);
    data.total = parseFloat(data.total, 10);

    const url = `/api/ctf/flags/${data.id}`;
    ajaxAndReload('PUT', url, data, `${data.name} updated!`);
});

/* Delete challenge */
$modal.find('form').on('click', '.delete-challenge', function deleteChallenge(event) {
    const $form = $(event.delegateTarget);
    const flagID = $form.find("input[name=id]").val();
    const flagName = $form.find("input[name=name]").val();

    BootstrapDialog.confirm(`Are you sure you want to delete "${flagName}"`, yes => {
        if(!yes) { return; }

        const url = `/api/ctf/flags/${flagID}`;
        ajaxJSON('DELETE', url).done(() => {
            $('.flag-config-table > tbody').find(`[data-flag-id=${flagID}]`).remove();
            $modal.modal('hide');
        }).fail((xhr) => {
            alert(getXhrErr(xhr));
        });
    });
});

/* Upload Challenges via CSV */
const $csvUploadForm = $('.flag-config-csv-upload form');

(function initChallengeCsvEditor() {
    const placeholderFlagCsv = `
     Name,  Category, Designer, Hidden,                   Flag, Points, Description
 crypto-1,       CTF,      Bob,  false,   flag{1337_ch4ll3ng0},    100,  easy peasy
 crypto-2,       CTF,      Bob,   true,  flag{_!@*scr4mbl3r)(},    150,
   prog-1,      Prog,      Bob,  false, flag{why all the work},     50,
  Stage 1,      Wifi,      Dan,  false,          that was easy,     25,
`.slice(1, -1); // trim the newlines at the start and end (but not the spaces)

    $csvUploadForm.find('textarea').val(placeholderFlagCsv);
})();

$csvUploadForm.submit(function addChallengesViaCSV(event) {
    event.preventDefault();
    const rawCSV = $csvUploadForm.find('textarea').val();

    let data;
    try {
        data = $.csv.toObjects(rawCSV, { onParseValue: e => e.trimLeft() });
    } catch (e) {
        alert(`CSV Upload failed: ${e.message}`)
        throw e;
    }

    // Lowercase all keys, cast values to scalars, rename fields as needed
    data = data.map(row => {
        row = Object.assign(...Object.entries(row).map(([k, v]) => ({[k.toLowerCase()]: v })));
        row.hidden = row.hidden === "true";
        row.total = parseFloat(row.points);
        delete row.points;
        return row;
    });

    const url = `/api/ctf/flags/`;
    ajaxAndReload('POST', url, data, `${data.length} challenges created!`);
});
<|endoftext|>


<commit_before>jsio('from common.javascript import Class, bind');
jsio('import browser.events as events');
jsio('import browser.dom as dom');
jsio('import browser.css as css');
jsio('import browser.ItemView');
jsio('import browser.ListComponent');

jsio('import .Panel');

css.loadStyles(jsio.__path);

exports = Class(Panel, function(supr) {

	this.init = function() {
		supr(this, 'init', arguments);
		this._label = this._item;
		this._listComponent = new browser.ListComponent(bind(this, '_onItemSelected'));
	}

	this.createContent = function() {
		supr(this, 'createContent');
		this.addClassName('ListPanel');
	}

	this.addItem = function(item) {
		item.addClassName('listItem');
		this._listComponent.addItem(item);
		this._content.appendChild(item.getElement());
		this._manager.resize();
	}

	this.focus = function() {
		supr(this, 'focus');
		this._listComponent.focus();
	}

	this._onItemSelected = function(item) {
		this._publish('ItemSelected', item);
	}
})<commit_msg>Add items to the list component after they are put into the DOM, such that the itemFocus finds the dom correctly when positioning itself<commit_after>jsio('from common.javascript import Class, bind');
jsio('import browser.events as events');
jsio('import browser.dom as dom');
jsio('import browser.css as css');
jsio('import browser.ItemView');
jsio('import browser.ListComponent');

jsio('import .Panel');

css.loadStyles(jsio.__path);

exports = Class(Panel, function(supr) {

	this.init = function() {
		supr(this, 'init', arguments);
		this._label = this._item;
		this._listComponent = new browser.ListComponent(bind(this, '_onItemSelected'));
	}

	this.createContent = function() {
		supr(this, 'createContent');
		this.addClassName('ListPanel');
	}

	this.addItem = function(item) {
		item.addClassName('listItem');
		this._content.appendChild(item.getElement());
		this._listComponent.addItem(item);
		this._manager.resize();
	}

	this.focus = function() {
		supr(this, 'focus');
		this._listComponent.focus();
	}

	this._onItemSelected = function(item) {
		this._publish('ItemSelected', item);
	}
})<|endoftext|>


<commit_before>Setup.prototype.SETUP_KEY = "setup";
Setup.prototype.ACTIVATED_KEY = "activated";

Setup.prototype.waitlistHost = "http://localhost:4000";
Setup.prototype.waitlistPaths = {
    reserve: '/u/reserve',
    check: '/u/check',
    setEmail: '/u/email/set'
}
Setup.prototype.waitlistCheckTimeout = 1000;

function Setup() {
    var _this = this;

    this.storage = new Storage();
    this.initialized = $.Deferred();

    var settingsLoaded = this.storage.getPrivateSettings();
    $.when(settingsLoaded).then(this.init.bind(this));
}

Setup.prototype.init = function(settings) {
    this.settings = settings;
    this.initialized.resolve();
}

Setup.prototype.openWaitlist = function() {
    chrome.tabs.create({
        url: chrome.extension.getURL("html/waiting.html")
    }, function() {});
}

Setup.prototype.openTutorial = function() {
    chrome.browserAction.onClicked.removeListener(this.openWaitlist.bind(this));
    chrome.browserAction.setPopup({ popup: "/html/popup.html" });

    this.storage.setPrivateSetting(
        this.SETUP_KEY,
        true,
        function() {
            chrome.tabs.create({
                url: chrome.extension.getURL("html/tutorial.html")
            }, function() {});
        }
    );
}

Setup.prototype.startBackgroundWaitlistCheck = function() {
    var _this = this;
    setTimeout(function interval() {
        _this.checkWaitlistStatus().then(function() {
            if (!_this.settings[_this.ACTIVATED_KEY]) {
                setTimeout(interval, _this.waitlistCheckTimeout);
            }
        });
    }, this.waitlistCheckTimeout);
}

Setup.prototype.checkWaitlistStatus = function() {
    if (!this.settings.waitlistID) {
        return this.registerOnWaitlist();
    } else {
        var _this = this,
            promise = $.Deferred(),
            url = this.waitlistHost + this.waitlistPaths.check;

        $.get(
            Utils.addURLParam(url, "id", this.settings.waitlistID),
            function(data) {
                console.log(data);
                _this.settings.waiting = data.waiting;
                _this.settings.rank = data.rank;

                _this.storage.setPrivateSettings(_this.settings, function() {
                    if (!_this.settings.waiting) {
                        return _this.activate();
                    }
                    promise.resolve();
                })
            }
        )

        return promise;
    }
}

Setup.prototype.registerOnWaitlist = function() {
    var _this = this,
        promise = $.Deferred();

    // if we already have an ID, let's get out of here
    if (_this.settings.waitlistID) {
        promise.resolve();
        return;
    }

    $.post(
        this.waitlistHost + this.waitlistPaths.reserve,
        {},
        function(data) {
            _this.settings.waitlistID = data.id;
            _this.settings.waiting = data.waiting;
            _this.settings.rank = data.rank;

            _this.storage.setPrivateSettings(
                _this.settings,
                function() {
                    if (!_this.settings.waiting) {
                        return _this.activate();
                    }
                    promise.resolve();
                }
            );
        }
    );

    return promise;
}

Setup.prototype.attachClickToWaitlist = function() {
    chrome.browserAction.setPopup({ popup: "" });
    chrome.browserAction.onClicked.addListener(this.openWaitlist);
}

Setup.prototype.attachClickToTutorial = function() {
    var _this = this,
        on = true,
        toggle = true;

    chrome.browserAction.setPopup({ popup: "" });
    chrome.browserAction.onClicked.removeListener(this.openWaitlist);

    chrome.browserAction.setBadgeText({ text: "start" });
    (function interval() {
        if (!on) return;

        if (toggle) {
            toggle = false;
            chrome.browserAction.setBadgeBackgroundColor({ color: "#3399CC" });
        } else {
            toggle = true;
            chrome.browserAction.setBadgeBackgroundColor({ color: "#EEEEEE" });
        }

        setTimeout(interval, 100);
    })();

    chrome.browserAction.onClicked.addListener(function() {
        on = false;
        chrome.browserAction.setBadgeText({ text: "" });
        _this.openTutorial();
    });
}

Setup.prototype.highlightIcon = function() {
    this.attachClickToTutorial();

}

Setup.prototype.activate = function() {
    var _this = this,
        activationDone = $.Deferred();
    this.settings[this.ACTIVATED_KEY] = true;
    this.storage.setPrivateSetting(
        this.ACTIVATED_KEY,
        true,
        function() {
            _this.kickOff();
            activationDone.resolve();
        }
    );

    return activationDone;
}

Setup.prototype.onInstall = function() {
    var _this = this;

    this.installing = true;

    $.when(this.initialized)
     .then(this.onStartup.bind(this))
     .then(function() {
        _this.installing = false;
        if (_this.settings.waiting) {
            _this.openWaitlist();
        }
     });
}

Setup.prototype.onStartup = function(settings) {
    var _this = this;

    return $.when(this.initialized)
     .then(function() {
        if (_this.settings[_this.ACTIVATED_KEY]) {
            return _this.kickOff();
        } else {
            return _this.checkWaitlistStatus().then(function() {
                if (!_this.settings[_this.ACTIVATED_KEY]) {
                    _this.attachClickToWaitlist();
                    _this.startBackgroundWaitlistCheck();
                }
                return $.Deferred().resolve();
            });
        }
     });
}

Setup.prototype.kickOff = function() {
    var _this = this;

    this.storage.getPrivateSettings(function(settings) {
        if (settings[_this.ACTIVATED_KEY] && settings[_this.SETUP_KEY]) {
            _this.delegate = new Delegate();
        } else {
            if (!_this.installing) {
                _this.highlightIcon();
            } else {
                _this.openTutorial();
            }
        }
    });
}

chrome.runtime.onInstalled.addListener(function() {
    var setup = new Setup();
    setup.onInstall();
});

chrome.runtime.onStartup.addListener(function() {
    var setup = new Setup();
    setup.onStartup();
});<commit_msg>blink the button with 'start' text when you're activated and ready to go<commit_after>Setup.prototype.SETUP_KEY = "setup";
Setup.prototype.ACTIVATED_KEY = "activated";

Setup.prototype.waitlistHost = "http://localhost:4000";
Setup.prototype.waitlistPaths = {
    reserve: '/u/reserve',
    check: '/u/check',
    setEmail: '/u/email/set'
}
Setup.prototype.waitlistCheckTimeout = 1000;

function Setup() {
    var _this = this;

    this.storage = new Storage();
    this.initialized = $.Deferred();

    var settingsLoaded = this.storage.getPrivateSettings();
    $.when(settingsLoaded).then(this.init.bind(this));
}

Setup.prototype.init = function(settings) {
    this.settings = settings;
    this.initialized.resolve();
}

Setup.prototype.openWaitlist = function() {
    chrome.tabs.create({
        url: chrome.extension.getURL("html/waiting.html")
    }, function() {});
}

Setup.prototype.openTutorial = function() {
    chrome.browserAction.onClicked.removeListener(this.openWaitlist.bind(this));
    chrome.browserAction.setPopup({ popup: "/html/popup.html" });

    this.storage.setPrivateSetting(
        this.SETUP_KEY,
        true,
        function() {
            chrome.tabs.create({
                url: chrome.extension.getURL("html/tutorial.html")
            }, function() {});
        }
    );
}

Setup.prototype.startBackgroundWaitlistCheck = function() {
    var _this = this;
    setTimeout(function interval() {
        _this.checkWaitlistStatus().then(function() {
            if (!_this.settings[_this.ACTIVATED_KEY]) {
                setTimeout(interval, _this.waitlistCheckTimeout);
            }
        });
    }, this.waitlistCheckTimeout);
}

Setup.prototype.checkWaitlistStatus = function() {
    if (!this.settings.waitlistID) {
        return this.registerOnWaitlist();
    } else {
        var _this = this,
            promise = $.Deferred(),
            url = this.waitlistHost + this.waitlistPaths.check;

        $.get(
            Utils.addURLParam(url, "id", this.settings.waitlistID),
            function(data) {
                console.log(data);
                _this.settings.waiting = data.waiting;
                _this.settings.rank = data.rank;

                _this.storage.setPrivateSettings(_this.settings, function() {
                    if (!_this.settings.waiting) {
                        return _this.activate();
                    }
                    promise.resolve();
                })
            }
        )

        return promise;
    }
}

Setup.prototype.registerOnWaitlist = function() {
    var _this = this,
        promise = $.Deferred();

    // if we already have an ID, let's get out of here
    if (_this.settings.waitlistID) {
        promise.resolve();
        return;
    }

    $.post(
        this.waitlistHost + this.waitlistPaths.reserve,
        {},
        function(data) {
            _this.settings.waitlistID = data.id;
            _this.settings.waiting = data.waiting;
            _this.settings.rank = data.rank;

            _this.storage.setPrivateSettings(
                _this.settings,
                function() {
                    if (!_this.settings.waiting) {
                        return _this.activate();
                    }
                    promise.resolve();
                }
            );
        }
    );

    return promise;
}

Setup.prototype.attachClickToWaitlist = function() {
    chrome.browserAction.setPopup({ popup: "" });
    chrome.browserAction.onClicked.addListener(this.openWaitlist);
}

Setup.prototype.attachClickToTutorial = function() {
    var _this = this,
        on = true,
        toggle = true;

    chrome.browserAction.setPopup({ popup: "" });
    chrome.browserAction.onClicked.removeListener(this.openWaitlist);

    chrome.browserAction.setBadgeText({ text: "start" });
    (function interval() {
        if (!on) return;

        if (toggle) {
            toggle = false;
            chrome.browserAction.setBadgeBackgroundColor({ color: "#83D589" });
        } else {
            toggle = true;
            chrome.browserAction.setBadgeBackgroundColor({ color: "#EEEEEE" });
        }

        setTimeout(interval, 500);
    })();

    chrome.browserAction.onClicked.addListener(function() {
        on = false;
        chrome.browserAction.setBadgeText({ text: "" });
        _this.openTutorial();
    });
}

Setup.prototype.highlightIcon = function() {
    this.attachClickToTutorial();

}

Setup.prototype.activate = function() {
    var _this = this,
        activationDone = $.Deferred();
    this.settings[this.ACTIVATED_KEY] = true;
    this.storage.setPrivateSetting(
        this.ACTIVATED_KEY,
        true,
        function() {
            _this.kickOff();
            activationDone.resolve();
        }
    );

    return activationDone;
}

Setup.prototype.onInstall = function() {
    var _this = this;

    this.installing = true;

    $.when(this.initialized)
     .then(this.onStartup.bind(this))
     .then(function() {
        _this.installing = false;
        if (_this.settings.waiting) {
            _this.openWaitlist();
        }
     });
}

Setup.prototype.onStartup = function(settings) {
    var _this = this;

    return $.when(this.initialized)
     .then(function() {
        if (_this.settings[_this.ACTIVATED_KEY]) {
            return _this.kickOff();
        } else {
            return _this.checkWaitlistStatus().then(function() {
                if (!_this.settings[_this.ACTIVATED_KEY]) {
                    _this.attachClickToWaitlist();
                    _this.startBackgroundWaitlistCheck();
                }
                return $.Deferred().resolve();
            });
        }
     });
}

Setup.prototype.kickOff = function() {
    var _this = this;

    this.storage.getPrivateSettings(function(settings) {
        if (settings[_this.ACTIVATED_KEY] && settings[_this.SETUP_KEY]) {
            _this.delegate = new Delegate();
        } else {
            if (!_this.installing) {
                _this.highlightIcon();
            } else {
                _this.openTutorial();
            }
        }
    });
}

chrome.runtime.onInstalled.addListener(function() {
    var setup = new Setup();
    setup.onInstall();
});

chrome.runtime.onStartup.addListener(function() {
    var setup = new Setup();
    setup.onStartup();
});<|endoftext|>


<commit_before>var TABULAR = {};
(function (exports) {
    'use strict';

    exports.enrich = function (OBJECT) {
        // Global lists
        var header, rowLab, rowLabLab, dataArr, headerObj, rowObj,
                // global functions
                tabularHelpers, defineLists, parseLabels, copy,
                // helper functions
                get, stringify, clone;

        parseLabels = function () {
            var i, that = copy(OBJECT);

            // Header labels?
            if (!that.header && that.has_header) {
                that.header = that.data.shift();
                //Do we have a row label?
                if (that.has_row_label) {
                    that.row_label_name = that.header.shift();
                    if (that.row_label_name.length === 0) {
                        delete that.row_label_name;
                    }
                }
            //No header - create an index based one
            } else if (!that.header) {
                that.header = [];
                for (i = 0; i < that.data[0]; i += 1) {
                    that.header.push(i);
                }
            }

            // Row labels?
            if (!that.row_labels && that.has_row_label) {
                that.row_labels = that.data.map(function (row) {
                    return row.shift();
                });
            } else if (!that.row_labels) {
                that.row_labels = [];
                for (i = 0; i < that.data; i += 1) {
                    that.row_labels.push(i);
                }
            }

            //Now creat data array from what left
            that.data = that.data.map(function (row) {
                return row.map(function (entry) {
                    if (Number.isFinite(entry * 1)) {
                        return entry * 1;
                    }
                });
            });

            //redefine global OBJECT
            OBJECT = that;
        };

        defineLists = function () {
            //Fix label repeats and assign global variables
            var i, spec = 1, that = OBJECT;
            header = that.header;
            rowLab = that.row_labels;
            rowLabLab = that.row_label_name;
            dataArr = that.data;
            headerObj = {};
            rowObj = {};

            //Store header names in a hash
            for (i = 0; i < header.length; i += 1) {
                while (headerObj.hasOwnProperty(header[i])) {
                    if (spec > 1) {
                        header[i] = header[i].replace(
                            new RegExp("_" + (spec - 1) + "$"),
                            ""
                        );
                    }
                    header[i] = header[i] + "_" + spec;
                    spec += 1;
                }
                headerObj[header[i]] = i;
                spec = 1;
            }

            //Finally create row label hash
            for (i = 0; i < rowLab.length; i += 1) {
                while (rowObj.hasOwnProperty(rowLab[i])) {
                    if (spec > 1) {
                        rowLab[i] = rowLab[i].replace(
                            new RegExp("_" + (spec - 1) + "$"),
                            ""
                        );
                    }
                    rowLab[i] = rowLab[i] + "_" + spec;
                    spec += 1;
                }
                rowObj[rowLab[i]] = i;
                spec = 1;
            }
        };

        tabularHelpers = function () {
            get = function (get_param, simple) {
                // Possible parameters: Header label vs number; Column label vs number
                var rows, heads, i, j, retArr = [], row_by_ind = false,
                        head_by_ind = false, temp_row;

                //deal with header input
                get_param = get_param || {};
                if (get_param.hasOwnProperty('header')) {
                    heads = get_param.header;
                } else if (get_param.hasOwnProperty('headers')) {
                    heads = get_param.headers;
                } else if (get_param.hasOwnProperty('header_index')) {
                    heads = get_param.header_index;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('header_indexes')) {
                    heads = get_param.header_indexes;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('header_indices')) {
                    heads = get_param.header_indices;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column')) {
                    heads = get_param.column;
                } else if (get_param.hasOwnProperty('columns')) {
                    heads = get_param.columns;
                } else if (get_param.hasOwnProperty('column_index')) {
                    heads = get_param.column_index;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column_indexes')) {
                    heads = get_param.column_indexes;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column_indices')) {
                    heads = get_param.column_indices;
                    head_by_ind = true;
                } else {
                    heads = header;
                }

                //convert to array if it is a single value
                if (typeof heads !== 'object' && !Array.isArray(heads)) {
                    heads = [heads];
                }

                //deal with rowumins input
                if (get_param.hasOwnProperty('row')) {
                    rows = get_param.row;
                } else if (get_param.hasOwnProperty('rows')) {
                    rows = get_param.rows;
                } else if (get_param.hasOwnProperty(rowLabLab)) {
                    rows = get_param[rowLabLab];
                } else if (get_param.hasOwnProperty(rowLabLab + 's')) {
                    rows = get_param[rowLabLab + 's'];
                } else if (get_param.hasOwnProperty(rowLabLab + 'es')) {
                    rows = get_param[rowLabLab + 'es'];
                } else if (get_param.hasOwnProperty('row_index')) {
                    rows = get_param.row_index;
                    row_by_ind = true;
                } else if (get_param.hasOwnProperty('row_indexes')) {
                    rows = get_param.row_indexes;
                    row_by_ind = true;
                } else if (get_param.hasOwnProperty('row_indices')) {
                    rows = get_param.row_indices;
                    row_by_ind = true;
                } else {
                    rows = rowLab;
                }

                //convert to array if it is a single value
                if (typeof rows !== 'object' && !Array.isArray(rows)) {
                    rows = [rows];
                }

                if (!Array.isArray(heads) || !Array.isArray(rows)) {
                    console.error('Invalid Get Parameters');
                    return false;
                }

                for (i = 0; i < rows.length; i += 1) {
                    //grab the rows of interest
                    retArr[i] = [];
                    if (row_by_ind) {
                        temp_row = dataArr[rows[i]];
                    } else {
                        temp_row = dataArr[rowObj[rows[i]]];
                    }
                    //grab the appropriate columns
                    for (j = 0; j < heads.length; j += 1) {
                        if (head_by_ind) {
                            retArr[i].push(temp_row[heads[j]]);
                        } else {
                            retArr[i].push(temp_row[headerObj[heads[j]]]);
                        }
                    }
                }
                if (simple) {
                    retArr = retArr.map(function (row) {
                        if (row.length === 1) {
                            return row[0];
                        }
                        return row;
                    });
                    if (retArr.length === 1) {
                        retArr = retArr[0];
                    }
                    return retArr;
                }

                return exports.enrich({
                    data: retArr,
                    header: heads,
                    row_labels: rows
                });
            };

            stringify = function () {
                var that = this.clone(), i, j;
                for (i = 0; i < that.data.length; i += 1) {
                    for (j = 0; j < that.data[i].length; j += 1) {
                        if (that.data[i][j] && typeof that.data[i][j] === 'number') {
                            //round it, note if it is an integer or has less than 6
                            // digits it will go back to where it was. Also, toPrecision
                            // is a significant figure finder, not a decimal finder
                            // meaning this will drop all but 6 digits to represent a
                            // number
                            that.data[i][j] = that.data[i][j].toPrecision(6) * 1;
                        }
                    }
                }
                return JSON.stringify(that);
            };

            clone = function () {
                var that = this, newcopy, l1, l2, i, j;
                //Uses simple p/s to return a getted version of the object
                newcopy = copy(that);
                l1 = Object.getOwnPropertyNames(that);
                l2 = Object.getOwnPropertyNames(newcopy);

                for (i = 0; i < l1.length; i += 1) {
                    for (j = 0; j < l2.length; j += 1) {
                        if (l1[i] === l2[j]) {
                            l1.splice(i, 1);
                            l2.splice(j, 1);
                            i -= 1;
                            break;
                        }
                    }
                }
                for (i = 0; i < l1.length; i += 1) {
                    if (typeof that[l1[i]] !== 'function') {
                        Object.defineProperty(newcopy, l1[i], {
                            enumerable: false,
                            configurable: false,
                            writable: false,
                            value: that[l1[i]]
                        });
                    }
                }
                //delete newcopy[ID];
                return exports.enrich(newcopy);
            };
        };

        copy = function (x) {
            //Just copies
            return JSON.parse(JSON.stringify(x));
        };

        if (typeof OBJECT === 'object' && !Array.isArray(OBJECT) && !OBJECT.hasOwnProperty('get')) {
            parseLabels();
            defineLists();
            tabularHelpers();
            Object.defineProperty(OBJECT, 'clone', {value: clone, enumerable: false});
            Object.defineProperty(OBJECT, 'stringify', {value: stringify, enumerable: false});
            Object.defineProperty(OBJECT, 'get', {value: get, enumerable: false});
            return OBJECT;
        }
    };

    exports.parse = function (file_string) {
        return file_string.replace(/^\s*$/mg, '').split('\n').map(function (y) {
            return y.split('\t');
        });
    };

}(
    ("undefined" !== typeof module && module.exports)
        ? module.exports
        : TABULAR
));
<commit_msg>update tabular get<commit_after>var TABULAR = {};
(function (exports) {
    'use strict';

    exports.enrich = function (OBJECT) {
        // Global lists
        var header, rowLab, rowLabLab, dataArr, headerObj, rowObj,
                // global functions
                tabularHelpers, defineLists, parseLabels, copy,
                // helper functions
                get, stringify, clone;

        parseLabels = function () {
            var i, that = copy(OBJECT);

            // Header labels?
            if (!that.header && that.has_header) {
                that.header = that.data.shift();
                //Do we have a row label?
                if (that.has_row_label) {
                    that.row_label_name = that.header.shift();
                    if (that.row_label_name.length === 0) {
                        delete that.row_label_name;
                    }
                }
            //No header - create an index based one
            } else if (!that.header) {
                that.header = [];
                for (i = 0; i < that.data[0]; i += 1) {
                    that.header.push(i);
                }
            }

            // Row labels?
            if (!that.row_labels && that.has_row_label) {
                that.row_labels = that.data.map(function (row) {
                    return row.shift();
                });
            } else if (!that.row_labels) {
                that.row_labels = [];
                for (i = 0; i < that.data; i += 1) {
                    that.row_labels.push(i);
                }
            }

            //Now creat data array from what left
            that.data = that.data.map(function (row) {
                return row.map(function (entry) {
                    if (Number.isFinite(entry * 1)) {
                        return entry * 1;
                    }
                });
            });

            //redefine global OBJECT
            OBJECT = that;
        };

        defineLists = function () {
            //Fix label repeats and assign global variables
            var i, spec = 1, that = OBJECT;
            header = that.header;
            rowLab = that.row_labels;
            rowLabLab = that.row_label_name;
            dataArr = that.data;
            headerObj = {};
            rowObj = {};

            //Store header names in a hash
            for (i = 0; i < header.length; i += 1) {
                while (headerObj.hasOwnProperty(header[i])) {
                    if (spec > 1) {
                        header[i] = header[i].replace(
                            new RegExp("_" + (spec - 1) + "$"),
                            ""
                        );
                    }
                    header[i] = header[i] + "_" + spec;
                    spec += 1;
                }
                headerObj[header[i]] = i;
                spec = 1;
            }

            //Finally create row label hash
            for (i = 0; i < rowLab.length; i += 1) {
                while (rowObj.hasOwnProperty(rowLab[i])) {
                    if (spec > 1) {
                        rowLab[i] = rowLab[i].replace(
                            new RegExp("_" + (spec - 1) + "$"),
                            ""
                        );
                    }
                    rowLab[i] = rowLab[i] + "_" + spec;
                    spec += 1;
                }
                rowObj[rowLab[i]] = i;
                spec = 1;
            }
        };

        tabularHelpers = function () {
            get = function (get_param, simple) {
                // Possible parameters: Header label vs number; Column label vs number
                var rows, heads, i, j, retArr = [], row_by_ind = false,
                        head_by_ind = false, temp_row, ret_row_label = [];

                //deal with header input
                get_param = get_param || {};
                if (get_param.hasOwnProperty('header')) {
                    heads = get_param.header;
                } else if (get_param.hasOwnProperty('headers')) {
                    heads = get_param.headers;
                } else if (get_param.hasOwnProperty('header_index')) {
                    heads = get_param.header_index;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('header_indexes')) {
                    heads = get_param.header_indexes;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('header_indices')) {
                    heads = get_param.header_indices;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column')) {
                    heads = get_param.column;
                } else if (get_param.hasOwnProperty('columns')) {
                    heads = get_param.columns;
                } else if (get_param.hasOwnProperty('column_index')) {
                    heads = get_param.column_index;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column_indexes')) {
                    heads = get_param.column_indexes;
                    head_by_ind = true;
                } else if (get_param.hasOwnProperty('column_indices')) {
                    heads = get_param.column_indices;
                    head_by_ind = true;
                } else {
                    heads = header;
                }

                //convert to array if it is a single value
                if (typeof heads !== 'object' && !Array.isArray(heads)) {
                    heads = [heads];
                }

                //deal with rowumins input
                if (get_param.hasOwnProperty('row')) {
                    rows = get_param.row;
                } else if (get_param.hasOwnProperty('rows')) {
                    rows = get_param.rows;
                } else if (get_param.hasOwnProperty(rowLabLab)) {
                    rows = get_param[rowLabLab];
                } else if (get_param.hasOwnProperty(rowLabLab + 's')) {
                    rows = get_param[rowLabLab + 's'];
                } else if (get_param.hasOwnProperty(rowLabLab + 'es')) {
                    rows = get_param[rowLabLab + 'es'];
                } else if (get_param.hasOwnProperty('row_index')) {
                    rows = get_param.row_index;
                    row_by_ind = true;
                } else if (get_param.hasOwnProperty('row_indexes')) {
                    rows = get_param.row_indexes;
                    row_by_ind = true;
                } else if (get_param.hasOwnProperty('row_indices')) {
                    rows = get_param.row_indices;
                    row_by_ind = true;
                } else {
                    rows = rowLab;
                }

                //convert to array if it is a single value
                if (typeof rows !== 'object' && !Array.isArray(rows)) {
                    rows = [rows];
                }

                if (!Array.isArray(heads) || !Array.isArray(rows)) {
                    console.error('Invalid Get Parameters');
                    return false;
                }

                ret_row_label = [];
                for (i = 0; i < rows.length; i += 1) {
                    //grab the rows of interest
                    retArr[i] = [];
                    if (row_by_ind) {
                        temp_row = dataArr[rows[i]];
                        ret_row_label[i] = rowLab[rows[i]];
                    } else {
                        temp_row = dataArr[rowObj[rows[i]]];
                        ret_row_label[i] = rows[i];
                    }
                    //grab the appropriate columns
                    for (j = 0; j < heads.length; j += 1) {
                        if (head_by_ind) {
                            retArr[i].push(temp_row[heads[j]]);
                        } else {
                            retArr[i].push(temp_row[headerObj[heads[j]]]);
                        }
                    }
                }
                if (simple) {
                    retArr = retArr.map(function (row) {
                        if (row.length === 1) {
                            return row[0];
                        }
                        return row;
                    });
                    if (retArr.length === 1) {
                        retArr = retArr[0];
                    }
                    return retArr;
                }

                return exports.enrich({
                    data: retArr,
                    header: heads,
                    row_labels: ret_row_label
                });
            };

            stringify = function () {
                var that = this.clone(), i, j;
                for (i = 0; i < that.data.length; i += 1) {
                    for (j = 0; j < that.data[i].length; j += 1) {
                        if (that.data[i][j] && typeof that.data[i][j] === 'number') {
                            //round it, note if it is an integer or has less than 6
                            // digits it will go back to where it was. Also, toPrecision
                            // is a significant figure finder, not a decimal finder
                            // meaning this will drop all but 6 digits to represent a
                            // number
                            that.data[i][j] = that.data[i][j].toPrecision(6) * 1;
                        }
                    }
                }
                return JSON.stringify(that);
            };

            clone = function () {
                var that = this, newcopy, l1, l2, i, j;
                //Uses simple p/s to return a getted version of the object
                newcopy = copy(that);
                l1 = Object.getOwnPropertyNames(that);
                l2 = Object.getOwnPropertyNames(newcopy);

                for (i = 0; i < l1.length; i += 1) {
                    for (j = 0; j < l2.length; j += 1) {
                        if (l1[i] === l2[j]) {
                            l1.splice(i, 1);
                            l2.splice(j, 1);
                            i -= 1;
                            break;
                        }
                    }
                }
                for (i = 0; i < l1.length; i += 1) {
                    if (typeof that[l1[i]] !== 'function') {
                        Object.defineProperty(newcopy, l1[i], {
                            enumerable: false,
                            configurable: false,
                            writable: false,
                            value: that[l1[i]]
                        });
                    }
                }
                //delete newcopy[ID];
                return exports.enrich(newcopy);
            };
        };

        copy = function (x) {
            //Just copies
            return JSON.parse(JSON.stringify(x));
        };

        if (typeof OBJECT === 'object' && !Array.isArray(OBJECT) && !OBJECT.hasOwnProperty('get')) {
            parseLabels();
            defineLists();
            tabularHelpers();
            Object.defineProperty(OBJECT, 'clone', {value: clone, enumerable: false});
            Object.defineProperty(OBJECT, 'stringify', {value: stringify, enumerable: false});
            Object.defineProperty(OBJECT, 'get', {value: get, enumerable: false});
            return OBJECT;
        }
    };

    exports.parse = function (file_string) {
        return file_string.replace(/^\s*$/mg, '').split('\n').map(function (y) {
            return y.split('\t');
        });
    };

}(
    ("undefined" !== typeof module && module.exports)
        ? module.exports
        : TABULAR
));
<|endoftext|>


<commit_before>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`
     send('startup')}>

About 5 Calls

Why Calling Works

Calling members of Congress is the most effective way to have your voice heard. Calls are tallied by staffers and the count is given to your representatives, informing them how strongly their constituents feel about a current issue. The sooner you reach out, the more likely it is that your voice will influence their position.

Don’t just take it from us:

“Here’s Why You Should Call, Not Email, Your Legislators” NY Times

“Don’t just write to your representatives. Call them — and go to town halls.” Vox

“I can tell you the calls we’ve gotten in my district office and here in Washington surprised me, meaning the numbers of calls.”  Washington Post

“Most members tell me blizzard of angry constituent calls were most impt factor in getting the House to sideline the amdt” Robert Costa

5 Calls does the research for each issue, determining which representatives are most influential for which topic, collecting phone numbers for those offices and writing scripts that clearly articulate a progressive position. You just have to call.

Are we not covering an issue we should be?  Please reach out.

Calling Tips

Calls should take less than a minute. You’ll be speaking to a staffer, so make your point clearly so they can tally your opinion correctly. The provided scripts are useful but you can add your own words.

Be respectful. The staffers that pick up the phone aren’t looking to challenge you and you should treat them with the same respect you expect from them, regardless of which party they work for.

Who made 5 Calls?

5 Calls is a volunteer effort. Founded and run by husband & wife team Nick and Rebecca, many dedicated volunteers contribute design, code and issues to the site including: @monteiro, @stewartsc, @liamdanger, @capndesign, @jameshome, @beau, @cdoremus, @ocderby, @mr0grog and more supporters.

Our iOS app is made by @subdigital, @mccarron, @chrisbrandow and more supporters

Our Android app is made by @dekthedek, gregliest and more supporters

Our issues, scripts and social team is @charmbtrippin, sara.n.g, amanda.l, @djpiebob, @theRati, @lhhargrave, amanda.n, @sagerke, Eleanor Wertman and more contributors.

We want to make advocacy accessible. We hope 5 Calls will make it effortless for regular people to have a voice when it’s needed most.

Join us

This project is open source and volunteer made. If you’d like to join us in developing useful tools for citizens, please get in touch via Twitter or email.

  `;
}
<commit_msg>Replace twitter with github link for Katie<commit_after>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`
     send('startup')}>

About 5 Calls

Why Calling Works

Calling members of Congress is the most effective way to have your voice heard. Calls are tallied by staffers and the count is given to your representatives, informing them how strongly their constituents feel about a current issue. The sooner you reach out, the more likely it is that your voice will influence their position.

Don’t just take it from us:

“Here’s Why You Should Call, Not Email, Your Legislators” NY Times

“Don’t just write to your representatives. Call them — and go to town halls.” Vox

“I can tell you the calls we’ve gotten in my district office and here in Washington surprised me, meaning the numbers of calls.”  Washington Post

“Most members tell me blizzard of angry constituent calls were most impt factor in getting the House to sideline the amdt” Robert Costa

5 Calls does the research for each issue, determining which representatives are most influential for which topic, collecting phone numbers for those offices and writing scripts that clearly articulate a progressive position. You just have to call.

Are we not covering an issue we should be?  Please reach out.

Calling Tips

Calls should take less than a minute. You’ll be speaking to a staffer, so make your point clearly so they can tally your opinion correctly. The provided scripts are useful but you can add your own words.

Be respectful. The staffers that pick up the phone aren’t looking to challenge you and you should treat them with the same respect you expect from them, regardless of which party they work for.

Who made 5 Calls?

5 Calls is a volunteer effort. Founded and run by husband & wife team Nick and Rebecca, many dedicated volunteers contribute design, code and issues to the site including: @monteiro, @stewartsc, @liamdanger, @capndesign, @jameshome, @beau, @cdoremus, @ocderby, @mr0grog and more supporters.

Our iOS app is made by @subdigital, @mccarron, @chrisbrandow and more supporters

Our Android app is made by dektar, gregliest and more supporters

Our issues, scripts and social team is @charmbtrippin, sara.n.g, amanda.l, @djpiebob, @theRati, @lhhargrave, amanda.n, @sagerke, Eleanor Wertman and more contributors.

We want to make advocacy accessible. We hope 5 Calls will make it effortless for regular people to have a voice when it’s needed most.

Join us

This project is open source and volunteer made. If you’d like to join us in developing useful tools for citizens, please get in touch via Twitter or email.

  `;
}
<|endoftext|>