<commit_before>import uuid from 'node-uuid';

import JexlEnvironment from './JexlEnvironment.js';

const registeredActions = {};
window.registerAction = (name, ActionClass) => {
  registeredActions[name] = ActionClass;
};

/**
 * Download the implementation of the given action from the server.
 *
 * @param {Action} action Action object from the server.
 * @promise {Function} The action class for the given recipe's action.
 * @rejects {Error} Rejects if the action could not be loaded or did not
 *     register itself.
 */
export function loadActionImplementation(action) {
  const cache = loadActionImplementation._cache;

  if (!(action.name in cache)) {
    cache[action.name] = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = action.implementation_url;
      script.onload = () => {
        if (!(action.name in registeredActions)) {
          reject(new Error(`Could not find action with name ${action.name}.`));
        } else {
          resolve(registeredActions[action.name]);
        }
      };
      document.head.appendChild(script);
    });
  }

  return cache[action.name];
}
loadActionImplementation._cache = {};

/**
 * Fetch an action object from the server for the given recipe.
 *
 * @param {Recipe} recipe Recipe object from the server.
 * @promise {Action} The Action object from the server.
 */
export function fetchAction(recipe) {
  const cache = fetchAction._cache;

  if (!(recipe.action in cache)) {
    const headers = { Accept: 'application/json' };
    cache[recipe.action] = fetch(`/api/v1/action/${recipe.action}/`, { headers })
      .then(response => response.json());
  }

  return cache[recipe.action];
}
fetchAction._cache = {};

/**
 * Get a user id. If one doesn't exist yet, make one up and store it in local storage.
 * @return {String} A stored or generated UUID
 */
export function getUserId() {
  let userId = localStorage.getItem('userId');
  if (userId === null) {
    userId = uuid.v4();
    localStorage.setItem('userId', userId);
  }
  return userId;
}

/**
 * Fetch all enabled recipes from the server.
 * @promise Resolves with a list of all enabled recipes.
 */
export function fetchRecipes() {
  const { recipeUrl } = document.documentElement.dataset;
  const headers = { Accept: 'application/json' };

  return fetch(`${recipeUrl}?enabled=true`, { headers })
  .then(response => response.json());
}

/**
 * Fetch client information from the Normandy server.
 * @promise Resolves with an object containing client info.
 */
export function classifyClient() {
  let { classifyUrl } = document.documentElement.dataset;
  const headers = { Accept: 'application/json' };

  classifyUrl = new URL(classifyUrl, window.location.href);

  return fetch(classifyUrl.href, { headers })
  .then(response => response.json())
  .then(classification => {
    // Parse request time
    classification.request_time = new Date(classification.request_time);
    return classification;
  });
}

/**
 * Fetch and execute the actions for the given recipe.
 *
 * @param {Recipe} recipe - Recipe retrieved from the server.
 * @promise Resolves once the action has executed.
 */
export async function runRecipe(recipe, driver, options = {}) {
  const action = await fetchAction(recipe);
  const ActionImplementation = await loadActionImplementation(action);
  if (options.testing !== undefined) {
    driver.testing = options.testing;
  }

  return new ActionImplementation(driver, recipe).execute();
}

/**
 * Generate a context object for JEXL filter expressions.
 * @return {object}
 */
export async function filterContext(driver) {
  const classification = await classifyClient();
  const client = await driver.client();

  return {
    normandy: {
      locale: driver.locale,
      userId: getUserId(),
      ...client,
      ...classification,
    },
  };
}

/**
 * Match a recipe against a context using its filter expression.
 * @param  {Recipe} recipe  Recipe fetched from the server.
 * @param  {object} context Context returned by filterContext.
 * @promise Resolves with a list containing the recipe and a boolean
 *     signifying if the filter passed or failed.
 */
export function doesRecipeMatch(recipe, context) {
  const jexlEnv = new JexlEnvironment({ recipe, ...context });
  return jexlEnv.eval(recipe.filter_expression).then(value => [recipe, !!value]);
}
<commit_msg>Removed uuid stuff from self repair runner<commit_after>import JexlEnvironment from './JexlEnvironment.js';

const registeredActions = {};
window.registerAction = (name, ActionClass) => {
  registeredActions[name] = ActionClass;
};

/**
 * Download the implementation of the given action from the server.
 *
 * @param {Action} action Action object from the server.
 * @promise {Function} The action class for the given recipe's action.
 * @rejects {Error} Rejects if the action could not be loaded or did not
 *     register itself.
 */
export function loadActionImplementation(action) {
  const cache = loadActionImplementation._cache;

  if (!(action.name in cache)) {
    cache[action.name] = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = action.implementation_url;
      script.onload = () => {
        if (!(action.name in registeredActions)) {
          reject(new Error(`Could not find action with name ${action.name}.`));
        } else {
          resolve(registeredActions[action.name]);
        }
      };
      document.head.appendChild(script);
    });
  }

  return cache[action.name];
}
loadActionImplementation._cache = {};

/**
 * Fetch an action object from the server for the given recipe.
 *
 * @param {Recipe} recipe Recipe object from the server.
 * @promise {Action} The Action object from the server.
 */
export function fetchAction(recipe) {
  const cache = fetchAction._cache;

  if (!(recipe.action in cache)) {
    const headers = { Accept: 'application/json' };
    cache[recipe.action] = fetch(`/api/v1/action/${recipe.action}/`, { headers })
      .then(response => response.json());
  }

  return cache[recipe.action];
}
fetchAction._cache = {};

/**
 * Fetch all enabled recipes from the server.
 * @promise Resolves with a list of all enabled recipes.
 */
export function fetchRecipes() {
  const { recipeUrl } = document.documentElement.dataset;
  const headers = { Accept: 'application/json' };

  return fetch(`${recipeUrl}?enabled=true`, { headers })
  .then(response => response.json());
}

/**
 * Fetch client information from the Normandy server.
 * @promise Resolves with an object containing client info.
 */
export function classifyClient() {
  let { classifyUrl } = document.documentElement.dataset;
  const headers = { Accept: 'application/json' };

  classifyUrl = new URL(classifyUrl, window.location.href);

  return fetch(classifyUrl.href, { headers })
  .then(response => response.json())
  .then(classification => {
    // Parse request time
    classification.request_time = new Date(classification.request_time);
    return classification;
  });
}

/**
 * Fetch and execute the actions for the given recipe.
 *
 * @param {Recipe} recipe - Recipe retrieved from the server.
 * @promise Resolves once the action has executed.
 */
export async function runRecipe(recipe, driver, options = {}) {
  const action = await fetchAction(recipe);
  const ActionImplementation = await loadActionImplementation(action);
  if (options.testing !== undefined) {
    driver.testing = options.testing;
  }

  return new ActionImplementation(driver, recipe).execute();
}

/**
 * Generate a context object for JEXL filter expressions.
 * @return {object}
 */
export async function filterContext(driver) {
  const classification = await classifyClient();
  const client = await driver.client();

  return {
    normandy: {
      locale: driver.locale,
      userId: driver.userId,
      ...client,
      ...classification,
    },
  };
}

/**
 * Match a recipe against a context using its filter expression.
 * @param  {Recipe} recipe  Recipe fetched from the server.
 * @param  {object} context Context returned by filterContext.
 * @promise Resolves with a list containing the recipe and a boolean
 *     signifying if the filter passed or failed.
 */
export function doesRecipeMatch(recipe, context) {
  const jexlEnv = new JexlEnvironment({ recipe, ...context });
  return jexlEnv.eval(recipe.filter_expression).then(value => [recipe, !!value]);
}
<|endoftext|>


<commit_before>/*
 * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
 *
 * @author Julius Härtl <jus@bitgrid.net>
 *
 * @license GNU AGPL version 3 or any later version
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

app.directive('appPopoverMenuUtils', function () {
    'use strict';
    return {
        restrict: 'C',
        link: function (scope, elm) {
            var menu = elm.find('.popovermenu');
            var button = elm.find('button');
            button.click(function (e) {
                menu.toggleClass('hidden');
                if(!menu.hasClass('hidden')) {
                    button.css('display','block');
                } else {
                    button.css('display','');
                }
				e.stopPropagation();
			});
            scope.$on('documentClicked', function (scope, event) {
                /* prevent closing popover if target has no-close class */
                if (event.target !== button && !$(event.target).hasClass('no-close')) {
                    menu.addClass('hidden');
                }
                button.css('display','');
            });
        }
    };
});
<commit_msg>Fix #167 - hide opened menus<commit_after>/*
 * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
 *
 * @author Julius Härtl <jus@bitgrid.net>
 *
 * @license GNU AGPL version 3 or any later version
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

app.directive('appPopoverMenuUtils', function () {
    'use strict';
    return {
        restrict: 'C',
        link: function (scope, elm) {
            var menu = elm.find('.popovermenu');
            var button = elm.find('button');
            button.click(function (e) {
                $('.popovermenu').addClass('hidden');
                menu.toggleClass('hidden');
                if(!menu.hasClass('hidden')) {
                    button.css('display','block');
                } else {
                    button.css('display','');
                }
				e.stopPropagation();
			});
            scope.$on('documentClicked', function (scope, event) {
                /* prevent closing popover if target has no-close class */
                if (event.target !== button && !$(event.target).hasClass('no-close')) {
                    menu.addClass('hidden');
                }
                button.css('display','');
            });
        }
    };
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Card, CardHeader, CardText } from 'material-ui/Card';
import IconButton from 'material-ui/IconButton';
import Clear from 'material-ui/svg-icons/content/clear';
import {
  propTypes as routerPropTypes,
  withRouter,
} from 'react-router-dom';

import {
  setDisplayMode,
  setComposeState,
} from '../actions/ui';
import { postListing, updateListing } from '../actions/listings';
import { postSeek } from '../actions/seeks';
import ComposeForm from '../components/ComposeForm';
import SeekComposeForm from '../components/SeekComposeForm';
import RedirectToCas from '../components/RedirectToCas';

const overlayStyle = {
  position: 'fixed',
  bottom: '0',
  zIndex: '99',
  right: '5%',
  width: '40vw',
  minWidth: '16rem',
};

const showStyle = {};
const hideStyle = { display: 'none' };

class ComposeOverlay extends Component {
  static propTypes = {
    ...routerPropTypes,
    dispatch: PropTypes.func.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    user: PropTypes.shape({
      loggedIn: PropTypes.bool.isRequired,
    }).isRequired,
    currentUserLoading: PropTypes.bool.isRequired,
    isEdit: PropTypes.bool.isRequired,
    mode: PropTypes.string.isRequired,
  };

  state = {
    mode: this.props.mode,
    expanded: true,
  }

  handleRequestClose = event => this.props.dispatch(setComposeState(false))

  handleSubmitListing = (data) => {
    this.props.dispatch(postListing({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    },
    `Successfully created listing ${data.title}`,
    ));
    this.props.history.push('/listings/mine');
    this.handleRequestClose();
  }

  handleSubmitSeek = (data) => {
    this.props.dispatch(postSeek({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    },
    `Successfully created seek ${data.title}`,
    ));
    this.props.history.push('/seeks/mine');
    this.handleRequestClose();
  }

  handleEditListing = (data) => {
    this.props.dispatch(updateListing({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    }, `Successfully updated listing ${data.title}`));
    this.handleRequestClose();
  }

  handleToggle = (event, isInputChecked) => {
    this.props.dispatch(setDisplayMode(isInputChecked ? 'seeks' : 'listings'));
  }

  handleExpandChange = (expanded) => {
    this.setState({ expanded });
  }

  render() {
    if (!this.props.currentUserLoading && !this.props.user.loggedIn) {
      return <RedirectToCas />;
    }

    return (

        <Card expanded={this.state.expanded} onExpandChange={this.handleExpandChange} zDepth={2}>
          <CardHeader title={this.props.isEdit ? 'Edit' : 'Compose'} actAsExpander>
            <IconButton
              onTouchTap={this.handleRequestClose}
              style={{ float: 'right', marginTop: '-15px', marginRight: '-15px' }}
            >
              <Clear />
            </IconButton>
          </CardHeader>
          <CardText style={this.state.expanded ? showStyle : hideStyle}>
            { (this.state.mode === 'listings') ?
              <ComposeForm
                onSubmit={this.props.isEdit ? this.handleEditListing : this.handleSubmitListing}
                initialValues={
                  this.props.isEdit ?
                  { ...this.props.listing, price: this.props.listing.price / 100 } : {}
                }
              />
              : <SeekComposeForm onSubmit={this.handleSubmitSeek} />
            }
          </CardText>
        </Card>

    );
  }
}

const mapStateToProps = state => ({
  user: state.currentUser,
  form: state.form,
  mode: state.composeState.mode,
  currentUserLoading: state.currentUserLoading,
  isEdit: state.composeState.isEdit,
  listing: state.composeState.listing,
  seek: state.composeState.seek,
});

export default withRouter(connect(mapStateToProps)(ComposeOverlay));
<commit_msg>code formatting conventions<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Card, CardHeader, CardText } from 'material-ui/Card';
import IconButton from 'material-ui/IconButton';
import Clear from 'material-ui/svg-icons/content/clear';
import {
  propTypes as routerPropTypes,
  withRouter,
} from 'react-router-dom';

