<commit_before>import { breakpoint } from '../../config/style'
import { Component } from 'react'
import Footer from '../footer'
import Head from 'next/head'
import Nav from '../nav'
import style, { globalStyle } from './style'
import ToggleNav from '../toggle-nav'
import { withRouter } from 'next/router'

const description = `
  🎉 Small file size.
  🌟 Simple, functional API.
  🐣 Morph from anything, to anything.
  ⏱ Queue multiple animations on a timeline.
  🚀 Powerful playback control
`

class Layout extends Component {
  constructor (props) {
    super(props)

    this.state = {
      navOpen: typeof props.router.query.nav !== 'undefined' || (props.isServer
        ? false
        : window.navOpen === true &&
          window.matchMedia(`(min-width: ${breakpoint}px)`).matches
      )
    }

    this.state.navOpen

    this.toggleNav = this.toggleNav.bind(this)
    this.focusNav = this.focusNav.bind(this)
    this.hideNav = this.hideNav.bind(this)
    this.hideNavSmallScreens = this.hideNavSmallScreens.bind(this)
  }

  toggleNav (e) {
    e.preventDefault()

    const navOpen = !this.state.navOpen

    this.setState({ navOpen })

    if (!this.props.isServer) {
      window.navOpen = navOpen
    }
  }

  focusNav () {
    this.setState({ navOpen: true })

    if (!this.props.isServer) {
      window.navOpen = true
    }
  }

  hideNav () {
    this.setState({ navOpen: false })
  }

  hideNavSmallScreens () {
    this.setState({
      navOpen: window.navOpen === true &&
        window.matchMedia(`(min-width: ${breakpoint}px)`).matches
    })
  }

  render () {
    return (

          <title>{ this.props.title }</title>

          <meta
            name="description"
            content="{ description }"
          />

          <meta charset="utf-8" />
          <meta name="author" content="Colin Meinke" />

          <meta
            name="viewport"
            content="initial-scale=1.0, width=device-width"
          />

          <meta
            property="og:type"
            content="website"
          />

          <meta
            property="og:site_name"
            content="Wilderness"
          />

          <meta
            property="og:title"
            content="{ this.props.title }"
          />

          <meta
            property="og:description"
            content="{ description }"
          />

          <link
            href="https://fonts.googleapis.com/css?family=Open+Sans:400|Karla:400|Roboto+Mono:400"
            rel="stylesheet"
          />

              <ToggleNav
                handleClick={ this.toggleNav }
                open={ this.state.navOpen }
              />
              { this.props.children }

    )
  }
}

Layout.defaultProps = {
  title: 'Wilderness',
  isServer: typeof window === 'undefined'
}

export default withRouter(Layout)
<commit_msg>fix meta valies<commit_after>import { breakpoint } from '../../config/style'
import { Component } from 'react'
import Footer from '../footer'
import Head from 'next/head'
import Nav from '../nav'
import style, { globalStyle } from './style'
import ToggleNav from '../toggle-nav'
import { withRouter } from 'next/router'

const description = `
  🎉 Small file size.
  🌟 Simple, functional API.
  🐣 Morph from anything, to anything.
  ⏱ Queue multiple animations on a timeline.
  🚀 Powerful playback control
`

class Layout extends Component {
  constructor (props) {
    super(props)

    this.state = {
      navOpen: typeof props.router.query.nav !== 'undefined' || (props.isServer
        ? false
        : window.navOpen === true &&
          window.matchMedia(`(min-width: ${breakpoint}px)`).matches
      )
    }

    this.state.navOpen

    this.toggleNav = this.toggleNav.bind(this)
    this.focusNav = this.focusNav.bind(this)
    this.hideNav = this.hideNav.bind(this)
    this.hideNavSmallScreens = this.hideNavSmallScreens.bind(this)
  }

  toggleNav (e) {
    e.preventDefault()

    const navOpen = !this.state.navOpen

    this.setState({ navOpen })

    if (!this.props.isServer) {
      window.navOpen = navOpen
    }
  }

  focusNav () {
    this.setState({ navOpen: true })

    if (!this.props.isServer) {
      window.navOpen = true
    }
  }

  hideNav () {
    this.setState({ navOpen: false })
  }

  hideNavSmallScreens () {
    this.setState({
      navOpen: window.navOpen === true &&
        window.matchMedia(`(min-width: ${breakpoint}px)`).matches
    })
  }

  render () {
    return (

          <title>{ this.props.title }</title>

          <meta
            name="description"
            content={ description }
          />

          <meta charset="utf-8" />
          <meta name="author" content="Colin Meinke" />

          <meta
            name="viewport"
            content="initial-scale=1.0, width=device-width"
          />

          <meta
            property="og:type"
            content="website"
          />

          <meta
            property="og:site_name"
            content="Wilderness"
          />

          <meta
            property="og:title"
            content={ this.props.title }
          />

          <meta
            property="og:description"
            content={ description }
          />

          <link
            href="https://fonts.googleapis.com/css?family=Open+Sans:400|Karla:400|Roboto+Mono:400"
            rel="stylesheet"
          />

              <ToggleNav
                handleClick={ this.toggleNav }
                open={ this.state.navOpen }
              />
              { this.props.children }

    )
  }
}

Layout.defaultProps = {
  title: 'Wilderness',
  isServer: typeof window === 'undefined'
}

export default withRouter(Layout)
<|endoftext|>


<commit_before>var PlaylistTabs = {
    elems: {
        playlists:'#playlists',
    },
    init: function() {
        var lists = $(PlaylistTabs.elems.playlists).children();

        if (onSharedList) {
            // We don't need to make tabs clickable.
            $.each(lists, function(idx) {
                if (idx !== 0) {
                    $(this).remove();
                }
            });
            return;
        }

        $.each(lists, function(idx) {
            $(this).click(function() {
                playlistManager.getCurrentPlaylist().sync();

                var highlight = 'playlist-tab-on';
                lists.removeClass(highlight);
                $(this).addClass(highlight);
                playlistManager.loadLocal(idx);
            });
        });
    },
    currentIdx: function() {
        var playlistIdx;
        $.each($(PlaylistTabs.elems.playlists).children(), function(idx) {
            if ($(this).hasClass('playlist-tab-on')) {
                playlistIdx = idx;
            }
        });
        return playlistIdx;
    }
};

var PlaylistControl = {
    elems: {
        sharePlaylistBtn:".playlist-section .share-playlist-button",
        clearPlaylistBtn:".playlist-section .clear-playlist-button",
        playlistFilterInput:".playlist-section #search-playlist-input"
    },
    init: function() {
        if (onSharedList) {
            $(PlaylistControl.elems.sharePlaylistBtn).remove();
        }

        $(this.elems.sharePlaylistBtn).click(function() {
            var playlist = playlistManager.getCurrentPlaylist();
            if (playlist.empty()) {
                NotifyManager.sharePlaylist(false);
                return;
            }

            // There's a possible collision, but it doesn't matter.
            var uuid = Math.uuid(8);
            $.ajax({
                type: "POST",
                url: API_PLAYLIST_URL,
                data: {
                    'key' : uuid,
                    'playlist' : playlist.toJsonStr(true)
                },
                crossDomain: true
            });

            NotifyManager.sharePlaylist(true, fullHost + '/?playlist=' + uuid);

            // Logging
            if (window.dropbeat &&
                typeof window.dropbeat === "object" && dropbeat.logApiAction) {
                dropbeat.logApiAction("dropbeat", "playlist-manage/share");
            }
        });

        $(this.elems.clearPlaylistBtn).click(function() {
            var playlist = playlistManager.getCurrentPlaylist();
            playlist.clear(true);
            playlist.sync();
            NotifyManager.playlistCleared();

            // Logging
            if (window.dropbeat &&
                typeof window.dropbeat === "object" && dropbeat.logApiAction) {
                dropbeat.logApiAction("dropbeat", "playlist-manage/clear");
            }
        });

        $(this.elems.playlistFilterInput).
            bind("propertychange keyup input paste", function(event) {
            clearTimeout(PlaylistControl.filterTimer);
            var that = $(this);
            PlaylistControl.filterTimer = setTimeout(function () {
                PlaylistControl.filter(that.val().toLowerCase());
            }, 800);
        });
    },
    filterTimer: null,
    filter: function(keyword) {
        var tempPlaylist = new Playlist();
        var current = playlistManager.getCurrentPlaylist();
        if (keyword) {
            for (var i=0; i<current.length(); i++) {
                var m = current.getWithIdx(i);
                if (m.title.toLowerCase().indexOf(keyword) !== -1) {
                    // XXX: Avoid public access to `playlist`!
                    tempPlaylist.playlist.push(m);
                }
                tempPlaylist.toTable(true);
            }
        } else {
            playlistManager.getCurrentPlaylist().toTable(true);
        }
    },
    load: function(key) {
        // `playlistCallback` is executed later.
        $.ajax({
            url: API_PLAYLIST_URL,
            data: {'key': key, 'type': 'jsonp'},
            dataType: 'jsonp'
        });

        // Logging
        if (window.dropbeat &&
            typeof window.dropbeat === "object" && dropbeat.logApiAction) {
            dropbeat.logApiAction("dropbeat", "playlist-manage/load");
        }
    },
    generate: function(key, id) {
        if (!id) {
            id = Math.floor((1 + Math.random()) * 0x10000).
                toString(16).substring(1);
            playlistManager.add(
                playlistManager.shareKey, new Playlist());
        }

        // Will call `relayPoll`
        $.ajax({
            url: API_PLAYLIST_AUTOGEN,
            data: decodeURIComponent(
                $.param({
                    'key': key,
                    'id': id,
                    'type': 'jsonp'
                })),
            dataType: 'jsonp',
            timeout: 15000
        });
    }
};

function relayPoll(data) {
    if (data) {
        var remains = data[0].remains;
        for (var i=0; i<data.length; i++) {
            if (data[i].remains < remains) {
                remains = data[i].remains;
            }
            if (!data[i].id) {
                continue;
            }
            playlistManager.getCurrentPlaylist().add(new Music(data[i]), true);
        }
        if (remains > 0) {
            PlaylistControl.generate(data[0].key, data[0].poll_id);
        }

    }
}

function playlistCallback(data) {
    if (data) {
        var playlist = new Playlist();
        for (var i=0; i<data.length; i++) {
            m = new Music(data[i]);
            playlist.add(m);

        }

        playlistManager.add(
            playlistManager.shareKey, playlist);
        playlistManager.updatePlaylistView();
        NotifyManager.playlistLoaded();
    } else {
        // XXX: Warn for invalid key
        alert('Invalid access');
        location.href = fullHost;
    }
};

