<commit_before>import Vue from 'vue'
import Vuex from 'vuex'
import * as api from './api'

Vue.use(Vuex)

const shipSizes = {
  carrier: 5,
  battleship: 4,
  cruiser: 3,
  submarine: 3,
  destroyer: 2,
}

export default new Vuex.Store({
  state: {
    player: {
      ID: undefined,
      nickname: 'anonymous',
      ships: {
        carrier: {},
        battleship: {},
        cruiser: {},
        submarine: {},
        destroyer: {},
      },
      bombs: [],
    },
    opponent: {
      nickname: 'anonymous',
      grid: [],
    },
    game: {
      ID: undefined,
    },
  },
  mutations: {
    setGameID(state, ID) {
      Vue.set(state.game, 'ID', ID)
    },
    setPlayerID(state, ID) {
      Vue.set(state.player, 'ID', ID)
    },
    setPlayerNickname(state, nickname) {
      if (nickname === '') {
        Vue.set(state.player, 'nickname', 'Anonymous')
      } else {
        Vue.set(state.player, 'nickname', nickname)
      }
    },
    setOpponentNickname(state, nickname) {
      if (nickname === '') {
        Vue.set(state.opponent, 'nickname', 'Anonymous')
      } else {
        Vue.set(state.opponent, 'nickname', nickname)
      }
    },
    setShip(state, payload) {
      // TODO: don't allow invalid ships to be committed
      const ship = state.player.ships[payload.shipName]
      if (ship !== undefined) {
        Vue.set(ship, 'start', payload.position)
        let { x, y } = payload.position
        if (payload.rotation === 'h') {
          x += shipSizes[payload.shipName] - 1
        } else {
          y += shipSizes[payload.shipName] - 1
        }
        Vue.set(ship, 'end', { x, y })
      }
    },
    setBombedPositions(state, bombs) {
      Vue.set(state.player, 'bombs', bombs)
    },
    setOpponentShips(state, grid) {
      Vue.set(state.opponent, 'grid', grid)
    },
  },
  actions: {
    newGame({ commit, state }, { okCallback, errorCallback }) {
      api.newGame(
        state.player.nickname,
        ([gameID, playerID]) => {
          commit('setGameID', gameID)
          commit('setPlayerID', playerID)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error creating new game: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    waitForPlayer({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForPlayer(
        state.player.ID,
        () => {
          dispatch('getOpponentNickname', {})
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for player, trying again...')
            dispatch('waitForPlayer', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for player: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    joinGame({ dispatch, commit, state }, { gameID, okCallback, errorCallback }) {
      api.joinGame(
        gameID,
        state.player.nickname,
        (playerID) => {
          commit('setGameID', gameID)
          commit('setPlayerID', playerID)
          dispatch('getOpponentNickname', {})
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error joining game: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    getOpponentNickname({ commit, state }, { okCallback, errorCallback }) {
      api.getOpponentNickname(
        state.player.ID,
        (nickname) => {
          commit('setOpponentNickname', nickname)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error getting nickname: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    submitGrid({ state }, { grid, okCallback, errorCallback }) {
      api.submitGrid(
        state.player.ID,
        grid,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error submitting grid: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    waitForYourTurn({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForYourTurn(
        state.player.ID,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for turn, trying again...')
            dispatch('waitForYourTurn', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for turn: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    bombPosition({ state }, { pos, okCallback, errorCallback }) {
      const [x, y] = pos
      api.bombPosition(
        state.player.ID,
        x,
        y,
        (bombSuccess) => {
          if (typeof okCallback === 'function') okCallback(bombSuccess)
        },
        (e) => {
          console.error(`Error bombing position [${x}, ${y}] : ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    getBombedPositions({ commit, state }, { okCallback, errorCallback }) {
      api.getBombedPositions(
        state.player.ID,
        (bombedPositions) => {
          commit('setBombedPositions', bombedPositions)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error getting bombed positions: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    // Note: not actually used
    waitForGameEnd({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForGameEnd(
        state.player.ID,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for game end, trying again...')
            dispatch('waitForGameEnd', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for game end: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    getGameEnd({ commit, state }, { okCallback, errorCallback }) {
      api.getGameEnd(
        state.player.ID,
        ({ game_over: gameOver, won, grid }) => {
          if (gameOver) {
            commit('setOpponentShips', grid)
          }
          if (typeof okCallback === 'function') okCallback({ gameOver, won })
        },
        (e) => {
          console.error(`Error getting game end: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
  },
})
<commit_msg>Fix inconsistency in default nickname<commit_after>import Vue from 'vue'
import Vuex from 'vuex'
import * as api from './api'

Vue.use(Vuex)

const shipSizes = {
  carrier: 5,
  battleship: 4,
  cruiser: 3,
  submarine: 3,
  destroyer: 2,
}

export default new Vuex.Store({
  state: {
    player: {
      ID: undefined,
      nickname: 'Anonymous',
      ships: {
        carrier: {},
        battleship: {},
        cruiser: {},
        submarine: {},
        destroyer: {},
      },
      bombs: [],
    },
    opponent: {
      nickname: 'Anonymous',
      grid: [],
    },
    game: {
      ID: undefined,
    },
  },
  mutations: {
    setGameID(state, ID) {
      Vue.set(state.game, 'ID', ID)
    },
    setPlayerID(state, ID) {
      Vue.set(state.player, 'ID', ID)
    },
    setPlayerNickname(state, nickname) {
      if (nickname === '') {
        Vue.set(state.player, 'nickname', 'Anonymous')
      } else {
        Vue.set(state.player, 'nickname', nickname)
      }
    },
    setOpponentNickname(state, nickname) {
      if (nickname === '') {
        Vue.set(state.opponent, 'nickname', 'Anonymous')
      } else {
        Vue.set(state.opponent, 'nickname', nickname)
      }
    },
    setShip(state, payload) {
      // TODO: don't allow invalid ships to be committed
      const ship = state.player.ships[payload.shipName]
      if (ship !== undefined) {
        Vue.set(ship, 'start', payload.position)
        let { x, y } = payload.position
        if (payload.rotation === 'h') {
          x += shipSizes[payload.shipName] - 1
        } else {
          y += shipSizes[payload.shipName] - 1
        }
        Vue.set(ship, 'end', { x, y })
      }
    },
    setBombedPositions(state, bombs) {
      Vue.set(state.player, 'bombs', bombs)
    },
    setOpponentShips(state, grid) {
      Vue.set(state.opponent, 'grid', grid)
    },
  },
  actions: {
    newGame({ commit, state }, { okCallback, errorCallback }) {
      api.newGame(
        state.player.nickname,
        ([gameID, playerID]) => {
          commit('setGameID', gameID)
          commit('setPlayerID', playerID)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error creating new game: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    waitForPlayer({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForPlayer(
        state.player.ID,
        () => {
          dispatch('getOpponentNickname', {})
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for player, trying again...')
            dispatch('waitForPlayer', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for player: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    joinGame({ dispatch, commit, state }, { gameID, okCallback, errorCallback }) {
      api.joinGame(
        gameID,
        state.player.nickname,
        (playerID) => {
          commit('setGameID', gameID)
          commit('setPlayerID', playerID)
          dispatch('getOpponentNickname', {})
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error joining game: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    getOpponentNickname({ commit, state }, { okCallback, errorCallback }) {
      api.getOpponentNickname(
        state.player.ID,
        (nickname) => {
          commit('setOpponentNickname', nickname)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error getting nickname: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    submitGrid({ state }, { grid, okCallback, errorCallback }) {
      api.submitGrid(
        state.player.ID,
        grid,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error submitting grid: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    waitForYourTurn({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForYourTurn(
        state.player.ID,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for turn, trying again...')
            dispatch('waitForYourTurn', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for turn: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    bombPosition({ state }, { pos, okCallback, errorCallback }) {
      const [x, y] = pos
      api.bombPosition(
        state.player.ID,
        x,
        y,
        (bombSuccess) => {
          if (typeof okCallback === 'function') okCallback(bombSuccess)
        },
        (e) => {
          console.error(`Error bombing position [${x}, ${y}] : ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    getBombedPositions({ commit, state }, { okCallback, errorCallback }) {
      api.getBombedPositions(
        state.player.ID,
        (bombedPositions) => {
          commit('setBombedPositions', bombedPositions)
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          console.error(`Error getting bombed positions: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
    // Note: not actually used
    waitForGameEnd({ dispatch, state }, { okCallback, errorCallback }) {
      api.waitForGameEnd(
        state.player.ID,
        () => {
          if (typeof okCallback === 'function') okCallback()
        },
        (e) => {
          if (e.code === -1000) {
            // timed out
            console.warn('Timed out waiting for game end, trying again...')
            dispatch('waitForGameEnd', { okCallback, errorCallback })
          } else {
            console.error(`Error waiting for game end: ${e.message}`)
            if (typeof errorCallback === 'function') errorCallback()
          }
        },
      )
    },
    getGameEnd({ commit, state }, { okCallback, errorCallback }) {
      api.getGameEnd(
        state.player.ID,
        ({ game_over: gameOver, won, grid }) => {
          if (gameOver) {
            commit('setOpponentShips', grid)
          }
          if (typeof okCallback === 'function') okCallback({ gameOver, won })
        },
        (e) => {
          console.error(`Error getting game end: ${e.message}`)
          if (typeof errorCallback === 'function') errorCallback()
        },
      )
    },
  },
})
<|endoftext|>


<commit_before>var Backbone = require('backbone'),
    template = require("../templates/map.hbs"),
    service = require('../models/service'),
    servicetype = require('../models/servicetype'),
    hashtrack = require('hashtrack'),
    i18n = require('i18next-client'),
    search = require('../search')
;

module.exports = Backbone.View.extend({
    initialize: function(){
        var self = this;
        this.query = "";
        this.services = new service.PublicServices();
        this.servicetypes = new servicetype.ServiceTypes();
        //this.render();
    },

    render: function() {
        var $el = this.$el;

        var self=this;
        this.$el.html(template({
            services: this.services,
            query: hashtrack.getVar('q'),
        }));

        var $scv = this.$el.find('#search_controls');
        var SearchControlView = new search.SearchControls({
            $el: $scv,
        });
        SearchControlView.render();

        function initialize() {
            var mapOptions = {
                center: { lat: 33.8869, lng: 35.5131},
                zoom: 8
            };
            self.map = new google.maps.Map(document.getElementById('map_canvas'),
                mapOptions);
            search.refetchServices(self.query).then(function(){
                self.updateResults();
            });
        }

        initialize();
    },

    updateResults: function() {
        var self = this;
        var bounds = new google.maps.LatLngBounds();
        var services = search.services.data();
        $.each(services, function() {
            var service = this;

            // this.location, from PostGIS, is in the form POINT(LONG LAT)
            // Google Maps API's LatLng() constructor expects LAT, LONG parameters
            var long_lat_str = /(-?\d+\.\d+) (-?\d+\.\d+)/.exec(this.location);
            if (long_lat_str) {
                var myLatlng = new google.maps.LatLng(long_lat_str[2], long_lat_str[1]);
                bounds.extend(myLatlng);
                var marker = new google.maps.Marker({
                    position: myLatlng,
                    title: this.name,
                    icon: new google.maps.MarkerImage(
                        this.servicetype.icon_url,
                        null,
                        null,
                        new google.maps.Point(12, 12),
                        new google.maps.Size(24, 24)
                    ),
                });
                window.marker = marker;
                marker.setMap(self.map);
                google.maps.event.addListener(marker, 'click', function() {
                    location.hash = '#/service/' + service.id;
                })
            }
        });
        self.map.fitBounds(bounds);
        var zoom = self.map.getZoom();
        if (zoom > 10) {
            self.map.setZoom(10);
        }
    },

    events: {
        "search": function(_, query) {
            var self = this;
            search.refetchServices().then(function(){
                self.updateResults();
            })
        },
        "input input.query": function(e) {
            var query = $(e.target).val();
            hashtrack.setVar('q', query);
        },
        "change .query-service-type": function(e) {
            hashtrack.setVar('t', $(e.target).val());
        },
        "input keyup": function(e) {
            if (e.keyCode === 13) {
                return false;
            }
        },
    }
})
<commit_msg>390 Remove old markers from map<commit_after>var Backbone = require('backbone'),
    template = require("../templates/map.hbs"),
    service = require('../models/service'),
    servicetype = require('../models/servicetype'),
    hashtrack = require('hashtrack'),
    i18n = require('i18next-client'),
    search = require('../search')
;

module.exports = Backbone.View.extend({
    initialize: function(){
        var self = this;
        this.query = "";
        this.services = new service.PublicServices();
        this.servicetypes = new servicetype.ServiceTypes();
        //this.render();
        this.markers = [];
    },

    render: function() {
        var $el = this.$el;

        var self=this;
        this.$el.html(template({
            services: this.services,
            query: hashtrack.getVar('q'),
        }));

        var $scv = this.$el.find('#search_controls');
        var SearchControlView = new search.SearchControls({
            $el: $scv,
        });
        SearchControlView.render();

        function initialize() {
            var mapOptions = {
                center: { lat: 33.8869, lng: 35.5131},
                zoom: 8
            };
            self.map = new google.maps.Map(document.getElementById('map_canvas'),
                mapOptions);
            search.refetchServices(self.query).then(function(){
                self.updateResults();
            });
        }

        initialize();
    },

    updateResults: function() {
        var self = this;
        $.each(self.markers, function() {
            this.setMap(null);
        });
        self.markers = [];
        var bounds = new google.maps.LatLngBounds();
        var services = search.services.data();
        $.each(services, function() {
            var service = this;

            // this.location, from PostGIS, is in the form POINT(LONG LAT)
            // Google Maps API's LatLng() constructor expects LAT, LONG parameters
            var long_lat_str = /(-?\d+\.\d+) (-?\d+\.\d+)/.exec(this.location);
            if (long_lat_str) {
                var myLatlng = new google.maps.LatLng(long_lat_str[2], long_lat_str[1]);
                bounds.extend(myLatlng);
                var marker = new google.maps.Marker({
                    position: myLatlng,
                    title: this.name,
                    icon: new google.maps.MarkerImage(
                        this.servicetype.icon_url,
                        null,
                        null,
                        new google.maps.Point(12, 12),
                        new google.maps.Size(24, 24)
                    ),
                });
                window.marker = marker;
                marker.setMap(self.map);
                self.markers.push(marker);
                google.maps.event.addListener(marker, 'click', function() {
                    location.hash = '#/service/' + service.id;
                })
            }
        });
        self.map.fitBounds(bounds);
        var zoom = self.map.getZoom();
        if (zoom > 10) {
            self.map.setZoom(10);
        }
    },

    events: {
        "search": function(_, query) {
            var self = this;
            search.refetchServices().then(function(){
                self.updateResults();
            })
        },
        "input input.query": function(e) {
            var query = $(e.target).val();
            hashtrack.setVar('q', query);
        },
        "change .query-service-type": function(e) {
            hashtrack.setVar('t', $(e.target).val());
        },
        "input keyup": function(e) {
            if (e.keyCode === 13) {
                return false;
            }
        },
    }
})
<|endoftext|>


<commit_before>(function(){
  var RoomiesApp = React.createClass({
    loadUserFromServer: function() {
      $.ajax({
        url: "/users",
        dataType: 'json',
        success: function(data) {
          this.setState({data: data});
        }.bind(this),
        error: function(xhr, status, err) {
          console.error("/users", status, err.toString());
        }.bind(this)
      });
    },
    getInitialState: function() {
      return (
        {
          data: {
            user: {
              first_name: "",
              profile_picture_url: "",
              houses: []
            }
          }
        }
      );
    },
    componentDidMount: function() {
      this.loadUserFromServer();
    },
    render: function() {
      return (
        <AllRoomieElements data={this.state.data} />
      );
    }
  });

  var AllRoomieElements = React.createClass({
    render: function() {
      var data = this.props.data;
      return (

          <NavBar user={data.user} />
          <UserHouses houses={data.user.houses} />

      );
    }
  });

  var NavBar = React.createClass({
    searchUsers: function(search) {
      var $btn = $("#user-search-btn").button('loading');
      var url = "/search"
      $.ajax({
        url: url,
        dataType: "json",
        data: {search: search},
        success: function(data) {
          this.replaceState(data);
          $btn.button('reset');
          if ($('#search-results').attr('aria-expanded') === "false") {
            $("#search-results").dropdown("toggle");
          };
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
          $btn.button('reset');
        }.bind(this)
      });
    },
    getInitialState: function() {
      return (
        {
          users: [
            {
              id: "",
              first_name:"",
              houses: []
            }
          ]
        }
      );
    },
    render: function() {
      var user = this.props.user;
      var error = this.state.error;
      var userNodes;
      if (error) {
        userNodes = error;
      }
      else {
        userNodes = this.state.users.map(function(searchedUser) {
          return (

              <SearchedUser searchedUser={searchedUser} user={user} />

          );
        });
      };
      return (

              <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
                Toggle navigation

              </button>

                  <UserSearchForm onUsersSearch={this.searchUsers}/>

                    {userNodes}

{user.first_name}

logout

      );
    }
  });

  var UserSearchForm = React.createClass({
    handleSubmit: function(e) {
      e.preventDefault();
      var search = this.refs.search.getDOMNode().value.trim();
      if (!search) {
        return;
      };
      this.props.onUsersSearch(search);
      this.refs.search.getDOMNode().value = '';
    },
    render: function() {
      return (
        <form className="navbar-form navbar-left" role="search" onSubmit={this.handleSubmit}>

            <input type="text" className="form-control" placeholder="Search Users" ref="search" />

          <button type="submit" className="btn btn-default" id="user-search-btn" data-loading-text="Searching...">Search</button>
        </form>
      );
    }
  });

  var SearchedUser = React.createClass({
    showModal: function(e) {
      e.preventDefault();
      $("#searched-user-modal" + this.props.searchedUser.id).modal("toggle");
    },
    render: function() {
      var user = this.props.user;
      var searchedUser = this.props.searchedUser;
      return (

          <form onSubmit={this.showModal}>
            <button type="submit" className="btn btn-default btn-block" data-toggle="modal" data-target={"searched-user-modal" + searchedUser.id}>

{searchedUser.first_name}

            </button>
          </form>
          <SearchedUserModal searchedUser={searchedUser} user={user} />

      );
    }
  });

  var SearchedUserModal = React.createClass({
    render: function() {
      var user = this.props.user;
      var searchedUser = this.props.searchedUser;
      var houses = searchedUser.houses;
      var totalHousesMessage = (houses.length === 1) ? "1 house":(houses.length + " houses");
      var houseNodes = houses.map(function(house) {
        var isAMember;
        var joinHouseForm;
        _.each(user.houses, function(loggedInUserHouse){
          if (loggedInUserHouse.id === house.id) {
            isAMember = true;
          }
        });
        if (isAMember) {
          joinHouseForm =
You are already a member of this house!
;
        }
        else {
          joinHouseForm = <JoinHouseForm house={house} onJoinHouse={this.joinHouse} />;
        };
        return (

{house.name}

            {joinHouseForm}

        );
      });
      return (

                <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

{searchedUser.display_name}

{searchedUser.first_name} belongs to {totalHousesMessage}:

                {houseNodes}

      );
    }
  });

  var JoinHouseForm = React.createClass({
    handleSubmit: function(e) {
      e.preventDefault();

    },
    render: function() {
      var house = this.props.house;
      return (
        <form className="form-inline" onSubmit={this.handleSubmit}>

            Password
            <input type="password" className="form-control" id="house-password" placeholder="Enter password" ref="password" />

          <button type="submit" id={"join-house-btn" + house.id} className="btn btn-default">Join house</button>
        </form>
      );
    }
  });

  var UserHouses = React.createClass({
    render: function() {
      var houseNodes = this.props.houses.map(function(house) {
        return (
          <House house={house} key={house.id} />
        );
      });
      if (houseNodes.length === 0) {
        return (

You don't belong to any houses yet!

        );
      }
      else {
        return (

            {houseNodes}

        );
      }
    }
  });

  var House = React.createClass({
    loadHouseFromServer: function() {
      var url = "/houses/" + this.props.house.id;
      $.ajax({
        url: "/houses/" + this.props.house.id,
        dataType: 'json',
        success: function(data) {
          this.setState(data);
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
        }.bind(this)
      });
    },
    loadExpensesFromServer: function() {
      var url = "/houses/" + this.props.house.id + "/expenses";
      $.ajax({
        url: url,
        dataType: 'json',
        success: function(data) {
          this.setState(data);
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
        }.bind(this)
      });
    },
    handleExpenseSubmit: function(expense) {
      var houseId = this.props.house.id;
      var amount = expense.amount;
      var description = expense.description;
      var $btn = $("#expense-form-button" + houseId).button('loading');

      $.ajax({
        url: "/expenses",
        dataType: 'json',
        type: 'POST',
        data: {house_id: houseId, amount_string: amount, note: description},
        success: function(data) {
          this.loadExpensesFromServer();
          $btn.button("reset");
        }.bind(this),
        error: function(xhr, status, err) {
          console.error("/expenses", status, err.toString());
          $btn.button("reset");
        }.bind(this)
      });
    },
    getInitialState: function() {
      return(
        {
          house: {
            users: []
          },
          expenses: [
            {
              id: "",
              amount: "",
              note: "",
              created_at: "",
              user: {profile_picture_url: ""},
              charges: [
                {
                  id: "",
                  amount: "",
                  user: {profile_picture_url: ""},
                  date_completed: ""
                }
              ]
            }
          ]
        }
      );
    },
    componentDidMount: function() {
      this.loadHouseFromServer();
    },
    render: function() {
      var house = this.props.house;
      var users = this.state.house.users;
      var expenses = this.state.expenses;

      var userNodes = users.map(function(user) {
        return (

        );
      });
      return (

{house.name} {userNodes}

                <ExpenseForm houseId={house.id} onExpenseSubmit={this.handleExpenseSubmit} />

                <HouseExpenses expenses={expenses} loadExpensesFromServer={this.loadExpensesFromServer} />

      );
    }
  });

  var HouseExpenses = React.createClass({
    componentDidMount: function() {
      this.props.loadExpensesFromServer();
    },
    render: function() {
      var expenses = this.props.expenses;
      var expenseNodes = expenses.map(function(expense) {
        return (

added ${expense.amount} for {expense.note}

{Dateify.sayDays(expense.created_at) + " at " + Dateify.sayTime(expense.created_at)}

            <ExpenseCharges expense={expense} />

        );
      });
      if (expenses.length > 0) {
        return (

            {expenseNodes}

        );
      }
      else {
        return (

No expenses have been added yet!

        );
      };
    }
  });

  var ExpenseCharges = React.createClass({
    render: function() {
      var expense = this.props.expense;
      var expenseId = "expense-charges" + expense.id;
      var numPending = expense.charges.length;
      var expenseStatus = "btn-warning"

      _.each(expense.charges, function(charge) {
        if (charge.date_completed) {
          numPending--;
        };
      });

      if (numPending === 0) {
        expenseStatus = "btn-success";
      };

      var chargeNodes = expense.charges.map(function(charge) {
        var message;
        var chargeStatus;
        var glyphicon;
        var date_completed = charge.date_completed;
        if (date_completed) {
          message = "paid $" + charge.amount + " " + Dateify.sayDays(date_completed) + " at " + Dateify.sayTime(date_completed);
          chargeStatus = "bg-success";
          glyphicon = "glyphicon glyphicon-ok-circle"
        }
        else {
          message = "owes $" + charge.amount;
          chargeStatus = "bg-warning"
        }
        return (

               {message}

        );
      });
      return (

             Charges {numPending} pending

            {chargeNodes}

      );
    }
  });

  var ExpenseForm = React.createClass({
    handleSubmit: function(e) {
      e.preventDefault();
      var amount = this.refs.amount.getDOMNode().value.trim();
      var description = this.refs.description.getDOMNode().value.trim();
      if (!amount || !description) {
        return;
      }
      this.props.onExpenseSubmit({amount: amount, description: description});
      this.refs.amount.getDOMNode().value = '';
      this.refs.description.getDOMNode().value = '';
    },
    render: function() {
      var houseId = this.props.houseId;
      return (
        <form className="expense-form" onSubmit={this.handleSubmit}>

            Amount (in dollars)

$

              <input type="text" className="form-control" id={"inputAmount" + houseId} placeholder="Amount" ref="amount" />

            Expense description
            <textarea className="form-control" id={"inputDescription" + houseId} placeholder="Add a description" rows="3" ref="description"></textarea>

            <button type="submit" className="btn btn-primary btn-block" id={"expense-form-button" + houseId} data-loading-text="Adding...">Add Expense</button>

        </form>
      );
    }
  });

  React.render(
    <RoomiesApp />,
    document.getElementById('content')
  );
}());<commit_msg>add TODO comment for JoinHouseForm<commit_after>(function(){
  var RoomiesApp = React.createClass({
    loadUserFromServer: function() {
      $.ajax({
        url: "/users",
        dataType: 'json',
        success: function(data) {
          this.setState({data: data});
        }.bind(this),
        error: function(xhr, status, err) {
          console.error("/users", status, err.toString());
        }.bind(this)
      });
    },
    getInitialState: function() {
      return (
        {
          data: {
            user: {
              first_name: "",
              profile_picture_url: "",
              houses: []
            }
          }
        }
      );
    },
    componentDidMount: function() {
      this.loadUserFromServer();
    },
    render: function() {
      return (
        <AllRoomieElements data={this.state.data} />
      );
    }
  });

  var AllRoomieElements = React.createClass({
    render: function() {
      var data = this.props.data;
      return (

          <NavBar user={data.user} />
          <UserHouses houses={data.user.houses} />

      );
    }
  });

  var NavBar = React.createClass({
    searchUsers: function(search) {
      var $btn = $("#user-search-btn").button('loading');
      var url = "/search"
      $.ajax({
        url: url,
        dataType: "json",
        data: {search: search},
        success: function(data) {
          this.replaceState(data);
          $btn.button('reset');
          if ($('#search-results').attr('aria-expanded') === "false") {
            $("#search-results").dropdown("toggle");
          };
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
          $btn.button('reset');
        }.bind(this)
      });
    },
    getInitialState: function() {
      return (
        {
          users: [
            {
              id: "",
              first_name:"",
              houses: []
            }
          ]
        }
      );
    },
    render: function() {
      var user = this.props.user;
      var error = this.state.error;
      var userNodes;
      if (error) {
        userNodes = error;
      }
      else {
        userNodes = this.state.users.map(function(searchedUser) {
          return (

              <SearchedUser searchedUser={searchedUser} user={user} />

          );
        });
      };
      return (

              <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
                Toggle navigation

              </button>

                  <UserSearchForm onUsersSearch={this.searchUsers}/>

                    {userNodes}

{user.first_name}

logout

      );
    }
  });

  var UserSearchForm = React.createClass({
    handleSubmit: function(e) {
      e.preventDefault();
      var search = this.refs.search.getDOMNode().value.trim();
      if (!search) {
        return;
      };
      this.props.onUsersSearch(search);
      this.refs.search.getDOMNode().value = '';
    },
    render: function() {
      return (
        <form className="navbar-form navbar-left" role="search" onSubmit={this.handleSubmit}>

            <input type="text" className="form-control" placeholder="Search Users" ref="search" />

          <button type="submit" className="btn btn-default" id="user-search-btn" data-loading-text="Searching...">Search</button>
        </form>
      );
    }
  });

  var SearchedUser = React.createClass({
    showModal: function(e) {
      e.preventDefault();
      $("#searched-user-modal" + this.props.searchedUser.id).modal("toggle");
    },
    render: function() {
      var user = this.props.user;
      var searchedUser = this.props.searchedUser;
      return (

          <form onSubmit={this.showModal}>
            <button type="submit" className="btn btn-default btn-block" data-toggle="modal" data-target={"searched-user-modal" + searchedUser.id}>

{searchedUser.first_name}

            </button>
          </form>
          <SearchedUserModal searchedUser={searchedUser} user={user} />

      );
    }
  });

  var SearchedUserModal = React.createClass({
    render: function() {
      var user = this.props.user;
      var searchedUser = this.props.searchedUser;
      var houses = searchedUser.houses;
      var totalHousesMessage = (houses.length === 1) ? "1 house":(houses.length + " houses");
      var houseNodes = houses.map(function(house) {
        var isAMember;
        var joinHouseForm;
        _.each(user.houses, function(loggedInUserHouse){
          if (loggedInUserHouse.id === house.id) {
            isAMember = true;
          }
        });
        if (isAMember) {
          joinHouseForm =
You are already a member of this house!
;
        }
        else {
          joinHouseForm = <JoinHouseForm house={house} onJoinHouse={this.joinHouse} />;
        };
        return (

{house.name}

            {joinHouseForm}

        );
      });
      return (

                <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

{searchedUser.display_name}

{searchedUser.first_name} belongs to {totalHousesMessage}:

                {houseNodes}

      );
    }
  });

  var JoinHouseForm = React.createClass({
    // TODO: form needs to actually join the user to the house
    handleSubmit: function(e) {
      e.preventDefault();
    },
    render: function() {
      var house = this.props.house;
      return (
        <form className="form-inline" onSubmit={this.handleSubmit}>

            Password
            <input type="password" className="form-control" id="house-password" placeholder="Enter password" ref="password" />

          <button type="submit" id={"join-house-btn" + house.id} className="btn btn-default">Join house</button>
        </form>
      );
    }
  });

  var UserHouses = React.createClass({
    render: function() {
      var houseNodes = this.props.houses.map(function(house) {
        return (
          <House house={house} key={house.id} />
        );
      });
      if (houseNodes.length === 0) {
        return (

You don't belong to any houses yet!

        );
      }
      else {
        return (

            {houseNodes}

        );
      }
    }
  });

  var House = React.createClass({
    loadHouseFromServer: function() {
      var url = "/houses/" + this.props.house.id;
      $.ajax({
        url: "/houses/" + this.props.house.id,
        dataType: 'json',
        success: function(data) {
          this.setState(data);
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
        }.bind(this)
      });
    },
    loadExpensesFromServer: function() {
      var url = "/houses/" + this.props.house.id + "/expenses";
      $.ajax({
        url: url,
        dataType: 'json',
        success: function(data) {
          this.setState(data);
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(url, status, err.toString());
        }.bind(this)
      });
    },
    handleExpenseSubmit: function(expense) {
      var houseId = this.props.house.id;
      var amount = expense.amount;
      var description = expense.description;
      var $btn = $("#expense-form-button" + houseId).button('loading');

      $.ajax({
        url: "/expenses",
        dataType: 'json',
        type: 'POST',
        data: {house_id: houseId, amount_string: amount, note: description},
        success: function(data) {
          this.loadExpensesFromServer();
          $btn.button("reset");
        }.bind(this),
        error: function(xhr, status, err) {
          console.error("/expenses", status, err.toString());
          $btn.button("reset");
        }.bind(this)
      });
    },
    getInitialState: function() {
      return(
        {
          house: {
            users: []
          },
          expenses: [
            {
              id: "",
              amount: "",
              note: "",
              created_at: "",
              user: {profile_picture_url: ""},
              charges: [
                {
                  id: "",
                  amount: "",
                  user: {profile_picture_url: ""},
                  date_completed: ""
                }
              ]
            }
          ]
        }
      );
    },
    componentDidMount: function() {
      this.loadHouseFromServer();
    },
    render: function() {
      var house = this.props.house;
      var users = this.state.house.users;
      var expenses = this.state.expenses;

      var userNodes = users.map(function(user) {
        return (

        );
      });
      return (

{house.name} {userNodes}

                <ExpenseForm houseId={house.id} onExpenseSubmit={this.handleExpenseSubmit} />

                <HouseExpenses expenses={expenses} loadExpensesFromServer={this.loadExpensesFromServer} />

      );
    }
  });

  var HouseExpenses = React.createClass({
    componentDidMount: function() {
      this.props.loadExpensesFromServer();
    },
    render: function() {
      var expenses = this.props.expenses;
      var expenseNodes = expenses.map(function(expense) {
        return (

added ${expense.amount} for {expense.note}

{Dateify.sayDays(expense.created_at) + " at " + Dateify.sayTime(expense.created_at)}

            <ExpenseCharges expense={expense} />

        );
      });
      if (expenses.length > 0) {
        return (

            {expenseNodes}

        );
      }
      else {
        return (

No expenses have been added yet!

        );
      };
    }
  });

  var ExpenseCharges = React.createClass({
    render: function() {
      var expense = this.props.expense;
      var expenseId = "expense-charges" + expense.id;
      var numPending = expense.charges.length;
      var expenseStatus = "btn-warning"

      _.each(expense.charges, function(charge) {
        if (charge.date_completed) {
          numPending--;
        };
      });

      if (numPending === 0) {
        expenseStatus = "btn-success";
      };

      var chargeNodes = expense.charges.map(function(charge) {
        var message;
        var chargeStatus;
        var glyphicon;
        var date_completed = charge.date_completed;
        if (date_completed) {
          message = "paid $" + charge.amount + " " + Dateify.sayDays(date_completed) + " at " + Dateify.sayTime(date_completed);
          chargeStatus = "bg-success";
          glyphicon = "glyphicon glyphicon-ok-circle"
        }
        else {
          message = "owes $" + charge.amount;
          chargeStatus = "bg-warning"
        }
        return (

               {message}

        );
      });
      return (

             Charges {numPending} pending

            {chargeNodes}

      );
    }
  });

  var ExpenseForm = React.createClass({
    handleSubmit: function(e) {
      e.preventDefault();
      var amount = this.refs.amount.getDOMNode().value.trim();
      var description = this.refs.description.getDOMNode().value.trim();
      if (!amount || !description) {
        return;
      }
      this.props.onExpenseSubmit({amount: amount, description: description});
      this.refs.amount.getDOMNode().value = '';
      this.refs.description.getDOMNode().value = '';
    },
    render: function() {
      var houseId = this.props.houseId;
      return (
        <form className="expense-form" onSubmit={this.handleSubmit}>

            Amount (in dollars)

$

              <input type="text" className="form-control" id={"inputAmount" + houseId} placeholder="Amount" ref="amount" />

            Expense description
            <textarea className="form-control" id={"inputDescription" + houseId} placeholder="Add a description" rows="3" ref="description"></textarea>

            <button type="submit" className="btn btn-primary btn-block" id={"expense-form-button" + houseId} data-loading-text="Adding...">Add Expense</button>

        </form>
      );
    }
  });

  React.render(
    <RoomiesApp />,
    document.getElementById('content')
  );
}());<|endoftext|>


<commit_before>var PubSub = require('pubsub-js');

var events = {
  fonts: false,
  leaflet: false
};

// Wait for webfonts to load
PubSub.subscribe('webfonts.active', function() {
  events.fonts = true;
});

// Poll to see if Leaflet maps have loaded yet
// This doesn't expose an event so we have to manually wait and hope
// If leaflet ever exposes an event this could be refactored and the tests
// sped up significantly
var hasMap = (document.querySelectorAll('.map').length > 0);
setTimeout(function() {
  events.leaflet = true;
}, (hasMap ? 10000 : 0))

// Wait for window load event
window.addEventListener('load', function() {

  // Poll to see whether things have loaded yet
  var poll = setInterval(function() {

    // If things haven't loaded yet, go round again
    if(!(events.leaflet && events.fonts)) {
      return;
    }

    // Once things have loaded, stop polling
    clearInterval(poll);

    // And tell phantom to get on with it
    if (typeof window.callPhantom === 'function') {
      window.callPhantom('takeShot');
    }

  }, 100);

});
<commit_msg>Add some additional fudge factor to the phantomjs callback<commit_after>var PubSub = require('pubsub-js');

var events = {
  fonts: false,
  leaflet: false
};

// Wait for webfonts to load
PubSub.subscribe('webfonts.active', function() {
  events.fonts = true;
});

// Poll to see if Leaflet maps have loaded yet
// This doesn't expose an event so we have to manually wait and hope
// If leaflet ever exposes an event this could be refactored and the tests
// sped up significantly
var hasMap = (document.querySelectorAll('.map').length > 0);
setTimeout(function() {
  events.leaflet = true;
}, (hasMap ? 10000 : 0))

// Wait for window load event
window.addEventListener('load', function() {

  // Poll to see whether things have loaded yet
  var poll = setInterval(function() {

    // If things haven't loaded yet, go round again
    if(!(events.leaflet && events.fonts)) {
      return;
    }

    // Once things have loaded, stop polling
    clearInterval(poll);

    // And tell phantom to get on with it
    // But give it an extra second of readiness fudge factor :-|
    if (typeof window.callPhantom === 'function') {
      setTimeout(function() {

        window.callPhantom('takeShot');
      }, 1000);
    }

  }, 100);

});
<|endoftext|>


<commit_before>/* @flow */
import deepFreeze from 'deep-freeze';
import { getActiveRealmEmoji, getAllRealmEmoji } from '../emojiSelectors';

describe('getActiveRealmEmoji', () => {
  test('filter out all deactivated emojis', () => {
    const state = {
      accounts: [
        {
          realm: 'https://example.com',
        },
      ],
      realm: {
        emoji: {
          smile: {
            deactivated: false,
            source_url: '/static/user_upload/smile.png',
          },
          laugh: {
            deactivated: false,
            source_url: '/static/user_upload/laugh.png',
          },
          sad: {
            deactivated: true,
            source_url: '/static/user_upload/sad.png',
          },
        },
      },
    };

    const expectedResult = {
      smile: {
        deactivated: false,
        source_url: 'https://example.com/static/user_upload/smile.png',
      },
      laugh: {
        deactivated: false,
        source_url: 'https://example.com/static/user_upload/laugh.png',
      },
    };

    expect(getActiveRealmEmoji(deepFreeze(state))).toEqual(expectedResult);
  });
});

describe('getAllRealmEmoji', () => {
  test('get realm emojis with absolute url', () => {
    const state = {
      accounts: [{ realm: 'https://example.com' }],
      realm: {
        emoji: {
          smile: {
            source_url: '/static/user_upload/smile.png',
          },
          laugh: {
            source_url: 'https://example.com/static/user_upload/laugh.png',
          },
        },
      },
    };

    const expectedResult = {
      smile: {
        source_url: 'https://example.com/static/user_upload/smile.png',
      },
      laugh: {
        source_url: 'https://example.com/static/user_upload/laugh.png',
      },
    };

    expect(getAllRealmEmoji(deepFreeze(state))).toEqual(expectedResult);
  });
});
<commit_msg>emoji: Fix discrepancies in emojiSelectors-tests.<commit_after>/* @flow */
import deepFreeze from 'deep-freeze';
import { getActiveRealmEmoji, getAllRealmEmoji } from '../emojiSelectors';

describe('getActiveRealmEmoji', () => {
  test('filter out all deactivated emojis', () => {
    const state = {
      accounts: [
        {
          realm: 'https://example.com',
        },
      ],
      realm: {
        emoji: {
          1: {
            deactivated: false,
            source_url: '/static/user_upload/smile.png',
          },
          2: {
            deactivated: false,
            source_url: '/static/user_upload/laugh.png',
          },
          3: {
            deactivated: true,
            source_url: '/static/user_upload/sad.png',
          },
        },
      },
    };

    const expectedResult = {
      1: {
        deactivated: false,
        source_url: 'https://example.com/static/user_upload/smile.png',
      },
      2: {
        deactivated: false,
        source_url: 'https://example.com/static/user_upload/laugh.png',
      },
    };

    expect(getActiveRealmEmoji(deepFreeze(state))).toEqual(expectedResult);
  });
});

describe('getAllRealmEmoji', () => {
  test('get realm emojis with absolute url', () => {
    const state = {
      accounts: [{ realm: 'https://example.com' }],
      realm: {
        emoji: {
          1: {
            source_url: '/static/user_upload/smile.png',
          },
          2: {
            source_url: 'https://example.com/static/user_upload/laugh.png',
          },
        },
      },
    };

    const expectedResult = {
      1: {
        source_url: 'https://example.com/static/user_upload/smile.png',
      },
      2: {
        source_url: 'https://example.com/static/user_upload/laugh.png',
      },
    };

    expect(getAllRealmEmoji(deepFreeze(state))).toEqual(expectedResult);
  });
});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import _isEmpty from 'lodash/isEmpty'

const DictionaryField = (propsArg) => (WrappedComponent) => {
	return class DictionaryField extends Component {
		static propTypes = {
			labelKey: PropTypes.string,
		}
		render() {
			if (!_isEmpty(propsArg)) {
				return <WrappedComponent {...Object.assign({},this.props,propsArg)} />
			} else {
				return null
			}
		}
	}
}

export default DictionaryField
<commit_msg>Simplify DictionaryField<commit_after>import React, { Component } from 'react'
import _isEmpty from 'lodash/isEmpty'

const DictionaryField = (propsArg) => (WrappedComponent) => {
	return class DictionaryField extends Component {
		render() {
			if (!_isEmpty(propsArg)) {
				return <WrappedComponent {...Object.assign({},this.props,propsArg)} />
			} else {
				return null
			}
		}
	}
}

export default DictionaryField
<|endoftext|>


<commit_before>module("polyfills", {

  setup: function() {
      this.editable = document.createElement('div');
      this.editable.setAttribute('contenteditable', 'true');
      document.body.appendChild(this.editable);
  },

  teardown: function() {
    this.editable.parentNode.removeChild(this.editable);
  }
});

test("Check element.normalize is preserving caret position", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('boo'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(text3);

  r.setStartAndEnd(text2, 3);
  r.select();

  this.editable.normalize();

  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 1, "Normalize merged nodes");
  ok(s.anchorNode === this.editable.firstChild, "Anchor element is correct after normalize");
  ok(s.anchorOffset === 7 , "Anchor offset is correct after normalize");
});

test("Check element.normalize is preserving selection", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('third'),
      text4 = document.createTextNode('fourth'),
      br = document.createElement('br'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(br);
  this.editable.appendChild(text3);
  this.editable.appendChild(text4);

  r.setStart(text2, 0);
  r.setEndAfter(br);
  r.select();

  this.editable.normalize();
  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 3, "Normalize merged nodes");
  equal(s.anchorNode, this.editable.firstChild, "Anchor element is correct after normalize");
  equal(s.anchorOffset, 4 , "Anchor offset is correct after normalize");
  ok((s.focusNode === this.editable && s.focusOffset === 2)|| (s.focusNode === this.editable.lastChild && s.focusOffset === 0), "Focus element and offset is correct after normalize");
});

test("Check element.normalize is preserving selection 2", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('third'),
      text4 = document.createTextNode('fourth'),
      br = document.createElement('br'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(br);
  this.editable.appendChild(text3);
  this.editable.appendChild(text4);

  r.setStartBefore(br);
  r.setEnd(text4, 0);
  r.select();

  this.editable.normalize();
  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 3, "Normalize merged nodes");

  ok((s.anchorNode === this.editable && s.anchorOffset === 1) || (s.anchorNode === this.editable.firstChild && s.anchorOffset === this.editable.firstChild.length), "Anchor element and offset is correct after normalize");
  equal(s.focusNode, this.editable.childNodes[2], "Focus element is correct after normalize");
  equal(s.focusOffset, 5 , "Focus offset is correct after normalize");
});

test('Check if normalization bug checking restores original caret/selection position', function() {
  var input = document.createElement('input'),
      ceditable = document.createElement('div'),
      scrollOriginalPos, r;

  input.type = 'text';
  input.value = 'test';
  input.style.marginTop = '3000px';
  document.body.appendChild(input);
  input.focus();
  input.setSelectionRange(1, 3);

  scrollOriginalPos = window.pageYOffset;

  ok(document.activeElement === input && input.selectionStart === 1 && input.selectionEnd === 3 ,'input correctly selected');
  wysihtml.polyfills(window, document).normalizeHasCaretError();
  ok(document.activeElement === input && input.selectionStart === 1 && input.selectionEnd === 3 ,'input correctly selected after caret error testing');
  ok(scrollOriginalPos === window.pageYOffset, 'scroll position kept after caret error testing');
  document.body.removeChild(input);

  ceditable.setAttribute('contenteditable', true);
  ceditable.innerHTML = 'test';
  ceditable.style.marginTop = '3000px';
  document.body.appendChild(ceditable);
  ceditable.focus();
  r = rangy.createRange();
  r.setStart(ceditable.firstChild, 1);
  r.setEnd(ceditable.firstChild, 3);
  r.select();

  scrollOriginalPos = window.pageYOffset;

  ok(document.activeElement === ceditable && rangy.getSelection().anchorOffset === 1 && rangy.getSelection().focusOffset === 3,'contenteditable correctly selected');
  wysihtml.polyfills(window, document).normalizeHasCaretError();
  ok(document.activeElement === ceditable && rangy.getSelection().anchorOffset === 1 && rangy.getSelection().focusOffset === 3,'contenteditable correctly selected after caret error testing');
  ok(scrollOriginalPos === window.pageYOffset, 'scroll position kept after caret error testing');
  document.body.removeChild(ceditable);

});
<commit_msg>[TESTS] Fix failling unit tests due to wrong time of checking the Y offset<commit_after>module("polyfills", {

  setup: function() {
      this.editable = document.createElement('div');
      this.editable.setAttribute('contenteditable', 'true');
      document.body.appendChild(this.editable);
  },

  teardown: function() {
    this.editable.parentNode.removeChild(this.editable);
  }
});

test("Check element.normalize is preserving caret position", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('boo'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(text3);

  r.setStartAndEnd(text2, 3);
  r.select();

  this.editable.normalize();

  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 1, "Normalize merged nodes");
  ok(s.anchorNode === this.editable.firstChild, "Anchor element is correct after normalize");
  ok(s.anchorOffset === 7 , "Anchor offset is correct after normalize");
});

test("Check element.normalize is preserving selection", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('third'),
      text4 = document.createTextNode('fourth'),
      br = document.createElement('br'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(br);
  this.editable.appendChild(text3);
  this.editable.appendChild(text4);

  r.setStart(text2, 0);
  r.setEndAfter(br);
  r.select();

  this.editable.normalize();
  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 3, "Normalize merged nodes");
  equal(s.anchorNode, this.editable.firstChild, "Anchor element is correct after normalize");
  equal(s.anchorOffset, 4 , "Anchor offset is correct after normalize");
  ok((s.focusNode === this.editable && s.focusOffset === 2)|| (s.focusNode === this.editable.lastChild && s.focusOffset === 0), "Focus element and offset is correct after normalize");
});

test("Check element.normalize is preserving selection 2", function() {
  var text1 = document.createTextNode('test'),
      text2 = document.createTextNode('foo'),
      text3 = document.createTextNode('third'),
      text4 = document.createTextNode('fourth'),
      br = document.createElement('br'),
      r = rangy.createRange(),
      s;

  this.editable.appendChild(text1);
  this.editable.appendChild(text2);
  this.editable.appendChild(br);
  this.editable.appendChild(text3);
  this.editable.appendChild(text4);

  r.setStartBefore(br);
  r.setEnd(text4, 0);
  r.select();

  this.editable.normalize();
  s = rangy.getSelection();

  ok(this.editable.childNodes.length === 3, "Normalize merged nodes");

  ok((s.anchorNode === this.editable && s.anchorOffset === 1) || (s.anchorNode === this.editable.firstChild && s.anchorOffset === this.editable.firstChild.length), "Anchor element and offset is correct after normalize");
  equal(s.focusNode, this.editable.childNodes[2], "Focus element is correct after normalize");
  equal(s.focusOffset, 5 , "Focus offset is correct after normalize");
});

test('Check if normalization bug checking restores original caret/selection position', function() {
  var input = document.createElement('input'),
      ceditable = document.createElement('div'),
      scrollOriginalPos, r;

  input.type = 'text';
  input.value = 'test';
  input.style.marginTop = '3000px';
  document.body.appendChild(input);
  input.focus();
  input.setSelectionRange(1, 3);

  ok(document.activeElement === input && input.selectionStart === 1 && input.selectionEnd === 3 ,'input correctly selected');
  scrollOriginalPos = window.pageYOffset;
  wysihtml.polyfills(window, document).normalizeHasCaretError();
  ok(scrollOriginalPos === window.pageYOffset, 'scroll position kept after caret error testing');
  ok(document.activeElement === input && input.selectionStart === 1 && input.selectionEnd === 3 ,'input correctly selected after caret error testing');
  document.body.removeChild(input);

  ceditable.setAttribute('contenteditable', true);
  ceditable.innerHTML = 'test';
  ceditable.style.marginTop = '3000px';
  document.body.appendChild(ceditable);
  ceditable.focus();
  r = rangy.createRange();
  r.setStart(ceditable.firstChild, 1);
  r.setEnd(ceditable.firstChild, 3);
  r.select();

  ok(document.activeElement === ceditable && rangy.getSelection().anchorOffset === 1 && rangy.getSelection().focusOffset === 3,'contenteditable correctly selected');
  scrollOriginalPos = window.pageYOffset;
  wysihtml.polyfills(window, document).normalizeHasCaretError();
  ok(scrollOriginalPos === window.pageYOffset, 'scroll position kept after caret error testing');
  ok(document.activeElement === ceditable && rangy.getSelection().anchorOffset === 1 && rangy.getSelection().focusOffset === 3,'contenteditable correctly selected after caret error testing');
  document.body.removeChild(ceditable);

});
<|endoftext|>


<commit_before>export const IMPORT_DATASET = 'Import dataset. Source:';
export const RENDER_MAP_LAYER_TYPE = 'Render map layer type:';
export const FILTER_DASHBOARD_BY_DATASET = 'Filter dashboard by dataset';
export const EXPORT_EMBED_CODE = 'Get embed code';
export const SELECT_MAP_LAYER_TYPE = 'Selected map layer type';
export const SHARE_DASHBOARD = 'Share dashboard';
export const EXPORT_DASHBOARD_PNG = 'Export dashboard (png)';
export const EXPORT_DASHBOARD_PDF = 'Export dashboard (pdf)';
export const SHARE_VISUALISATION = 'Share visualisation';
export const EXPORT_VISUALISATION_PNG = 'Export visualisation (png)';
export const EXPORT_VISUALISATION_PDF = 'Export visualisation (pdf)';
export const TRANSFORM_DATASET = 'Transform dataset';
export const SELECT_VISUALISATION_TYPE = 'Select vis type:';
export const DASHBOARD_FILTER_CHANGE = 'Change dashboard filter';<commit_msg>[#1656] Add newline to analystic.js constant file<commit_after>export const IMPORT_DATASET = 'Import dataset. Source:';
export const RENDER_MAP_LAYER_TYPE = 'Render map layer type:';
export const FILTER_DASHBOARD_BY_DATASET = 'Filter dashboard by dataset';
export const EXPORT_EMBED_CODE = 'Get embed code';
export const SELECT_MAP_LAYER_TYPE = 'Selected map layer type';
export const SHARE_DASHBOARD = 'Share dashboard';
export const EXPORT_DASHBOARD_PNG = 'Export dashboard (png)';
export const EXPORT_DASHBOARD_PDF = 'Export dashboard (pdf)';
export const SHARE_VISUALISATION = 'Share visualisation';
export const EXPORT_VISUALISATION_PNG = 'Export visualisation (png)';
export const EXPORT_VISUALISATION_PDF = 'Export visualisation (pdf)';
export const TRANSFORM_DATASET = 'Transform dataset';
export const SELECT_VISUALISATION_TYPE = 'Select vis type:';
export const DASHBOARD_FILTER_CHANGE = 'Change dashboard filter';
<|endoftext|>


<commit_before>/**
 * UniqueItems
 */
var uniqueItemsAttribute = function uniqueItems(property, propertyValue, attributeValue, propertyAttributes, callback) {

  var self =  this;

  each(propertyValue, function(index, value) {
    if ((propertyValue.indexOf(value) < index)) {
      self.addError();
    }
  });

  return callback();

};

// Export
Validation.prototype.addAttribute('uniqueItems', uniqueItemsAttribute);<commit_msg>The ‘uniqueItems’ attribute is now fully compliant with the JSON Schema Internet Draft<commit_after>(function() {

  /**
   * UniqueItems
   */
  var attribute = function uniqueItems(property, propertyValue, attributeValue, propertyAttributes, callback) {

    var self =  this;

    each(propertyValue, function(index, value) {

      if (isString(value)) {
        if ((propertyValue.indexOf(value) < index)) {
          self.addError();
        }
      }

      if (isObject(value) || isArray(value)) {
        propertyValue.forEach(function(subValue, subIndex) {

          if (subIndex !== index) {
            if (isEqual(value, subValue)) {
              self.addError();
            }
          }

        });
      }

    });

    return callback();

  };

  // Export
  Validation.prototype.addAttribute('uniqueItems', attribute);

}());<|endoftext|>


<commit_before>import randomWords from "random-words";

const key = "thorium_clientId";
let clientId = sessionStorage.getItem(key);
let windows = [];
let broadcastChannel;
if (window.BroadcastChannel) {
  broadcastChannel = new BroadcastChannel("thorium_clientCount");
}
// This client ID implementation works as follows:
export function initializeClient() {
  if (window.BroadcastChannel) {
    broadcastChannel.onmessage = function(ev) {
      if (ev.data === "clientPing") {
        if (clientId) {
          broadcastChannel.postMessage(clientId);
        }
      } else {
        if (!windows.includes(ev.data)) windows.push(ev.data);
      }
    };
    getClientId();
  }
}

function setClient(id) {
  sessionStorage.setItem(key, id);
  clientId = id;
}
export function getClientId() {
  return new Promise(resolve => {
    if (clientId) {
      return resolve(clientId);
    }

    const clientList = getClientList();

    if (broadcastChannel) {
      broadcastChannel.postMessage("clientPing");
      // Lets give a bit of time for the windows to get back
      setTimeout(() => {
        if (clientId) {
          return resolve(clientId);
        }
        for (let i = 0; i < clientList.length; i++) {
          if (!windows.includes(clientList[i])) {
            setClient(clientList[i]);
            return resolve(clientId);
          }
        }
        if (!clientId) {
          setClient(randomWords(3).join("-"));
          clientList.push(clientId);
          localStorage.setItem(key, JSON.stringify(clientList));
          return resolve(clientId);
        }
      }, 500);
    } else {
      setClient(clientList[0]);
      return resolve(clientId);
    }
  }).then(res => {
    return res;
  });
}
export function setClientId(id) {
  const clientList = getClientList();
  const clientIndex = clientList.indexOf(clientId);
  setClient(id);
  clientList[clientIndex] = id;
  localStorage.setItem(key, JSON.stringify(clientList));
}

function getClientList() {
  let clientList = null;
  try {
    clientList = JSON.parse(localStorage.getItem(key));
  } catch {
    // It errored - it either doesn't exist or isn't JSON.
    // If it's blank, create a new one
  }
  if (!clientList) {
    clientList = [localStorage.getItem(key) || randomWords(3).join("-")];
    localStorage.setItem(key, JSON.stringify(clientList));
  }
  return clientList;
}
<commit_msg>Fixes an issue with getting the client ID<commit_after>import randomWords from "random-words";

const key = "thorium_clientPersistentId";
let clientId = sessionStorage.getItem(key);
let windows = [];
let broadcastChannel;
if (window.BroadcastChannel) {
  broadcastChannel = new BroadcastChannel("thorium_clientCount");
}
// This client ID implementation works as follows:
export function initializeClient() {
  if (window.BroadcastChannel) {
    broadcastChannel.onmessage = function(ev) {
      if (ev.data === "clientPing") {
        if (clientId) {
          broadcastChannel.postMessage(clientId);
        }
      } else {
        if (!windows.includes(ev.data)) windows.push(ev.data);
      }
    };
    getClientId();
  }
}

function setClient(id) {
  sessionStorage.setItem(key, id);
  clientId = id;
}
export function getClientId() {
  return new Promise(resolve => {
    if (clientId) {
      return resolve(clientId);
    }

    const clientList = getClientList();

    if (broadcastChannel) {
      broadcastChannel.postMessage("clientPing");
      // Lets give a bit of time for the windows to get back
      setTimeout(() => {
        if (clientId) {
          return resolve(clientId);
        }
        for (let i = 0; i < clientList.length; i++) {
          if (!windows.includes(clientList[i])) {
            setClient(clientList[i]);
            return resolve(clientId);
          }
        }
        if (!clientId) {
          setClient(randomWords(3).join("-"));
          clientList.push(clientId);
          localStorage.setItem(key, JSON.stringify(clientList));
          return resolve(clientId);
        }
      }, 500);
    } else {
      setClient(clientList[0]);
      return resolve(clientId);
    }
  }).then(res => {
    return res;
  });
}
export function setClientId(id) {
  const clientList = getClientList();
  const clientIndex = clientList.indexOf(clientId);
  setClient(id);
  clientList[clientIndex] = id;
  localStorage.setItem(key, JSON.stringify(clientList));
}

function getClientList() {
  let clientList = null;
  try {
    clientList = JSON.parse(localStorage.getItem(key));
  } catch {
    // It errored - it either doesn't exist or isn't JSON.
    // If it's blank, create a new one
  }

  if (!clientList) {
    clientList = [
      localStorage.getItem("thorium_clientId") || randomWords(3).join("-")
    ];
    localStorage.setItem(key, JSON.stringify(clientList));
  }
  return clientList;
}
<|endoftext|>


<commit_before>const eslint = require('gulp-eslint');
const gulpIf = require('gulp-if');

/**
 * A helper function to lint **and fix** a set of paths
 * @param  {Object} gulp The gulp instance
 * @param  {Array}  list The list of paths on which to operate
 * @return {Object}      The gulp stream
 */
const lintAndFix = (gulp, list) => {
    // declare a helper function to check if a file is fixed
    //
    const isFixed = (file) => file.eslint != null && file.eslint.fixed;

    // lint the files in the path with the fix option & return the stream
    //
    return gulp.src(list, { base: './' })
        .pipe(eslint({ fix: true }))
        .pipe(gulpIf(isFixed, gulp.dest('./')))
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
};

module.exports = lintAndFix;
<commit_msg>comment cleanup<commit_after>// npm dependencies
//
const eslint = require('gulp-eslint');
const gulpIf = require('gulp-if');

/**
 * A helper function to lint **and fix** a set of paths
 * @param  {Object} gulp The gulp instance
 * @param  {Array}  list The list of paths on which to operate
 * @return {Object}      The gulp stream
 */
const lintAndFix = (gulp, list) => {
    // declare a helper function to check if a file is fixed
    //
    const isFixed = (file) => file.eslint != null && file.eslint.fixed;

    // lint the files in the path with the fix option & return the stream
    //
    return gulp.src(list, { base: './' })
        .pipe(eslint({ fix: true }))
        .pipe(gulpIf(isFixed, gulp.dest('./')))
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
};

module.exports = lintAndFix;
<|endoftext|>


<commit_before>"use strict";

var _ = require("underscore");
var $$ = require("substance-application").$$;
var NodeView = require("../node/node_view");
var TextView = require("../text/text_view");
var Annotator = require("substance-document").Annotator;

// Lens.Cover.View
// ==========================================================================

var CoverView = function(node, viewFactory) {
  NodeView.call(this, node, viewFactory);

  this.$el.attr({id: node.id});
  this.$el.addClass("content-node cover");

  this.childViews = {
    "title": null
  };
};

CoverView.Prototype = function() {

  this.render = function() {
    NodeView.prototype.render.call(this);

    if (this.node.document.published_on) {
      this.content.appendChild($$('.published-on', {
        html: new Date(this.node.document.published_on).toDateString()
      }));
    }

    var titleView = this.childViews["title"] = new TextView(this.node, this.viewFactory, {property: "title"});
    this.content.appendChild(titleView.render().el);
    titleView.el.classList.add("title");

    this.authorsEl = $$('.authors', {
      contenteditable: false
    });

    this.renderAuthors();
    this.content.appendChild(this.authorsEl);

    return this;
  };

  this.renderAuthors = function() {
    this.authorsEl.innerHTML = "";

    var authors = this.node.document.getAuthors();
    _.each(authors, function(a) {
      var authorEl = $$('a.toggle-author', {
        id: "toggle_"+a.id,
        href: "#",
        text: a.name,
        'data-id': a.id,
      });

      this.authorsEl.appendChild(authorEl);
    }, this);
  };

  this.onGraphUpdate = function(op) {
    // Call super handler and return if that has processed the operation already
    if (NodeView.prototype.onGraphUpdate.call(this, op)) {
      return true;
    }

    if (_.isEqual(op.path, ["document","title"])) {
      this.childViews["title"].renderContent();
      return true;
    } else if (_.isEqual(op.path, ["document", "authors"])) {
      this.renderAuthors();
    }

    // Otherwise deal with annotation changes
    // Note: the annotations do not get attached to ["document", "title"],
    // as it seems strange to annotate a property which is used in such an indirect way
    if (Annotator.changesAnnotations(this.node.document, op, ["cover", "title"])) {
      //console.log("Rerendering TextView due to annotation update", op);
      this.childViews["title"].renderContent();
      return true;
    }
  };
};

CoverView.Prototype.prototype = NodeView.prototype;
CoverView.prototype = new CoverView.Prototype();

module.exports = CoverView;
<commit_msg>Publish-date (optional) is no longer editable.<commit_after>"use strict";

var _ = require("underscore");
var $$ = require("substance-application").$$;
var NodeView = require("../node/node_view");
var TextView = require("../text/text_view");
var Annotator = require("substance-document").Annotator;

// Lens.Cover.View
// ==========================================================================

var CoverView = function(node, viewFactory) {
  NodeView.call(this, node, viewFactory);

  this.$el.attr({id: node.id});
  this.$el.addClass("content-node cover");

  this.childViews = {
    "title": null
  };
};

CoverView.Prototype = function() {

  this.render = function() {
    NodeView.prototype.render.call(this);

    if (this.node.document.published_on) {
      this.content.appendChild($$('.published-on', {
        contenteditable: false,
        html: new Date(this.node.document.published_on).toDateString()
      }));
    }

    var titleView = this.childViews["title"] = new TextView(this.node, this.viewFactory, {property: "title"});
    this.content.appendChild(titleView.render().el);
    titleView.el.classList.add("title");

    this.authorsEl = $$('.authors', {
      contenteditable: false
    });

    this.renderAuthors();
    this.content.appendChild(this.authorsEl);

    return this;
  };

  this.renderAuthors = function() {
    this.authorsEl.innerHTML = "";

    var authors = this.node.document.getAuthors();
    _.each(authors, function(a) {
      var authorEl = $$('a.toggle-author', {
        id: "toggle_"+a.id,
        href: "#",
        text: a.name,
        'data-id': a.id,
      });

      this.authorsEl.appendChild(authorEl);
    }, this);
  };

  this.onGraphUpdate = function(op) {
    // Call super handler and return if that has processed the operation already
    if (NodeView.prototype.onGraphUpdate.call(this, op)) {
      return true;
    }

    if (_.isEqual(op.path, ["document","title"])) {
      this.childViews["title"].renderContent();
      return true;
    } else if (_.isEqual(op.path, ["document", "authors"])) {
      this.renderAuthors();
    }

    // Otherwise deal with annotation changes
    // Note: the annotations do not get attached to ["document", "title"],
    // as it seems strange to annotate a property which is used in such an indirect way
    if (Annotator.changesAnnotations(this.node.document, op, ["cover", "title"])) {
      //console.log("Rerendering TextView due to annotation update", op);
      this.childViews["title"].renderContent();
      return true;
    }
  };
};

CoverView.Prototype.prototype = NodeView.prototype;
CoverView.prototype = new CoverView.Prototype();

module.exports = CoverView;
<|endoftext|>


<commit_before>
import {days} from "../helpers";
import {Entry} from "./Entry";

export class BrowsingData
{
  constructor()
  {
    Object.assign(this, {
      tabs: [],
      session: [],
      topSites: [],
      bookmarks: [],
      bookmarks_all: [],
      storage: null,
    });
  }

  load()
  {
    return Promise.all([
      this.loadBookmarks(),
      this.loadSession(),
      this.loadTopsites(),
      this.loadTabs(),
      this.loadStorage()
    ])
  }

  async searchHistory(options)
  {
    return browser.history.search(options)
  }

  async loadSession()
  {
    let tabs = await browser.sessions.getRecentlyClosed({maxResults: 15});
    tabs = tabs.map(t => t.tab || t);
    tabs.length = Math.min(tabs.length, 15);
    return this.session = Entry.process(tabs, {props: {source: 'session'}} );

  }

  async loadHistory(options)
  {
    let history = await browser.history.search(options);
    return Entry.process(history, {props: {source: 'history'}} );

  }

  async loadTopsites()
  {
    let sites = await browser.topSites.get();
    sites.length = Math.min(sites.length, 15);
    return this.topSites = Entry.process(sites, {props: {source: 'topsite'}});

  }

  async loadTabs()
  {
    let tabs = await browser.tabs.query({});
    return this.tabs.set(Entry.process(tabs, {props: {source: 'tab', type: 'tab', weight: 50}}));
  }

  async loadBookmarks()
  {
    const bookmarks = (await browser.bookmarks.search({}));
    this.bookmarks_all = bookmarks;
    this.bookmarks = Entry.process(bookmarks.filter(isWebsite), {
      props: {source: 'bookmark'},
      setup: b => b.weight = Math.max(5, 30 - days.age(b.dateAdded)),
      reactive: true
    });

    function isWebsite(bookmarkNode) { return bookmarkNode.type == 'bookmark' && !bookmarkNode.url.match(/%s|{searchTerms}/) }

  }

  async loadStorage()
  {
    this.storage = await browser.storage.local.get() || {};
  }
}<commit_msg>dont show incognito tabs in recently closed<commit_after>
import {days} from "../helpers";
import {Entry} from "./Entry";

export class BrowsingData
{
  constructor()
  {
    Object.assign(this, {
      tabs: [],
      session: [],
      topSites: [],
      bookmarks: [],
      bookmarks_all: [],
      storage: null,
    });
  }

  load()
  {
    return Promise.all([
      this.loadBookmarks(),
      this.loadSession(),
      this.loadTopsites(),
      this.loadTabs(),
      this.loadStorage()
    ])
  }

  async searchHistory(options)
  {
    return browser.history.search(options)
  }

  async loadSession()
  {
    let tabs = await browser.sessions.getRecentlyClosed({maxResults: 25});
    tabs = tabs
        .map(t => t.tab || t)
        .filter(tab => !tab.incognito);
    tabs.length = Math.min(tabs.length, 15);
    return this.session = Entry.process(tabs, {props: {source: 'session'}} );

  }

  async loadHistory(options)
  {
    let history = await browser.history.search(options);
    return Entry.process(history, {props: {source: 'history'}} );

  }

  async loadTopsites()
  {
    let sites = await browser.topSites.get();
    sites.length = Math.min(sites.length, 15);
    return this.topSites = Entry.process(sites, {props: {source: 'topsite'}});

  }

  async loadTabs()
  {
    let tabs = await browser.tabs.query({});
    return this.tabs.set(Entry.process(tabs, {props: {source: 'tab', type: 'tab', weight: 50}}));
  }

  async loadBookmarks()
  {
    const bookmarks = (await browser.bookmarks.search({}));
    this.bookmarks_all = bookmarks;
    this.bookmarks = Entry.process(bookmarks.filter(isWebsite), {
      props: {source: 'bookmark'},
      setup: b => b.weight = Math.max(5, 30 - days.age(b.dateAdded)),
      reactive: true
    });

    function isWebsite(bookmarkNode) { return bookmarkNode.type == 'bookmark' && !bookmarkNode.url.match(/%s|{searchTerms}/) }

  }

  async loadStorage()
  {
    this.storage = await browser.storage.local.get() || {};
  }
}<|endoftext|>


<commit_before>/* global describe, it, xit */
/* jslint node: true, esnext: true */

"use strict";

const chai = require('chai');
const assert = chai.assert;
const expect = chai.expect;
const should = chai.should();

const defineGrammar = require('../lib/grammar');

describe("calculator",
  function () {
    function Value(value) {
      return Object.create(null, {
        value: {
          value: value
        }
      });
    }

    let myGrammar = defineGrammar({
      terminals: {
        'number': {}
      },
      prefix: {
        '(': {
          nud: function (grammar) {
            const e = grammar.expression(0);
            grammar.advance(')');
            return e;
          }
        }
      },
      infix: {
        ')': {},
        '+': {
          precedence: 50,
          combine: function (left, right) {
            return Value(left.value + right.value);
          }
        },
        '-': {
          precedence: 50,
          combine: function (left, right) {
            return Value(left.value - right.value);
          }
        },
        '*': {
          precedence: 60,
          combine: function (left, right) {
            return Value(left.value * right.value);
          }
        },
        '/': {
          precedence: 60,
          combine: function (left, right) {
            return Value(left.value / right.value);
          }
        }
      }
    });

    it("evaluates", function () {
      assert.equal(myGrammar.parse("1 + 41 * 3 - 2").value, 122);
    });

    it("evaluates with prefix op", function () {
      assert.equal(myGrammar.parse("(1 + 41)").value, 42);
    });
  });
<commit_msg>test: more expressions<commit_after>/* global describe, it, xit */
/* jslint node: true, esnext: true */

"use strict";

const chai = require('chai');
const assert = chai.assert;
const expect = chai.expect;
const should = chai.should();

const defineGrammar = require('../lib/grammar');

describe("calculator",
  function () {
    function Value(value) {
      return Object.create(null, {
        value: {
          value: value
        }
      });
    }

    let myGrammar = defineGrammar({
      terminals: {
        'number': {}
      },
      prefix: {
        '(': {
          nud: function (grammar) {
            const e = grammar.expression(0);
            grammar.advance(')');
            return e;
          }
        }
      },
      infix: {
        ')': {},
        '+': {
          precedence: 50,
          combine: function (left, right) {
            return Value(left.value + right.value);
          }
        },
        '-': {
          precedence: 50,
          combine: function (left, right) {
            return Value(left.value - right.value);
          }
        },
        '*': {
          precedence: 60,
          combine: function (left, right) {
            return Value(left.value * right.value);
          }
        },
        '/': {
          precedence: 60,
          combine: function (left, right) {
            return Value(left.value / right.value);
          }
        }
      }
    });

    it("evaluates", function () {
      assert.equal(myGrammar.parse("1 + 41 * 3 - 2").value, 122);
    });

    it("evaluates with prefix op", function () {
      assert.equal(myGrammar.parse("(1 + 41)").value, 42);
    });

    it("evaluates with prefix op 2", function () {
      assert.equal(myGrammar.parse("(1 + 41) * 2").value, 84);
    });
  });
<|endoftext|>


<commit_before>// ==UserScript==
// @name         NeoGAF MonkeyBot
// @namespace    http://github.com/petetnt/neogaf-monkeybot
// @version      0.0.1
// @description  Helper functions for NeoGAF's ModBot posts
// @author       PeteTNT
// @match        http://www.neogaf.com/forum/showthread.php?t=*
// @match        http://www.neogaf.com/forum/showpost.php?p=*
// @require http://code.jquery.com/jquery-latest.js
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText

// ==/UserScript==

var steamProfileName = "INSERT_STEAM_PROFILE_NAME_HERE",
    apiKey = "INSERT_YOUR_APIKEY_HERE",
    steamID = null,
    ownedGames = JSON.parse(localStorage.getItem("steamGameList")) || [],
    lastUpdate = localStorage.getItem("steamGameListUpdatedOn") || "";

function parseOwnedGames(json) {
    'use strict';
    var games = json.games,
        game = null;

    for (game in games) {
        if (games.hasOwnProperty(game)) {
            ownedGames.push(games[game].name);
        }
    }

    localStorage.setItem("steamGameList", JSON.stringify(ownedGames));
    localStorage.setItem("steamGameListUpdatedOn", new Date().toDateString());
    matchGames();
}

function matchGames() {
    'use strict';
    var modbotPosts = $("[data-username='ModBot'");

    modbotPosts.each(function (idx, elem) {
        var $elem = $(elem),
            text = $elem.text(),
            giveaways = text.match(/^ *(.*\b(?:MB-)\b.*)/gm);

        Object.keys(giveaways || {}).forEach(function (key) {
            var line = giveaways[key],
                name = line.split("--")[0].trim();

            if (ownedGames.indexOf(name) !== -1) {
                $elem.html($elem.html().replace(name, "IN LIBRARY  &nbsp" + name + ""));
            }
        });

        $(elem).replaceWith($elem);
    });
}

function getSteamID() {
    'use strict';
    var url = "http://steamcommunity.com/id/" + steamProfileName + "/?xml=1";
    GM_xmlhttpRequest({ // jshint ignore:line
        method: "GET",
        url: url,
        onload: function(response) {
            var xmlDoc = $.parseXML(response.responseText),
                $xml = $(xmlDoc);
            steamID = $xml.find("steamID64").text();
            loadOwnedGames();
        },
        onerror: function() {
            console.error("Retrieving SteamID failed. Make sure you have correcly entered your Steam profile name");
        }
    });
}

function loadOwnedGames() {
    'use strict';
    if (!steamID) {
        if( steamProfileName !== "") {
            getSteamID();
        } else {
            console.error("Retrieving SteamID failed. Make sure you have included your Steam profile name or SteamID");
            return;
        }
    }
    var service = "http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?",
        params = "key=" + apiKey + "&include_appinfo=1&steamid=" + steamID + "&format=json",
        url = service + params;

    GM_xmlhttpRequest({ // jshint ignore:line
        method: "GET",
        url: url,
        onload: function(response) {
            parseOwnedGames(JSON.parse(response.responseText).response);
        }
    });
}

if (ownedGames.length === 0 || new Date().toDateString !== lastUpdate) {
    loadOwnedGames();
} else {
    matchGames();
}

(function () {
    'use strict';
    var head,
        style;

    head = document.getElementsByTagName('head')[0];
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = ".inLibraryFlag {background: url(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUNDNzBFNTUyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUNDNzBFNTYyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5Q0M3MEU1MzIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5Q0M3MEU1NDIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv3vUKAAAAAlSURBVHjaYvz//z8DsYARpFhISAivjnfv3jGSp3jUGeQ4AyDAADZHNe2nyOBrAAAAAElFTkSuQmCC') no-repeat 4px 4px #4F95BD; left: 0; top: 42px; font-size: 10px; color: #111111; height: 18px; line-height: 19px; padding: 0 0 0 18px; white-space: nowrap; z-index: 5; display: inline-block; width: 60px; margin-right: 5px;} .inLibraryText { opacity: 0.7; }";
    head.appendChild(style);

}());
<commit_msg>Fixed iFrame problems caused by ads<commit_after>// ==UserScript==
// @name         NeoGAF MonkeyBot
// @namespace    http://github.com/petetnt/neogaf-monkeybot
// @version      0.0.1
// @description  Helper functions for NeoGAF's ModBot posts
// @author       PeteTNT
// @match        http://www.neogaf.com/forum/showthread.php?t=*
// @match        http://www.neogaf.com/forum/showpost.php?p=*
// @require http://code.jquery.com/jquery-latest.js
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText

// ==/UserScript==

var steamProfileName = "INSERT_STEAM_PROFILE_NAME_HERE",
    apiKey = "INSERT_YOUR_APIKEY_HERE",
    steamID = null,
    ownedGames = JSON.parse(localStorage.getItem("steamGameList")) || [],
    lastUpdate = localStorage.getItem("steamGameListUpdatedOn") || "";

function parseOwnedGames(json) {
    'use strict';
    var games = json.games,
        game = null;

    for (game in games) {
        if (games.hasOwnProperty(game)) {
            ownedGames.push(games[game].name);
        }
    }

    localStorage.setItem("steamGameList", JSON.stringify(ownedGames));
    localStorage.setItem("steamGameListUpdatedOn", new Date().toDateString());
    matchGames();
}

function matchGames() {
    'use strict';
    var modbotPosts = $("[data-username='ModBot']");

    modbotPosts.each(function (idx, elem) {
        var $elem = $(elem),
            text = $elem.text(),
            giveaways = text.match(/^ *(.*\b(?:MB-)\b.*)/gm);

        Object.keys(giveaways || {}).forEach(function (key) {
            var line = giveaways[key],
                name = line.split("--")[0].trim();

            if (ownedGames.indexOf(name) !== -1) {
                $elem.html($elem.html().replace(name, "IN LIBRARY  &nbsp" + name + ""));
            }
        });

        $(elem).replaceWith($elem);
    });
}

function getSteamID() {
    'use strict';
    var url = "http://steamcommunity.com/id/" + steamProfileName + "/?xml=1";
    GM_xmlhttpRequest({ // jshint ignore:line
        method: "GET",
        url: url,
        onload: function(response) {
            var xmlDoc = $.parseXML(response.responseText),
                $xml = $(xmlDoc);
            steamID = $xml.find("steamID64").text();
            loadOwnedGames();
        },
        onerror: function() {
            console.error("Retrieving SteamID failed. Make sure you have correcly entered your Steam profile name");
        }
    });
}

function loadOwnedGames() {
    'use strict';
    if (!steamID) {
        if( steamProfileName !== "") {
            getSteamID();
        } else {
            console.error("Retrieving SteamID failed. Make sure you have included your Steam profile name or SteamID");
            return;
        }
    }
    var service = "http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?",
        params = "key=" + apiKey + "&include_appinfo=1&steamid=" + steamID + "&format=json",
        url = service + params;

    GM_xmlhttpRequest({ // jshint ignore:line
        method: "GET",
        url: url,
        onload: function(response) {
            parseOwnedGames(JSON.parse(response.responseText).response);
        }
    });
}

if (window.top !== window.self) {
    return;
} else {
    if (ownedGames.length === 0 || new Date().toDateString !== lastUpdate) {
        loadOwnedGames();
    } else {
        matchGames();
    }
}

(function () {
    'use strict';
    var head,
        style;

    head = document.getElementsByTagName('head')[0];
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = ".inLibraryFlag {background: url(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUNDNzBFNTUyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUNDNzBFNTYyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5Q0M3MEU1MzIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5Q0M3MEU1NDIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv3vUKAAAAAlSURBVHjaYvz//z8DsYARpFhISAivjnfv3jGSp3jUGeQ4AyDAADZHNe2nyOBrAAAAAElFTkSuQmCC') no-repeat 4px 4px #4F95BD; left: 0; top: 42px; font-size: 10px; color: #111111; height: 18px; line-height: 19px; padding: 0 0 0 18px; white-space: nowrap; z-index: 5; display: inline-block; width: 60px; margin-right: 5px;} .inLibraryText { opacity: 0.7; }";
    head.appendChild(style);

}());
<|endoftext|>