import {
  setDisplayMode,
  setComposeState,
} from '../actions/ui';
import { postListing, updateListing } from '../actions/listings';
import { postSeek } from '../actions/seeks';
import ComposeForm from '../components/ComposeForm';
import SeekComposeForm from '../components/SeekComposeForm';
import RedirectToCas from '../components/RedirectToCas';

const overlayStyle = {
  position: 'fixed',
  bottom: '0',
  zIndex: '99',
  right: '5%',
  width: '40vw',
  minWidth: '16rem',
};

const showStyle = {};
const hideStyle = { display: 'none' };

class ComposeOverlay extends Component {
  static propTypes = {
    ...routerPropTypes,
    dispatch: PropTypes.func.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    user: PropTypes.shape({
      loggedIn: PropTypes.bool.isRequired,
    }).isRequired,
    currentUserLoading: PropTypes.bool.isRequired,
    isEdit: PropTypes.bool.isRequired,
    mode: PropTypes.string.isRequired,
  };

  state = {
    mode: this.props.mode,
    expanded: true,
  }

  handleRequestClose = event => this.props.dispatch(setComposeState(false))

  handleSubmitListing = (data) => {
    this.props.dispatch(postListing({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    },
    `Successfully created listing ${data.title}`,
    ));
    this.props.history.push('/listings/mine');
    this.handleRequestClose();
  }

  handleSubmitSeek = (data) => {
    this.props.dispatch(postSeek({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    },
    `Successfully created seek ${data.title}`,
    ));
    this.props.history.push('/seeks/mine');
    this.handleRequestClose();
  }

  handleEditListing = (data) => {
    this.props.dispatch(updateListing({
      ...data,
      price: data.price ? Math.round(parseFloat(data.price) * 100) : 0,
    },
    `Successfully updated listing ${data.title}`,
    ));
    this.handleRequestClose();
  }

  handleToggle = (event, isInputChecked) => {
    this.props.dispatch(setDisplayMode(isInputChecked ? 'seeks' : 'listings'));
  }

  handleExpandChange = (expanded) => {
    this.setState({ expanded });
  }

  render() {
    if (!this.props.currentUserLoading && !this.props.user.loggedIn) {
      return <RedirectToCas />;
    }

    return (

        <Card expanded={this.state.expanded} onExpandChange={this.handleExpandChange} zDepth={2}>
          <CardHeader title={this.props.isEdit ? 'Edit' : 'Compose'} actAsExpander>
            <IconButton
              onTouchTap={this.handleRequestClose}
              style={{ float: 'right', marginTop: '-15px', marginRight: '-15px' }}
            >
              <Clear />
            </IconButton>
          </CardHeader>
          <CardText style={this.state.expanded ? showStyle : hideStyle}>
            { (this.state.mode === 'listings') ?
              <ComposeForm
                onSubmit={this.props.isEdit ? this.handleEditListing : this.handleSubmitListing}
                initialValues={
                  this.props.isEdit ?
                  { ...this.props.listing, price: this.props.listing.price / 100 } : {}
                }
              />
              : <SeekComposeForm onSubmit={this.handleSubmitSeek} />
            }
          </CardText>
        </Card>

    );
  }
}

const mapStateToProps = state => ({
  user: state.currentUser,
  form: state.form,
  mode: state.composeState.mode,
  currentUserLoading: state.currentUserLoading,
  isEdit: state.composeState.isEdit,
  listing: state.composeState.listing,
  seek: state.composeState.seek,
});

export default withRouter(connect(mapStateToProps)(ComposeOverlay));
<|endoftext|>