var UrlAdder = {
    elems:{
        urlAddField:".add-by-url-section .url-input-field-wrapper",
        urlAddInput:".add-by-url-section #add-by-url-input",
        urlAddButton:".add-by-url-section .add-button",
        loadingSpinner:".add-by-url-section .loading-spinner"
    },
    init: function() {
        var that = this;
        $(this.elems.urlAddButton).click(function() {
            url = $(that.elems.urlAddInput).val();
            if (!url ||
                (url.indexOf('youtube.com') === -1
                    && url.indexOf('youtu.be') === -1
                    && url.indexOf('soundcloud.com') === -1)) {
                NotifyManager.invalidAdderUrl();
            } else {
                UrlAdder.onSubmit(url);
            }
        });
    },
    adding: false,
    onSubmit: function(url) {
        if (!UrlAdder.adding) {
            UrlAdder.hideAll();
            UrlAdder.adding = true;
            $.ajax({
                url: API_RESOLVE_URL,
                data: {'url': url, 'type': 'jsonp'},
            });
        }

        // Logging
        if (window.dropbeat &&
            typeof window.dropbeat === "object" && dropbeat.logApiAction) {
            dropbeat.logApiAction("dropbeat", "playlist-manage/load-from-url");
        }
    },
    showAll: function() {
        $(this.elems.urlAddField).show();
        $(this.elems.loadingSpinner).hide();
    },
    hideAll: function() {
        $(this.elems.urlAddField).hide();
        $(this.elems.loadingSpinner).show();
    },
    clearInput: function(){
        $(this.elems.urlAddInput).val("");
    }
};

function urlAddCallback(data) {
    // Add to current playlist
    if (data) {
        var playlist = playlistManager.getCurrentPlaylist();
        data.title = titleEscape(data.title);
        var success = playlist.add(new Music(data), true);
        if (success) {
            playlistManager.getCurrentPlaylist().sync();
        }
        NotifyManager.playlistChangeNotify(success);

        UrlAdder.adding = false;
    } else {
        // Notify failure
        NotifyManager.invalidAdderUrl();
    }
    UrlAdder.showAll();
    UrlAdder.clearInput();
}
<commit_msg>show adding animation for already indexed artist tracks<commit_after>var PlaylistTabs = {
    elems: {
        playlists:'#playlists',
    },
    init: function() {
        var lists = $(PlaylistTabs.elems.playlists).children();

        if (onSharedList) {
            // We don't need to make tabs clickable.
            $.each(lists, function(idx) {
                if (idx !== 0) {
                    $(this).remove();
                }
            });
            return;
        }

        $.each(lists, function(idx) {
            $(this).click(function() {
                playlistManager.getCurrentPlaylist().sync();

                var highlight = 'playlist-tab-on';
                lists.removeClass(highlight);
                $(this).addClass(highlight);
                playlistManager.loadLocal(idx);
            });
        });
    },
    currentIdx: function() {
        var playlistIdx;
        $.each($(PlaylistTabs.elems.playlists).children(), function(idx) {
            if ($(this).hasClass('playlist-tab-on')) {
                playlistIdx = idx;
            }
        });
        return playlistIdx;
    }
};

var PlaylistControl = {
    elems: {
        sharePlaylistBtn:".playlist-section .share-playlist-button",
        clearPlaylistBtn:".playlist-section .clear-playlist-button",
        playlistFilterInput:".playlist-section #search-playlist-input"
    },
    init: function() {
        if (onSharedList) {
            $(PlaylistControl.elems.sharePlaylistBtn).remove();
        }

        $(this.elems.sharePlaylistBtn).click(function() {
            var playlist = playlistManager.getCurrentPlaylist();
            if (playlist.empty()) {
                NotifyManager.sharePlaylist(false);
                return;
            }

            // There's a possible collision, but it doesn't matter.
            var uuid = Math.uuid(8);
            $.ajax({
                type: "POST",
                url: API_PLAYLIST_URL,
                data: {
                    'key' : uuid,
                    'playlist' : playlist.toJsonStr(true)
                },
                crossDomain: true
            });

            NotifyManager.sharePlaylist(true, fullHost + '/?playlist=' + uuid);

            // Logging
            if (window.dropbeat &&
                typeof window.dropbeat === "object" && dropbeat.logApiAction) {
                dropbeat.logApiAction("dropbeat", "playlist-manage/share");
            }
        });

        $(this.elems.clearPlaylistBtn).click(function() {
            var playlist = playlistManager.getCurrentPlaylist();
            playlist.clear(true);
            playlist.sync();
            NotifyManager.playlistCleared();

            // Logging
            if (window.dropbeat &&
                typeof window.dropbeat === "object" && dropbeat.logApiAction) {
                dropbeat.logApiAction("dropbeat", "playlist-manage/clear");
            }
        });

        $(this.elems.playlistFilterInput).
            bind("propertychange keyup input paste", function(event) {
            clearTimeout(PlaylistControl.filterTimer);
            var that = $(this);
            PlaylistControl.filterTimer = setTimeout(function () {
                PlaylistControl.filter(that.val().toLowerCase());
            }, 800);
        });
    },
    filterTimer: null,
    filter: function(keyword) {
        var tempPlaylist = new Playlist();
        var current = playlistManager.getCurrentPlaylist();
        if (keyword) {
            for (var i=0; i<current.length(); i++) {
                var m = current.getWithIdx(i);
                if (m.title.toLowerCase().indexOf(keyword) !== -1) {
                    // XXX: Avoid public access to `playlist`!
                    tempPlaylist.playlist.push(m);
                }
                tempPlaylist.toTable(true);
            }
        } else {
            playlistManager.getCurrentPlaylist().toTable(true);
        }
    },
    load: function(key) {
        // `playlistCallback` is executed later.
        $.ajax({
            url: API_PLAYLIST_URL,
            data: {'key': key, 'type': 'jsonp'},
            dataType: 'jsonp'
        });

        // Logging
        if (window.dropbeat &&
            typeof window.dropbeat === "object" && dropbeat.logApiAction) {
            dropbeat.logApiAction("dropbeat", "playlist-manage/load");
        }
    },
    generate: function(key, id) {
        if (!id) {
            id = Math.floor((1 + Math.random()) * 0x10000).
                toString(16).substring(1);
            playlistManager.add(
                playlistManager.shareKey, new Playlist());
        }

        // Will call `relayPoll`
        $.ajax({
            url: API_PLAYLIST_AUTOGEN,
            data: decodeURIComponent(
                $.param({
                    'key': key,
                    'id': id,
                    'type': 'jsonp'
                })),
            dataType: 'jsonp',
            timeout: 15000
        });
    }
};

function relayPoll(data) {
    if (data) {
        var noMorePoll =
            $.grep(data, function(e) {return !e.remains}).length !== 0;
        if (data.length > 1 && noMorePoll) {
            function doSetTimeout(obj, i) {
                setTimeout(function() {
                    playlistManager.
                        getCurrentPlaylist().add(new Music(obj), true);
                }, 100 * i);
            }
            for (i=0; i<data.length; i++) {
                doSetTimeout(data[i], i);
            }
            return;
        }

        for (i=0; i<data.length; i++) {
            if (!data[i].id) {
                continue;
            }
            playlistManager.getCurrentPlaylist().add(new Music(data[i]), true);
        }
        if (!noMorePoll) {
            PlaylistControl.generate(data[0].key, data[0].poll_id);
        }

    }
}

function playlistCallback(data) {
    if (data) {
        var playlist = new Playlist();
        for (var i=0; i<data.length; i++) {
            m = new Music(data[i]);
            playlist.add(m);

        }

        playlistManager.add(
            playlistManager.shareKey, playlist);
        playlistManager.updatePlaylistView();
        NotifyManager.playlistLoaded();
    } else {
        // XXX: Warn for invalid key
        alert('Invalid access');
        location.href = fullHost;
    }
};

var UrlAdder = {
    elems:{
        urlAddField:".add-by-url-section .url-input-field-wrapper",
        urlAddInput:".add-by-url-section #add-by-url-input",
        urlAddButton:".add-by-url-section .add-button",
        loadingSpinner:".add-by-url-section .loading-spinner"
    },
    init: function() {
        var that = this;
        $(this.elems.urlAddButton).click(function() {
            url = $(that.elems.urlAddInput).val();
            if (!url ||
                (url.indexOf('youtube.com') === -1
                    && url.indexOf('youtu.be') === -1
                    && url.indexOf('soundcloud.com') === -1)) {
                NotifyManager.invalidAdderUrl();
            } else {
                UrlAdder.onSubmit(url);
            }
        });
    },
    adding: false,
    onSubmit: function(url) {
        if (!UrlAdder.adding) {
            UrlAdder.hideAll();
            UrlAdder.adding = true;
            $.ajax({
                url: API_RESOLVE_URL,
                data: {'url': url, 'type': 'jsonp'},
            });
        }

        // Logging
        if (window.dropbeat &&
            typeof window.dropbeat === "object" && dropbeat.logApiAction) {
            dropbeat.logApiAction("dropbeat", "playlist-manage/load-from-url");
        }
    },
    showAll: function() {
        $(this.elems.urlAddField).show();
        $(this.elems.loadingSpinner).hide();
    },
    hideAll: function() {
        $(this.elems.urlAddField).hide();
        $(this.elems.loadingSpinner).show();
    },
    clearInput: function(){
        $(this.elems.urlAddInput).val("");
    }
};