<commit_before>var stuff = [
  ['Dwyneed', 'SMS a human who will talk you out of purchases', false],
  ['Watercoolr', 'Pop culture updates to keep up with your informed coworkers', false],
  ['Pinebox', 'Birchbox for morticians', false],
  ["You're Probably Not Dying",  'Chrome plugin that redirects WebMD traffic to /r/aww', false],
  ["Morekat", 'Hire watchers to up your Meerkat statistics', false],
  ['DickStarter', 'Crowdfund whaling voyages', false],
  ["Clickab.ly", 'A web tool to help journalists remove information from headlines', false],
  ['Keuritos', 'Keurig for burrito filling', false],
  ['Streamless', 'On-demand DVD delivery', false],
  ['Thrones Scoreboard', 'Real-time Game of Thrones updates pushed to Apple Watch', false],
  ['Pettr', 'Connects lonely animals with people to pet them', false],
  ['ClutterCasher', 'Storage service that eBays things you haven’t asked for in awhile', false],
  ['InCase by Amazon', 'Recommendation engine sends you things it predicts you want', false],
  ['SweepUnder', 'Service to hide your apartment clutter on short notice', false],
  ['Lush', 'Amazon Dash for boxed wine', false],
  ['Soapsy', 'Scrub mean tweets about a company when you send them a resume', false],
  ['Parenting Helicopter', 'A livestreaming drone that surveils your teen', false],
  ['Marathon', 'Strava for Netflix powerusers', false],
  ['ScaleFindr', 'Identify the right KPIs by finding metrics that sound really big', false],
  ['WatchBoard', 'A wearable keyboard for smartwatches (by Microsoft)', false],
  ['WatchMore', 'A wearable second screen for your Apple Watch', false],
  ['StubHub for GrubHub', 'Flip your delivery', false],
  ['Auf', "Disables your phone's display and blocks all incoming messages", false],
  ['Monetize.me', 'Share your $cashtag, BTC address, PayPal and more', false],
  ['CrowdTaker', 'Aggregates reactions and informs you of your opinion on the news', false],
  ['Cannt', 'Generates last-minute excuses to get out of plans', false],
  ['Stüul',  'A tall, elegant chair for people with standing desks', false],
  ['Slock', 'Like Slack, but secure', false],
  ['Credditor', 'Automatically post source in comments when your content is posted on Reddit', false],
  ['Crammr', 'Beautiful tool for automating adoption of your brand messaging', false],
  ['APPS by TOMS', 'For every app you buy, one is sent to a village', false],
  ['BaeWatch', 'Sends a notification whenever your partner likes their ex’s selfie', false],
  ['Synonymo.us', 'Rephrases your app idea as "Uber for X" or "Tinder for Y"', false],
  ['FlaskRabbit', 'On-demand covert liquor delivery', false],
  ['DeListicle', ' Translate listicles to normal long-form essays', false],
  ['Slacker', 'Slack plugin that auto-replies "brb phone" during work hours', false],
  ['Snapkat', 'Tweet live video, but like, only for teens and marketers', false],
  ['WeWorkTomorrow', 'A coworking space with no desks', false],
  ['RantStormer', 'Copy and paste a blogpost to chop it into numbered tweets', false],
  ['Fauxffeur', 'Lifelike driver statue for the front seat of your self-driving car', false],
  ['Soylrig', 'Keurig for Soylent', false],
  ['FaceFeed', 'Uses an algorithm to generate content and shares it', false],
  ['iSweariDid', 'Tweets about how hard you worked out today, no matter what', false],
  ['SolitAIR', "3D print files for Microsoft's classic Solitaire game IRL", false],
  ['Uber Your Enemies Glitter', 'Send glitter, for 2-10x as much on nights and weekends', false],
  ['EggSquirt', 'Keurig for caviar', false],
  ['Meerkat', 'A small African carnivoran belonging to the mongoose family', false],
  ['Skullscription', 'Monthtly disposable helmets for CitiBikers', false],
  ['Miser', 'An OkCupid companion tool that invests the money you save by no-showing', false],
  ['Maybook', 'RSVPs "Maybe" to every Facebook event invite', false],
  ['TiffSkipper', 'Temporarily deactivates your Facebook until November 9, 2016', false],
  ['Spoofify.fm', 'Automatically scrobbles every Pitchfork BNM selection for cred', false],
  ['Teen Text', 'An elegant, dad-friendly app that translates emoji to English', false],
  ['CrapRabbit', "Marketplace for contractors to pick up your dog's poop", false],
  ['Swerpes', 'An unobtrusive, iOS compatible STD testing kit for Tinder users', false],
  ['TieLocker','Automatically sends a tie every month directly to your storage space', false],
  ['DeskNest', 'A rollable nap pad that fits under your desk', false],
  ['Coverjudge', 'Tinder for books', false],
  ['Civickstarter', 'Kickstarter for public works', false],
  ['Fit Good', 'Glassdor-style descriptions of how employees dress', false],
  ['Rockbox', 'Five pet rocks delivered to your door every month', false],
  ['BragTagg', 'Adds "Founder & CEO" to your Twitter bio when you have an idea', false],

  ['Newscombinator', 'Like Google-News, but for hackers', 'newscombinator'],
  ['Meer-katalytics', 'Complete analytics for all Meerkats', 'meer-katalytics'],
  ['4 Minute Workout', "The world's fastest full body workout in just 4 minutes", '4-minute-workout'],
  ['Pug A Day', 'Send a friend 100 days of Pug pictures', 'pug-a-day'],
  ['Pick Up, Knock Down', 'Get a compliment and an insult from a stranger', 'pick-me-up-knock-me-down'],
  ['TreadDesk', 'Walk while you work', 'treaddesk'],
  ['Wrte.io', 'Email with a paywall. Charge for reading your email.', 'wrte-io'],
  ['DystopiaTracker', 'Explore predictions about the future and their realizations', 'dystopiatracker'],
  ['alibi.', 'Verify your story with passive geo tracking and Touch ID', 'alibi-2'],
  ['Dreeps', "A game that plays itself because you're too busy", 'dreeps'],
  ['Hushbook', 'Tinder for unfriending on Facebook', 'hushbook'],
  ['Slater', 'Schedule your Slack messages now, post them later', 'slater-by-convergely'],
  ['Blindfold', 'Siri for sex. Your digital foreplay assistant.', 'blindfold'],
  ['Flinch', 'Staring contest with strangers', 'flinch'],
  ['Ship Snow, Yo', 'We Ship Snow To Anyone in the US', 'ship-snow-yo'],
  ['Hoffice', "Cowork at someone's home", 'hoffice'],
  ['Hotels By Day', 'Experience a great hotel by day without paying for the night', 'hotels-by-day'],
  ['Gutsy', 'Find out what your friends truly think of you', 'gutsy'],
  ['Wevorce', 'Streamline the divorce process online', 'wevorce'],
  ['Rise', 'All-you-can-fly, private air travel membership', 'rise-5'],
  ['Dreaming With Jeff', 'Sleep better with the sound of Jeff Bridges', 'dreaming-with-jeff'],
  ['Am I Going Down?', 'Analyzes the chance of your flight crashing.', 'am-i-going-down'],
  ['Toothpick', "Calculate your tip based on the server's gender", 'toothpick'],
  ['Nudifier', 'Make people in photos look faux naked.', 'nudifier'],
  ['Invisible Boyfriend', 'Finally, a boyfriend your family can believe in.', 'invisible-boyfriend'],
  ['Self-Destructing Book', '24 hours to read a book before it self-destructs', 'self-destructing-book'],
  ['Sailo', 'Airbnb for boats', 'sailo'],
  ['Rejection Therapy', 'A game to conquer fear by getting rejected', 'rejection-therapy'],
  ['Be My Eyes', 'Lend your eyes to the blind', 'be-my-eyes-2'],
  ['Ship Your Enemies Glitter', 'Send glitter to the people that deserve it', 'ship-your-enemies-glitter'],
  ['Love Actualized', 'App for falling in love', 'love-actualized'],
  ['Vibease', "World's first wearable smart vibrator", 'vibease'],
  ['Hotel My Phone', "Borrow a friend's phone & use it as your own.", 'hotel-my-phone'],
  ['Listen to Twitter', 'Convert the sentiment of any topic into a unique melody', 'listen-to-twitter'],
  ['Pay with a Tweet', 'Sell your product for the price of a tweet', 'pay-with-a-tweet'],
  ['Blade', 'Helicopter ride sharing', 'blade'],
  ['gitr', 'Tinder for developers', 'girt'],
  ['Wonker', 'User-generated explanations on complex current events', 'wonker'],
  ['Make It Rain', 'Random gifs when you get paid via Stripe', 'make-it-rain'],
  ['Where have you cried?', 'Opened-sourced map of where people have publicly cried', 'where-have-you-cried'],
  ['Slackroulette', 'Chat with Slackers', 'slackroulette'],
  ['What Color Is It?', 'See the hex color for every second of the day', 'what-color-is-it'],
  ['Hello Santa', 'Video Chat Live with Santa!', 'hello-santa'],
  ['Stormy', 'Twitter Tweet Storms, made easy', 'stormy'],
  ['Futurism', 'Product Hunt for news about the future', 'futurism'],
  ['Likecreeper', "Randomly hit like on a friend's horrifyingly old instagram", 'likecreeper'],
  ['Bobler', 'Record 36 sec podcasts', 'bobler'],
  ['Stamp Yo Face!', 'Get your own custom-made selfie stamp', 'stamp-yo-face'],
  ['Yo B*tch', 'Yo app by Aaron Paul', 'yo-b-tch'],
  ['No CAPTCHA reCAPTCHA', "Update to CAPTCHA just asks if you're robot", 'no-captcha-recaptcha'],
  ['Plague', 'Spread content like a virus', 'plague'],
  ['Endangered Sounds Museum', 'Nostalgic sounds from the past', 'endangered-sounds-museum'],
  ['Selfie Dolls', 'Have a selfie doll made in your likeness', 'selfie-dolls'],
  ['Pushh', 'Keep track of your fitness selfies', 'pushh'],
  ['Emojiary', 'Tap into your feelings - using emoji', 'emojiary'],
  ['Sock Swag', 'Custom-branded socks for your company', 'sock-swag'],
  ['Ethan', 'A messaging app for messaging Ethan', 'ethan'],
  ['Capsule', 'Text the future', 'capsule'],
  ['Companion', 'Walking home alone is dangerous. Stop doing it alone.', 'companion'],
  ['Push for Pizza', 'The easiest way to order pizza. EVER.', 'push-for-pizza'],
  ['Startup Legitimizer', 'Make your startup appear super legit', 'startup-legitimizer-2'],
  ['Shady URL', "Don't just shorten your URL, make it suspicious and frighten", 'shady-url'],
  ['Geocities-izer', 'Make any site look like a throwback Geocities site', 'geocities-izer-2'],
  ['Somebody', 'Send a message delivered verbally by a stranger', 'somebody'],
  ['Mermur', 'Anonymous text messages. No replies.', 'mermur'],
  ['Lukewarm Emailer', 'Build a list of people on Twitter, reach out w/ a cold email', 'lukewarm-emailer'],
];

(function() {
  $(window).on('resize', function() {
    $('section').height($(window).height());
  }).trigger('resize');

  if(isMobile.any) {
    $('body').addClass('mobile')
  }

  shuffle(stuff);
  shuffle(stuff); // For good measure
  shuffle(stuff); // Becuase why not?

  // Add in a plug :)
  stuff.splice(7, 0, ['ReadMe.io', 'Beautiful Documentation Made Easy', 'readme-2']);

  var i = 0;
  var next = stuff[i];
  $('.item.current h3').text(next[0]);
  $('.item.current p').text(next[1]);

  var score = (window.localStorage['score'] || 0)*1;
  var total = (window.localStorage['total'] || 0)*1;

  function updateScore() {
    $('.update-score').text(score);
    $('.update-total').text(total);
    $('.update-percent').text(Math.floor((score / total) * 100) + "%");

    window.localStorage['total'] = total;
    window.localStorage['score'] = score;
  };

  $('.btn-next, .button').on({
    'touchstart' : function(){
      $(this).addClass('active');
    },
    'touchend': function() {
      $(this).removeClass('active');
    }
  });

  $('.button').click(function() {
    var real = next[2];
    var correct = $(this).attr('guess') == (real ? 'real' : 'fake');

    if(isMobile.any) {
      $('body').addClass('answer');
      $('.btn-next').click(function(e) {
        $('body').removeClass('answer');
        return false;
      });
    }

    if(real != 'readme-2') {
      total++;
      if(correct) score++;
      updateScore();
    }

    i++;

    if(i >= stuff.length) i = 0;
    next = stuff[i];

    if(!next) {
      console.log('i', i);
    }

    $('.quote').addClass('hide');
    setTimeout(function() {
      $('.quote').remove();
    }, 1000);

    $('.score').addClass('on');
    $('.item.next').addClass('hide');

    $('.item.current').addClass('next').removeClass('current');
    $('.right').removeClass('yes no readme');
    if(real == 'readme-2') {
      $('.right').addClass('readme');
    } else {
      $('.right').addClass(correct ? 'yes' : 'no');
    }

    var text = '';
    if(correct) {
      if(real) {
        text = 'Yup, this is a real product!';
      } else {
        text = 'You spotted the fake!';
      }
    } else {
      if(real) {
        text = 'Actually, it is real!';
      } else {
        text = 'Nope, not real… yet!';
      }
    }

    if(real == 'readme-2') {
      text = 'Okay, shameless plug… But check it out!';
    }

    $('.score h4').html(text);

    var clone = function(side) {
      var $el = $('#demo').clone().attr('id', null);
      $('.' + side + ' .items').append($el.addClass('current'));
      $('h3', $el).text(next[0]);
      $('p', $el).text(next[1]);
    };

    clone('left');
    clone('right');

    $('.item.current').show();
    $('.left .item.current').addClass('fadein');
  });

  function shuffle(array) {
    var currentIndex = array.length, temporaryValue, randomIndex ;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {

      // Pick a remaining element...
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;

      // And swap it with the current element.
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }

    return array;
  }
})();
<commit_msg>Some new @fakeproducthunt ideas<commit_after>var stuff = [
  ['Dwyneed', 'SMS a human who will talk you out of purchases', false],
  ['Watercoolr', 'Pop culture updates to keep up with your informed coworkers', false],
  ['Pinebox', 'Birchbox for morticians', false],
  ["You're Probably Not Dying",  'Chrome plugin that redirects WebMD traffic to /r/aww', false],
  ["Morekat", 'Hire watchers to up your Meerkat statistics', false],
  ['DickStarter', 'Crowdfund whaling voyages', false],
  ["Clickab.ly", 'A web tool to help journalists remove information from headlines', false],
  ['Keuritos', 'Keurig for burrito filling', false],
  ['Streamless', 'On-demand DVD delivery', false],
  ['Thrones Scoreboard', 'Real-time Game of Thrones updates pushed to Apple Watch', false],
  ['Pettr', 'Connects lonely animals with people to pet them', false],
  ['ClutterCasher', 'Storage service that eBays things you haven’t asked for in awhile', false],
  ['InCase by Amazon', 'Recommendation engine sends you things it predicts you want', false],
  ['SweepUnder', 'Service to hide your apartment clutter on short notice', false],
  ['Lush', 'Amazon Dash for boxed wine', false],
  ['Soapsy', 'Scrub mean tweets about a company when you send them a resume', false],
  ['Parenting Helicopter', 'A livestreaming drone that surveils your teen', false],
  ['Marathon', 'Strava for Netflix powerusers', false],
  ['ScaleFindr', 'Identify the right KPIs by finding metrics that sound really big', false],
  ['WatchBoard', 'A wearable keyboard for smartwatches (by Microsoft)', false],
  ['WatchMore', 'A wearable second screen for your Apple Watch', false],
  ['StubHub for GrubHub', 'Flip your delivery', false],
  ['Auf', "Disables your phone's display and blocks all incoming messages", false],
  ['Monetize.me', 'Share your $cashtag, BTC address, PayPal and more', false],
  ['CrowdTaker', 'Aggregates reactions and informs you of your opinion on the news', false],
  ['Cannt', 'Generates last-minute excuses to get out of plans', false],
  ['Stüul',  'A tall, elegant chair for people with standing desks', false],
  ['Slock', 'Like Slack, but secure', false],
  ['Credditor', 'Automatically post source in comments when your content is posted on Reddit', false],
  ['Crammr', 'Beautiful tool for automating adoption of your brand messaging', false],
  ['APPS by TOMS', 'For every app you buy, one is sent to a village', false],
  ['BaeWatch', 'Sends a notification whenever your partner likes their ex’s selfie', false],
  ['Synonymo.us', 'Rephrases your app idea as "Uber for X" or "Tinder for Y"', false],
  ['FlaskRabbit', 'On-demand covert liquor delivery', false],
  ['DeListicle', ' Translate listicles to normal long-form essays', false],
  ['Slacker', 'Slack plugin that auto-replies "brb phone" during work hours', false],
  ['Snapkat', 'Tweet live video, but like, only for teens and marketers', false],
  ['WeWorkTomorrow', 'A coworking space with no desks', false],
  ['RantStormer', 'Copy and paste a blogpost to chop it into numbered tweets', false],
  ['Fauxffeur', 'Lifelike driver statue for the front seat of your self-driving car', false],
  ['Soylrig', 'Keurig for Soylent', false],
  ['FaceFeed', 'Uses an algorithm to generate content and shares it', false],
  ['iSweariDid', 'Tweets about how hard you worked out today, no matter what', false],
  ['SolitAIR', "3D print files for Microsoft's classic Solitaire game IRL", false],
  ['Uber Your Enemies Glitter', 'Send glitter, for 2-10x as much on nights and weekends', false],
  ['EggSquirt', 'Keurig for caviar', false],
  ['Meerkat', 'A small African carnivoran belonging to the mongoose family', false],
  ['Skullscription', 'Monthtly disposable helmets for CitiBikers', false],
  ['Miser', 'An OkCupid companion tool that invests the money you save by no-showing', false],
  ['Maybook', 'RSVPs "Maybe" to every Facebook event invite', false],
  ['TiffSkipper', 'Temporarily deactivates your Facebook until November 9, 2016', false],
  ['Spoofify.fm', 'Automatically scrobbles every Pitchfork BNM selection for cred', false],
  ['Teen Text', 'An elegant, dad-friendly app that translates emoji to English', false],
  ['CrapRabbit', "Marketplace for contractors to pick up your dog's poop", false],
  ['Swerpes', 'An unobtrusive, iOS compatible STD testing kit for Tinder users', false],
  ['TieLocker','Automatically sends a tie every month directly to your storage space', false],
  ['DeskNest', 'A rollable nap pad that fits under your desk', false],
  ['Coverjudge', 'Tinder for books', false],
  ['Civickstarter', 'Kickstarter for public works', false],
  ['Fit Good', 'Glassdor-style descriptions of how employees dress', false],
  ['Rockbox', 'Five pet rocks delivered to your door every month', false],
  ['BragTagg', 'Adds "Founder & CEO" to your Twitter bio when you have an idea', false],
  ['Standup', 'Teleconferencing with unchangeable time limit to kill timewasting', false],
  ['Triage Surge', 'Uber for ambulances', false],
  ['Ship Your Enemies Bees', 'Ship Your Enemies Glitter for sociopaths', false],
  ['LinkedInstagram', 'Suite of fun filters tools for professional pictures', false],

  ['Newscombinator', 'Like Google-News, but for hackers', 'newscombinator'],
  ['Meer-katalytics', 'Complete analytics for all Meerkats', 'meer-katalytics'],
  ['4 Minute Workout', "The world's fastest full body workout in just 4 minutes", '4-minute-workout'],
  ['Pug A Day', 'Send a friend 100 days of Pug pictures', 'pug-a-day'],
  ['Pick Up, Knock Down', 'Get a compliment and an insult from a stranger', 'pick-me-up-knock-me-down'],
  ['TreadDesk', 'Walk while you work', 'treaddesk'],
  ['Wrte.io', 'Email with a paywall. Charge for reading your email.', 'wrte-io'],
  ['DystopiaTracker', 'Explore predictions about the future and their realizations', 'dystopiatracker'],
  ['alibi.', 'Verify your story with passive geo tracking and Touch ID', 'alibi-2'],
  ['Dreeps', "A game that plays itself because you're too busy", 'dreeps'],
  ['Hushbook', 'Tinder for unfriending on Facebook', 'hushbook'],
  ['Slater', 'Schedule your Slack messages now, post them later', 'slater-by-convergely'],
  ['Blindfold', 'Siri for sex. Your digital foreplay assistant.', 'blindfold'],
  ['Flinch', 'Staring contest with strangers', 'flinch'],
  ['Ship Snow, Yo', 'We Ship Snow To Anyone in the US', 'ship-snow-yo'],
  ['Hoffice', "Cowork at someone's home", 'hoffice'],
  ['Hotels By Day', 'Experience a great hotel by day without paying for the night', 'hotels-by-day'],
  ['Gutsy', 'Find out what your friends truly think of you', 'gutsy'],
  ['Wevorce', 'Streamline the divorce process online', 'wevorce'],
  ['Rise', 'All-you-can-fly, private air travel membership', 'rise-5'],
  ['Dreaming With Jeff', 'Sleep better with the sound of Jeff Bridges', 'dreaming-with-jeff'],
  ['Am I Going Down?', 'Analyzes the chance of your flight crashing.', 'am-i-going-down'],
  ['Toothpick', "Calculate your tip based on the server's gender", 'toothpick'],
  ['Nudifier', 'Make people in photos look faux naked.', 'nudifier'],
  ['Invisible Boyfriend', 'Finally, a boyfriend your family can believe in.', 'invisible-boyfriend'],
  ['Self-Destructing Book', '24 hours to read a book before it self-destructs', 'self-destructing-book'],
  ['Sailo', 'Airbnb for boats', 'sailo'],
  ['Rejection Therapy', 'A game to conquer fear by getting rejected', 'rejection-therapy'],
  ['Be My Eyes', 'Lend your eyes to the blind', 'be-my-eyes-2'],
  ['Ship Your Enemies Glitter', 'Send glitter to the people that deserve it', 'ship-your-enemies-glitter'],
  ['Love Actualized', 'App for falling in love', 'love-actualized'],
  ['Vibease', "World's first wearable smart vibrator", 'vibease'],
  ['Hotel My Phone', "Borrow a friend's phone & use it as your own.", 'hotel-my-phone'],
  ['Listen to Twitter', 'Convert the sentiment of any topic into a unique melody', 'listen-to-twitter'],
  ['Pay with a Tweet', 'Sell your product for the price of a tweet', 'pay-with-a-tweet'],
  ['Blade', 'Helicopter ride sharing', 'blade'],
  ['gitr', 'Tinder for developers', 'girt'],
  ['Wonker', 'User-generated explanations on complex current events', 'wonker'],
  ['Make It Rain', 'Random gifs when you get paid via Stripe', 'make-it-rain'],
  ['Where have you cried?', 'Opened-sourced map of where people have publicly cried', 'where-have-you-cried'],
  ['Slackroulette', 'Chat with Slackers', 'slackroulette'],
  ['What Color Is It?', 'See the hex color for every second of the day', 'what-color-is-it'],
  ['Hello Santa', 'Video Chat Live with Santa!', 'hello-santa'],
  ['Stormy', 'Twitter Tweet Storms, made easy', 'stormy'],
  ['Futurism', 'Product Hunt for news about the future', 'futurism'],
  ['Likecreeper', "Randomly hit like on a friend's horrifyingly old instagram", 'likecreeper'],
  ['Bobler', 'Record 36 sec podcasts', 'bobler'],
  ['Stamp Yo Face!', 'Get your own custom-made selfie stamp', 'stamp-yo-face'],
  ['Yo B*tch', 'Yo app by Aaron Paul', 'yo-b-tch'],
  ['No CAPTCHA reCAPTCHA', "Update to CAPTCHA just asks if you're robot", 'no-captcha-recaptcha'],
  ['Plague', 'Spread content like a virus', 'plague'],
  ['Endangered Sounds Museum', 'Nostalgic sounds from the past', 'endangered-sounds-museum'],
  ['Selfie Dolls', 'Have a selfie doll made in your likeness', 'selfie-dolls'],
  ['Pushh', 'Keep track of your fitness selfies', 'pushh'],
  ['Emojiary', 'Tap into your feelings - using emoji', 'emojiary'],
  ['Sock Swag', 'Custom-branded socks for your company', 'sock-swag'],
  ['Ethan', 'A messaging app for messaging Ethan', 'ethan'],
  ['Capsule', 'Text the future', 'capsule'],
  ['Companion', 'Walking home alone is dangerous. Stop doing it alone.', 'companion'],
  ['Push for Pizza', 'The easiest way to order pizza. EVER.', 'push-for-pizza'],
  ['Startup Legitimizer', 'Make your startup appear super legit', 'startup-legitimizer-2'],
  ['Shady URL', "Don't just shorten your URL, make it suspicious and frighten", 'shady-url'],
  ['Geocities-izer', 'Make any site look like a throwback Geocities site', 'geocities-izer-2'],
  ['Somebody', 'Send a message delivered verbally by a stranger', 'somebody'],
  ['Mermur', 'Anonymous text messages. No replies.', 'mermur'],
  ['Lukewarm Emailer', 'Build a list of people on Twitter, reach out w/ a cold email', 'lukewarm-emailer'],
];

(function() {
  $(window).on('resize', function() {
    $('section').height($(window).height());
  }).trigger('resize');

  if(isMobile.any) {
    $('body').addClass('mobile')
  }

  shuffle(stuff);
  shuffle(stuff); // For good measure
  shuffle(stuff); // Becuase why not?

  // Add in a plug :)
  stuff.splice(7, 0, ['ReadMe.io', 'Beautiful Documentation Made Easy', 'readme-2']);

  var i = 0;
  var next = stuff[i];
  $('.item.current h3').text(next[0]);
  $('.item.current p').text(next[1]);

  var score = (window.localStorage['score'] || 0)*1;
  var total = (window.localStorage['total'] || 0)*1;

  function updateScore() {
    $('.update-score').text(score);
    $('.update-total').text(total);
    $('.update-percent').text(Math.floor((score / total) * 100) + "%");

    window.localStorage['total'] = total;
    window.localStorage['score'] = score;
  };

  $('.btn-next, .button').on({
    'touchstart' : function(){
      $(this).addClass('active');
    },
    'touchend': function() {
      $(this).removeClass('active');
    }
  });

  $('.button').click(function() {
    var real = next[2];
    var correct = $(this).attr('guess') == (real ? 'real' : 'fake');

    if(isMobile.any) {
      $('body').addClass('answer');
      $('.btn-next').click(function(e) {
        $('body').removeClass('answer');
        return false;
      });
    }

    if(real != 'readme-2') {
      total++;
      if(correct) score++;
      updateScore();
    }

    i++;

    if(i >= stuff.length) i = 0;
    next = stuff[i];

    if(!next) {
      console.log('i', i);
    }

    $('.quote').addClass('hide');
    setTimeout(function() {
      $('.quote').remove();
    }, 1000);

    $('.score').addClass('on');
    $('.item.next').addClass('hide');

    $('.item.current').addClass('next').removeClass('current');
    $('.right').removeClass('yes no readme');
    if(real == 'readme-2') {
      $('.right').addClass('readme');
    } else {
      $('.right').addClass(correct ? 'yes' : 'no');
    }

    var text = '';
    if(correct) {
      if(real) {
        text = 'Yup, this is a real product!';
      } else {
        text = 'You spotted the fake!';
      }
    } else {
      if(real) {
        text = 'Actually, it is real!';
      } else {
        text = 'Nope, not real… yet!';
      }
    }

    if(real == 'readme-2') {
      text = 'Okay, shameless plug… But check it out!';
    }

    $('.score h4').html(text);

    var clone = function(side) {
      var $el = $('#demo').clone().attr('id', null);
      $('.' + side + ' .items').append($el.addClass('current'));
      $('h3', $el).text(next[0]);
      $('p', $el).text(next[1]);
    };

    clone('left');
    clone('right');

    $('.item.current').show();
    $('.left .item.current').addClass('fadein');
  });

  function shuffle(array) {
    var currentIndex = array.length, temporaryValue, randomIndex ;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {

      // Pick a remaining element...
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;

      // And swap it with the current element.
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }

    return array;
  }
})();
<|endoftext|>


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