function urlAddCallback(data) {
    // Add to current playlist
    if (data) {
        var playlist = playlistManager.getCurrentPlaylist();
        data.title = titleEscape(data.title);
        var success = playlist.add(new Music(data), true);
        if (success) {
            playlistManager.getCurrentPlaylist().sync();
        }
        NotifyManager.playlistChangeNotify(success);

        UrlAdder.adding = false;
    } else {
        // Notify failure
        NotifyManager.invalidAdderUrl();
    }
    UrlAdder.showAll();
    UrlAdder.clearInput();
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'

export default class List extends Component{
  constructor(props){
    super(props);
    this.state = {
      collapse: false,
    }
  }
  toggleCollapse(){
    this.setState({
      collapse: !this.state.collapse
    });
  }
  render(){
    let attendees = this.props.attendees
    let category = this.props.category
    let orderby = this.props.orderby
    let asc = this.props.asc
    let onChange = this.props.onChange
    let collapse = this.state.collapse

    let userList = []

    if(!collapse)
      for(let attendee of attendees){
        userList.push(

            { [attendee.firstname, attendee.lastname].join(' ') }
            { attendee.date.toLocaleString() }

        )
      }
    let collapseContent = collapse ? (keyboard_arrow_left) : (keyboard_arrow_down)
    let tableHeaders = [
       onChange("NAME",!asc)} className={'mdl-data-table__cell--non-numeric ' + (orderby=="NAME" ? (asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' ) : '')} colSpan='1'>{category}	,
       onChange("DATE",!asc)} className={'mdl-data-table__cell--non-numeric ' + (orderby=="DATE" ? (asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' ) : '')} colSpan='1'>Registrerings dato	,
       this.toggleCollapse()}>{collapseContent}
    ]
    return (

        { tableHeaders }

      { userList }

    )
  }
}

/*
const List = ({ category, attendees, orderby, asc, onChange }) => {

}
*/
//export default List;
<commit_msg>Removed some ancient comments<commit_after>import React, { Component } from 'react'

export default class List extends Component{
  constructor(props){
    super(props);
    this.state = {
      collapse: false,
    }
  }
  toggleCollapse(){
    this.setState({
      collapse: !this.state.collapse
    });
  }
  render(){
    let attendees = this.props.attendees
    let category = this.props.category
    let orderby = this.props.orderby
    let asc = this.props.asc
    let onChange = this.props.onChange
    let collapse = this.state.collapse

    let userList = []

    if(!collapse)
      for(let attendee of attendees){
        userList.push(

            { [attendee.firstname, attendee.lastname].join(' ') }
            { attendee.date.toLocaleString() }

        )
      }
    let collapseContent = collapse ? (keyboard_arrow_left) : (keyboard_arrow_down)
    let tableHeaders = [
       onChange("NAME",!asc)} className={'mdl-data-table__cell--non-numeric ' + (orderby=="NAME" ? (asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' ) : '')} colSpan='1'>{category}	,
       onChange("DATE",!asc)} className={'mdl-data-table__cell--non-numeric ' + (orderby=="DATE" ? (asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' ) : '')} colSpan='1'>Registrerings dato	,
       this.toggleCollapse()}>{collapseContent}
    ]
    return (

        { tableHeaders }

      { userList }

    )
  }
}
<|endoftext|>


<commit_before>/** Spreadsheet **/
(function() {

function getRowLabel(value){
  if (this.options.spreadsheet.tickFormatter){
    //TODO maybe pass the xaxis formatter to the custom tick formatter as an opt-out?
    return this.options.spreadsheet.tickFormatter(value);
  }
  else {
    var t = _.find(this.axes.x.ticks, function(t){return t.v == value;});
    if (t) {
      return t.label;
    }
    return value;
  }
}

var
  D = Flotr.DOM,
  _ = Flotr._;

Flotr.addPlugin('spreadsheet', {
  options: {
    show: false,           // => show the data grid using two tabs
    tabGraphLabel: 'Graph',
    tabDataLabel: 'Data',
    toolbarDownload: 'Download CSV', // @todo: add better language support
    toolbarSelectAll: 'Select all',
    csvFileSeparator: ',',
    decimalSeparator: '.',
    tickFormatter: null,
    initialTab: 'graph'
  },
  /**
   * Builds the tabs in the DOM
   */
  callbacks: {
    'flotr:afterconstruct': function(){
      // @TODO necessary?
      //this.el.select('.flotr-tabs-group,.flotr-datagrid-container').invoke('remove');

      if (!this.options.spreadsheet.show) return;

      var ss = this.spreadsheet,
        container = D.node('

'),
        graph = D.node('
'+this.options.spreadsheet.tabGraphLabel+'
'),
        data = D.node('
'+this.options.spreadsheet.tabDataLabel+'
'),
        offset;

      ss.tabsContainer = container;
      ss.tabs = { graph : graph, data : data };

      D.insert(container, graph);
      D.insert(container, data);
      D.insert(this.el, container);

      offset = D.size(data).height + 2;
      this.plotOffset.bottom += offset;

      D.setStyles(container, {top: this.canvasHeight-offset+'px'});

      this.
        _observe(graph, 'click',  function(){ss.showTab('graph');}).
        _observe(data, 'click', function(){ss.showTab('data');});
      if (this.options.spreadsheet.initialTab !== 'graph'){
        ss.showTab(this.options.spreadsheet.initialTab);
      }
    }
  },
  /**
   * Builds a matrix of the data to make the correspondance between the x values and the y values :
   * X value => Y values from the axes
   * @return {Array} The data grid
   */
  loadDataGrid: function(){
    if (this.seriesData) return this.seriesData;

    var s = this.series,
        dg = [];

    /* The data grid is a 2 dimensions array. There is a row for each X value.
     * Each row contains the x value and the corresponding y value for each serie ('undefined' if there isn't one)
    **/
    _.each(s, function(serie, i){
      _.each(serie.data, function (v) {
        var x = v[0],
            y = v[1],
            r = _.detect(dg, function(row) {return row[0] === x;});
        if (r) {
          r[i+1] = y;
        } else {
          var newRow = [];
          newRow[0] = x;
          newRow[i+1] = y;
          dg.push(newRow);
        }
      });
    });

    // The data grid is sorted by x value
    this.seriesData = _.sortBy(dg, function (v) {
      return v[0];
    });
    return this.seriesData;
  },
  /**
   * Constructs the data table for the spreadsheet
   * @todo make a spreadsheet manager (Flotr.Spreadsheet)
   * @return {Element} The resulting table element
   */
  constructDataGrid: function(){
    // If the data grid has already been built, nothing to do here
    if (this.spreadsheet.datagrid) return this.spreadsheet.datagrid;

    var s = this.series,
        datagrid = this.spreadsheet.loadDataGrid(),
        colgroup = ['<colgroup><col />'],
        buttonDownload, buttonSelect, t;

    // First row : series' labels
    var html = ['

'];
    html.push(' 	');
    _.each(s, function(serie,i){
      html.push(''+(serie.label || String.fromCharCode(65+i))+'	');
      colgroup.push('<col />');
    });
    html.push('
');
    // Data rows
    _.each(datagrid, function(row){
      html.push('
');
      _.times(s.length+1, function(i){
        var tag = 'td',
            value = row[i],
            // TODO: do we really want to handle problems with floating point
            // precision here?
            content = (!_.isUndefined(value) ? Math.round(value*100000)/100000 : '');
        if (i === 0) {
          tag = 'th';
          var label = getRowLabel.call(this, content);
          if (label) content = label;
        }

        html.push('<'+tag+(tag=='th'?' scope="row"':'')+'>'+content+'</'+tag+'>');
      }, this);
      html.push('
');
    }, this);
    colgroup.push('</colgroup>');
    t = D.node(html.join(''));

    /**
     * @TODO disabled this
    if (!Flotr.isIE || Flotr.isIE == 9) {
      function handleMouseout(){
        t.select('colgroup col.hover, th.hover').invoke('removeClassName', 'hover');
      }
      function handleMouseover(e){
        var td = e.element(),
          siblings = td.previousSiblings();
        t.select('th[scope=col]')[siblings.length-1].addClassName('hover');
        t.select('colgroup col')[siblings.length].addClassName('hover');
      }
      _.each(t.select('td'), function(td) {
        Flotr.EventAdapter.
          observe(td, 'mouseover', handleMouseover).
          observe(td, 'mouseout', handleMouseout);
      });
    }
    */

    buttonDownload = D.node(
      '<button type="button" class="flotr-datagrid-toolbar-button">' +
      this.options.spreadsheet.toolbarDownload +
      '</button>');

    buttonSelect = D.node(
      '<button type="button" class="flotr-datagrid-toolbar-button">' +
      this.options.spreadsheet.toolbarSelectAll+
      '</button>');

    this.
      _observe(buttonDownload, 'click', _.bind(this.spreadsheet.downloadCSV, this)).
      _observe(buttonSelect, 'click', _.bind(this.spreadsheet.selectAllData, this));

    var toolbar = D.node('

');
    D.insert(toolbar, buttonDownload);
    D.insert(toolbar, buttonSelect);

    var containerHeight =this.canvasHeight - D.size(this.spreadsheet.tabsContainer).height-2,
        container = D.node('

');

    D.insert(container, toolbar);
    D.insert(container, t);
    D.insert(this.el, container);
    this.spreadsheet.datagrid = t;
    this.spreadsheet.container = container;

    return t;
  },
  /**
   * Shows the specified tab, by its name
   * @todo make a tab manager (Flotr.Tabs)
   * @param {String} tabName - The tab name
   */
  showTab: function(tabName){
    if (this.spreadsheet.activeTab === tabName){
      return;
    }
    switch(tabName) {
      case 'graph':
        D.hide(this.spreadsheet.container);
        D.removeClass(this.spreadsheet.tabs.data, 'selected');
        D.addClass(this.spreadsheet.tabs.graph, 'selected');
      break;
      case 'data':
        if (!this.spreadsheet.datagrid)
          this.spreadsheet.constructDataGrid();
        D.show(this.spreadsheet.container);
        D.addClass(this.spreadsheet.tabs.data, 'selected');
        D.removeClass(this.spreadsheet.tabs.graph, 'selected');
      break;
    }
    this.spreadsheet.activeTab = tabName;
  },
  /**
   * Selects the data table in the DOM for copy/paste
   */
  selectAllData: function(){
    if (this.spreadsheet.tabs) {
      var selection, range, doc, win, node = this.spreadsheet.constructDataGrid();

      this.spreadsheet.showTab('data');

      // deferred to be able to select the table
      setTimeout(function () {
        if ((doc = node.ownerDocument) && (win = doc.defaultView) &&
            win.getSelection && doc.createRange &&
            (selection = window.getSelection()) &&
            selection.removeAllRanges) {
            range = doc.createRange();
            range.selectNode(node);
            selection.removeAllRanges();
            selection.addRange(range);
        }
        else if (document.body && document.body.createTextRange &&
                (range = document.body.createTextRange())) {
            range.moveToElementText(node);
            range.select();
        }
      }, 0);
      return true;
    }
    else return false;
  },
  /**
   * Converts the data into CSV in order to download a file
   */
  downloadCSV: function(){
    var csv = '',
        series = this.series,
        options = this.options,
        dg = this.spreadsheet.loadDataGrid(),
        separator = encodeURIComponent(options.spreadsheet.csvFileSeparator);

    if (options.spreadsheet.decimalSeparator === options.spreadsheet.csvFileSeparator) {
      throw "The decimal separator is the same as the column separator ("+options.spreadsheet.decimalSeparator+")";
    }

    // The first row
    _.each(series, function(serie, i){
      csv += separator+'"'+(serie.label || String.fromCharCode(65+i)).replace(/\"/g, '\\"')+'"';
    });

    csv += "%0D%0A"; // \r\n

    // For each row
    csv += _.reduce(dg, function(memo, row){
      var rowLabel = getRowLabel.call(this, row[0]) || '';
      rowLabel = '"'+(rowLabel+'').replace(/\"/g, '\\"')+'"';
      var numbers = row.slice(1).join(separator);
      if (options.spreadsheet.decimalSeparator !== '.') {
        numbers = numbers.replace(/\./g, options.spreadsheet.decimalSeparator);
      }
      return memo + rowLabel+separator+numbers+"%0D%0A"; // \t and \r\n
    }, '', this);

    if (Flotr.isIE && Flotr.isIE < 9) {
      csv = csv.replace(new RegExp(separator, 'g'), decodeURIComponent(separator)).replace(/%0A/g, '\n').replace(/%0D/g, '\r');
      window.open().document.write(csv);
    }
    else window.open('data:text/csv,'+csv);
  }
});
})();
<commit_msg>throw an error if an illegal tab name is given<commit_after>/** Spreadsheet **/
(function() {

function getRowLabel(value){
  if (this.options.spreadsheet.tickFormatter){
    //TODO maybe pass the xaxis formatter to the custom tick formatter as an opt-out?
    return this.options.spreadsheet.tickFormatter(value);
  }
  else {
    var t = _.find(this.axes.x.ticks, function(t){return t.v == value;});
    if (t) {
      return t.label;
    }
    return value;
  }
}

var
  D = Flotr.DOM,
  _ = Flotr._;

Flotr.addPlugin('spreadsheet', {
  options: {
    show: false,           // => show the data grid using two tabs
    tabGraphLabel: 'Graph',
    tabDataLabel: 'Data',
    toolbarDownload: 'Download CSV', // @todo: add better language support
    toolbarSelectAll: 'Select all',
    csvFileSeparator: ',',
    decimalSeparator: '.',
    tickFormatter: null,
    initialTab: 'graph'
  },
  /**
   * Builds the tabs in the DOM
   */
  callbacks: {
    'flotr:afterconstruct': function(){
      // @TODO necessary?
      //this.el.select('.flotr-tabs-group,.flotr-datagrid-container').invoke('remove');

      if (!this.options.spreadsheet.show) return;

      var ss = this.spreadsheet,
        container = D.node('

'),
        graph = D.node('
'+this.options.spreadsheet.tabGraphLabel+'
'),
        data = D.node('
'+this.options.spreadsheet.tabDataLabel+'
'),
        offset;

      ss.tabsContainer = container;
      ss.tabs = { graph : graph, data : data };

      D.insert(container, graph);
      D.insert(container, data);
      D.insert(this.el, container);

      offset = D.size(data).height + 2;
      this.plotOffset.bottom += offset;

      D.setStyles(container, {top: this.canvasHeight-offset+'px'});

      this.
        _observe(graph, 'click',  function(){ss.showTab('graph');}).
        _observe(data, 'click', function(){ss.showTab('data');});
      if (this.options.spreadsheet.initialTab !== 'graph'){
        ss.showTab(this.options.spreadsheet.initialTab);
      }
    }
  },
  /**
   * Builds a matrix of the data to make the correspondance between the x values and the y values :
   * X value => Y values from the axes
   * @return {Array} The data grid
   */
  loadDataGrid: function(){
    if (this.seriesData) return this.seriesData;

    var s = this.series,
        dg = [];

    /* The data grid is a 2 dimensions array. There is a row for each X value.
     * Each row contains the x value and the corresponding y value for each serie ('undefined' if there isn't one)
    **/
    _.each(s, function(serie, i){
      _.each(serie.data, function (v) {
        var x = v[0],
            y = v[1],
            r = _.detect(dg, function(row) {return row[0] === x;});
        if (r) {
          r[i+1] = y;
        } else {
          var newRow = [];
          newRow[0] = x;
          newRow[i+1] = y;
          dg.push(newRow);
        }
      });
    });

    // The data grid is sorted by x value
    this.seriesData = _.sortBy(dg, function (v) {
      return v[0];
    });
    return this.seriesData;
  },
  /**
   * Constructs the data table for the spreadsheet
   * @todo make a spreadsheet manager (Flotr.Spreadsheet)
   * @return {Element} The resulting table element
   */
  constructDataGrid: function(){
    // If the data grid has already been built, nothing to do here
    if (this.spreadsheet.datagrid) return this.spreadsheet.datagrid;

    var s = this.series,
        datagrid = this.spreadsheet.loadDataGrid(),
        colgroup = ['<colgroup><col />'],
        buttonDownload, buttonSelect, t;

    // First row : series' labels
    var html = ['

'];
    html.push(' 	');
    _.each(s, function(serie,i){
      html.push(''+(serie.label || String.fromCharCode(65+i))+'	');
      colgroup.push('<col />');
    });
    html.push('
');
    // Data rows
    _.each(datagrid, function(row){
      html.push('
');
      _.times(s.length+1, function(i){
        var tag = 'td',
            value = row[i],
            // TODO: do we really want to handle problems with floating point
            // precision here?
            content = (!_.isUndefined(value) ? Math.round(value*100000)/100000 : '');
        if (i === 0) {
          tag = 'th';
          var label = getRowLabel.call(this, content);
          if (label) content = label;
        }

        html.push('<'+tag+(tag=='th'?' scope="row"':'')+'>'+content+'</'+tag+'>');
      }, this);
      html.push('
');
    }, this);
    colgroup.push('</colgroup>');
    t = D.node(html.join(''));

    /**
     * @TODO disabled this
    if (!Flotr.isIE || Flotr.isIE == 9) {
      function handleMouseout(){
        t.select('colgroup col.hover, th.hover').invoke('removeClassName', 'hover');
      }
      function handleMouseover(e){
        var td = e.element(),
          siblings = td.previousSiblings();
        t.select('th[scope=col]')[siblings.length-1].addClassName('hover');
        t.select('colgroup col')[siblings.length].addClassName('hover');
      }
      _.each(t.select('td'), function(td) {
        Flotr.EventAdapter.
          observe(td, 'mouseover', handleMouseover).
          observe(td, 'mouseout', handleMouseout);
      });
    }
    */

    buttonDownload = D.node(
      '<button type="button" class="flotr-datagrid-toolbar-button">' +
      this.options.spreadsheet.toolbarDownload +
      '</button>');

    buttonSelect = D.node(
      '<button type="button" class="flotr-datagrid-toolbar-button">' +
      this.options.spreadsheet.toolbarSelectAll+
      '</button>');

    this.
      _observe(buttonDownload, 'click', _.bind(this.spreadsheet.downloadCSV, this)).
      _observe(buttonSelect, 'click', _.bind(this.spreadsheet.selectAllData, this));

    var toolbar = D.node('

');
    D.insert(toolbar, buttonDownload);
    D.insert(toolbar, buttonSelect);

    var containerHeight =this.canvasHeight - D.size(this.spreadsheet.tabsContainer).height-2,
        container = D.node('

');

    D.insert(container, toolbar);
    D.insert(container, t);
    D.insert(this.el, container);
    this.spreadsheet.datagrid = t;
    this.spreadsheet.container = container;

    return t;
  },
  /**
   * Shows the specified tab, by its name
   * @todo make a tab manager (Flotr.Tabs)
   * @param {String} tabName - The tab name
   */
  showTab: function(tabName){
    if (this.spreadsheet.activeTab === tabName){
      return;
    }
    switch(tabName) {
      case 'graph':
        D.hide(this.spreadsheet.container);
        D.removeClass(this.spreadsheet.tabs.data, 'selected');
        D.addClass(this.spreadsheet.tabs.graph, 'selected');
      break;
      case 'data':
        if (!this.spreadsheet.datagrid)
          this.spreadsheet.constructDataGrid();
        D.show(this.spreadsheet.container);
        D.addClass(this.spreadsheet.tabs.data, 'selected');
        D.removeClass(this.spreadsheet.tabs.graph, 'selected');
      break;
      default:
        throw 'Illegal tab name: ' + tabName;
    }
    this.spreadsheet.activeTab = tabName;
  },
  /**
   * Selects the data table in the DOM for copy/paste
   */
  selectAllData: function(){
    if (this.spreadsheet.tabs) {
      var selection, range, doc, win, node = this.spreadsheet.constructDataGrid();

      this.spreadsheet.showTab('data');

      // deferred to be able to select the table
      setTimeout(function () {
        if ((doc = node.ownerDocument) && (win = doc.defaultView) &&
            win.getSelection && doc.createRange &&
            (selection = window.getSelection()) &&
            selection.removeAllRanges) {
            range = doc.createRange();
            range.selectNode(node);
            selection.removeAllRanges();
            selection.addRange(range);
        }
        else if (document.body && document.body.createTextRange &&
                (range = document.body.createTextRange())) {
            range.moveToElementText(node);
            range.select();
        }
      }, 0);
      return true;
    }
    else return false;
  },
  /**
   * Converts the data into CSV in order to download a file
   */
  downloadCSV: function(){
    var csv = '',
        series = this.series,
        options = this.options,
        dg = this.spreadsheet.loadDataGrid(),
        separator = encodeURIComponent(options.spreadsheet.csvFileSeparator);

    if (options.spreadsheet.decimalSeparator === options.spreadsheet.csvFileSeparator) {
      throw "The decimal separator is the same as the column separator ("+options.spreadsheet.decimalSeparator+")";
    }

    // The first row
    _.each(series, function(serie, i){
      csv += separator+'"'+(serie.label || String.fromCharCode(65+i)).replace(/\"/g, '\\"')+'"';
    });

    csv += "%0D%0A"; // \r\n

    // For each row
    csv += _.reduce(dg, function(memo, row){
      var rowLabel = getRowLabel.call(this, row[0]) || '';
      rowLabel = '"'+(rowLabel+'').replace(/\"/g, '\\"')+'"';
      var numbers = row.slice(1).join(separator);
      if (options.spreadsheet.decimalSeparator !== '.') {
        numbers = numbers.replace(/\./g, options.spreadsheet.decimalSeparator);
      }
      return memo + rowLabel+separator+numbers+"%0D%0A"; // \t and \r\n
    }, '', this);

    if (Flotr.isIE && Flotr.isIE < 9) {
      csv = csv.replace(new RegExp(separator, 'g'), decodeURIComponent(separator)).replace(/%0A/g, '\n').replace(/%0D/g, '\r');
      window.open().document.write(csv);
    }
    else window.open('data:text/csv,'+csv);
  }
});
})();
<|endoftext|>


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

/*
 * IMPORTANT: This file was auto-generated by "grunt generate-config". Please do not modify this directly. Instead
 * please modify scenery/package.json to control dependencies.
 *
 * RequireJS configuration file for the scenery sim.
 * Paths are relative to the location of this file.
 */

require.config( {

  deps: [ 'scenery-tests' ],

  paths: {

    // Third-party libs
    text: '../../sherpa/lib/text-2.0.12',

    // PhET plugins
    audio: '../../chipper/js/requirejs-plugins/audio',
    image: '../../chipper/js/requirejs-plugins/image',
    mipmap: '../../chipper/js/requirejs-plugins/mipmap',
    string: '../../chipper/js/requirejs-plugins/string',
    ifphetio: '../../chipper/js/requirejs-plugins/ifphetio',

    // PhET libs, uppercase names to identify them in require.js imports.
    // IMPORTANT: DO NOT modify. This file is auto-generated. See documentation at the top.
    AXON: '../../axon/js',
    BRAND: '../../brand/' + phet.chipper.brand + '/js',
    DOT: '../../dot/js',
    JOIST: '../../joist/js',
    KITE: '../../kite/js',
    PHETCOMMON: '../../phetcommon/js',
    PHET_CORE: '../../phet-core/js',
    PHET_IO: '../../phet-io/js',
    REPOSITORY: '..',
    SCENERY: '.',
    SCENERY_PHET: '../../scenery-phet/js',
    SUN: '../../sun/js',
    TAMBO: '../../tambo/js',
    TANDEM: '../../tandem/js'
  },

  // optional cache buster to make browser refresh load all included scripts, can be disabled with ?cacheBuster=false
  urlArgs: phet.chipper.getCacheBusterArgs()
} );
<commit_msg>renamed "audio" plugin to "sound"<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/*
 * IMPORTANT: This file was auto-generated by "grunt generate-config". Please do not modify this directly. Instead
 * please modify scenery/package.json to control dependencies.
 *
 * RequireJS configuration file for the scenery sim.
 * Paths are relative to the location of this file.
 */

require.config( {

  deps: [ 'scenery-tests' ],

  paths: {

    // Third-party libs
    text: '../../sherpa/lib/text-2.0.12',

    // PhET plugins
    sound: '../../chipper/js/requirejs-plugins/sound',
    image: '../../chipper/js/requirejs-plugins/image',
    mipmap: '../../chipper/js/requirejs-plugins/mipmap',
    string: '../../chipper/js/requirejs-plugins/string',
    ifphetio: '../../chipper/js/requirejs-plugins/ifphetio',

    // PhET libs, uppercase names to identify them in require.js imports.
    // IMPORTANT: DO NOT modify. This file is auto-generated. See documentation at the top.
    AXON: '../../axon/js',
    BRAND: '../../brand/' + phet.chipper.brand + '/js',
    DOT: '../../dot/js',
    JOIST: '../../joist/js',
    KITE: '../../kite/js',
    PHETCOMMON: '../../phetcommon/js',
    PHET_CORE: '../../phet-core/js',
    PHET_IO: '../../phet-io/js',
    REPOSITORY: '..',
    SCENERY: '.',
    SCENERY_PHET: '../../scenery-phet/js',
    SUN: '../../sun/js',
    TAMBO: '../../tambo/js',
    TANDEM: '../../tandem/js'
  },

  // optional cache buster to make browser refresh load all included scripts, can be disabled with ?cacheBuster=false
  urlArgs: phet.chipper.getCacheBusterArgs()
} );
<|endoftext|>


<commit_before>var expect = chai.expect;
var should = chai.should();

describe("hello world", function() {
	it("zomg it works", function(){
		console.log('***expect***', expect)
	})
})<commit_msg>Add tests<commit_after>// var expect = chai.expect;
// var should = chai.should();
var assert = chai.assert;

describe("#chunk", function() {
  it("should be able to chunk if the array length is unevenly divided by size", function() {
    assert.deepEqual(bruhdash.chunk([1,2,3,4,5], 2), [[1,2], [3,4], [5]]);
  })
  it("for empty arrays should return an empty array", function() {
    assert.deepEqual(bruhdash.chunk([], 2), [] );
  })
  it("should return an empty array if size param is 0", function() {
    assert.deepEqual(brudhash.chunk([1,2,3], 0), []);
  })
  it("should return the original array in an array if size param is equal to the array length", function() {
    assert.deepEqual(bruhdash.chunk([1,2,3], 3), [[1,2,3]]);
  })
  it("should return the original array in an array if size param is more than array length", function(){
    assert.deepEqual(bruhdash.chunk([1,2,3],5), [[1,2,3]]);
  })
  it("should ")

})

describe("#compact", function() {
  it("should remove all falsy values", function() {
    assert.deepEqual(brudhash.compact(1, false, null, 0, '', NaN, 2), [1,2]);
  })
})

describe("#difference", function() {
  it("should find the difference of two arrays", function() {
    assert.deepEqual(brudash.difference([1,2,3], [1,2,5,1,2]), [3]);
  })
})

describe("#drop", function() {
  it("should drop the first element of the array and return the remainder if no amount to drop is specified", function() {
    assert.deepEqual(brudash.drop([1,2,3]), [2,3]);
  })
  it("should return the entire array if the amount to drop is 0", function(){
    assert.deepEqual(bruhdash.drop([1,2,3]), [1,2,3]);
  })
  it("should drop the correct amount of array elements", function() {
    assert.deepEqual(bruhdash.drop([1,2,3,4,5], 2), [3,4,5]);
  })
})

describe("#dropRight", function() {
  it("should drop the last element of the array if no amount to drop is specified", function() {
    assert.deepEqual(bruhdash.dropRight([1,2,3]), [1,2]);
  })
  it("should return the entire array if the amount to drop is 0", function() {
    assert.deepEqual(bruhdash.drop([1,2,3]), [1,2,3]);
  })
  it("should drop the correct amount of array elements from the right side", function() {
    assert.deepEqual(bruhdash.drop([1,2,3,4,5], 2), [1,2,3]);
  })

})

describe("#fill", function() {
  it("should fill entire array if no start and end is specified", function(){
    assert.deepEqual(bruhdash.fill([1,2,3], 'a'), ['a', 'a', 'a']);
  })
  it("should fill an array with specified start and end indexes", function() {
    assert.deepEqual(bruhdash.fill([4,6,8,10], '*', 1, 3), [4, '*', '*', 10]);
  })
})

describe("#first", function() {
  it('should return the first element of an array', function(){
    assert.deepEqual(bruhdash.first([1,2,3]), 1)
  })
})

describe("#indexOf", function() {
  it('should return index of first value matched from left to right', function() {
    assert.deepEqual(bruhdash.indexOf([1,2,3], 2), 1);
  })
  it('should return -1 if value cannot be found in array', function() {
    assert.deepEqual(bruhdash.indexOf([1,2,3], 4), -1);
  })

})

describe("#initial", function() {
  it('should return all but the last element', function() {
    assert.deepEqual(bruhdash.initial([1,2,3]), [1,2])
  })
})

describe("#last", function() {
  it('should return the last element of an array', function() {
    assert.deepEqual(bruhdash.last([1,2,3]), 3);
  })
})

describe("#lastIndexOf", function() {
  it('should return index of first value matched from right to left', function() {
    assert.deepEqual(bruhdash.lastIndexOf([1,2,3], 2), 1)
  })
  it('should return -1 if value cannot be found in array', function() {
    assert.deepEqual(bruhdash.lastIndexOf([1,2,3], 4), -1);
  })
})

describe("#pull", function() {
  it('should pull all specified values from array', function() {
    assert.deepEqual(bruhdash.pull(['a','b','c'], 'a', 'b'), ['c']);
  })
})

describe("#pullAt", function() {
  it('should pull the elements of the arrays from the indices specified', function() {
    assert.deepEqual(bruhdash.pullAt([1,2,3,4,5], [1,3]), [1,3,5]);
  })
})

// describe("#rest", function() {

// })

describe("#slice", function() {
  it('should return a new array of the start and end indices specified', function() {
    assert.deepEqual(bruhdash.slice([1,2,3,4,5], 2,4), [3,4]);
  })
})

describe("#take", function() {
  it('should return a slice of the array with n elements from the beginning', function() {
    assert.deepEqual(bruhdash.take([1,2,3,4,5], 3), [1,2,3]);
  })
  it('should return a slice of just the first element if no amount to take is specified', function() {
    assert.deepEqual(bruhdash.take([1,2,3]), [1]);
  })
  it('should return an empty array if the amount to take is 0', function() {
    assert.deepEqual(bruhdash.take([1,2,3], 0), []);
  })
  it('should return the same array if the amount to slice is greater than the array length', function() {
    assert.deepEqual(bruhdash.take([1,2,3], 5), [1,2,3]);
  })
})

describe("#takeRight", function() {
  it('should return a slice of the array with n elements from the end', function() {
    assert.deepEqual(bruhdash.takeRight([1,2,3,4,5], 3), [3,4,5]);
  })
  it('should return a slice of just the last element if no amount to take is specified', function() {
    assert.deepEqual(bruhdash.takeRight([1,2,3]), 3);
  })
  it('should return an empty array if the amount to take is 0', function() {
    assert.deepEqual(bruhdash.takeRight([1,2,3], 0), []);
  })
  it('should return the same array if the amount to slice is greater than the array length', function() {
    assert.deepEqual(bruhdash.take([1,2,3], 5), [1,2,3]);
  })
})

describe("#zip", function() {
  it('should return an array of grouped elements', function() {
    assert.deepEqual(bruhdash.zip([1,2,3], ['a','b','c']),[[1,'a'],[2,'b'],[3,'c']] );
  })
})

describe("#unzip", function() {
  it('should return an array of ungrouped elements', function() {
    assert.deepEqual(bruhdash.unzip([[1,'a'],[2,'b'],[3,'c']]), [[1,2,3], ['a','b','c']]);
  })
})

describe("#without", function() {
  it('should return an array without the values specified', function() {
    assert.deepEqual(bruhdash.without([1,2,3,4,5], 3,4), [1,2,5])
  })
})

describe("#forEach", function() {
  it('should be able to iterate over an array', function() {
    var result = [];
    assert.deepEqual(bruhdash.forEach([1,2,3], function(item) {
      result.push(item)
    }), [1,2,3])
  })
  it('should be able to iterate over an object', function(){
    var result = [];
    assert.deepEqual(bruhdash.forEach({'a':1, 'b':2, 'c':3}, function(item) {
      result.push(item)
    }), [1,2,3])
  })
})

describe("#map", function() {
  it('should be able to iterate over arrays and return a new array', function() {
    assert.deepEqual(bruhdash.map([1,2,3], function(item) {
      return item * 2
    }), [2,4,6])
  }
  it('should be able to iterate over objects and return a new array', function() {
    assert.deepEqual(bruhdash.map({'a':1, 'b':2, 'c':3}, function(item) {
      return item * 2
    }), [2,4,6])
  })
})

describe("#filter", function() {
  it('should be able to filter arrays', function() {
    assert.deepEqual(bruhdash.filter([1,2,3,4], function(item) {
      return item % 2 === 0
    }), [2,4])
  })
  it('should be able to filter objects', function() {
    assert.deepEqual(bruhdash.filter({'a':1, 'b':2, 'c':3}, function(item) {
      return item % 2 === 0
    }), [2,4])
  })
})

describe("#reduce", function() {
  it('should be able to return accumulated value for an array', function() {
    assert.deepEqual(bruhdash.reduce([1,2,3], function(total, value){
      return total + value
    }), 6)
  }
  it('should be able to return accumulated value for an object', function() {
    assert.deepEqual(bruhdash.reduce({'a':1, 'b':2, 'c':3}, function(total, value) {
      return total + value
    }), 6)
  })
})
<|endoftext|>


<commit_before>// Copyright 2013-2019, University of Colorado Boulder

/**
 * A radial gradient that can be passed into the 'fill' or 'stroke' parameters.
 *
 * SVG gradients, see http://www.w3.org/TR/SVG/pservers.html
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

define( require => {
  'use strict';

  const Gradient = require( 'SCENERY/util/Gradient' );
  const inherit = require( 'PHET_CORE/inherit' );
  const scenery = require( 'SCENERY/scenery' );
  const SVGRadialGradient = require( 'SCENERY/display/SVGRadialGradient' );
  const Vector2 = require( 'DOT/Vector2' );

  /**
   * @constructor
   * @extends Gradient
   *
   * TODO: add the ability to specify the color-stops inline. possibly [ [0,color1], [0.5,color2], [1,color3] ]
   *
   * TODO: support Vector2s as p0 and p1
   *
   * @param {number} x0 - X coordinate of the start point (ratio 0) in the local coordinate frame
   * @param {number} y0 - Y coordinate of the start point (ratio 0) in the local coordinate frame
   * @param {number} r0 - Radius of the start point (ratio 0) in the local coordinate frame
   * @param {number} x1 - X coordinate of the end point (ratio 1) in the local coordinate frame
   * @param {number} y1 - Y coordinate of the end point (ratio 1) in the local coordinate frame
   * @param {number} r1 - Radius of the end point (ratio 1) in the local coordinate frame
   */
  function RadialGradient( x0, y0, r0, x1, y1, r1 ) {
    // @public {Vector2}
    this.start = new Vector2( x0, y0 );
    this.end = new Vector2( x1, y1 );

    // @public {number}
    this.startRadius = r0;
    this.endRadius = r1;

    // @public {Vector2} - linear function from radius to point on the line from start to end
    this.focalPoint = this.start.plus( this.end.minus( this.start ).times( this.startRadius / ( this.startRadius - this.endRadius ) ) );

    // @public {boolean}
    this.startIsLarger = this.startRadius > this.endRadius;

    // @public {Vector2}
    this.largePoint = this.startIsLarger ? this.start : this.end;

    // @public {number}
    this.maxRadius = Math.max( this.startRadius, this.endRadius );
    this.minRadius = Math.min( this.startRadius, this.endRadius );

    // make sure that the focal point is in both circles. SVG doesn't support rendering outside of them
    if ( this.startRadius >= this.endRadius ) {
      assert && assert( this.focalPoint.minus( this.start ).magnitude <= this.startRadius );
    }
    else {
      assert && assert( this.focalPoint.minus( this.end ).magnitude <= this.endRadius );
    }

    Gradient.call( this );
  }

  scenery.register( 'RadialGradient', RadialGradient );

  inherit( Gradient, RadialGradient, {

    isRadialGradient: true,

    /**
     * Returns a fresh gradient given the starting parameters
     * @protected
     * @override
     *
     * @returns {CanvasGradient}
     */
    createCanvasGradient: function() {
      // use the global scratch canvas instead of creating a new Canvas
      return scenery.scratchContext.createRadialGradient( this.start.x, this.start.y, this.startRadius, this.end.x, this.end.y, this.endRadius );
    },

    /**
     * Creates an SVG paint object for creating/updating the SVG equivalent definition.
     * @public
     *
     * @param {SVGBlock} svgBlock
     * @returns {SVGGradient|SVGPattern}
     */
    createSVGPaint: function( svgBlock ) {
      return SVGRadialGradient.createFromPool( svgBlock, this );
    },

    /**
     * Returns stops suitable for direct SVG use.
     * @public
     * @override
     *
     * NOTE: SVG has certain stop requirements, so we need to remap/reverse in some cases.
     *
     * @returns {Array.<{ ratio: {number}, stop: {Color|string|Property.<Color|string|null>|null} }>}
     */
    getSVGStops: function() {
      const startIsLarger = this.startIsLarger;
      const maxRadius = this.maxRadius;
      const minRadius = this.minRadius;

      //TODO: replace with dot.Util.linear
      // maps x linearly from [a0,b0] => [a1,b1]
      function linearMap( a0, b0, a1, b1, x ) {
        return a1 + ( x - a0 ) * ( b1 - a1 ) / ( b0 - a0 );
      }

      function mapStop( stop ) {
        // flip the stops if the start has a larger radius
        let ratio = startIsLarger ? 1 - stop.ratio : stop.ratio;

        // scale the stops properly if the smaller radius isn't 0
        if ( minRadius > 0 ) {
          // scales our ratio from [0,1] => [minRadius/maxRadius,0]
          ratio = linearMap( 0, 1, minRadius / maxRadius, 1, ratio );
        }

        return {
          ratio: ratio,
          color: stop.color
        };
      }

      const stops = this.stops.map( mapStop );

      // switch the direction we apply stops in, so that the ratios always are increasing.
      if ( startIsLarger ) {
        stops.reverse();
      }

      return stops;
    },

    toString: function() {
      let result = 'new scenery.RadialGradient( ' + this.start.x + ', ' + this.start.y + ', ' + this.startRadius + ', ' + this.end.x + ', ' + this.end.y + ', ' + this.endRadius + ' )';

      _.each( this.stops, function( stop ) {
        result += '.addColorStop( ' + stop.ratio + ', \'' + stop.color.toString() + '\' )';
      } );

      return result;
    }
  } );

  return RadialGradient;
} );
<commit_msg>Safari workaround for radial gradient issue, see https://github.com/phetsims/sun/issues/526<commit_after>// Copyright 2013-2019, University of Colorado Boulder

/**
 * A radial gradient that can be passed into the 'fill' or 'stroke' parameters.
 *
 * SVG gradients, see http://www.w3.org/TR/SVG/pservers.html
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

define( require => {
  'use strict';

  const Gradient = require( 'SCENERY/util/Gradient' );
  const inherit = require( 'PHET_CORE/inherit' );
  const platform = require( 'PHET_CORE/platform' );
  const scenery = require( 'SCENERY/scenery' );
  const SVGRadialGradient = require( 'SCENERY/display/SVGRadialGradient' );
  const Vector2 = require( 'DOT/Vector2' );

  /**
   * @constructor
   * @extends Gradient
   *
   * TODO: add the ability to specify the color-stops inline. possibly [ [0,color1], [0.5,color2], [1,color3] ]
   *
   * TODO: support Vector2s as p0 and p1
   *
   * @param {number} x0 - X coordinate of the start point (ratio 0) in the local coordinate frame
   * @param {number} y0 - Y coordinate of the start point (ratio 0) in the local coordinate frame
   * @param {number} r0 - Radius of the start point (ratio 0) in the local coordinate frame
   * @param {number} x1 - X coordinate of the end point (ratio 1) in the local coordinate frame
   * @param {number} y1 - Y coordinate of the end point (ratio 1) in the local coordinate frame
   * @param {number} r1 - Radius of the end point (ratio 1) in the local coordinate frame
   */
  function RadialGradient( x0, y0, r0, x1, y1, r1 ) {
    // @public {Vector2}
    this.start = new Vector2( x0, y0 );
    this.end = new Vector2( x1, y1 );

    // If we are using Safari, we need to work around incorrect gradient handling for now,
    // see https://github.com/phetsims/sun/issues/526
    if ( platform.safari ) {
      var x = ( x0 + x1 ) / 2;
      var y = ( y0 + y1 ) / 2;
      this.start.x = x;
      this.start.y = y;
      this.end.x = x;
      this.end.y = y;
    }

    // @public {number}
    this.startRadius = r0;
    this.endRadius = r1;

    // @public {Vector2} - linear function from radius to point on the line from start to end
    this.focalPoint = this.start.plus( this.end.minus( this.start ).times( this.startRadius / ( this.startRadius - this.endRadius ) ) );

    // @public {boolean}
    this.startIsLarger = this.startRadius > this.endRadius;

    // @public {Vector2}
    this.largePoint = this.startIsLarger ? this.start : this.end;

    // @public {number}
    this.maxRadius = Math.max( this.startRadius, this.endRadius );
    this.minRadius = Math.min( this.startRadius, this.endRadius );

    // make sure that the focal point is in both circles. SVG doesn't support rendering outside of them
    if ( this.startRadius >= this.endRadius ) {
      assert && assert( this.focalPoint.minus( this.start ).magnitude <= this.startRadius );
    }
    else {
      assert && assert( this.focalPoint.minus( this.end ).magnitude <= this.endRadius );
    }

    Gradient.call( this );
  }

  scenery.register( 'RadialGradient', RadialGradient );

  inherit( Gradient, RadialGradient, {

    isRadialGradient: true,

    /**
     * Returns a fresh gradient given the starting parameters
     * @protected
     * @override
     *
     * @returns {CanvasGradient}
     */
    createCanvasGradient: function() {
      // use the global scratch canvas instead of creating a new Canvas
      return scenery.scratchContext.createRadialGradient( this.start.x, this.start.y, this.startRadius, this.end.x, this.end.y, this.endRadius );
    },

    /**
     * Creates an SVG paint object for creating/updating the SVG equivalent definition.
     * @public
     *
     * @param {SVGBlock} svgBlock
     * @returns {SVGGradient|SVGPattern}
     */
    createSVGPaint: function( svgBlock ) {
      return SVGRadialGradient.createFromPool( svgBlock, this );
    },

    /**
     * Returns stops suitable for direct SVG use.
     * @public
     * @override
     *
     * NOTE: SVG has certain stop requirements, so we need to remap/reverse in some cases.
     *
     * @returns {Array.<{ ratio: {number}, stop: {Color|string|Property.<Color|string|null>|null} }>}
     */
    getSVGStops: function() {
      const startIsLarger = this.startIsLarger;
      const maxRadius = this.maxRadius;
      const minRadius = this.minRadius;

      //TODO: replace with dot.Util.linear
      // maps x linearly from [a0,b0] => [a1,b1]
      function linearMap( a0, b0, a1, b1, x ) {
        return a1 + ( x - a0 ) * ( b1 - a1 ) / ( b0 - a0 );
      }

      function mapStop( stop ) {
        // flip the stops if the start has a larger radius
        let ratio = startIsLarger ? 1 - stop.ratio : stop.ratio;

        // scale the stops properly if the smaller radius isn't 0
        if ( minRadius > 0 ) {
          // scales our ratio from [0,1] => [minRadius/maxRadius,0]
          ratio = linearMap( 0, 1, minRadius / maxRadius, 1, ratio );
        }

        return {
          ratio: ratio,
          color: stop.color
        };
      }

      const stops = this.stops.map( mapStop );

      // switch the direction we apply stops in, so that the ratios always are increasing.
      if ( startIsLarger ) {
        stops.reverse();
      }

      return stops;
    },

    toString: function() {
      let result = 'new scenery.RadialGradient( ' + this.start.x + ', ' + this.start.y + ', ' + this.startRadius + ', ' + this.end.x + ', ' + this.end.y + ', ' + this.endRadius + ' )';

      _.each( this.stops, function( stop ) {
        result += '.addColorStop( ' + stop.ratio + ', \'' + stop.color.toString() + '\' )';
      } );

      return result;
    }
  } );

  return RadialGradient;
} );
<|endoftext|>


<commit_before>class DataScenarioConfig {
  constructor(scenarios) {
    this.scenarios = scenarios;
  }

  /**
     * Declares that test throws error.
     * Can pass an Error object or regex matching expected message.
     *
     * @param {*} err
     */
  throws(err) {
    this.scenarios.forEach(scenario => scenario.throws(err));
    return this;
  }

  /**
     * Declares that test should fail.
     * If test passes - throws an error.
     * Can pass an Error object or regex matching expected message.
     *
     */
  fails() {
    this.scenarios.forEach(scenario => scenario.fails());
    return this;
  }

  /**
     * Retry this test for x times
     *
     * @param {*} retries
     */
  retry(retries) {
    this.scenarios.forEach(scenario => scenario.retry(retries));
    return this;
  }

  /**
     * Set timeout for this test
     * @param {*} timeout
     */
  timeout(timeout) {
    this.scenarios.forEach(scenario => scenario.timeout(timeout));
    return this;
  }

  /**
     * Configures a helper.
     * Helper name can be omitted and values will be applied to first helper.
     */
  config(helper, obj) {
    this.scenarios.forEach(scenario => scenario.config(helper, obj));
    return this;
  }

  /**
     * Append a tag name to scenario title
     * @param {*} tagName
     */
  tag(tagName) {
    this.scenarios.forEach(scenario => scenario.tag(tagName));
    return this;
  }
}

module.exports = DataScenarioConfig;
<commit_msg> Fix:Data().Scenario().injectDependencies() is not a function (#1794)<commit_after>class DataScenarioConfig {
  constructor(scenarios) {
    this.scenarios = scenarios;
  }

  /**
     * Declares that test throws error.
     * Can pass an Error object or regex matching expected message.
     *
     * @param {*} err
     */
  throws(err) {
    this.scenarios.forEach(scenario => scenario.throws(err));
    return this;
  }

  /**
     * Declares that test should fail.
     * If test passes - throws an error.
     * Can pass an Error object or regex matching expected message.
     *
     */
  fails() {
    this.scenarios.forEach(scenario => scenario.fails());
    return this;
  }

  /**
     * Retry this test for x times
     *
     * @param {*} retries
     */
  retry(retries) {
    this.scenarios.forEach(scenario => scenario.retry(retries));
    return this;
  }

  /**
     * Set timeout for this test
     * @param {*} timeout
     */
  timeout(timeout) {
    this.scenarios.forEach(scenario => scenario.timeout(timeout));
    return this;
  }

  /**
     * Configures a helper.
     * Helper name can be omitted and values will be applied to first helper.
     */
  config(helper, obj) {
    this.scenarios.forEach(scenario => scenario.config(helper, obj));
    return this;
  }

  /**
     * Append a tag name to scenario title
     * @param {*} tagName
     */
  tag(tagName) {
    this.scenarios.forEach(scenario => scenario.tag(tagName));
    return this;
  }

  /**
     * Pass in additional objects to inject into test
     * @param {*} obj
     */
  inject(obj) {
    this.scenarios.forEach(scenario => scenario.inject(obj));
    return this;
  }

  /**
     * Dynamically injects dependencies, see https://codecept.io/pageobjects/#dynamic-injection
     * @param {*} dependencies
     */
  injectDependencies(dependencies) {
    this.scenarios.forEach(scenario => scenario.injectDependencies(dependencies));
    return this;
  }
}

module.exports = DataScenarioConfig;
<|endoftext|>


<commit_before><commit_msg>update functions definition<commit_after><|endoftext|>


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

/**
 * Main entry point for PhET translation web app. This is where ExpressJS gets configured and the routes are set up.
 *
 * @author John Blanco
 * @author Aaron Davis
 */

/* jslint node: true */
'use strict';

// modules
var assert = require( 'assert' );
var express = require( 'express' );
var doT = require( 'express-dot' );
var parseArgs = require( 'minimist' );
var winston = require( 'winston' );
var bodyParser = require( 'body-parser' );
var query = require( 'pg-query' );
var fs = require( 'fs' );

// constants
var LISTEN_PORT = 16372;
var PREFERENCES_FILE;

/*
 * When running on simian or figaro, rosetta is run under user "phet-admin". However, "process.env.HOME" will get
 * the user who is starting the process's home directory, not phet-admin's home directory, therefore we need to use
 * a different approach to get the home directory.
 */
if ( !/^win/.test( process.platform ) ) {
  var passwdUser = require( 'passwd-user' );
  PREFERENCES_FILE = passwdUser.sync( process.getuid() ).homedir + '/.phet/build-local.json';
}
else {
  PREFERENCES_FILE = process.env.HOME + '/.phet/build-local.json';
}

// ensure that the preferences file exists and has the required fields
assert( fs.existsSync( PREFERENCES_FILE ), 'missing preferences file ' + PREFERENCES_FILE );
var preferences = require( PREFERENCES_FILE );
assert( preferences.githubUsername, 'githubUsername is missing from ' + PREFERENCES_FILE );
assert( preferences.githubPassword, 'githubPassword is missing from ' + PREFERENCES_FILE );
assert( preferences.buildServerAuthorizationCode, 'buildServerAuthorizationCode is missing from ' + PREFERENCES_FILE );
assert( preferences.rosettaSessionSecret, 'rosettaSessionSecret is missing from ' + PREFERENCES_FILE +
                                          '. To set this up for local testing add any string as the value for "rosettaSessionSecret"' );

preferences.productionServerURL = preferences.productionServerURL || 'https://phet.colorado.edu';
preferences.productionServerName = preferences.productionServerName || 'figaro.colorado.edu';

/*
 * Add "babelBranch": "tests" in build-local.json for rosetta testing, so that commits will change the 'tests' branch
 * of babel instead of master. Make sure to checkout the tests branch in babel on the server as well.
 */
preferences.babelBranch = preferences.babelBranch || 'master';

assert( preferences.babelBranch === 'master' || preferences.babelBranch === 'tests', 'BRANCH must be set to either master or tests' );

// initialize globals
global.preferences = preferences;

// must be required after global.preferences has been initialized
var routes = require( __dirname + '/js/routes' );

// configure postgres connection
if ( preferences.pgConnectionString ) {
  query.connectionParameters = preferences.pgConnectionString;
}
else {
  query.connectionParameters = 'postgresql://localhost/rosetta';
}

// Handle command line input
// First 2 args provide info about executables, ignore
var commandLineArgs = process.argv.slice( 2 );

var parsedCommandLineOptions = parseArgs( commandLineArgs, {
  boolean: true
} );

var defaultOptions = {

  // options for supporting help, currently no other options are supported, but this might change
  help: false,
  h: false
};

for ( var key in parsedCommandLineOptions ) {
  if ( key !== '_' && parsedCommandLineOptions.hasOwnProperty( key ) && !defaultOptions.hasOwnProperty( key ) ) {
    console.log( 'Unrecognized option: ' + key );
    console.log( 'try --help for usage information.' );
    return;
  }
}

// If help flag, print help and usage info
if ( parsedCommandLineOptions.hasOwnProperty( 'help' ) || parsedCommandLineOptions.hasOwnProperty( 'h' ) ) {
  console.log( 'Usage:' );
  console.log( '  node rosetta.js [options]' );
  console.log( '' );
  console.log( 'Options:' );
  console.log(
    '  --help (print usage and exit)\n' +
    '    type: bool  default: false\n' );
  return;
}

// add timestamps
winston.remove( winston.transports.Console );
winston.add( winston.transports.Console, { 'timestamp': function(){
  var date = new Date();
  return date.toLocaleString() + ' GMT-' + date.getTimezoneOffset() / 60;
} } );

// log startup message
winston.log( 'info', '---- rosetta starting up, about to create ExpressJS app ----' );

// Create and configure the ExpressJS app
var app = express();
app.set( 'views', __dirname + '/html/views' );
app.set( 'view engine', 'dot' );
app.engine( 'html', doT.__express );

// set static directories for css, img, and js
app.use( '/translate/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/img', express.static( __dirname + '/public/img' ) );
app.use( '/translate/js', express.static( __dirname + '/public/js' ) );

// need cookieParser middleware before we can do anything with cookies
app.use( express.cookieParser() );
app.use( express.session( { secret: preferences.rosettaSessionSecret } ) );
app.use( bodyParser.json() );
app.use( bodyParser.urlencoded( { extended: false } ) );

//----------------------------------------------------------------------------
// Set up the routes.  The order matters.
//----------------------------------------------------------------------------

// route that checks whether the user is logged in
app.get( '/translate*', routes.checkForValidSession );

// landing page for the translation utility
app.get( '/translate', routes.chooseSimulationAndLanguage );

// route for translating a specific sim to a specific language
app.get( '/translate/sim/:simName?/:targetLocale?', routes.translateSimulation );
app.post( '/translate/sim/save/:simName?/:targetLocale?', routes.saveStrings );
app.post( '/translate/sim/:simName?/:targetLocale?', routes.submitStrings );

// route for extracting strings from a sim
app.get( '/translate/extractStrings', routes.extractStringsAPI );

app.get( '/translate/logout', routes.logout );

// fall through route
app.get( '/*', routes.pageNotFound );

// start the server
app.listen( LISTEN_PORT, function() { winston.log( 'info', 'Listening on port ' + LISTEN_PORT ); } );
<commit_msg>simplified timestamp, since it already seems to include the GMT offset when run on figaro<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Main entry point for PhET translation web app. This is where ExpressJS gets configured and the routes are set up.
 *
 * @author John Blanco
 * @author Aaron Davis
 */

/* jslint node: true */
'use strict';

// modules
var assert = require( 'assert' );
var express = require( 'express' );
var doT = require( 'express-dot' );
var parseArgs = require( 'minimist' );
var winston = require( 'winston' );
var bodyParser = require( 'body-parser' );
var query = require( 'pg-query' );
var fs = require( 'fs' );

// constants
var LISTEN_PORT = 16372;
var PREFERENCES_FILE;

/*
 * When running on simian or figaro, rosetta is run under user "phet-admin". However, "process.env.HOME" will get
 * the user who is starting the process's home directory, not phet-admin's home directory, therefore we need to use
 * a different approach to get the home directory.
 */
if ( !/^win/.test( process.platform ) ) {
  var passwdUser = require( 'passwd-user' );
  PREFERENCES_FILE = passwdUser.sync( process.getuid() ).homedir + '/.phet/build-local.json';
}
else {
  PREFERENCES_FILE = process.env.HOME + '/.phet/build-local.json';
}

// ensure that the preferences file exists and has the required fields
assert( fs.existsSync( PREFERENCES_FILE ), 'missing preferences file ' + PREFERENCES_FILE );
var preferences = require( PREFERENCES_FILE );
assert( preferences.githubUsername, 'githubUsername is missing from ' + PREFERENCES_FILE );
assert( preferences.githubPassword, 'githubPassword is missing from ' + PREFERENCES_FILE );
assert( preferences.buildServerAuthorizationCode, 'buildServerAuthorizationCode is missing from ' + PREFERENCES_FILE );
assert( preferences.rosettaSessionSecret, 'rosettaSessionSecret is missing from ' + PREFERENCES_FILE +
                                          '. To set this up for local testing add any string as the value for "rosettaSessionSecret"' );

preferences.productionServerURL = preferences.productionServerURL || 'https://phet.colorado.edu';
preferences.productionServerName = preferences.productionServerName || 'figaro.colorado.edu';

/*
 * Add "babelBranch": "tests" in build-local.json for rosetta testing, so that commits will change the 'tests' branch
 * of babel instead of master. Make sure to checkout the tests branch in babel on the server as well.
 */
preferences.babelBranch = preferences.babelBranch || 'master';

assert( preferences.babelBranch === 'master' || preferences.babelBranch === 'tests', 'BRANCH must be set to either master or tests' );

// initialize globals
global.preferences = preferences;

// must be required after global.preferences has been initialized
var routes = require( __dirname + '/js/routes' );

// configure postgres connection
if ( preferences.pgConnectionString ) {
  query.connectionParameters = preferences.pgConnectionString;
}
else {
  query.connectionParameters = 'postgresql://localhost/rosetta';
}

// Handle command line input
// First 2 args provide info about executables, ignore
var commandLineArgs = process.argv.slice( 2 );

var parsedCommandLineOptions = parseArgs( commandLineArgs, {
  boolean: true
} );

var defaultOptions = {

  // options for supporting help, currently no other options are supported, but this might change
  help: false,
  h: false
};

for ( var key in parsedCommandLineOptions ) {
  if ( key !== '_' && parsedCommandLineOptions.hasOwnProperty( key ) && !defaultOptions.hasOwnProperty( key ) ) {
    console.log( 'Unrecognized option: ' + key );
    console.log( 'try --help for usage information.' );
    return;
  }
}

// If help flag, print help and usage info
if ( parsedCommandLineOptions.hasOwnProperty( 'help' ) || parsedCommandLineOptions.hasOwnProperty( 'h' ) ) {
  console.log( 'Usage:' );
  console.log( '  node rosetta.js [options]' );
  console.log( '' );
  console.log( 'Options:' );
  console.log(
    '  --help (print usage and exit)\n' +
    '    type: bool  default: false\n' );
  return;
}

// add timestamps
winston.remove( winston.transports.Console );
winston.add( winston.transports.Console, { 'timestamp': function(){
  var date = new Date();
  return date.toLocaleString();
} } );

// log startup message
winston.log( 'info', '---- rosetta starting up, about to create ExpressJS app ----' );

// Create and configure the ExpressJS app
var app = express();
app.set( 'views', __dirname + '/html/views' );
app.set( 'view engine', 'dot' );
app.engine( 'html', doT.__express );

// set static directories for css, img, and js
app.use( '/translate/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/img', express.static( __dirname + '/public/img' ) );
app.use( '/translate/js', express.static( __dirname + '/public/js' ) );

// need cookieParser middleware before we can do anything with cookies
app.use( express.cookieParser() );
app.use( express.session( { secret: preferences.rosettaSessionSecret } ) );
app.use( bodyParser.json() );
app.use( bodyParser.urlencoded( { extended: false } ) );

//----------------------------------------------------------------------------
// Set up the routes.  The order matters.
//----------------------------------------------------------------------------

// route that checks whether the user is logged in
app.get( '/translate*', routes.checkForValidSession );

// landing page for the translation utility
app.get( '/translate', routes.chooseSimulationAndLanguage );

// route for translating a specific sim to a specific language
app.get( '/translate/sim/:simName?/:targetLocale?', routes.translateSimulation );
app.post( '/translate/sim/save/:simName?/:targetLocale?', routes.saveStrings );
app.post( '/translate/sim/:simName?/:targetLocale?', routes.submitStrings );

// route for extracting strings from a sim
app.get( '/translate/extractStrings', routes.extractStringsAPI );

app.get( '/translate/logout', routes.logout );

// fall through route
app.get( '/*', routes.pageNotFound );

// start the server
app.listen( LISTEN_PORT, function() { winston.log( 'info', 'Listening on port ' + LISTEN_PORT ); } );
<|endoftext|>


<commit_before>import React from 'react'
import Helmet from 'react-helmet'
import './speaker-page.scss'

const SpeakerPage = ({
  title: name,
  company,
  companyWebsite,
  slug,
  talk,
  body,
  bio,
  twitter,
  github,
  website,
  path,
}) => {
  // eslint-disable-next-line
  const profilePic = require(`../images/speakers/${slug}.jpg`)

  return (

      <Helmet
        meta={[
          {
            property: 'og:title',
            content: `${name} - JSConf Iceland 2018`,
          },
          { property: 'og:url', content: `https://2018.jsconf.is${path}/` },
          { property: 'og:description', content: bio },
          {
            property: 'og:image',
            content: `https://2018.jsconf.is/assets/social/speakers/${
              slug
            }.jpg`,
          },
          {
            property: 'og:image:width',
            content: '1200',
          },
          {
            property: 'og:image:height',
            content: '630',
          },
          {
            name: 'twitter:title',
            content: `${name} - JSConf Iceland 2018`,
          },
          {
            name: 'twitter:image',
            content: `https://2018.jsconf.is/assets/social/speakers/${
              slug
            }.jpg`,
          },
          { name: 'twitter:description', content: bio },
        ]}
      />

{name}

          {company &&
            (companyWebsite ? (

                {company}

            ) : (
              {company}
            ))}

{talk}

About {name}

            {bio &&
{bio}
}
            {(website || twitter || github) && (

                  Find {name.split(' ')[0]} online at:

                  {website && (

                        {website.replace(/^(https?:)?\/\//, '')}

                  )}
                  {twitter && (

                      <a href={`https://twitter.com/${twitter}`}>
                        twitter.com/{twitter}

                  )}
                  {github && (

                      <a href={`https://github.com/${github}`}>
                        github.com/{github}

                  )}

            )}

  )
}

SpeakerPage.propTypes = {
  title: React.PropTypes.string,
  path: React.PropTypes.string,
  bio: React.PropTypes.string,
  slug: React.PropTypes.string,
  body: React.PropTypes.string,
  company: React.PropTypes.string,
  companyWebsite: React.PropTypes.string,
  twitter: React.PropTypes.string,
  github: React.PropTypes.string,
  website: React.PropTypes.string,
  talk: React.PropTypes.node,
}

export default SpeakerPage
<commit_msg>Remove empty blocks<commit_after>import React from 'react'
import Helmet from 'react-helmet'
import './speaker-page.scss'

const SpeakerPage = ({
  title: name,
  company,
  companyWebsite,
  slug,
  talk,
  body,
  bio,
  twitter,
  github,
  website,
  path,
}) => {
  // eslint-disable-next-line
  const profilePic = require(`../images/speakers/${slug}.jpg`)

  return (

      <Helmet
        meta={[
          {
            property: 'og:title',
            content: `${name} - JSConf Iceland 2018`,
          },
          { property: 'og:url', content: `https://2018.jsconf.is${path}/` },
          { property: 'og:description', content: bio },
          {
            property: 'og:image',
            content: `https://2018.jsconf.is/assets/social/speakers/${
              slug
            }.jpg`,
          },
          {
            property: 'og:image:width',
            content: '1200',
          },
          {
            property: 'og:image:height',
            content: '630',
          },
          {
            name: 'twitter:title',
            content: `${name} - JSConf Iceland 2018`,
          },
          {
            name: 'twitter:image',
            content: `https://2018.jsconf.is/assets/social/speakers/${
              slug
            }.jpg`,
          },
          { name: 'twitter:description', content: bio },
        ]}
      />

{name}

          {company &&
            (companyWebsite ? (

                {company}

            ) : (
              {company}
            ))}

      {body && (

{talk}

      )}

            {bio && (

About {name}

                {bio &&
{bio}
}

            )}
            {(website || twitter || github) && (

                  Find {name.split(' ')[0]} online at:

                  {website && (

                        {website.replace(/^(https?:)?\/\//, '')}

                  )}
                  {twitter && (

                      <a href={`https://twitter.com/${twitter}`}>
                        twitter.com/{twitter}

                  )}
                  {github && (

                      <a href={`https://github.com/${github}`}>
                        github.com/{github}

                  )}

            )}

  )
}

SpeakerPage.propTypes = {
  title: React.PropTypes.string,
  path: React.PropTypes.string,
  bio: React.PropTypes.string,
  slug: React.PropTypes.string,
  body: React.PropTypes.string,
  company: React.PropTypes.string,
  companyWebsite: React.PropTypes.string,
  twitter: React.PropTypes.string,
  github: React.PropTypes.string,
  website: React.PropTypes.string,
  talk: React.PropTypes.node,
}

export default SpeakerPage
<|endoftext|>