/**
 * Node representing the biker in the view.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  // Modules
  var Biker = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/model/Biker' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var EFACBaseNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EFACBaseNode' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var EFACModelImageNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EFACModelImageNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var HSlider = require( 'SUN/HSlider' );
  // var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );

  /**
   * @param {Biker} biker EnergySource
   * @param {Property<boolean>} energyChunksVisible
   * @param {ModelViewTransform2} modelViewTransform
   * @constructor
   */
  function BikerNode( biker, energyChunksVisible, modelViewTransform ) {

    // Add args to constructor as needed
    EFACBaseNode.call( this, biker, modelViewTransform );

    var spokesImage = new EFACModelImageNode( Biker.REAR_WHEEL_SPOKES_IMAGE, modelViewTransform );
    var backLegRootNode = new Node();
    var frontLegRootNode = new Node();
    var backLegImageNodes = [];
    var frontLegImageNodes = [];

    for ( var i = 0; i < Biker.NUM_LEG_IMAGES; i++ ) {
      // Back leg image nodes
      backLegImageNodes.push( new EFACModelImageNode( Biker.BACK_LEG_IMAGES[ i ], modelViewTransform ) );
      backLegImageNodes[ i ].setVisible( false );
      backLegRootNode.addChild( backLegImageNodes[ i ] );

      // Front leg image nodes
      frontLegImageNodes.push( new EFACModelImageNode( Biker.FRONT_LEG_IMAGES[ i ], modelViewTransform ) );
      frontLegImageNodes[ i ].setVisible( false );
      frontLegRootNode.addChild( frontLegImageNodes[ i ] );
    }

    var upperBodyNormal = new EFACModelImageNode( Biker.RIDER_NORMAL_UPPER_BODY_IMAGE, modelViewTransform );
    var upperBodyTired = new EFACModelImageNode( Biker.RIDER_TIRED_UPPER_BODY_IMAGE, modelViewTransform );

    // Animate legs by setting image visibility based on crank arm angle
    var visibleBackLeg = backLegImageNodes[ 0 ];
    var visibleFrontLeg = frontLegImageNodes[ 0 ];
    biker.crankAngleProperty.link( function( angle ) {
      console.log('angle', biker.crankAngle);
      assert && assert( angle >= 0 && angle <= 2*Math.PI );
      var i = biker.mapAngleToImageIndex( angle );
      visibleFrontLeg.setVisible( false );
      visibleBackLeg.setVisible( false );
      visibleFrontLeg = frontLegImageNodes[ i ];
      visibleBackLeg = backLegImageNodes[ i ];
      visibleFrontLeg.setVisible( true );
      visibleBackLeg.setVisible( true );
    } );

    // TODO "feed me" button

    biker.bikerHasEnergyProperty.link( function( hasEnergy ) {
      // feedMeButton.setVisible(!hasEnergy);
      upperBodyNormal.setVisible( hasEnergy );
      upperBodyTired.setVisible( !hasEnergy );
    } );

    // Slider to control crank speed
    // TODO: these numeric literals are brittle. Ask about correct way to control positions.
    var crankSlider = new HSlider( biker.targetCrankAngularVelocityProperty, {
      min: 0,
      max: Biker.MAX_ANGULAR_VELOCITY_OF_CRANK
    }, {
      trackSize: new Dimension2( 200, 5 ),
      thumbSize: new Dimension2( 20, 40 ) // Default: ( 22, 45 )
    } );

    this.addChild( new Panel( crankSlider, {
      fill: EFACConstants.CONTROL_PANEL_BACKGROUND_COLOR,
      centerX: 0,
      centerY: 110,
      cornerRadius: 8
    } ) );

    this.addChild( spokesImage );
    this.addChild( backLegRootNode );
    this.addChild( new EFACModelImageNode( Biker.FRAME_IMAGE, modelViewTransform ) );
    this.addChild( frontLegRootNode );
    this.addChild( upperBodyNormal );
    this.addChild( upperBodyTired );
  }

  energyFormsAndChanges.register( 'BikerNode', BikerNode );

  return inherit( EFACBaseNode, BikerNode );
} );
<commit_msg>Remove print statement now that bug was found<commit_after>// Copyright 2016, University of Colorado Boulder

/**
 * Node representing the biker in the view.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  // Modules
  var Biker = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/model/Biker' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var EFACBaseNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EFACBaseNode' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var EFACModelImageNode = require( 'ENERGY_FORMS_AND_CHANGES/energy-systems/view/EFACModelImageNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var HSlider = require( 'SUN/HSlider' );
  // var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Panel = require( 'SUN/Panel' );

  /**
   * @param {Biker} biker EnergySource
   * @param {Property<boolean>} energyChunksVisible
   * @param {ModelViewTransform2} modelViewTransform
   * @constructor
   */
  function BikerNode( biker, energyChunksVisible, modelViewTransform ) {

    // Add args to constructor as needed
    EFACBaseNode.call( this, biker, modelViewTransform );

    var spokesImage = new EFACModelImageNode( Biker.REAR_WHEEL_SPOKES_IMAGE, modelViewTransform );
    var backLegRootNode = new Node();
    var frontLegRootNode = new Node();
    var backLegImageNodes = [];
    var frontLegImageNodes = [];

    for ( var i = 0; i < Biker.NUM_LEG_IMAGES; i++ ) {
      // Back leg image nodes
      backLegImageNodes.push( new EFACModelImageNode( Biker.BACK_LEG_IMAGES[ i ], modelViewTransform ) );
      backLegImageNodes[ i ].setVisible( false );
      backLegRootNode.addChild( backLegImageNodes[ i ] );

      // Front leg image nodes
      frontLegImageNodes.push( new EFACModelImageNode( Biker.FRONT_LEG_IMAGES[ i ], modelViewTransform ) );
      frontLegImageNodes[ i ].setVisible( false );
      frontLegRootNode.addChild( frontLegImageNodes[ i ] );
    }

    var upperBodyNormal = new EFACModelImageNode( Biker.RIDER_NORMAL_UPPER_BODY_IMAGE, modelViewTransform );
    var upperBodyTired = new EFACModelImageNode( Biker.RIDER_TIRED_UPPER_BODY_IMAGE, modelViewTransform );

    // Animate legs by setting image visibility based on crank arm angle
    var visibleBackLeg = backLegImageNodes[ 0 ];
    var visibleFrontLeg = frontLegImageNodes[ 0 ];
    biker.crankAngleProperty.link( function( angle ) {
      assert && assert( angle >= 0 && angle <= 2 * Math.PI );
      var i = biker.mapAngleToImageIndex( angle );
      visibleFrontLeg.setVisible( false );
      visibleBackLeg.setVisible( false );
      visibleFrontLeg = frontLegImageNodes[ i ];
      visibleBackLeg = backLegImageNodes[ i ];
      visibleFrontLeg.setVisible( true );
      visibleBackLeg.setVisible( true );
    } );

    // TODO "feed me" button

    biker.bikerHasEnergyProperty.link( function( hasEnergy ) {
      // feedMeButton.setVisible(!hasEnergy);
      upperBodyNormal.setVisible( hasEnergy );
      upperBodyTired.setVisible( !hasEnergy );
    } );

    // Slider to control crank speed
    // TODO: these numeric literals are brittle. Ask about correct way to control positions.
    var crankSlider = new HSlider( biker.targetCrankAngularVelocityProperty, {
      min: 0,
      max: Biker.MAX_ANGULAR_VELOCITY_OF_CRANK
    }, {
      trackSize: new Dimension2( 200, 5 ),
      thumbSize: new Dimension2( 20, 40 ) // Default: ( 22, 45 )
    } );

    this.addChild( new Panel( crankSlider, {
      fill: EFACConstants.CONTROL_PANEL_BACKGROUND_COLOR,
      centerX: 0,
      centerY: 110,
      cornerRadius: 8
    } ) );

    this.addChild( spokesImage );
    this.addChild( backLegRootNode );
    this.addChild( new EFACModelImageNode( Biker.FRAME_IMAGE, modelViewTransform ) );
    this.addChild( frontLegRootNode );
    this.addChild( upperBodyNormal );
    this.addChild( upperBodyTired );
  }

  energyFormsAndChanges.register( 'BikerNode', BikerNode );

  return inherit( EFACBaseNode, BikerNode );
} );
<|endoftext|>


<commit_before>import React, {PureComponent} from 'react';
import {browserHistory} from 'react-router';

import CityViewStore from '../../stores/city-view-store';
import MainStore from '../../stores/main-store';

import {PanelHeader, PanelBody} from '../panel';
import LinesTree from './lines-tree';
import Year from './year';
import KmIndicator from './km-indicator';
import Tags from '../tags';

class CityView extends PureComponent {
  constructor(props, context) {
    super(props, context);

    this.urlName = this.props.params.city_url_name;

    this.bindedOnChange = this.onChange.bind(this);
    this.bindedOnYearChange = this.onYearChange.bind(this);
    this.bindedOnLineToggle = this.onLineToggle.bind(this);
    this.bindedOnAllLinesToggle = this.onAllLinesToggle.bind(this);
  }

  onChange() {
    this.setState(CityViewStore.getState(this.urlName));
  }

  componentWillMount() {
    CityViewStore.addChangeListener(this.bindedOnChange);
  }

  componentDidMount() {
    MainStore.setLoading();
    CityViewStore.load(this.urlName, this.params()).then(() => {
      MainStore.unsetLoading();
    });
  }

  componentWillUnmount() {
    CityViewStore.unload(this.urlName);
    CityViewStore.removeChangeListener(this.bindedOnChange);
  }

  componentDidUpdate(prevProps, prevState) {
    if (!prevState) return;

    if (prevState.linesShown.length != this.state.linesShown.length) {
      this.onLinesShownChange();
    }
  }

  params() {
    return this.props.location.query;
  }

  updateParams(newParams) {
    const params = Object.assign({}, this.params(), newParams);

    // We delete null params
    Object.keys(params).forEach((key) => (params[key] == null) && delete params[key]);

    // We delete systems param if the new ones include the lines param
    if (newParams.lines && this.params().system_id) {
      delete params.system_id;
    }

    // If new params are equal to the current ones, we don't push the state to the
    // browser history
    if (JSON.stringify(params) === JSON.stringify(this.params())) return;

    browserHistory.push({...this.props.location, query: params});
  }

  onYearChange() {
    if (this.state.playing) return;
    const newYear = this.state.currentYear;
    this.updateParams({year: newYear});
    CityViewStore.setKmYear(this.urlName, newYear, null);
  }

  onLineToggle(lineUrlName) {
    CityViewStore.toggleLine(this.urlName, lineUrlName);
  }

  onAllLinesToggle(system_id, checked) {
    CityViewStore.toggleAllLines(this.urlName, system_id, checked);
  }

  onLinesShownChange() {
    let linesShown;

    if (this.state.linesShown.length == this.state.lines.length) {
      linesShown = null;
    } else {
      linesShown = this.state.linesShown.join(',');
    }

    this.updateParams({lines: linesShown});
  }

  systemTitle() {
    const systemId = parseInt(this.params().system_id);
    const system = this.state.systems.find(s => s.id == systemId);

    if (!system) return;

    const interpolations = {
      city: this.context.cityName,
      system: system.name
    }

    return <Tags
            title="city.system_title"
            description="city.description"
            interpolations={interpolations}
          />
  }

  render() {
    if (!this.state) return null;

    return (
        <PanelBody>
          {this.params().system_id && this.systemTitle()}

            <Year
              urlName={this.urlName}
              min={(this.state.years || {}).start}
              max={(this.state.years || {}).end}
              year={this.state.currentYear}
              playing={this.state.playing}
              onYearChange={this.bindedOnYearChange}
            />
            <KmIndicator
              kmOperative={this.state.kmOperative}
              kmUnderConstruction={this.state.kmUnderConstruction}
            />

          { this.state.systems.map((system) => {
            const systemLines = this.state.lines.filter(line => line.system_id == system.id);
            return systemLines.length ?
            <LinesTree
              key={system.id}
              systemId={system.id}
              name={system.name}
              defaultExpanded={true}
              lines={systemLines}
              linesShown={this.state.linesShown}
              onLineToggle={this.bindedOnLineToggle}
              onAllLinesToggle={this.bindedOnAllLinesToggle}
            /> : null })
          }
        </PanelBody>
    )
  }
}

CityView.contextTypes = {
  cityName: React.PropTypes.string
}

export default CityView
<commit_msg>CityView: Fixes lines/system_id params relationship<commit_after>import React, {PureComponent} from 'react';
import {browserHistory} from 'react-router';

import CityViewStore from '../../stores/city-view-store';
import MainStore from '../../stores/main-store';

import {PanelHeader, PanelBody} from '../panel';
import LinesTree from './lines-tree';
import Year from './year';
import KmIndicator from './km-indicator';
import Tags from '../tags';

class CityView extends PureComponent {
  constructor(props, context) {
    super(props, context);

    this.urlName = this.props.params.city_url_name;

    this.bindedOnChange = this.onChange.bind(this);
    this.bindedOnYearChange = this.onYearChange.bind(this);
    this.bindedOnLineToggle = this.onLineToggle.bind(this);
    this.bindedOnAllLinesToggle = this.onAllLinesToggle.bind(this);
  }

  onChange() {
    this.setState(CityViewStore.getState(this.urlName));
  }

  componentWillMount() {
    CityViewStore.addChangeListener(this.bindedOnChange);
  }

  componentDidMount() {
    MainStore.setLoading();
    CityViewStore.load(this.urlName, this.params()).then(() => {
      MainStore.unsetLoading();
    });
  }

  componentWillUnmount() {
    CityViewStore.unload(this.urlName);
    CityViewStore.removeChangeListener(this.bindedOnChange);
  }

  componentDidUpdate(prevProps, prevState) {
    if (!prevState) return;

    if (prevState.linesShown.length != this.state.linesShown.length) {
      this.onLinesShownChange();
    }
  }

  params() {
    return this.props.location.query;
  }

  updateParams(newParams) {
    const params = Object.assign({}, this.params(), newParams);

    // We delete null params
    Object.keys(params).forEach((key) => (params[key] == null) && delete params[key]);

    // If new params are equal to the current ones, we don't push the state to the
    // browser history
    if (JSON.stringify(params) === JSON.stringify(this.params())) return;

    browserHistory.push({...this.props.location, query: params});
  }

  onYearChange() {
    if (this.state.playing) return;
    const newYear = this.state.currentYear;
    this.updateParams({year: newYear});
    CityViewStore.setKmYear(this.urlName, newYear, null);
  }

  onLineToggle(lineUrlName) {
    CityViewStore.toggleLine(this.urlName, lineUrlName);
  }

  onAllLinesToggle(system_id, checked) {
    CityViewStore.toggleAllLines(this.urlName, system_id, checked);
  }

  onLinesShownChange() {
    let linesShown;

    if (this.state.linesShown.length == this.state.lines.length) {
      linesShown = null;
    } else {
      linesShown = this.state.linesShown.join(',');
    }

    // An update in the lines parameter should remove the system_id param
    this.updateParams({lines: linesShown, system_id: null});
  }

  systemTitle() {
    const systemId = parseInt(this.params().system_id);
    const system = this.state.systems.find(s => s.id == systemId);

    if (!system) return;

    const interpolations = {
      city: this.context.cityName,
      system: system.name
    }

    return <Tags
            title="city.system_title"
            description="city.description"
            interpolations={interpolations}
          />
  }

  render() {
    if (!this.state) return null;

    return (
        <PanelBody>
          {this.params().system_id && this.systemTitle()}

            <Year
              urlName={this.urlName}
              min={(this.state.years || {}).start}
              max={(this.state.years || {}).end}
              year={this.state.currentYear}
              playing={this.state.playing}
              onYearChange={this.bindedOnYearChange}
            />
            <KmIndicator
              kmOperative={this.state.kmOperative}
              kmUnderConstruction={this.state.kmUnderConstruction}
            />

          { this.state.systems.map((system) => {
            const systemLines = this.state.lines.filter(line => line.system_id == system.id);
            return systemLines.length ?
            <LinesTree
              key={system.id}
              systemId={system.id}
              name={system.name}
              defaultExpanded={true}
              lines={systemLines}
              linesShown={this.state.linesShown}
              onLineToggle={this.bindedOnLineToggle}
              onAllLinesToggle={this.bindedOnAllLinesToggle}
            /> : null })
          }
        </PanelBody>
    )
  }
}

CityView.contextTypes = {
  cityName: React.PropTypes.string
}

export default CityView
<|endoftext|>


<commit_before>/*
* jQuery Mobile Framework : "customTextInput" plugin for text inputs, textareas (based on code from Filament Group,Inc)
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* Note: Code is in draft form and is subject to change
*/
(function($){
jQuery.fn.customTextInput = function(options){
	return $(this).each(function(){
		var input = $(this);

		var o = $.extend({
			search: input.is('[type="search"],[data-type="search"]')
			//defaultTheme: "a"
		}, options);

		$('label[for='+input.attr('id')+']').addClass('ui-input-text');

		input.addClass('ui-input-text');

		var focusedEl = input;

		//"search" input widget
		if(o.search){
			focusedEl = input.wrap('

').parent();
			var clearbtn = $('clear text')
				.buttonMarkup({icon: 'delete', iconpos: 'notext', corners:true, shadow:true})
				.click(function(){
					input.val('');
					toggleClear();
					return false;
				})
				.appendTo(focusedEl);

			function toggleClear(){
				if(input.val() == ''){
					clearbtn.addClass('ui-input-clear-hidden');
				}
				else{
					clearbtn.removeClass('ui-input-clear-hidden');
				}
			}

			toggleClear();
			input.keyup(toggleClear);
		}
		else{
			input.addClass('ui-corner-all ui-body-c');
		}

		input
			.focus(function(){
				focusedEl.addClass('ui-focus');
			})
			.blur(function(){
				focusedEl.removeClass('ui-focus');
			});

		//autogrow
		if(input.is('textarea')){
			input.keydown(function(){
				if( input[0].offsetHeight < input[0].scrollHeight ){
					input.css({height: input[0].scrollHeight + 10 });
				}
			})
		}
	});
};
})(jQuery);
<commit_msg>clicking the clear button will focus on the input and trigger change event.<commit_after>/*
* jQuery Mobile Framework : "customTextInput" plugin for text inputs, textareas (based on code from Filament Group,Inc)
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* Note: Code is in draft form and is subject to change
*/
(function($){
jQuery.fn.customTextInput = function(options){
	return $(this).each(function(){
		var input = $(this);

		var o = $.extend({
			search: input.is('[type="search"],[data-type="search"]')
			//defaultTheme: "a"
		}, options);

		$('label[for='+input.attr('id')+']').addClass('ui-input-text');

		input.addClass('ui-input-text');

		var focusedEl = input;

		//"search" input widget
		if(o.search){
			focusedEl = input.wrap('

').parent();
			var clearbtn = $('clear text')
				.buttonMarkup({icon: 'delete', iconpos: 'notext', corners:true, shadow:true})
				.click(function(){
					input.val('').focus();
					input.trigger('change');
					clearbtn.addClass('ui-input-clear-hidden');
					return false;
				})
				.appendTo(focusedEl);

			function toggleClear(){
				if(input.val() == ''){
					clearbtn.addClass('ui-input-clear-hidden');
				}
				else{
					clearbtn.removeClass('ui-input-clear-hidden');
				}
			}

			toggleClear();
			input.keyup(toggleClear);
		}
		else{
			input.addClass('ui-corner-all ui-body-c');
		}

		input
			.focus(function(){
				focusedEl.addClass('ui-focus');
			})
			.blur(function(){
				focusedEl.removeClass('ui-focus');
			});

		//autogrow
		if(input.is('textarea')){
			input.keydown(function(){
				if( input[0].offsetHeight < input[0].scrollHeight ){
					input.css({height: input[0].scrollHeight + 10 });
				}
			})
		}
	});
};
})(jQuery);
<|endoftext|>


<commit_before>var PieceFunctions = {}

PieceFunctions.getBoardPosition = function($el){

	console.log('getBoardPosition')

	if (!!$el) throw "no piece on board available!"

}

PieceFunctions.clearActiveSquares = function(){
	$('div.active-home-square').removeClass('active-home-square');
	$('div.movable').removeClass('movable');
}

PieceFunctions.highlightOnlyHomeSpace = function($piece){
	PieceFunctions.clearActiveSquares();
	$piece.parent('.square').addClass('active-home-square')
}

PieceFunctions.activate = function($piece){

  	PieceFunctions.highlightOnlyHomeSpace($piece);

	var side = $piece.data('side');
	if (!side) throw "no side determined!"
	var type = $piece.data('type');
	if (!type) throw "no type determined!"

	if (!PieceFunctions.highLightAvailableMoves[type]) throw "no moves available for this type!"

  	PieceFunctions.highLightAvailableMoves[type]( $piece );

  }

PieceFunctions.highLightAvailableMoves = {

	pawn: function($piece){
		console.log('moving like a pawn!!');

		var coordinates = { x: $piece.data('posx'), y: $piece.data('posy') };

		console.log(coordinates);

		$('.square[data-posx="' + (coordinates.x+1) + '"][data-posy="'+ (coordinates.y) +'"]' ).addClass('movable');
		$('.square[data-posx="' + (coordinates.x) + '"][data-posy="'+ (coordinates.y+1) +'"]' ).addClass('movable');
		$('.square[data-posx="' + (coordinates.x-1) + '"][data-posy="'+ (coordinates.y) +'"]' ).addClass('movable');
		$('.square[data-posx="' + (coordinates.x) + '"][data-posy="'+ (coordinates.y-1) +'"]' ).addClass('movable');
	},
	knight: function($piece){
		console.log('moving like a knight!!');
	},
	bishop: function($piece){
		console.log('moving like a bishop!!');
	},
	rook: function($piece){
		console.log('moving like a rook!!');
	},
	queen: function($piece){
		console.log('moving like a queen!!');
	},
	king: function($piece){
		console.log('moving like a king!!');
	}
}

<commit_msg>adds highlighting for pawn moves!<commit_after>var PieceFunctions = {}

PieceFunctions.getBoardPosition = function($el){

	console.log('getBoardPosition')

	if (!!$el) throw "no piece on board available!"

}

PieceFunctions.clearActiveSquares = function(){
	$('div.active-home-square').removeClass('active-home-square');
	$('div.movable').removeClass('movable');
}

PieceFunctions.highlightOnlyHomeSpace = function($piece){
	PieceFunctions.clearActiveSquares();
	$piece.parent('.square').addClass('active-home-square')
}

PieceFunctions.activate = function($piece){

  	PieceFunctions.highlightOnlyHomeSpace($piece);

	var side = $piece.data('side');
	if (!side) throw "no side determined!"
	var type = $piece.data('type');
	if (!type) throw "no type determined!"

	if (!PieceFunctions.highLightAvailableMoves[type]) throw "no moves available for this type!"

  	PieceFunctions.highLightAvailableMoves[type]( $piece );

  }

PieceFunctions.highLightAvailableMoves = {

	pawn: function($piece){
		console.log('moving like a pawn!!');

		var coordinates = { x: $piece.data('posx'), y: $piece.data('posy') };

		var $targetSquare;
		var $targetPiece;

		$targetSquare = BoardFunctions.squareSelector.NorthOf(coordinates);
		$targetPiece = $targetSquare.children('.piece')

		if (!$targetPiece[0]) $targetSquare.addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.SouthOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!$targetPiece[0]) $targetSquare.addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.ToEastOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!$targetPiece[0]) $targetSquare.addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.ToWestOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!$targetPiece[0]) $targetSquare.addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.NorthEastOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!!$targetPiece[0]) $targetPiece.parent('.square').addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.SouthEastOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!!$targetPiece[0]) $targetPiece.parent('.square').addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.ToNorthWestOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!!$targetPiece[0]) $targetPiece.parent('.square').addClass('movable');

		$targetSquare = BoardFunctions.squareSelector.ToSouthWestOf(coordinates);

		$targetPiece = $targetSquare.children('.piece')
		if (!!$targetPiece[0]) $targetPiece.parent('.square').addClass('movable');

	},
	knight: function($piece){
		console.log('moving like a knight!!');
	},
	bishop: function($piece){
		console.log('moving like a bishop!!');
	},
	rook: function($piece){
		console.log('moving like a rook!!');
	},
	queen: function($piece){
		console.log('moving like a queen!!');
	},
	king: function($piece){
		console.log('moving like a king!!');
	}
}

BoardFunctions.squareSelector = {
	NorthOf: function(coordinates){  return $('.square[data-posx="' + (coordinates.x) + '"][data-posy="'+ (coordinates.y + 1) +'"]' ) },
	SouthOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x) + '"][data-posy="'+ (coordinates.y - 1) +'"]' ) },
	ToEastOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x + 1) + '"][data-posy="'+ (coordinates.y) +'"]' ) },
	ToWestOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x - 1) + '"][data-posy="'+ (coordinates.y) +'"]' )},
	NorthEastOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x + 1) + '"][data-posy="'+ (coordinates.y + 1) +'"]' )},
	SouthEastOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x + 1) + '"][data-posy="'+ (coordinates.y - 1) +'"]' )},
	ToNorthWestOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x - 1) + '"][data-posy="'+ (coordinates.y + 1) +'"]' )},
	ToSouthWestOf: function(coordinates){ return $('.square[data-posx="' + (coordinates.x - 1) + '"][data-posy="'+ (coordinates.y - 1) +'"]' )}
}

<|endoftext|>


<commit_before>var Gopigo = require('./modules/node-gopigo');
var Commands = Gopigo.commands;
var Robot = Gopigo.robot;

class piJS
{
  /**
   * Constructor function. Gets called when class is made into a object
   * @param {json} cfg
   */
  constructor(cfg) {

    this._robot = null;
    this._cfg = cfg;

    this.createRobot();
    this.initRobot();
    this.listenEvents();
    this.sendReadyMessage();
  }

  sendReadyMessage() {
    console.log('piJS Module ready');
    console.log(' Welcome to the GoPiGo test application             ')
    console.log(' When asked, insert a command to test your GoPiGo   ')
    console.log(' (!) For a complete list of commands, please type help')
  }

  listenEvents() {
    this._roboton('init', (res) => {
      this.onInit(res);
    });
    this._roboton('error', (err) => {
      this.onError(err);
    });
    this._roboton('free', () => {
      this.onFree();
    });
    this._roboton('halt', () => {
      this.onHalt();
    });
    this._roboton('close',() => {
      this.onClose();
    });
    this._roboton('reset', () => {
      this.onReset();
    });
    this._roboton('normalVoltage', (voltage) => {
      this.onNormalVoltage(voltage);
    });
    this._roboton('lowVoltage', (voltage) => {
      this.onLowVoltage(voltage);
    });
    this._roboton('criticalVoltage', (voltage) => {
      this.onCriticalVoltage(voltage);
    })
  }

  createRobot() {
    this._robot = new Robot({
      minVoltage: 5.5,
      criticalVoltage: 1.2,
      debug: true,
      ultrasonicSensorPin: this._cfg.ultrasonicPin,
      //IRReceiverSensorPin: irreceiverPin
    })
  }

  initRobot() {
    this._robot.init()
  }

  askForCommand() {
    rl.question('What do you want me to do? > ', function (answer) {
      handleAnswer(answer)
    })
  }

  handleAnswer(answer) {
    let message = '';
    switch (answer) {
      case 'help':
        console.log('')
        console.log('reset => performs a reset of LEDs and servo motor')
        console.log('left led on => turn left led on')
        console.log('left led off => turn left led off')
        console.log('right led on => turn right led on')
        console.log('right led off => turn right led off')
        console.log('move forward => moves the GoPiGo forward')
        console.log('move backward => moves the GoPiGo backward')
        console.log('turn left => turns the GoPiGo to the left')
        console.log('turn right => turns the GoPiGo to the right')
        console.log('stop => stops the GoPiGo')
        console.log('increase speed => increases the motors speed')
        console.log('decrease speed => decreases the motors speed')
        console.log('voltage => returns the voltage value')
        console.log('servo test => performs a servo test')
        console.log('ultrasonic distance => returns the distance from an object')
        console.log('move forward with PID => moves the GoPiGo forward with PID')
        console.log('move backward with PID => moves the GoPiGo backward with PID')
        console.log('rotate left => rotates the GoPiGo to the left')
        console.log('rotate right => rotates the GoPiGo to the right')
        console.log('set encoder targeting => sets the encoder targeting')
        console.log('firmware version => returns the firmware version')
        console.log('board revision => returns the board revision')
        console.log('ir receive => returns the data from the IR receiver')
        console.log('exit => exits from this test')
        console.log('')
        break
      case 'reset':
        this._robotreset()
        break
      case 'left led on':
        var res = this._robotledLeft.on()
        console.log('Left led on::'+res)
        break
      case 'left led off':
        var res = this._robotledLeft.off()
        console.log('Left led off::'+res)
        break;
      case 'right led on':
        var res = this._robotledRight.on()
        console.log('Right led on::'+res)
        break;
      case 'right led off':
        var res = this._robotledRight.off()
        console.log('Right led off::'+res)
        break
      case 'move forward':
      case 'w':
        var res = this._robotmotion.forward(false)
        console.log('Moving forward::' + res)
        break;
      case 'turn left':
      case 'a':
        var res = this._robotmotion.left()
        console.log('Turning left::' + res)
        break;
      case 'turn right':
      case 'd':
        var res = this._robotmotion.right()
        console.log('Turning right::' + res)
        break;
      case 'move backward':
      case 's':
        var res = this._robotmotion.backward(false)
        console.log('Moving backward::' + res)
        break;
      case 'stop':
      case 'x':
        var res = this._robotmotion.stop()
        console.log('Stop::' + res)
        break
      case 'increase speed':
      case 't':
        var res = this._robotmotion.increaseSpeed();
        console.log('Increasing speed::' + res);
        break;
      case 'decrease speed':
      case 'g':
        var res = this._robotmotion.decreaseSpeed();
        console.log('Decreasing speed::' + res);
        break;
      case 'voltage':
      case 'v':
        var res = this._robotboard.getVoltage();
        console.log('Voltage::' + res + ' V');
        break;
      case 'servo test':
      case 'b':
        this._robotservo.move(0);
        console.log('Servo in position 0');

        this._robotboard.wait(1000);
        this._robotservo.move(180);
        console.log('Servo in position 180');

        this._robotboard.wait(1000);
        this._robotservo.move(90);
        console.log('Servo in position 90');
        break
      case 'exit':
      case 'z':
        this._robotclose();
        process.exit();
        break;
      case 'ultrasonic distance':
      case 'u':
        var res = this._robotultraSonicSensor.getDistance();
        console.log('Ultrasonic Distance::' + res + ' cm');
        break
      case 'ir receive':
        var res = this._robotIRReceiverSensor.read();
        console.log('IR Receiver data::');
        console.log(res);
        break;
      case 'l':
        // TODO
        break
      case 'move forward with pid':
      case 'i':
        var res = this._robotmotion.forward(true);
        console.log('Moving forward::' + res);
        break;
      case 'move backward with pid':
      case 'k':
        var res = this._robotmotion.backward(true);
        console.log('Moving backward::' + res);
        break;
      case 'rotate left':
      case 'n':
        var res = this._robotmotion.leftWithRotation();
        console.log('Rotating left::' + res);
        break;
      case 'rotate right':
      case 'm':
        var res = this._robotmotion.rightWithRotation();
        console.log('Rotating right::' + res);
        break;
      case 'set encoder targeting':
      case 'y':
        var res = this._robotencoders.targeting(1, 1, 18);
        console.log('Setting encoder targeting:1:1:18::' + res);
        break;
      case 'firmware version':
      case 'f':
        var res = this._robotboard.version()
        console.log('Firmware version::' + res);
        break;
      case 'board revision':
        var res = this._robotboard.revision()
        console.log('Board revision::' + res);
        break;
    }

    this._robotboard.wait(1000);
    this.askForCommand();
  }

  /**
   * Events
   */

  onInit(res) {
    if (res) {
      console.log('GoPiGo Ready!')
      askForCommand()
    } else {
      console.log('Something went wrong during the init.')
    }
  }

  onError(err) {
    console.log('Something went wrong');
    console.log(err);
  }

  onFree() {
    console.log('GoPiGo is free to go');
  }

  onHalt() {
    console.log('GoPiGo is halted');
  }

  onClose() {
    console.log('GoPiGo is going to sleep');
  }

  onReset() {
    console.log('GoPiGo is resetting')
  }

  onNormalVoltage(voltage) {
    console.log('Voltage is ok [' + voltage + ']');
  };

  onLowVoltage(voltage) {
    console.log('(!!) Voltage is low ['+voltage+']')
  };

  onCriticalVoltage(voltage) {
    console.log('(!!!) Voltage is critical ['+voltage+']')
  };

}

module.exports = piJS;<commit_msg>Dep issue<commit_after>var Gopigo = require('../node-gopigo');
var Commands = Gopigo.commands;
var Robot = Gopigo.robot;

class piJS
{
  /**
   * Constructor function. Gets called when class is made into a object
   * @param {Object} cfg
   */
  constructor(cfg) {

    this._robot = null;
    this._cfg = cfg;

    this.createRobot();
    this.initRobot();
    this.listenEvents();
    this.sendReadyMessage();
  }

  sendReadyMessage() {
    console.log('piJS Module ready');
    console.log(' Welcome to the GoPiGo test application             ')
    console.log(' When asked, insert a command to test your GoPiGo   ')
    console.log(' (!) For a complete list of commands, please type help')
  }

  listenEvents() {
    this._roboton('init', (res) => {
      this.onInit(res);
    });
    this._roboton('error', (err) => {
      this.onError(err);
    });
    this._roboton('free', () => {
      this.onFree();
    });
    this._roboton('halt', () => {
      this.onHalt();
    });
    this._roboton('close',() => {
      this.onClose();
    });
    this._roboton('reset', () => {
      this.onReset();
    });
    this._roboton('normalVoltage', (voltage) => {
      this.onNormalVoltage(voltage);
    });
    this._roboton('lowVoltage', (voltage) => {
      this.onLowVoltage(voltage);
    });
    this._roboton('criticalVoltage', (voltage) => {
      this.onCriticalVoltage(voltage);
    })
  }

  createRobot() {
    this._robot = new Robot({
      minVoltage: 5.5,
      criticalVoltage: 1.2,
      debug: true,
      ultrasonicSensorPin: this._cfg.ultrasonicPin,
      //IRReceiverSensorPin: irreceiverPin
    })
  }

  initRobot() {
    this._robot.init()
  }

  askForCommand() {
    rl.question('What do you want me to do? > ', function (answer) {
      handleAnswer(answer)
    })
  }

  handleAnswer(answer) {
    let message = '';
    switch (answer) {
      case 'help':
        console.log('')
        console.log('reset => performs a reset of LEDs and servo motor')
        console.log('left led on => turn left led on')
        console.log('left led off => turn left led off')
        console.log('right led on => turn right led on')
        console.log('right led off => turn right led off')
        console.log('move forward => moves the GoPiGo forward')
        console.log('move backward => moves the GoPiGo backward')
        console.log('turn left => turns the GoPiGo to the left')
        console.log('turn right => turns the GoPiGo to the right')
        console.log('stop => stops the GoPiGo')
        console.log('increase speed => increases the motors speed')
        console.log('decrease speed => decreases the motors speed')
        console.log('voltage => returns the voltage value')
        console.log('servo test => performs a servo test')
        console.log('ultrasonic distance => returns the distance from an object')
        console.log('move forward with PID => moves the GoPiGo forward with PID')
        console.log('move backward with PID => moves the GoPiGo backward with PID')
        console.log('rotate left => rotates the GoPiGo to the left')
        console.log('rotate right => rotates the GoPiGo to the right')
        console.log('set encoder targeting => sets the encoder targeting')
        console.log('firmware version => returns the firmware version')
        console.log('board revision => returns the board revision')
        console.log('ir receive => returns the data from the IR receiver')
        console.log('exit => exits from this test')
        console.log('')
        break
      case 'reset':
        this._robotreset()
        break
      case 'left led on':
        var res = this._robotledLeft.on()
        console.log('Left led on::'+res)
        break
      case 'left led off':
        var res = this._robotledLeft.off()
        console.log('Left led off::'+res)
        break;
      case 'right led on':
        var res = this._robotledRight.on()
        console.log('Right led on::'+res)
        break;
      case 'right led off':
        var res = this._robotledRight.off()
        console.log('Right led off::'+res)
        break
      case 'move forward':
      case 'w':
        var res = this._robotmotion.forward(false)
        console.log('Moving forward::' + res)
        break;
      case 'turn left':
      case 'a':
        var res = this._robotmotion.left()
        console.log('Turning left::' + res)
        break;
      case 'turn right':
      case 'd':
        var res = this._robotmotion.right()
        console.log('Turning right::' + res)
        break;
      case 'move backward':
      case 's':
        var res = this._robotmotion.backward(false)
        console.log('Moving backward::' + res)
        break;
      case 'stop':
      case 'x':
        var res = this._robotmotion.stop()
        console.log('Stop::' + res)
        break
      case 'increase speed':
      case 't':
        var res = this._robotmotion.increaseSpeed();
        console.log('Increasing speed::' + res);
        break;
      case 'decrease speed':
      case 'g':
        var res = this._robotmotion.decreaseSpeed();
        console.log('Decreasing speed::' + res);
        break;
      case 'voltage':
      case 'v':
        var res = this._robotboard.getVoltage();
        console.log('Voltage::' + res + ' V');
        break;
      case 'servo test':
      case 'b':
        this._robotservo.move(0);
        console.log('Servo in position 0');

        this._robotboard.wait(1000);
        this._robotservo.move(180);
        console.log('Servo in position 180');

        this._robotboard.wait(1000);
        this._robotservo.move(90);
        console.log('Servo in position 90');
        break
      case 'exit':
      case 'z':
        this._robotclose();
        process.exit();
        break;
      case 'ultrasonic distance':
      case 'u':
        var res = this._robotultraSonicSensor.getDistance();
        console.log('Ultrasonic Distance::' + res + ' cm');
        break
      case 'ir receive':
        var res = this._robotIRReceiverSensor.read();
        console.log('IR Receiver data::');
        console.log(res);
        break;
      case 'l':
        // TODO
        break
      case 'move forward with pid':
      case 'i':
        var res = this._robotmotion.forward(true);
        console.log('Moving forward::' + res);
        break;
      case 'move backward with pid':
      case 'k':
        var res = this._robotmotion.backward(true);
        console.log('Moving backward::' + res);
        break;
      case 'rotate left':
      case 'n':
        var res = this._robotmotion.leftWithRotation();
        console.log('Rotating left::' + res);
        break;
      case 'rotate right':
      case 'm':
        var res = this._robotmotion.rightWithRotation();
        console.log('Rotating right::' + res);
        break;
      case 'set encoder targeting':
      case 'y':
        var res = this._robotencoders.targeting(1, 1, 18);
        console.log('Setting encoder targeting:1:1:18::' + res);
        break;
      case 'firmware version':
      case 'f':
        var res = this._robotboard.version()
        console.log('Firmware version::' + res);
        break;
      case 'board revision':
        var res = this._robotboard.revision()
        console.log('Board revision::' + res);
        break;
    }

    this._robotboard.wait(1000);
    this.askForCommand();
  }

  /**
   * Events
   */

  onInit(res) {
    if (res) {
      console.log('GoPiGo Ready!')
      askForCommand()
    } else {
      console.log('Something went wrong during the init.')
    }
  }

  onError(err) {
    console.log('Something went wrong');
    console.log(err);
  }

  onFree() {
    console.log('GoPiGo is free to go');
  }

  onHalt() {
    console.log('GoPiGo is halted');
  }

  onClose() {
    console.log('GoPiGo is going to sleep');
  }

  onReset() {
    console.log('GoPiGo is resetting')
  }

  onNormalVoltage(voltage) {
    console.log('Voltage is ok [' + voltage + ']');
  };

  onLowVoltage(voltage) {
    console.log('(!!) Voltage is low ['+voltage+']')
  };

  onCriticalVoltage(voltage) {
    console.log('(!!!) Voltage is critical ['+voltage+']')
  };

}

module.exports = piJS;<|endoftext|>


<commit_before>
// a helper install script to install the documentation along with the program
// this runs whenever npm is activated or deactivated, so that the docs always
// reflect the current command.

var event = process.env.npm_lifecycle_event
  , npm = require("../npm")
  , exec = require("../lib/utils/exec")
  , log = require("../lib/utils/log")
  , fs = require("../lib/utils/graceful-fs")
  , path = require("path")
  , rm = require("../lib/utils/rm-rf")
  , mkdir = require("../lib/utils/mkdir-p")
  , manTarget = path.join(process.execPath, "../../share/man/man1")
  , exec = require("../lib/utils/exec")

log(event, "docs")

function dontPanic (er) {
  log(er, "doc install failed")
  log("probably still ok otherwise, though", "don't panic")
}

exec("manpath", [], null, true, function (er, code, stdout, stderr) {
  var manpath = er ? [] : stdout.trim().split(":")
  if (manpath.indexOf(path.dirname(manTarget)) === -1) {
    log("It seems " + manTarget + " might not be visible to man", "!")
    log("For greater justice, please add it to your man path", "!")
    log("See: man man", "!")
  }
  mkdir(manTarget, function (er) {
    if (er) dontPanic(er)
    else installDocs()
  })
})
function installDocs () {
  fs.readdir(path.join(process.cwd(), "man"), function (er, docs) {
    log(path.join(process.cwd(), "man"), "docs")
    log(manTarget, "docs")
    if (er) return
    ;(function R (doc) {
      if (!doc) return log("done", "docs")
      if (doc === "." || doc === "..") return R(docs.pop())
      var target = path.join(manTarget, "npm-"+doc)
      target = target.replace(/npm-npm\.1$/, "npm.1")
      switch (event) {
        case "activate":
          rm( target
            , function () {
                fs.symlink
                  ( path.join(process.cwd(), "man", doc)
                  , target
                  , function (er, ok) {
                      if (er) dontPanic(er)
                      else R(docs.pop())
                    }
                  )
              }
            )
        break
        case "deactivate":
          rm( target, function (er) { R(docs.pop()) })
        break
        default: throw new Error("invalid state"); break
      }
    })(docs.pop())
  })
}
<commit_msg>Remove the install-docs script, since npm does this itself now.<commit_after><|endoftext|>