<commit_before>/* code from https://developers.google.com/web/fundamentals/getting-started/primers/service-workers */

var CACHE_NAME = 'sptitle-cache-v1';
var urlsToCache = [
    'favicon-16x16.png',
    'index.html',
    'es6-promise.min.js',
    'fetch.min.js'
];

self.addEventListener('install', function (event) {
    console.log('Service Worker installing.');
    // Perform install steps
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(function (cache) {
                console.log('Opened cache');
                return cache.addAll(urlsToCache);
            })
    );
});

self.addEventListener('activate', function(event) {
  console.log('Service Worker activating.');
});

self.addEventListener('fetch', function (event) {
    console.log("service worker intercepting fetch()");
    event.respondWith(
        caches.match(event.request)
            .then(function (response) {
                // Cache hit - return response
                if (response) {
                    console.log('respond from cache for url ' + response.url);
                    return response;
                }
                // IMPORTANT: Clone the request. A request is a stream and
                // can only be consumed once. Since we are consuming this
                // once by cache and once by the browser for fetch, we need
                // to clone the response.
                var fetchRequest = event.request.clone();
                console.log("fetching request: " + fetchRequest);
                return fetch(fetchRequest).then(
                    function (response) {
                        // Check if we received a valid response
                        if (!response || response.status !== 200 || response.type !== 'basic') {
                            //console.log("Invalid response from fetch(): ", response);
                            return response;
                        }

                        // IMPORTANT: Clone the response. A response is a stream
                        // and because we want the browser to consume the response
                        // as well as the cache consuming the response, we need
                        // to clone it so we have two streams.
                        var responseToCache = response.clone();

                        caches.open(CACHE_NAME)
                            .then(function (cache) {
                                console.log("Cache the fetched response for request ", event.request);
                                cache.put(event.request, responseToCache);
                            });

                        return response;
                    }
                );
            }
            )
    );
});<commit_msg>service worker debugging<commit_after>/* code from https://developers.google.com/web/fundamentals/getting-started/primers/service-workers */

var CACHE_NAME = 'sptitle-cache-v1';
var urlsToCache = [
    'favicon-16x16.png',
    'index.html',
    'es6-promise.min.js',
    'fetch.min.js'
];

self.addEventListener('install', function (event) {
    debugger;
    console.log('Service Worker installing.');
    // Perform install steps
    // event.waitUntil(
    //     caches.open(CACHE_NAME)
    //         .then(function (cache) {
    //             console.log('Opened cache');
    //             return cache.addAll(urlsToCache);
    //         })
    // );
});

self.addEventListener('activate', function(event) {
  console.log('Service Worker activating.');
});

self.addEventListener('fetch', function (event) {
    debugger;
    console.log("service worker intercepting fetch()");
    event.respondWith(
        caches.match(event.request)
            .then(function (response) {
                // Cache hit - return response
                if (response) {
                    console.log('respond from cache for url ' + response.url);
                    return response;
                }
                // IMPORTANT: Clone the request. A request is a stream and
                // can only be consumed once. Since we are consuming this
                // once by cache and once by the browser for fetch, we need
                // to clone the response.
                var fetchRequest = event.request.clone();
                console.log("fetching request: ", fetchRequest);
                return fetch(fetchRequest, {mode: 'no-cors', credentials: 'include'}).then(
                    function (response) {
                        // Check if we received a valid response
                        if (!response || response.status !== 200 || response.type !== 'basic') {
                            //console.log("Invalid response from fetch(): ", response);
                            return response;
                        }

                        // IMPORTANT: Clone the response. A response is a stream
                        // and because we want the browser to consume the response
                        // as well as the cache consuming the response, we need
                        // to clone it so we have two streams.
                        var responseToCache = response.clone();

                        caches.open(CACHE_NAME)
                            .then(function (cache) {
                                console.log("Cache the fetched response for request ", event.request);
                                cache.put(event.request, responseToCache);
                            });

                        return response;
                    }
                ).catch(function(error) {
                    console.error('Fetching failed:', error);

                    throw error;
                });
            }
            )
    );
});<|endoftext|>


<commit_before>/* @flow */

import fromEventsCapture from './lib/fromEventsCapture';
import kefirBus from 'kefir-bus';
import React, {PropTypes} from 'react';
import ReactDOM, {findDOMNode} from 'react-dom';
import containByScreen from 'contain-by-screen';
import type {Options} from 'contain-by-screen';
import isEqual from 'lodash/isEqual';

class Float extends React.Component {
  static propTypes = {
    children: PropTypes.element.isRequired
  };

  render(): React.Element {
    return this.props.children;
  }
}

class Anchor extends React.Component {
  static propTypes = {
    children: PropTypes.element.isRequired
  };

  render(): React.Element {
    return this.props.children;
  }
}

type Props = {
  options?: ?Options;
  children?: any;
};
export default class FloatAnchor extends React.Component {
  props: Props;
  static propTypes = {
    options: PropTypes.object,
    children: PropTypes.node
  };

  static Anchor = Anchor;
  static Float = Float;

  _portal: ?HTMLElement;
  _portalRemoval: Object = kefirBus();

  _getAnchorChildren(): {anchor: ?React.Element, float: ?React.Element} {
    let anchor = null;
    let float = null;

    React.Children.forEach(this.props.children, child => {
      if (!child) return;
      if (child.type === Anchor) {
        anchor = child;
      } else if (child.type === Float) {
        float = child;
      }
    });

    return {anchor, float};
  }

  componentDidMount() {
    this._updateFloat();
  }

  componentDidUpdate(prevProps: typeof FloatAnchor.prototype.props) {
    let forceReposition = !isEqual(prevProps.options, this.props.options);
    if (forceReposition || prevProps.children !== this.props.children) {
      this._updateFloat(forceReposition);
    }
  }

  componentWillUnmount() {
    this._portalRemoval.emit(null);
  }

  _updateFloat(forceReposition: boolean=false) {
    const {float} = this._getAnchorChildren();

    if (float) {
      let shouldReposition = forceReposition;
      if (!this._portal) {
        shouldReposition = true;
        const portal = this._portal = document.createElement('div');
        portal.style.position = 'fixed';
        document.body.appendChild(portal);
        this._portalRemoval.take(1).onValue(() => {
          ReactDOM.unmountComponentAtNode(portal);
          portal.remove();
          this._portal = null;
        });
        const el = findDOMNode(this);
        fromEventsCapture(window, 'scroll')
          .filter(event => event.target.contains(el))
          .takeUntilBy(this._portalRemoval)
          .onValue(() => {
            this.reposition();
          });
      }
      (ReactDOM:any).unstable_renderSubtreeIntoContainer(
        this,
        float,
        this._portal,
        shouldReposition ?
          () => {
            this.reposition();
          } : null
      );
    } else {
      if (this._portal) {
        this._portalRemoval.emit(null);
      }
    }
  }

  reposition() {
    const portal = this._portal;
    if (portal) {
      containByScreen(portal, findDOMNode(this), this.props.options || {});
    }
  }

  render(): ?React.Element {
    const {anchor} = this._getAnchorChildren();
    return anchor;
  }
}
<commit_msg>FloatAnchor children validation<commit_after>/* @flow */

import fromEventsCapture from './lib/fromEventsCapture';
import kefirBus from 'kefir-bus';
import React, {PropTypes} from 'react';
import ReactDOM, {findDOMNode} from 'react-dom';
import containByScreen from 'contain-by-screen';
import type {Options} from 'contain-by-screen';
import isEqual from 'lodash/isEqual';

class Float extends React.Component {
  static propTypes = {
    children: PropTypes.element.isRequired
  };

  render(): React.Element {
    return this.props.children;
  }
}

class Anchor extends React.Component {
  static propTypes = {
    children: PropTypes.element.isRequired
  };

  render(): React.Element {
    return this.props.children;
  }
}

type Props = {
  options?: ?Options;
  children?: any;
};
export default class FloatAnchor extends React.Component {
  props: Props;
  static propTypes = {
    options: PropTypes.object,
    children: function(props, propName) {
      let failed = false;
      React.Children.forEach(props[propName], child => {
        if (child && child.type !== Anchor && child.type !== Float) {
          failed = true;
        }
      });
      if (failed) {
        return new Error('Children of FloatAnchor must be Float or Anchor elements.');
      }
    }
  };

  static Anchor = Anchor;
  static Float = Float;

  _portal: ?HTMLElement;
  _portalRemoval: Object = kefirBus();

  _getAnchorChildren(): {anchor: ?React.Element, float: ?React.Element} {
    let anchor = null;
    let float = null;

    React.Children.forEach(this.props.children, child => {
      if (!child) return;
      if (child.type === Anchor) {
        anchor = child;
      } else if (child.type === Float) {
        float = child;
      }
    });

    return {anchor, float};
  }

  componentDidMount() {
    this._updateFloat();
  }

  componentDidUpdate(prevProps: typeof FloatAnchor.prototype.props) {
    let forceReposition = !isEqual(prevProps.options, this.props.options);
    if (forceReposition || prevProps.children !== this.props.children) {
      this._updateFloat(forceReposition);
    }
  }

  componentWillUnmount() {
    this._portalRemoval.emit(null);
  }

  _updateFloat(forceReposition: boolean=false) {
    const {float} = this._getAnchorChildren();

    if (float) {
      let shouldReposition = forceReposition;
      if (!this._portal) {
        shouldReposition = true;
        const portal = this._portal = document.createElement('div');
        portal.style.position = 'fixed';
        document.body.appendChild(portal);
        this._portalRemoval.take(1).onValue(() => {
          ReactDOM.unmountComponentAtNode(portal);
          portal.remove();
          this._portal = null;
        });
        const el = findDOMNode(this);
        fromEventsCapture(window, 'scroll')
          .filter(event => event.target.contains(el))
          .takeUntilBy(this._portalRemoval)
          .onValue(() => {
            this.reposition();
          });
      }
      (ReactDOM:any).unstable_renderSubtreeIntoContainer(
        this,
        float,
        this._portal,
        shouldReposition ?
          () => {
            this.reposition();
          } : null
      );
    } else {
      if (this._portal) {
        this._portalRemoval.emit(null);
      }
    }
  }

  reposition() {
    const portal = this._portal;
    if (portal) {
      containByScreen(portal, findDOMNode(this), this.props.options || {});
    }
  }

  render(): ?React.Element {
    const {anchor} = this._getAnchorChildren();
    return anchor;
  }
}
<|endoftext|>


<commit_before>var data = [];

function inside(xp, yp, x, y) {
        for(var i = 0; i < xp.length; i++) {
                var dx1 = xp[i] - x;
                var dy1 = yp[i] - y;
                var dx2 = xp[(i + 1) % xp.length] - x;
                var dy2 = yp[(i + 1) % xp.length] - y;

                if(dx1 * dy2 - dx2 * dy1 < 0) {
                        return false;
                }
        }
        return true;
}

var init = function() {
        console.log('initializing map');
        $("#info").hide();
        moved = function(e) {
                var imwidth = $("#map").width();
                var imheight = $("#map").height();
                var x = e.offsetX / imwidth;
                var y = e.offsetY / imheight;
                var idx = -1;
                for(var i = 1; i <= 100; i++) {
                        if(data[i].x && data[i].y) {
                                if(inside(data[i].x, data[i].y, x, y)) {
                                        idx = i;
                                        break;
                                }
                        }
                }

                if(idx == -1) {
                        $("#info").hide();
                } else {
                        $("#info-head").text('Unit ' + idx);
                        if(data[idx].people) {
                                $("#info-bod").html(data[idx].people.join('
'));
                        } else {
                                $("#info-bod").html("???");
                        }
                        var imgoff = $("#image").offset();
                        var infow = $("#info").width();
                        var infoh = $("#info").height();
                        $("#info").offset({ top: e.offsetY + imgoff.top + 10, left: e.offsetX + imgoff.left - infoh / 2 });
                        $("#info").show();
                }
        }
        $("#image").mousemove(moved);
        $("#image").click(function(e) {
                var imwidth = $("#map").width();
                var imheight = $("#map").height();
                var x = e.offsetX / imwidth;
                var y = e.offsetY / imheight;
                $("#points").html($("#points").html() + x + "
" + y + "
");

        });

        for(var i = 0; i <= 100; i++) {
                data.push({});
        }

        $.ajax({
                url: "points.txt",
                success: function(d) {
                        d = d.split('\n');
                        for(var i = 1; i <= 100; i++) {
                                data[i].x = [];
                                data[i].y = [];
                                for(var j = 0; j < 4; j++) {
                                        data[i].x.push(Number(d[i * 8 - 8 + j * 2]));
                                        data[i].y.push(Number(d[i * 8 - 7 + j * 2]));
                                }
                        }
                        console.log(data);
                }
        });
        $.ajax({
                url: "people.txt",
                success: function(d) {
                        d = d.split('\n');
                        for(var i = 1; i <= 100; i++) {
                                data[i].people = [];
                                for(var j = 0; j < 4; j++) {
                                        data[i].people.push(d[i * 5 - 4 + j].replace(/-\s?/, ""));
                                }
                                if(!data[i].people[0]) {
                                        data[i].people = undefined;
                                }
                        }
                        console.log(data);
                }
        })
}

$(document).ready(init);
<commit_msg>fixed firefox support<commit_after>var data = [];

function inside(xp, yp, x, y) {
        for(var i = 0; i < xp.length; i++) {
                var dx1 = xp[i] - x;
                var dy1 = yp[i] - y;
                var dx2 = xp[(i + 1) % xp.length] - x;
                var dy2 = yp[(i + 1) % xp.length] - y;

                if(dx1 * dy2 - dx2 * dy1 < 0) {
                        return false;
                }
        }
        return true;
}

var init = function() {
        console.log('initializing map');
        $("#info").hide();
        moved = function(e) {
                var imwidth = $("#map").width();
                var imheight = $("#map").height();
                var off = $("#map").offset();
                var x = (e.pageX - off.left) / imwidth;
                var y = (e.pageY - off.top) / imheight;
                var idx = -1;
                for(var i = 1; i <= 100; i++) {
                        if(data[i].x && data[i].y) {
                                if(inside(data[i].x, data[i].y, x, y)) {
                                        idx = i;
                                        break;
                                }
                        }
                }

                if(idx == -1) {
                        $("#info").hide();
                } else {
                        $("#info-head").text('Unit ' + idx);
                        if(data[idx].people) {
                                $("#info-bod").html(data[idx].people.join('
'));
                        } else {
                                $("#info-bod").html("???");
                        }
                        var imgoff = $("#image").offset();
                        var infow = $("#info").width();
                        var infoh = $("#info").height();
                        $("#info").offset({ top: y * imheight + imgoff.top + 10, left: x * imwidth + imgoff.left - infoh / 2 });
                        $("#info").show();
                }
        }
        $("#image").mousemove(moved);
        $("#image").click(function(e) {
                var imwidth = $("#map").width();
                var imheight = $("#map").height();
                var x = e.offsetX / imwidth;
                var y = e.offsetY / imheight;
                $("#points").html($("#points").html() + x + "
" + y + "
");

        });

        for(var i = 0; i <= 100; i++) {
                data.push({});
        }

        $.ajax({
                url: "points.txt",
                success: function(d) {
                        d = d.split('\n');
                        for(var i = 1; i <= 100; i++) {
                                data[i].x = [];
                                data[i].y = [];
                                for(var j = 0; j < 4; j++) {
                                        data[i].x.push(Number(d[i * 8 - 8 + j * 2]));
                                        data[i].y.push(Number(d[i * 8 - 7 + j * 2]));
                                }
                        }
                        console.log(data);
                }
        });
        $.ajax({
                url: "people.txt",
                success: function(d) {
                        d = d.split('\n');
                        for(var i = 1; i <= 100; i++) {
                                data[i].people = [];
                                for(var j = 0; j < 4; j++) {
                                        data[i].people.push(d[i * 5 - 4 + j].replace(/-\s?/, ""));
                                }
                                if(!data[i].people[0]) {
                                        data[i].people = undefined;
                                }
                        }
                        console.log(data);
                }
        })
}

$(document).ready(init);
<|endoftext|>


<commit_before>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

import {type EventSubscription} from '../vendor/emitter/EventEmitter';
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import logError from '../Utilities/logError';
import NativeAppState from './NativeAppState';
import Platform from '../Utilities/Platform';

export type AppStateValues = 'inactive' | 'background' | 'active';

type AppStateEventDefinitions = {
  change: [AppStateValues],
  memoryWarning: [],
  blur: [],
  focus: [],
};

type NativeAppStateEventDefinitions = {
  appStateDidChange: [{app_state: AppStateValues}],
  appStateFocusChange: [boolean],
  memoryWarning: [],
};

/**
 * `AppState` can tell you if the app is in the foreground or background,
 * and notify you when the state changes.
 *
 * See https://reactnative.dev/docs/appstate
 */
class AppState {
  currentState: ?string = null;
  isAvailable: boolean;

  _emitter: ?NativeEventEmitter<NativeAppStateEventDefinitions>;

  constructor() {
    if (NativeAppState == null) {
      this.isAvailable = false;
    } else {
      this.isAvailable = true;

      const emitter: NativeEventEmitter<NativeAppStateEventDefinitions> =
        new NativeEventEmitter(
          // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
          // If you want to use the native module on other platforms, please remove this condition and test its behavior
          Platform.OS !== 'ios' ? null : NativeAppState,
        );
      this._emitter = emitter;

      this.currentState = NativeAppState.getConstants().initialAppState;

      let eventUpdated = false;

      // TODO: this is a terrible solution - in order to ensure `currentState`
      // prop is up to date, we have to register an observer that updates it
      // whenever the state changes, even if nobody cares. We should just
      // deprecate the `currentState` property and get rid of this.
      emitter.addListener('appStateDidChange', appStateData => {
        eventUpdated = true;
        this.currentState = appStateData.app_state;
      });

      // TODO: see above - this request just populates the value of `currentState`
      // when the module is first initialized. Would be better to get rid of the
      // prop and expose `getCurrentAppState` method directly.
      // $FlowExpectedError[incompatible-call]
      NativeAppState.getCurrentAppState(appStateData => {
        // It's possible that the state will have changed here & listeners need to be notified
        if (!eventUpdated && this.currentState !== appStateData.app_state) {
          this.currentState = appStateData.app_state;
          emitter.emit('appStateDidChange', appStateData);
        }
      }, logError);
    }
  }

  // TODO: now that AppState is a subclass of NativeEventEmitter, we could
  // deprecate `addEventListener` and `removeEventListener` and just use
  // addListener` and `listener.remove()` directly. That will be a breaking
  // change though, as both the method and event names are different
  // (addListener events are currently required to be globally unique).
  /**
   * Add a handler to AppState changes by listening to the `change` event type
   * and providing the handler.
   *
   * See https://reactnative.dev/docs/appstate#addeventlistener
   */
  addEventListener<K: $Keys<AppStateEventDefinitions>>(
    type: K,
    handler: (...$ElementType<AppStateEventDefinitions, K>) => void,
  ): EventSubscription {
    const emitter = this._emitter;
    if (emitter == null) {
      throw new Error('Cannot use AppState when `isAvailable` is false.');
    }
    switch (type) {
      case 'change':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const changeHandler: AppStateValues => void = handler;
        return emitter.addListener('appStateDidChange', appStateData => {
          changeHandler(appStateData.app_state);
        });
      case 'memoryWarning':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const memoryWarningHandler: () => void = handler;
        return emitter.addListener('memoryWarning', memoryWarningHandler);
      case 'blur':
      case 'focus':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const focusOrBlurHandler: () => void = handler;
        return emitter.addListener('appStateFocusChange', hasFocus => {
          if (type === 'blur' && !hasFocus) {
            focusOrBlurHandler();
          }
          if (type === 'focus' && hasFocus) {
            focusOrBlurHandler();
          }
        });
    }
    throw new Error('Trying to subscribe to unknown event: ' + type);
  }

  /**
   * @deprecated Use `remove` on the EventSubscription from `addEventListener`.
   */
  removeEventListener<K: $Keys<AppStateEventDefinitions>>(
    type: K,
    listener: (...$ElementType<AppStateEventDefinitions, K>) => mixed,
  ): void {
    const emitter = this._emitter;
    if (emitter == null) {
      throw new Error('Cannot use AppState when `isAvailable` is false.');
    }
    // NOTE: This will report a deprecation notice via `console.error`.
    switch (type) {
      case 'change':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        // $FlowIssue[incompatible-call]
        emitter.removeListener('appStateDidChange', listener);
        return;
      case 'memoryWarning':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        emitter.removeListener('memoryWarning', listener);
        return;
      case 'blur':
      case 'focus':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        // $FlowIssue[incompatible-call]
        emitter.addListener('appStateFocusChange', listener);
        return;
    }
    throw new Error('Trying to unsubscribe from unknown event: ' + type);
  }
}

module.exports = (new AppState(): AppState);
<commit_msg>(AppState) fix removeEventListener adding another listener when type is blur or focus (#33491)<commit_after>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

import {type EventSubscription} from '../vendor/emitter/EventEmitter';
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import logError from '../Utilities/logError';
import NativeAppState from './NativeAppState';
import Platform from '../Utilities/Platform';

export type AppStateValues = 'inactive' | 'background' | 'active';

type AppStateEventDefinitions = {
  change: [AppStateValues],
  memoryWarning: [],
  blur: [],
  focus: [],
};

type NativeAppStateEventDefinitions = {
  appStateDidChange: [{app_state: AppStateValues}],
  appStateFocusChange: [boolean],
  memoryWarning: [],
};

/**
 * `AppState` can tell you if the app is in the foreground or background,
 * and notify you when the state changes.
 *
 * See https://reactnative.dev/docs/appstate
 */
class AppState {
  currentState: ?string = null;
  isAvailable: boolean;

  _emitter: ?NativeEventEmitter<NativeAppStateEventDefinitions>;

  constructor() {
    if (NativeAppState == null) {
      this.isAvailable = false;
    } else {
      this.isAvailable = true;

      const emitter: NativeEventEmitter<NativeAppStateEventDefinitions> =
        new NativeEventEmitter(
          // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
          // If you want to use the native module on other platforms, please remove this condition and test its behavior
          Platform.OS !== 'ios' ? null : NativeAppState,
        );
      this._emitter = emitter;

      this.currentState = NativeAppState.getConstants().initialAppState;

      let eventUpdated = false;

      // TODO: this is a terrible solution - in order to ensure `currentState`
      // prop is up to date, we have to register an observer that updates it
      // whenever the state changes, even if nobody cares. We should just
      // deprecate the `currentState` property and get rid of this.
      emitter.addListener('appStateDidChange', appStateData => {
        eventUpdated = true;
        this.currentState = appStateData.app_state;
      });

      // TODO: see above - this request just populates the value of `currentState`
      // when the module is first initialized. Would be better to get rid of the
      // prop and expose `getCurrentAppState` method directly.
      // $FlowExpectedError[incompatible-call]
      NativeAppState.getCurrentAppState(appStateData => {
        // It's possible that the state will have changed here & listeners need to be notified
        if (!eventUpdated && this.currentState !== appStateData.app_state) {
          this.currentState = appStateData.app_state;
          emitter.emit('appStateDidChange', appStateData);
        }
      }, logError);
    }
  }

  // TODO: now that AppState is a subclass of NativeEventEmitter, we could
  // deprecate `addEventListener` and `removeEventListener` and just use
  // addListener` and `listener.remove()` directly. That will be a breaking
  // change though, as both the method and event names are different
  // (addListener events are currently required to be globally unique).
  /**
   * Add a handler to AppState changes by listening to the `change` event type
   * and providing the handler.
   *
   * See https://reactnative.dev/docs/appstate#addeventlistener
   */
  addEventListener<K: $Keys<AppStateEventDefinitions>>(
    type: K,
    handler: (...$ElementType<AppStateEventDefinitions, K>) => void,
  ): EventSubscription {
    const emitter = this._emitter;
    if (emitter == null) {
      throw new Error('Cannot use AppState when `isAvailable` is false.');
    }
    switch (type) {
      case 'change':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const changeHandler: AppStateValues => void = handler;
        return emitter.addListener('appStateDidChange', appStateData => {
          changeHandler(appStateData.app_state);
        });
      case 'memoryWarning':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const memoryWarningHandler: () => void = handler;
        return emitter.addListener('memoryWarning', memoryWarningHandler);
      case 'blur':
      case 'focus':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        const focusOrBlurHandler: () => void = handler;
        return emitter.addListener('appStateFocusChange', hasFocus => {
          if (type === 'blur' && !hasFocus) {
            focusOrBlurHandler();
          }
          if (type === 'focus' && hasFocus) {
            focusOrBlurHandler();
          }
        });
    }
    throw new Error('Trying to subscribe to unknown event: ' + type);
  }

  /**
   * @deprecated Use `remove` on the EventSubscription from `addEventListener`.
   */
  removeEventListener<K: $Keys<AppStateEventDefinitions>>(
    type: K,
    listener: (...$ElementType<AppStateEventDefinitions, K>) => mixed,
  ): void {
    const emitter = this._emitter;
    if (emitter == null) {
      throw new Error('Cannot use AppState when `isAvailable` is false.');
    }
    // NOTE: This will report a deprecation notice via `console.error`.
    switch (type) {
      case 'change':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        // $FlowIssue[incompatible-call]
        emitter.removeListener('appStateDidChange', listener);
        return;
      case 'memoryWarning':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        emitter.removeListener('memoryWarning', listener);
        return;
      case 'blur':
      case 'focus':
        // $FlowIssue[invalid-tuple-arity] Flow cannot refine handler based on the event type
        // $FlowIssue[incompatible-call]
        emitter.removeListener('appStateFocusChange', listener);
        return;
    }
    throw new Error('Trying to unsubscribe from unknown event: ' + type);
  }
}

module.exports = (new AppState(): AppState);
<|endoftext|>


<commit_before>'use strict';

var Q = require('q');
var gulpUtil = require('gulp-util');
var childProcess = require('child_process');
var jetpack = require('fs-jetpack');
var asar = require('asar');
var utils = require('./utils');

var projectDir;
var tmpDir;
var releasesDir;
var readyAppDir;
var manifest;

var init = function () {
    projectDir = jetpack;
    tmpDir = projectDir.dir('./tmp', { empty: true });
    releasesDir = projectDir.dir('./releases');
    manifest = projectDir.read('app/package.json', 'json');
    readyAppDir = tmpDir.cwd(manifest.name);

    return Q();
};

var copyRuntime = function () {
    return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
};

var packageBuiltApp = function () {
    var deferred = Q.defer();

    asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function() {
        deferred.resolve();
    });

    return deferred.promise;
};

var finalize = function () {
    var deferred = Q.defer();

    projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico'));

    // Replace Electron icon for your own.
    var rcedit = require('rcedit');
    rcedit(readyAppDir.path('electron.exe'), {
        'icon': projectDir.path('resources/windows/icon.ico'),
        'version-string': {
            'ProductName': manifest.productName,
            'FileDescription': manifest.description,
        }
    }, function (err) {
        if (!err) {
            deferred.resolve();
        }
    });

    return deferred.promise;
};

var createInstaller = function () {
    var deferred = Q.defer();

    var finalPackageName = manifest.name + '_' + manifest.version + '.exe';
    var installScript = projectDir.read('resources/windows/installer.nsi');
    installScript = utils.replace(installScript, {
        name: manifest.name,
        productName: manifest.productName,
        version: manifest.version,
        src: readyAppDir.path(),
        dest: releasesDir.path(finalPackageName),
        icon: readyAppDir.path('icon.ico'),
        setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
        banner: projectDir.path('resources/windows/setup-banner.bmp'),
    });
    tmpDir.write('installer.nsi', installScript);

    gulpUtil.log('Building installer with NSIS...');

    // Remove destination file if already exists.
    releasesDir.remove(finalPackageName);

    // Note: NSIS have to be added to PATH (environment variables).
    var nsis = childProcess.spawn('makensis', [
        tmpDir.path('installer.nsi')
    ], {
        stdio: 'inherit'
    });
    nsis.on('close', function () {
        gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
        deferred.resolve();
    });

    return deferred.promise;
};

var cleanClutter = function () {
    return tmpDir.removeAsync('.');
};

module.exports = function () {
    return init()
    .then(copyRuntime)
    .then(packageBuiltApp)
    .then(finalize)
    .then(createInstaller)
    .then(cleanClutter);
};
<commit_msg>Added extra error message<commit_after>'use strict';

var Q = require('q');
var gulpUtil = require('gulp-util');
var childProcess = require('child_process');
var jetpack = require('fs-jetpack');
var asar = require('asar');
var utils = require('./utils');

var projectDir;
var tmpDir;
var releasesDir;
var readyAppDir;
var manifest;

var init = function () {
    projectDir = jetpack;
    tmpDir = projectDir.dir('./tmp', { empty: true });
    releasesDir = projectDir.dir('./releases');
    manifest = projectDir.read('app/package.json', 'json');
    readyAppDir = tmpDir.cwd(manifest.name);

    return Q();
};

var copyRuntime = function () {
    return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
};

var packageBuiltApp = function () {
    var deferred = Q.defer();

    asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function() {
        deferred.resolve();
    });

    return deferred.promise;
};

var finalize = function () {
    var deferred = Q.defer();

    projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico'));

    // Replace Electron icon for your own.
    var rcedit = require('rcedit');
    rcedit(readyAppDir.path('electron.exe'), {
        'icon': projectDir.path('resources/windows/icon.ico'),
        'version-string': {
            'ProductName': manifest.productName,
            'FileDescription': manifest.description,
        }
    }, function (err) {
        if (!err) {
            deferred.resolve();
        }
    });

    return deferred.promise;
};

var createInstaller = function () {
    var deferred = Q.defer();

    var finalPackageName = manifest.name + '_' + manifest.version + '.exe';
    var installScript = projectDir.read('resources/windows/installer.nsi');
    installScript = utils.replace(installScript, {
        name: manifest.name,
        productName: manifest.productName,
        version: manifest.version,
        src: readyAppDir.path(),
        dest: releasesDir.path(finalPackageName),
        icon: readyAppDir.path('icon.ico'),
        setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
        banner: projectDir.path('resources/windows/setup-banner.bmp'),
    });
    tmpDir.write('installer.nsi', installScript);

    gulpUtil.log('Building installer with NSIS...');

    // Remove destination file if already exists.
    releasesDir.remove(finalPackageName);

    // Note: NSIS have to be added to PATH (environment variables).
    var nsis = childProcess.spawn('makensis', [
        tmpDir.path('installer.nsi')
    ], {
        stdio: 'inherit'
    });
    nsis.on('error', function () {
        gulpUtil.log("NSIS script failed. Are you sure you've "
            + "installed NSIS and added it to PATH environment variable?");
    });
    nsis.on('close', function () {
        gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
        deferred.resolve();
    });

    return deferred.promise;
};

var cleanClutter = function () {
    return tmpDir.removeAsync('.');
};

module.exports = function () {
    return init()
    .then(copyRuntime)
    .then(packageBuiltApp)
    .then(finalize)
    .then(createInstaller)
    .then(cleanClutter);
};
<|endoftext|>


<commit_before>// "lazy load"- db may not be immediately initialized
db = searchIndex({ name: 'searchIndexInBrowser' })

// Functions
const search = function (q) {
  emptyElements(['searchResults'])
  db.SEARCH(q)
    .then(function(results) {
      //console.log(Array.isArray(results))
      console.log(JSON.stringify(results[0]))
      results.forEach(function(result) {
        console.log(result);
        populateResultsDiv(result)
    })
    .catch(function (error) {
      console.log(error.message)
    })
}

const indexData = function (data) {
  emptyElements(['title','body'])
  db.PUT([data])
    .then(function(message) {
      console.log(message)
    })
}

const populateResultsDiv = function(result) {
  console.log('Boom')
  console.log(result)
  //result = JSON.parse(result)
  var node = document.createElement('div')
  node.innerHTML = '' + result.obj.title + '
'
    + result.obj.body + '
---'
  document.getElementById('searchResults').appendChild(node)
}

const emptyElements = function (elements) {
  elements.forEach(function(element) {
    document.getElementById(element).innerHTML = ''
    document.getElementById(element).value = ''
  })
}

// index data
document.getElementById("indexContent").onmousedown = function() {
  const docTitle = document.getElementById("title").value
  const docBody = document.getElementById("body").value
  if ((docTitle + docBody).length > 0) indexData({ title: docTitle, body: docBody })
  console.log({ title: docTitle, body: docBody })
}

// do a search
document.getElementById("searchQuery").onkeyup = function() {
  search(document.getElementById("searchQuery").value)
  console.log('Search query: ')
  console.log(document.getElementById("searchQuery").value)
}

// First iteration to get anything going
/*
db.PUT(data).then(
  console.log('Data indexed')
)
setTimeout(function(){
  db.SEARCH('document').then(results => {
    console.log(results)
  })
}, 500)
*/

<commit_msg>Trying to fix promises for demo<commit_after>// "lazy load"- db may not be immediately initialized
const db = searchIndex({ name: 'searchIndexInBrowser' })

// Functions
const search = function (q) {
  emptyElements(['searchResults'])
  db.SEARCH(q)
    .then(function(results) {
      //console.log(Array.isArray(results))
      console.log(JSON.stringify(results[0]))
      results.forEach(function(result) {
        console.log(result);
        populateResultsDiv(result)
    })
    .catch(function (error) {
      console.log('Error while searching:')
      console.log(error.message)
    })
  })
}

const indexData = function (data) {
  emptyElements(['title','body'])
  db.PUT([data])
    .then(function(message) {
      console.log(message)
    })
    .catch(function (error) {
      console.log('Error while indexing:')
      console.log(error.message)
    })
}

const populateResultsDiv = function(result) {
  console.log('Boom')
  console.log(result)
  //result = JSON.parse(result)
  var node = document.createElement('div')
  node.innerHTML = '' + result.obj.title + '
'
    + result.obj.body + '
---'
  document.getElementById('searchResults').appendChild(node)
}

const emptyElements = function (elements) {
  elements.forEach(function(element) {
    document.getElementById(element).innerHTML = ''
    document.getElementById(element).value = ''
  })
}

// index data
document.getElementById("indexContent").onmousedown = function() {
  const docTitle = document.getElementById("title").value
  const docBody = document.getElementById("body").value
  if ((docTitle + docBody).length > 0) indexData({ title: docTitle, body: docBody })
  console.log({ title: docTitle, body: docBody })
}

// do a search
document.getElementById("searchQuery").onkeyup = function() {
  search(document.getElementById("searchQuery").value)
  console.log('Search query: ')
  console.log(document.getElementById("searchQuery").value)
}<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

var source = "https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv";
var got    = require("got");
var parse  = require("csv-parse");
var fs     = require("fs");

got(source, function (err, csv) {
    if (err) return console.error(err);
    parse(csv, null, function (err, data) {
        parsePorts(data, function (err) {
            console.info(err ? err : "ports.json updated successfully!");
            parseServices(data, function (err) {
                console.info(err ? err : "services.json updated successfully!");
            });
        });
    });
});

/* Fields:
 *
 * [ 'Service Name', 'Port Number', 'Transport Protocol', 'Description', 'Assignee', 'Contact', 'Registration Date', 'Modification Date',
 * 'Reference', 'Service Code', 'Known Unauthorized Uses', 'Assignment Notes' ]
 *
 * Examples:
 *
 * [ 'www', '80', 'udp', 'World Wide Web HTTP', '', '', '', '', '', '', '', 'This is a duplicate of the "http" service and should not be used for discovery purposes.' ]
 * [ 'ntp', '123', 'tcp', 'Network Time Protocol', '[Dave_Mills]', '[Dave_Mills]', '', '', '[RFC5905]', '', '', '' ]
 * [ 'http', '80', 'tcp', 'World Wide Web HTTP', '', '', '', '', '', '', '', 'Defined TXT keys: u=<username> p=<password> path=<path to document>' ]
 *
 */

function parsePorts(data, cb) {
    var output = {};
    data.forEach(function (entry) {
        if (entry[1] && entry[2] && !output[entry[1] + "/" + entry[2]] && !isNaN(Number(entry[1]))) {
            output[entry[1] + "/" + entry[2]] = {
                name: entry[0],
                description: entry[3]
            };
        }
    });
    fs.writeFile("./ports.json", JSON.stringify(output, null, 2), cb);
}

function parseServices(data, cb) {
    var output = {};
    data.forEach(function (entry) {
        if (!output[entry[0]] && entry[1] && entry[2] && typeof entry[0] === "string" &&
            entry[0].length && !isNaN(Number(entry[1]))) {
            output[entry[0]] = {
                ports: [Number(entry[1]) + "/" + entry[2]],
                description: entry[3] || undefined
            };
        } else if (output[entry[0]] && entry[1] && entry[2] && typeof entry[0] === "string" &&
                   entry[0].length && !isNaN(Number(entry[1])) &&
                   output[entry[0]].ports.indexOf(Number(entry[1]) + "/"  + entry[2]) === -1) {
            output[entry[0]].ports.push(entry[1] + "/"  + entry[2]);
        }
    });
    fs.writeFile("./services.json", JSON.stringify(output, null, 2), cb);
}
<commit_msg>CLI: Silent update<commit_after>#!/usr/bin/env node
"use strict";

var source = "https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv";
var got    = require("got");
var parse  = require("csv-parse");
var fs     = require("fs");

got(source, function (err, csv) {
    if (err) return console.error(err);
    parse(csv, null, function (err, data) {
        parsePorts(data, function (err) {
            if (err) return exit(err);
            parseServices(data, function (err) {
                if (err) return exit(err);
                process.exit(0);
            });
        });
    });
});

function exit(err) {
    console.err(err);
    process.exit(1);
}

/* Fields:
 *
 * [ 'Service Name', 'Port Number', 'Transport Protocol', 'Description', 'Assignee', 'Contact', 'Registration Date', 'Modification Date',
 * 'Reference', 'Service Code', 'Known Unauthorized Uses', 'Assignment Notes' ]
 *
 * Examples:
 *
 * [ 'www', '80', 'udp', 'World Wide Web HTTP', '', '', '', '', '', '', '', 'This is a duplicate of the "http" service and should not be used for discovery purposes.' ]
 * [ 'ntp', '123', 'tcp', 'Network Time Protocol', '[Dave_Mills]', '[Dave_Mills]', '', '', '[RFC5905]', '', '', '' ]
 * [ 'http', '80', 'tcp', 'World Wide Web HTTP', '', '', '', '', '', '', '', 'Defined TXT keys: u=<username> p=<password> path=<path to document>' ]
 *
 */

function parsePorts(data, cb) {
    var output = {};
    data.forEach(function (entry) {
        if (entry[1] && entry[2] && !output[entry[1] + "/" + entry[2]] && !isNaN(Number(entry[1]))) {
            output[entry[1] + "/" + entry[2]] = {
                name: entry[0],
                description: entry[3]
            };
        }
    });
    fs.writeFile("./ports.json", JSON.stringify(output, null, 2), cb);
}

function parseServices(data, cb) {
    var output = {};
    data.forEach(function (entry) {
        if (!output[entry[0]] && entry[1] && entry[2] && typeof entry[0] === "string" &&
            entry[0].length && !isNaN(Number(entry[1]))) {
            output[entry[0]] = {
                ports: [Number(entry[1]) + "/" + entry[2]],
                description: entry[3] || undefined
            };
        } else if (output[entry[0]] && entry[1] && entry[2] && typeof entry[0] === "string" &&
                   entry[0].length && !isNaN(Number(entry[1])) &&
                   output[entry[0]].ports.indexOf(Number(entry[1]) + "/"  + entry[2]) === -1) {
            output[entry[0]].ports.push(entry[1] + "/"  + entry[2]);
        }
    });
    fs.writeFile("./services.json", JSON.stringify(output, null, 2), cb);
}
<|endoftext|>


<commit_before>define(['player', 'enemy', 'disc', "constants", "sound-player", "renderer", "startMenu"],
    function (Player, Enemy, Disc, CONSTS, SoundPlayer, Renderer) {
		var Engine = (function () {
			var game,
				requestId,
				timeId;
			var Engine = function(){
				game = this;
				initializeGame(game);
			};

			Engine.prototype = {
				startGame : function startGame(difficulty) {
					difficultyLevel = difficulty || 6;
					Renderer.drawCanvas(game);
					Renderer.drawSVG(game);
					moveDisc(game);
					moveEnemy(game);
					requestId = requestAnimationFrame(startGame);
					endGame(game);
				}
			};

			// initialize
						function initializeGame(game){
				SoundPlayer.playStartGameSound();
				Renderer.drawPlayField();
				//TODO: get name from start screen
				game.player = new Player(CONSTS.player.name, CONSTS.player.start.x, CONSTS.player.start.y, CONSTS.participantRadius);
				game.enemy = new Enemy(CONSTS.enemy.name, CONSTS.enemy.start.x, CONSTS.enemy.start.y, CONSTS.participantRadius);
				game.disc = new Disc(CONSTS.startDisc.x, CONSTS.startDisc.y, CONSTS.startDisc.radius);
				//TODO: get difficulty from start screen
				game.difficulty = CONSTS.gameDifficulty;
				game.time = CONSTS.startTime;
				initializeTimer();
				movePlayer(Renderer.canvas, game.player);
			}

			function initializeTimer(){
				function runTimer() {
					++game.time;
				}

				timeId = setInterval(runTimer, 1000);
			}

			// player movement
			function movePlayer(canvas, player) {
				canvas.addEventListener('mousemove', function(evt) {
					movePlayerWithMouse(evt, canvas, player)
				}, false);
				// canvas.addEventListener('touchmove', function(evt) {
					// movePlayerWithMouse(evt, canvas, player)
				// }, false);
			}

			function movePlayerWithMouse(evt, canvas, player){
				var mousePos = getMousePosition(canvas, evt);
				if(mousePos.x < CONSTS.playfield.middle + 5 && player.x < CONSTS.playfield.middle + 5) {
					player.move(mousePos);
				}
			}

			function getMousePosition(canvas, evt) {
				var rect = canvas.getBoundingClientRect();
				return {
					x: evt.clientX - rect.left,
					y: evt.clientY - rect.top
				};
			}

			// enemy movement
			function moveEnemy(game) {
				var speed = getEnemyMoveSpeed(game.enemy, game.disc, game.difficulty);
				if (game.disc.x > CONSTS.playfield.middle) {
					// move enemy x
					if (game.disc.x >= game.enemy.x) {
						game.enemy.reset();
					} else {
						game.enemy.move("left", speed.x);
						// move enemy y
						if (game.disc.y < game.enemy.y) {
							game.enemy.move("up", speed.y);
						} else if (game.disc.y > game.enemy.y) {
							game.enemy.move("down", speed.y);
						}
					}
				} else {
					game.enemy.reset();
				}
			}

			function getEnemyMoveSpeed(enemy, disc, difficulty) {
				var dx = enemy.x - disc.x + 2.5,
					dy = enemy.y - disc.y + 2.5,
					distance = Math.sqrt(dx * dx + dy * dy);

				distance = distance === 0 ? 0.1 : distance;

				return {
				  x: Math.abs((dx / distance) * difficulty),
				  y: Math.abs((dy / distance) * difficulty)
				}
			}

			// disc movement and collision
			function moveDisc(game){
				game.disc.move();
				handleDiscCollisionWithParticipant(game.player, game.disc);
				handleDiscCollisionWithParticipant(game.enemy, game.disc);
				handleDiscCollisionWithWalls(game.disc);
			}

			function handleDiscCollisionWithParticipant(player, disc) {
				var dx = player.x - disc.x,
					dy = player.y - disc.y,
					distance = Math.sqrt(dx * dx + dy * dy);

				if (distance < player.radius + disc.radius) {
					distance = distance === 0 ? 0.1 : distance;

					var unitX = dx / distance,
						unitY = dy / distance,

						force = -2,
						forceX = unitX * force,
						forceY = unitY * force;

					disc.velocity.x += forceX;
					disc.velocity.y += forceY;

					SoundPlayer.playPlayerColissionSound();
				}
			}

			function handleDiscCollisionWithWalls(disc) {
				// bounce off the floor
				if (disc.y > CONSTS.playfield.height - disc.radius) {
					disc.y = CONSTS.playfield.height - disc.radius;
					disc.velocity.y = -Math.abs(disc.velocity.y);
					SoundPlayer.playHitWallSound();
				}

				// bounce off ceiling
				if (disc.y < disc.radius) {
					disc.y = disc.radius;
					disc.velocity.y = Math.abs(disc.velocity.y);
					SoundPlayer.playHitWallSound();
				}

				// bounce off right wall
				if (disc.x > CONSTS.playfield.width - disc.radius) {
					// goal on right side
					if (CONSTS.goalLine.min < disc.y && disc.y <  CONSTS.goalLine.max) {
						handleGoal(game.player);
					} else {
						disc.x = CONSTS.playfield.width - disc.radius;
						disc.velocity.x = -Math.abs(disc.velocity.x);
						SoundPlayer.playHitWallSound();
					}
				}

				// bounce off left wall
				if (disc.x < disc.radius) {
					// goal on left side
					if (CONSTS.goalLine.min < disc.y && disc.y < CONSTS.goalLine.max){
						handleGoal(game.enemy);
					} else {
						disc.x = disc.radius;
						disc.velocity.x = Math.abs(disc.velocity.x);
						SoundPlayer.playHitWallSound();
					}
				}
			}

			function handleGoal(player) {
				player.score += 1;
				game.disc.reset();
				SoundPlayer.playGoalSound();
			}

			// game end
			function endGame(game){
				if (game.player.score >= CONSTS.winningGoalCount) {
					Renderer.drawEndScreen(CONSTS.winMsg);
					stopAnimation(requestId);
				} else if (game.enemy.score >= CONSTS.winningGoalCount) {
					Renderer.drawEndScreen(CONSTS.overMsg);
					stopAnimation(requestId);
				}
			}

			function stopAnimation(requestId){
				if (requestId) {
					window.cancelAnimationFrame(requestId);
					requestId = undefined;
				}

				if (timeId){
					clearInterval(timeId);
				}
			}

			return Engine;
		  }());

    return new Engine();
});<commit_msg>Add touch event for player<commit_after>define(['player', 'enemy', 'disc', "constants", "sound-player", "renderer", "startMenu"],
    function (Player, Enemy, Disc, CONSTS, SoundPlayer, Renderer) {
		var Engine = (function () {
			var game,
				requestId,
				timeId;
			var Engine = function(){
				game = this;
				initializeGame(game);
			};

			Engine.prototype = {
				startGame : function startGame(difficulty) {
					difficultyLevel = difficulty || 6;
					Renderer.drawCanvas(game);
					Renderer.drawSVG(game);
					moveDisc(game);
					moveEnemy(game);
					requestId = requestAnimationFrame(startGame);
					endGame(game);
				}
			};

			// initialize
						function initializeGame(game){
				SoundPlayer.playStartGameSound();
				Renderer.drawPlayField();
				//TODO: get name from start screen
				game.player = new Player(CONSTS.player.name, CONSTS.player.start.x, CONSTS.player.start.y, CONSTS.participantRadius);
				game.enemy = new Enemy(CONSTS.enemy.name, CONSTS.enemy.start.x, CONSTS.enemy.start.y, CONSTS.participantRadius);
				game.disc = new Disc(CONSTS.startDisc.x, CONSTS.startDisc.y, CONSTS.startDisc.radius);
				//TODO: get difficulty from start screen
				game.difficulty = CONSTS.gameDifficulty;
				game.time = CONSTS.startTime;
				initializeTimer();
				movePlayer(Renderer.canvas, game.player);
			}

			function initializeTimer(){
				function runTimer() {
					++game.time;
				}

				timeId = setInterval(runTimer, 1000);
			}

			// player movement
			function movePlayer(canvas, player) {
				canvas.addEventListener('mousemove', function(evt) {
					movePlayerWithMouse(evt, canvas, player)
				}, false);
				canvas.addEventListener('touchmove', function(evt) {
					movePlayerWithMouse(evt, canvas, player)
				}, false);
			}

			function movePlayerWithMouse(evt, canvas, player){
				var mousePos = getMousePosition(canvas, evt);
				if(mousePos.x < CONSTS.playfield.middle + 5 && player.x < CONSTS.playfield.middle + 5) {
					player.move(mousePos);
				}
			}

			function getMousePosition(canvas, evt) {
				var rect = canvas.getBoundingClientRect();
				return {
					x: evt.clientX - rect.left,
					y: evt.clientY - rect.top
				};
			}

			// enemy movement
			function moveEnemy(game) {
				var speed = getEnemyMoveSpeed(game.enemy, game.disc, game.difficulty);
				if (game.disc.x > CONSTS.playfield.middle) {
					// move enemy x
					if (game.disc.x >= game.enemy.x) {
						game.enemy.reset();
					} else {
						game.enemy.move("left", speed.x);
						// move enemy y
						if (game.disc.y < game.enemy.y) {
							game.enemy.move("up", speed.y);
						} else if (game.disc.y > game.enemy.y) {
							game.enemy.move("down", speed.y);
						}
					}
				} else {
					game.enemy.reset();
				}
			}

			function getEnemyMoveSpeed(enemy, disc, difficulty) {
				var dx = enemy.x - disc.x + 2.5,
					dy = enemy.y - disc.y + 2.5,
					distance = Math.sqrt(dx * dx + dy * dy);

				distance = distance === 0 ? 0.1 : distance;

				return {
				  x: Math.abs((dx / distance) * difficulty),
				  y: Math.abs((dy / distance) * difficulty)
				}
			}

			// disc movement and collision
			function moveDisc(game){
				game.disc.move();
				handleDiscCollisionWithParticipant(game.player, game.disc);
				handleDiscCollisionWithParticipant(game.enemy, game.disc);
				handleDiscCollisionWithWalls(game.disc);
			}

			function handleDiscCollisionWithParticipant(player, disc) {
				var dx = player.x - disc.x,
					dy = player.y - disc.y,
					distance = Math.sqrt(dx * dx + dy * dy);

				if (distance < player.radius + disc.radius) {
					distance = distance === 0 ? 0.1 : distance;

					var unitX = dx / distance,
						unitY = dy / distance,

						force = -2,
						forceX = unitX * force,
						forceY = unitY * force;

					disc.velocity.x += forceX;
					disc.velocity.y += forceY;

					SoundPlayer.playPlayerColissionSound();
				}
			}

			function handleDiscCollisionWithWalls(disc) {
				// bounce off the floor
				if (disc.y > CONSTS.playfield.height - disc.radius) {
					disc.y = CONSTS.playfield.height - disc.radius;
					disc.velocity.y = -Math.abs(disc.velocity.y);
					SoundPlayer.playHitWallSound();
				}

				// bounce off ceiling
				if (disc.y < disc.radius) {
					disc.y = disc.radius;
					disc.velocity.y = Math.abs(disc.velocity.y);
					SoundPlayer.playHitWallSound();
				}

				// bounce off right wall
				if (disc.x > CONSTS.playfield.width - disc.radius) {
					// goal on right side
					if (CONSTS.goalLine.min < disc.y && disc.y <  CONSTS.goalLine.max) {
						handleGoal(game.player);
					} else {
						disc.x = CONSTS.playfield.width - disc.radius;
						disc.velocity.x = -Math.abs(disc.velocity.x);
						SoundPlayer.playHitWallSound();
					}
				}

				// bounce off left wall
				if (disc.x < disc.radius) {
					// goal on left side
					if (CONSTS.goalLine.min < disc.y && disc.y < CONSTS.goalLine.max){
						handleGoal(game.enemy);
					} else {
						disc.x = disc.radius;
						disc.velocity.x = Math.abs(disc.velocity.x);
						SoundPlayer.playHitWallSound();
					}
				}
			}

			function handleGoal(player) {
				player.score += 1;
				game.disc.reset();
				SoundPlayer.playGoalSound();
			}

			// game end
			function endGame(game){
				if (game.player.score >= CONSTS.winningGoalCount) {
					Renderer.drawEndScreen(CONSTS.winMsg);
					stopAnimation(requestId);
				} else if (game.enemy.score >= CONSTS.winningGoalCount) {
					Renderer.drawEndScreen(CONSTS.overMsg);
					stopAnimation(requestId);
				}
			}

			function stopAnimation(requestId){
				if (requestId) {
					window.cancelAnimationFrame(requestId);
					requestId = undefined;
				}

				if (timeId){
					clearInterval(timeId);
				}
			}

			return Engine;
		  }());

    return new Engine();
});<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/DeveloperError',
        './PerspectiveOffCenterFrustum'
    ], function(
        defined,
        defineProperties,
        DeveloperError,
        PerspectiveOffCenterFrustum) {
    "use strict";

    /**
     * The viewing frustum is defined by 6 planes.
     * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components
     * define the unit vector normal to the plane, and the w component is the distance of the
     * plane from the origin/camera position.
     *
     * @alias PerspectiveFrustum
     * @constructor
     *
     * @see PerspectiveOffCenterFrustum
     *
     * @example
     * var frustum = new Cesium.PerspectiveFrustum();
     * frustum.aspectRatio = canvas.clientWidth / canvas.clientHeight;
     * frustum.fov = Cesium.Math.PI_OVER_THREE;
     * frustum.near = 1.0;
     * frustum.far = 2.0;
     */
    function PerspectiveFrustum() {
        this._offCenterFrustum = new PerspectiveOffCenterFrustum();

        /**
         * The angle of the field of view (FOV), in radians.  This angle will be used
         * as the horizontal FOV if the width is greater than the height, otherwise
         * it will be the vertical FOV.
         * @type {Number}
         * @default undefined
         */
        this.fov = undefined;
        this._fov = undefined;
        this._fovy = undefined;

        this._sseDenominator = undefined;

        /**
         * The aspect ratio of the frustum's width to it's height.
         * @type {Number}
         * @default undefined
         */
        this.aspectRatio = undefined;
        this._aspectRatio = undefined;

        /**
         * The distance of the near plane.
         * @type {Number}
         * @default 1.0
         */
        this.near = 1.0;
        this._near = this.near;

        /**
         * The distance of the far plane.
         * @type {Number}
         * @default 500000000.0
         */
        this.far = 500000000.0;
        this._far = this.far;
    }

    function update(frustum) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(frustum.fov) || !defined(frustum.aspectRatio) || !defined(frustum.near) || !defined(frustum.far)) {
            throw new DeveloperError('fov, aspectRatio, near, or far parameters are not set.');
        }
        //>>includeEnd('debug');

        var f = frustum._offCenterFrustum;

        if (frustum.fov !== frustum._fov || frustum.aspectRatio !== frustum._aspectRatio ||
                frustum.near !== frustum._near || frustum.far !== frustum._far) {
            //>>includeStart('debug', pragmas.debug);
            if (frustum.fov < 0 || frustum.fov >= Math.PI) {
                throw new DeveloperError('fov must be in the range [0, PI).');
            }

            if (frustum.aspectRatio < 0) {
                throw new DeveloperError('aspectRatio must be positive.');
            }

            if (frustum.near < 0 || frustum.near > frustum.far) {
                throw new DeveloperError('near must be greater than zero and less than far.');
            }
            //>>includeEnd('debug');

            frustum._aspectRatio = frustum.aspectRatio;
            frustum._fov = frustum.fov;
            frustum._fovy = (frustum.aspectRatio <= 1) ? frustum.fov : Math.atan(Math.tan(frustum.fov * 0.5) / frustum.aspectRatio) * 2.0;
            frustum._near = frustum.near;
            frustum._far = frustum.far;
            frustum._sseDenominator = 2.0 * Math.tan(0.5 * frustum._fovy);

            f.top = frustum.near * Math.tan(0.5 * frustum._fovy);
            f.bottom = -f.top;
            f.right = frustum.aspectRatio * f.top;
            f.left = -f.right;
            f.near = frustum.near;
            f.far = frustum.far;
        }
    }

    defineProperties(PerspectiveFrustum.prototype, {
        /**
         * Gets the perspective projection matrix computed from the view frustum.
         * @memberof PerspectiveFrustum.prototype
         * @type {Matrix4}
         * @readonly
         *
         * @see PerspectiveFrustum#infiniteProjectionMatrix
         */
        projectionMatrix : {
            get : function() {
                update(this);
                return this._offCenterFrustum.projectionMatrix;
            }
        },

        /**
         * The perspective projection matrix computed from the view frustum with an infinite far plane.
         * @memberof PerspectiveFrustum.prototype
         * @type {Matrix4}
         * @readonly
         *
         * @see PerspectiveFrustum#projectionMatrix
         */
        infiniteProjectionMatrix : {
            get : function() {
                update(this);
                return this._offCenterFrustum.infiniteProjectionMatrix;
            }
        },

        /**
         * Gets the angle of the vertical field of view, in radians.
         * @memberof PerspectiveFrustum.prototype
         * @type {Number}
         * @readonly
         * @default undefined
         */
        fovy : {
            get : function() {
                update(this);
                return this._fovy;
            }
        },

        /**
         * @readonly
         * @private
         */
        sseDenominator : {
            get : function () {
                update(this);
                return this._sseDenominator;
            }
        }
    });

    /**
     * Creates a culling volume for this frustum.
     *
     * @param {Cartesian3} position The eye position.
     * @param {Cartesian3} direction The view direction.
     * @param {Cartesian3} up The up direction.
     * @returns {CullingVolume} A culling volume at the given position and orientation.
     *
     * @example
     * // Check if a bounding volume intersects the frustum.
     * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp);
     * var intersect = cullingVolume.computeVisibility(boundingVolume);
     */
    PerspectiveFrustum.prototype.computeCullingVolume = function(position, direction, up) {
        update(this);
        return this._offCenterFrustum.computeCullingVolume(position, direction, up);
    };

    /**
     * Returns the pixel's width and height in meters.
     *
     * @param {Number} drawingBufferWidth The width of the drawing buffer.
     * @param {Number} drawingBufferHeight The height of the drawing buffer.
     * @param {Number} distance The distance to the near plane in meters.
     * @param {Cartesian2} result The object onto which to store the result.
     * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively.
     *
     * @exception {DeveloperError} drawingBufferWidth must be greater than zero.
     * @exception {DeveloperError} drawingBufferHeight must be greater than zero.
     *
     * @example
     * // Example 1
     * // Get the width and height of a pixel.
     * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 1.0, new Cartesian2());
     *
     * @example
     * // Example 2
     * // Get the width and height of a pixel if the near plane was set to 'distance'.
     * // For example, get the size of a pixel of an image on a billboard.
     * var position = camera.position;
     * var direction = camera.direction;
     * var toCenter = Cesium.Cartesian3.subtract(primitive.boundingVolume.center, position, new Cesium.Cartesian3());      // vector from camera to a primitive
     * var toCenterProj = Cesium.Cartesian3.multiplyByScalar(direction, Cesium.Cartesian3.dot(direction, toCenter), new Cesium.Cartesian3()); // project vector onto camera direction vector
     * var distance = Cesium.Cartesian3.magnitude(toCenterProj);
     * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, distance, new Cartesian2());
     */
    PerspectiveFrustum.prototype.getPixelDimensions = function(drawingBufferWidth, drawingBufferHeight, distance, result) {
        update(this);
        return this._offCenterFrustum.getPixelDimensions(drawingBufferWidth, drawingBufferHeight, distance, result);
    };

    /**
     * Returns a duplicate of a PerspectiveFrustum instance.
     *
     * @param {PerspectiveFrustum} [result] The object onto which to store the result.
     * @returns {PerspectiveFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided.
     */
    PerspectiveFrustum.prototype.clone = function(result) {
        if (!defined(result)) {
            result = new PerspectiveFrustum();
        }

        result.aspectRatio = this.aspectRatio;
        result.fov = this.fov;
        result.near = this.near;
        result.far = this.far;

        // force update of clone to compute matrices
        result._aspectRatio = undefined;
        result._fov = undefined;
        result._near = undefined;
        result._far = undefined;

        this._offCenterFrustum.clone(result._offCenterFrustum);

        return result;
    };

    /**
     * Compares the provided PerspectiveFrustum componentwise and returns
     * true if they are equal, false otherwise.
     *
     * @param {PerspectiveFrustum} [other] The right hand side PerspectiveFrustum.
     * @returns {Boolean} true if they are equal, false otherwise.
     */
    PerspectiveFrustum.prototype.equals = function(other) {
        if (!defined(other)) {
            return false;
        }

        update(this);
        update(other);

        return (this.fov === other.fov &&
                this.aspectRatio === other.aspectRatio &&
                this.near === other.near &&
                this.far === other.far &&
                this._offCenterFrustum.equals(other._offCenterFrustum));
    };

    return PerspectiveFrustum;
});
<commit_msg>Update PerpectiveFrustum.js<commit_after>/*global define*/
define([
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/DeveloperError',
        './PerspectiveOffCenterFrustum'
    ], function(
        defined,
        defineProperties,
        DeveloperError,
        PerspectiveOffCenterFrustum) {
    "use strict";

    /**
     * The viewing frustum is defined by 6 planes.
     * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components
     * define the unit vector normal to the plane, and the w component is the distance of the
     * plane from the origin/camera position.
     *
     * @alias PerspectiveFrustum
     * @constructor
     *
     *
     * @example
     * var frustum = new Cesium.PerspectiveFrustum();
     * frustum.aspectRatio = canvas.clientWidth / canvas.clientHeight;
     * frustum.fov = Cesium.Math.PI_OVER_THREE;
     * frustum.near = 1.0;
     * frustum.far = 2.0;
     * @see PerspectiveOffCenterFrustum
     */
    function PerspectiveFrustum() {
        this._offCenterFrustum = new PerspectiveOffCenterFrustum();

        /**
         * The angle of the field of view (FOV), in radians.  This angle will be used
         * as the horizontal FOV if the width is greater than the height, otherwise
         * it will be the vertical FOV.
         * @type {Number}
         * @default undefined
         */
        this.fov = undefined;
        this._fov = undefined;
        this._fovy = undefined;

        this._sseDenominator = undefined;

        /**
         * The aspect ratio of the frustum's width to it's height.
         * @type {Number}
         * @default undefined
         */
        this.aspectRatio = undefined;
        this._aspectRatio = undefined;

        /**
         * The distance of the near plane.
         * @type {Number}
         * @default 1.0
         */
        this.near = 1.0;
        this._near = this.near;

        /**
         * The distance of the far plane.
         * @type {Number}
         * @default 500000000.0
         */
        this.far = 500000000.0;
        this._far = this.far;
    }

    function update(frustum) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(frustum.fov) || !defined(frustum.aspectRatio) || !defined(frustum.near) || !defined(frustum.far)) {
            throw new DeveloperError('fov, aspectRatio, near, or far parameters are not set.');
        }
        //>>includeEnd('debug');

        var f = frustum._offCenterFrustum;

        if (frustum.fov !== frustum._fov || frustum.aspectRatio !== frustum._aspectRatio ||
                frustum.near !== frustum._near || frustum.far !== frustum._far) {
            //>>includeStart('debug', pragmas.debug);
            if (frustum.fov < 0 || frustum.fov >= Math.PI) {
                throw new DeveloperError('fov must be in the range [0, PI).');
            }

            if (frustum.aspectRatio < 0) {
                throw new DeveloperError('aspectRatio must be positive.');
            }

            if (frustum.near < 0 || frustum.near > frustum.far) {
                throw new DeveloperError('near must be greater than zero and less than far.');
            }
            //>>includeEnd('debug');

            frustum._aspectRatio = frustum.aspectRatio;
            frustum._fov = frustum.fov;
            frustum._fovy = (frustum.aspectRatio <= 1) ? frustum.fov : Math.atan(Math.tan(frustum.fov * 0.5) / frustum.aspectRatio) * 2.0;
            frustum._near = frustum.near;
            frustum._far = frustum.far;
            frustum._sseDenominator = 2.0 * Math.tan(0.5 * frustum._fovy);

            f.top = frustum.near * Math.tan(0.5 * frustum._fovy);
            f.bottom = -f.top;
            f.right = frustum.aspectRatio * f.top;
            f.left = -f.right;
            f.near = frustum.near;
            f.far = frustum.far;
        }
    }

    defineProperties(PerspectiveFrustum.prototype, {
        /**
         * Gets the perspective projection matrix computed from the view frustum.
         * @memberof PerspectiveFrustum.prototype
         * @type {Matrix4}
         * @readonly
         *
         * @see PerspectiveFrustum#infiniteProjectionMatrix
         */
        projectionMatrix : {
            get : function() {
                update(this);
                return this._offCenterFrustum.projectionMatrix;
            }
        },

        /**
         * The perspective projection matrix computed from the view frustum with an infinite far plane.
         * @memberof PerspectiveFrustum.prototype
         * @type {Matrix4}
         * @readonly
         *
         * @see PerspectiveFrustum#projectionMatrix
         */
        infiniteProjectionMatrix : {
            get : function() {
                update(this);
                return this._offCenterFrustum.infiniteProjectionMatrix;
            }
        },

        /**
         * Gets the angle of the vertical field of view, in radians.
         * @memberof PerspectiveFrustum.prototype
         * @type {Number}
         * @readonly
         * @default undefined
         */
        fovy : {
            get : function() {
                update(this);
                return this._fovy;
            }
        },

        /**
         * @readonly
         * @private
         */
        sseDenominator : {
            get : function () {
                update(this);
                return this._sseDenominator;
            }
        }
    });

    /**
     * Creates a culling volume for this frustum.
     *
     * @param {Cartesian3} position The eye position.
     * @param {Cartesian3} direction The view direction.
     * @param {Cartesian3} up The up direction.
     * @returns {CullingVolume} A culling volume at the given position and orientation.
     *
     * @example
     * // Check if a bounding volume intersects the frustum.
     * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp);
     * var intersect = cullingVolume.computeVisibility(boundingVolume);
     */
    PerspectiveFrustum.prototype.computeCullingVolume = function(position, direction, up) {
        update(this);
        return this._offCenterFrustum.computeCullingVolume(position, direction, up);
    };

    /**
     * Returns the pixel's width and height in meters.
     *
     * @param {Number} drawingBufferWidth The width of the drawing buffer.
     * @param {Number} drawingBufferHeight The height of the drawing buffer.
     * @param {Number} distance The distance to the near plane in meters.
     * @param {Cartesian2} result The object onto which to store the result.
     * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively.
     *
     * @exception {DeveloperError} drawingBufferWidth must be greater than zero.
     * @exception {DeveloperError} drawingBufferHeight must be greater than zero.
     *
     * @example
     * // Example 1
     * // Get the width and height of a pixel.
     * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 1.0, new Cartesian2());
     *
     * @example
     * // Example 2
     * // Get the width and height of a pixel if the near plane was set to 'distance'.
     * // For example, get the size of a pixel of an image on a billboard.
     * var position = camera.position;
     * var direction = camera.direction;
     * var toCenter = Cesium.Cartesian3.subtract(primitive.boundingVolume.center, position, new Cesium.Cartesian3());      // vector from camera to a primitive
     * var toCenterProj = Cesium.Cartesian3.multiplyByScalar(direction, Cesium.Cartesian3.dot(direction, toCenter), new Cesium.Cartesian3()); // project vector onto camera direction vector
     * var distance = Cesium.Cartesian3.magnitude(toCenterProj);
     * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, distance, new Cartesian2());
     */
    PerspectiveFrustum.prototype.getPixelDimensions = function(drawingBufferWidth, drawingBufferHeight, distance, result) {
        update(this);
        return this._offCenterFrustum.getPixelDimensions(drawingBufferWidth, drawingBufferHeight, distance, result);
    };

    /**
     * Returns a duplicate of a PerspectiveFrustum instance.
     *
     * @param {PerspectiveFrustum} [result] The object onto which to store the result.
     * @returns {PerspectiveFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided.
     */
    PerspectiveFrustum.prototype.clone = function(result) {
        if (!defined(result)) {
            result = new PerspectiveFrustum();
        }

        result.aspectRatio = this.aspectRatio;
        result.fov = this.fov;
        result.near = this.near;
        result.far = this.far;

        // force update of clone to compute matrices
        result._aspectRatio = undefined;
        result._fov = undefined;
        result._near = undefined;
        result._far = undefined;

        this._offCenterFrustum.clone(result._offCenterFrustum);

        return result;
    };

    /**
     * Compares the provided PerspectiveFrustum componentwise and returns
     * true if they are equal, false otherwise.
     *
     * @param {PerspectiveFrustum} [other] The right hand side PerspectiveFrustum.
     * @returns {Boolean} true if they are equal, false otherwise.
     */
    PerspectiveFrustum.prototype.equals = function(other) {
        if (!defined(other)) {
            return false;
        }

        update(this);
        update(other);

        return (this.fov === other.fov &&
                this.aspectRatio === other.aspectRatio &&
                this.near === other.near &&
                this.far === other.far &&
                this._offCenterFrustum.equals(other._offCenterFrustum));
    };

    return PerspectiveFrustum;
});
<|endoftext|>


<commit_before>'use strict';

// Selects a random question.
function selectQuestion(questionType){
  var attackQuestions = JSON.parse(localStorage.getItem('attackQuestions'));
  var defenseQuestions = JSON.parse(localStorage.getItem('defenseQuestions'));
  if(questionType === 'attack'){
    var questionIndex = Math.floor(Math.random() * attackQuestions.length);
    var currentAttackQuestion = attackQuestions[questionIndex];
    attackQuestions.splice(questionIndex, 1);
    localStorage.setItem('attackQuestions', JSON.stringify(attackQuestions));
    return currentAttackQuestion;
  }else if(questionType === 'defense'){
    var questionIndex = Math.floor(Math.random() * defenseQuestions.length);
    var currentDefenseQuestion = defenseQuestions[questionIndex];
    defenseQuestions.splice(questionIndex, 1);
    localStorage.setItem('defenseQuestions', JSON.stringify(defenseQuestions));
    return currentDefenseQuestion;
  }else if(questionType === 'final'){
    finalQuestionOfGame = 'true';
    roundsPerEncounter = 1;
    return finalQuestion;
  }
}

function saveQuestionsToLocalStorage() {

  // Add encountersCompleted to localStorage if it doesn't already exist.
  if(localStorage.getItem('attackQuestions') === null){
    localStorage.setItem('attackQuestions', JSON.stringify(attackQuestions));
  }

  // Add finalQuestionOfGame to localStorage if it doesn't already exist.
  if(localStorage.getItem('defenseQuestions') === null){
    localStorage.setItem('defenseQuestions', JSON.stringify(defenseQuestions));
  }
}

// Selects a random enemy.
function selectEnemy(){
  //var enemyIndex = Math.floor(Math.random() * enemies.length);
  console.log(enemies[0]);
  return enemies[0];
}

// Handles the submit button being clicked in the form.
function handleSubmit(event){
  event.preventDefault();
  event.stopPropagation();

  // If player doesn't submit an answer, alert user to submit an answer.
  if(isNaN(parseInt(event.target.choices.value)) === true) {
    alert('Please select an answer before submitting.');
  }

  // Only allows the logic to proceed if the player already submitted an answer.
  if(!validSubmit && isNaN(parseInt(event.target.choices.value)) !== true){
    validSubmit = true;

    // If the player just answered the final question of the game.
    if(finalQuestionOfGame === 'true'){
      // Check if answer is correct.
      if(parseInt(event.target.choices.value) === selectedQuestion.correctAnswer){
        answerResponseEl.textContent = 'That is correct! You fixed the robot!';
        player.score += 1;
        player.win = true;

        saveLocalStorage();
        document.location.href = 'results.html';
      }else {
        player.win = false;

        saveLocalStorage();
        document.location.href = 'results.html';
      }
    }

    if(currentRound <= roundsPerEncounter){
      // Check if answer is correct.
      if(parseInt(event.target.choices.value) === selectedQuestion.correctAnswer){
        answerResponseEl.textContent = 'That is correct! Nice job!';
        player.score += 1;
      }else {
        answerResponseEl.textContent = selectedQuestion.incorrectAnswerResponse;
      }

      // The last round.
      if(currentRound === roundsPerEncounter){
        // Change text of button.
        submitButtonEl.textContent = 'Proceed';
        // The player can now leave the encounter.
        exitEncounter = true;
        validSubmit = false;
        currentRound += 1;
      }
    }else{
      // Ensure button will send player to select level.
      if(exitEncounter){
        // The player loses if they didn't gain any points this encounter.
        if(scoreAtEncounterStart === player.score){
          // The player loses.
          player.win = false;
          saveLocalStorage();
          document.location.href = 'results.html';
        }else{
          // The player moves onward.
          saveLocalStorage();
          document.location.href = 'select-level.html';
        }
      }
    }
  }
}

// Handles the "Go to Next Question" button being clicked in the form.
function handleNextQuestionClick(event){
  if (validSubmit){
    // Remove this button on the second to last round being completed.
    if(currentRound === roundsPerEncounter - 1){
      event.target.remove();
    }

    // Increment currentRound.
    currentRound += 1;
    // Create the next round.
    encounterRound();
    // Reset validSumbit for next round.
    validSubmit = false;

    var radioButtons = document.getElementsByClassName('answer-radio-button');
    for(var i = 0; i < radioButtons.length; i++) {
      radioButtons[i].checked = false;
    }

    answerResponseEl.textContent = '';
  }
}

// Creates a new round.
function encounterRound(){
  // Determine which type of question to ask this round.
  if(finalQuestionOfGame !== 'true'){
    if(attackQuestion){
      // Attack.
      selectedQuestion = selectQuestion('attack');
      attackQuestion = !attackQuestion;
    }else{
      // Defense
      selectedQuestion = selectQuestion('defense');
      attackQuestion = !attackQuestion;
    }
  }else{
    selectedQuestion = selectQuestion('final');
    document.getElementById('next-question').remove();
    console.log('got here!!!');
  }

  // Set the text pertaining to the question for the player to see.
  questionEl.textContent = selectedQuestion.question;
  answerChoiceOne.textContent = selectedQuestion.answerChoices[0];
  answerChoiceTwo.textContent = selectedQuestion.answerChoices[1];
  answerChoiceThree.textContent = selectedQuestion.answerChoices[2];
  answerChoiceFour.textContent = selectedQuestion.answerChoices[3];
}

// Loads the information needed for the combat phase of the game.
function loadLocalStorage(){
  // Load scene.
  scene = JSON.parse(localStorage.getItem('scene'));
  // Load player.
  player = JSON.parse(localStorage.getItem('player'));
  scoreAtEncounterStart = player.score;
  // Load historical figure.
  historicalFigure = JSON.parse(localStorage.getItem('historicalFigure'));
  finalQuestionOfGame = localStorage.getItem('finalQuestionOfGame');
}

// Saves the information needed for the select-level and results page.
function saveLocalStorage(){
  // Save player.
  localStorage.setItem('player', JSON.stringify(player));
  var tempEncountersCompleted = parseInt(localStorage.getItem('encountersCompleted'));
  tempEncountersCompleted += 1;
  localStorage.setItem('encountersCompleted', tempEncountersCompleted);
}

// Will be true when the submit button changes into a proceed button.
var exitEncounter = false;
// Will be true if this current round will be an attack.
var attackQuestion = true;
// Will be true if the player is on the "fix the robot" question.
var finalQuestionOfGame = 'false';
// How many rounds are per an encounter.
var roundsPerEncounter = 3;
// The round the player is currently on.
var currentRound = 1;
// The score the player starts with at the beginning of the encounter.
var scoreAtEncounterStart;
// The question for this round.
var selectedQuestion;
// Will be true if the player has made a submission during that round.
var validSubmit = false;

// The current instances the encounter will be dealing with.
var scene;
var player;
var historicalFigure;
var enemy = selectEnemy();

// Element containing the text of the question being asked.
var questionEl = document.getElementById('question');
// Elements containing the text of the answer choices given for the question.
var answerChoiceOne = document.getElementById('answer-choice-1');
var answerChoiceTwo = document.getElementById('answer-choice-2');
var answerChoiceThree = document.getElementById('answer-choice-3');
var answerChoiceFour = document.getElementById('answer-choice-4');
// The form containing the radio and submit buttons.
var answerFormEl = document.getElementById('answer-choices');
answerFormEl.addEventListener('submit', handleSubmit);
// Element containing the text of the answer response.
var answerResponseEl = document.getElementById('answer-response');
// The button for the submission.
var submitButtonEl = document.getElementById('submit-answer');
// The button for the next question.
var nextButtonEl = document.getElementById('next-question');
nextButtonEl.addEventListener('click', handleNextQuestionClick);

// The first round starts automatically.
saveQuestionsToLocalStorage();
loadLocalStorage();
encounterRound();

//DOM functionality for location
var locationImageEl = document.getElementById('location-image');
locationImageEl.setAttribute('src', scene.image);

//DOM functionality for player
var playerNameEl = document.getElementById('player-name');
playerNameEl.textContent = (player.name);
var playerImageEl = document.getElementById('player-image');
playerImageEl.setAttribute('src', player.image);
var playerSpeechEl = document.getElementById('player-speech');
playerSpeechEl.textContent = (player.speech);

//DOM functionality for historical figure
var historicalFigureNameEl = document.getElementById('historical-figure-name');
historicalFigureNameEl.textContent = (historicalFigure.name);
var historicalFigureSpeechEl = document.getElementById('historical-figure-speech');
historicalFigureSpeechEl.textContent = (historicalFigure.comments);
var historicalFigureImageEl = document.getElementById('historical-figure-image');
historicalFigureImageEl.setAttribute('src', historicalFigure.image[1]);

//DOM functionality for enemy
var enemyNameEl = document.getElementById('enemy-name');
enemyNameEl.textContent = (enemy.name);
var enemyImageEl = document.getElementById('enemy-image');
enemyImageEl.setAttribute('src', enemy.image);
var enemySpeechEl = document.getElementById('enemy-speech');
enemySpeechEl.textContent = (enemy.speech);
<commit_msg>Fixed historical image to be dynamic<commit_after>'use strict';

// Selects a random question.
function selectQuestion(questionType){
  var attackQuestions = JSON.parse(localStorage.getItem('attackQuestions'));
  var defenseQuestions = JSON.parse(localStorage.getItem('defenseQuestions'));
  if(questionType === 'attack'){
    var questionIndex = Math.floor(Math.random() * attackQuestions.length);
    var currentAttackQuestion = attackQuestions[questionIndex];
    attackQuestions.splice(questionIndex, 1);
    localStorage.setItem('attackQuestions', JSON.stringify(attackQuestions));
    return currentAttackQuestion;
  }else if(questionType === 'defense'){
    var questionIndex = Math.floor(Math.random() * defenseQuestions.length);
    var currentDefenseQuestion = defenseQuestions[questionIndex];
    defenseQuestions.splice(questionIndex, 1);
    localStorage.setItem('defenseQuestions', JSON.stringify(defenseQuestions));
    return currentDefenseQuestion;
  }else if(questionType === 'final'){
    finalQuestionOfGame = 'true';
    roundsPerEncounter = 1;
    return finalQuestion;
  }
}

function saveQuestionsToLocalStorage() {

  // Add encountersCompleted to localStorage if it doesn't already exist.
  if(localStorage.getItem('attackQuestions') === null){
    localStorage.setItem('attackQuestions', JSON.stringify(attackQuestions));
  }

  // Add finalQuestionOfGame to localStorage if it doesn't already exist.
  if(localStorage.getItem('defenseQuestions') === null){
    localStorage.setItem('defenseQuestions', JSON.stringify(defenseQuestions));
  }
}

// Selects a random enemy.
function selectEnemy(){
  //var enemyIndex = Math.floor(Math.random() * enemies.length);
  console.log(enemies[0]);
  return enemies[0];
}

// Handles the submit button being clicked in the form.
function handleSubmit(event){
  event.preventDefault();
  event.stopPropagation();

  // If player doesn't submit an answer, alert user to submit an answer.
  if(isNaN(parseInt(event.target.choices.value)) === true) {
    alert('Please select an answer before submitting.');
  }

  // Only allows the logic to proceed if the player already submitted an answer.
  if(!validSubmit && isNaN(parseInt(event.target.choices.value)) !== true){
    validSubmit = true;

    // If the player just answered the final question of the game.
    if(finalQuestionOfGame === 'true'){
      // Check if answer is correct.
      if(parseInt(event.target.choices.value) === selectedQuestion.correctAnswer){
        answerResponseEl.textContent = 'That is correct! You fixed the robot!';
        player.score += 1;
        player.win = true;

        saveLocalStorage();
        document.location.href = 'results.html';
      }else {
        player.win = false;

        saveLocalStorage();
        document.location.href = 'results.html';
      }
    }

    if(currentRound <= roundsPerEncounter){
      // Check if answer is correct.
      if(parseInt(event.target.choices.value) === selectedQuestion.correctAnswer){
        answerResponseEl.textContent = 'That is correct! Nice job!';
        player.score += 1;
      }else {
        answerResponseEl.textContent = selectedQuestion.incorrectAnswerResponse;
      }

      // The last round.
      if(currentRound === roundsPerEncounter){
        // Change text of button.
        submitButtonEl.textContent = 'Proceed';
        // The player can now leave the encounter.
        exitEncounter = true;
        validSubmit = false;
        currentRound += 1;
      }
    }else{
      // Ensure button will send player to select level.
      if(exitEncounter){
        // The player loses if they didn't gain any points this encounter.
        if(scoreAtEncounterStart === player.score){
          // The player loses.
          player.win = false;
          saveLocalStorage();
          document.location.href = 'results.html';
        }else{
          // The player moves onward.
          saveLocalStorage();
          document.location.href = 'select-level.html';
        }
      }
    }
  }
}

// Handles the "Go to Next Question" button being clicked in the form.
function handleNextQuestionClick(event){
  if (validSubmit){
    // Remove this button on the second to last round being completed.
    if(currentRound === roundsPerEncounter - 1){
      event.target.remove();
    }

    // Increment currentRound.
    currentRound += 1;
    // Create the next round.
    encounterRound();
    // Reset validSumbit for next round.
    validSubmit = false;

    var radioButtons = document.getElementsByClassName('answer-radio-button');
    for(var i = 0; i < radioButtons.length; i++) {
      radioButtons[i].checked = false;
    }

    answerResponseEl.textContent = '';
  }
}

// Creates a new round.
function encounterRound(){
  // Determine which type of question to ask this round.
  if(finalQuestionOfGame !== 'true'){
    if(attackQuestion){
      // Attack.
      selectedQuestion = selectQuestion('attack');
      attackQuestion = !attackQuestion;
    }else{
      // Defense
      selectedQuestion = selectQuestion('defense');
      attackQuestion = !attackQuestion;
    }
  }else{
    selectedQuestion = selectQuestion('final');
    document.getElementById('next-question').remove();
    console.log('got here!!!');
  }

  // Set the text pertaining to the question for the player to see.
  questionEl.textContent = selectedQuestion.question;
  answerChoiceOne.textContent = selectedQuestion.answerChoices[0];
  answerChoiceTwo.textContent = selectedQuestion.answerChoices[1];
  answerChoiceThree.textContent = selectedQuestion.answerChoices[2];
  answerChoiceFour.textContent = selectedQuestion.answerChoices[3];
}

// Loads the information needed for the combat phase of the game.
function loadLocalStorage(){
  // Load scene.
  scene = JSON.parse(localStorage.getItem('scene'));
  // Load player.
  player = JSON.parse(localStorage.getItem('player'));
  scoreAtEncounterStart = player.score;
  // Load historical figure.
  historicalFigure = JSON.parse(localStorage.getItem('historicalFigure'));
  finalQuestionOfGame = localStorage.getItem('finalQuestionOfGame');
}

// Saves the information needed for the select-level and results page.
function saveLocalStorage(){
  // Save player.
  localStorage.setItem('player', JSON.stringify(player));
  var tempEncountersCompleted = parseInt(localStorage.getItem('encountersCompleted'));
  tempEncountersCompleted += 1;
  localStorage.setItem('encountersCompleted', tempEncountersCompleted);
}

// Will be true when the submit button changes into a proceed button.
var exitEncounter = false;
// Will be true if this current round will be an attack.
var attackQuestion = true;
// Will be true if the player is on the "fix the robot" question.
var finalQuestionOfGame = 'false';
// How many rounds are per an encounter.
var roundsPerEncounter = 3;
// The round the player is currently on.
var currentRound = 1;
// The score the player starts with at the beginning of the encounter.
var scoreAtEncounterStart;
// The question for this round.
var selectedQuestion;
// Will be true if the player has made a submission during that round.
var validSubmit = false;

// The current instances the encounter will be dealing with.
var scene;
var player;
var historicalFigure;
var enemy = selectEnemy();

// Element containing the text of the question being asked.
var questionEl = document.getElementById('question');
// Elements containing the text of the answer choices given for the question.
var answerChoiceOne = document.getElementById('answer-choice-1');
var answerChoiceTwo = document.getElementById('answer-choice-2');
var answerChoiceThree = document.getElementById('answer-choice-3');
var answerChoiceFour = document.getElementById('answer-choice-4');
// The form containing the radio and submit buttons.
var answerFormEl = document.getElementById('answer-choices');
answerFormEl.addEventListener('submit', handleSubmit);
// Element containing the text of the answer response.
var answerResponseEl = document.getElementById('answer-response');
// The button for the submission.
var submitButtonEl = document.getElementById('submit-answer');
// The button for the next question.
var nextButtonEl = document.getElementById('next-question');
nextButtonEl.addEventListener('click', handleNextQuestionClick);

// The first round starts automatically.
saveQuestionsToLocalStorage();
loadLocalStorage();
encounterRound();

//DOM functionality for location
var locationImageEl = document.getElementById('location-image');
locationImageEl.setAttribute('src', scene.image);

//DOM functionality for player
var playerNameEl = document.getElementById('player-name');
playerNameEl.textContent = (player.name);
var playerImageEl = document.getElementById('player-image');
playerImageEl.setAttribute('src', player.image);
var playerSpeechEl = document.getElementById('player-speech');
playerSpeechEl.textContent = (player.speech);

//DOM functionality for historical figure
var historicalFigureNameEl = document.getElementById('historical-figure-name');
historicalFigureNameEl.textContent = (historicalFigure.name);
var historicalFigureSpeechEl = document.getElementById('historical-figure-speech');
historicalFigureSpeechEl.textContent = (historicalFigure.comments);
var historicalFigureImageEl = document.getElementById('historical-figure-image');
historicalFigureImageEl.setAttribute('src', historicalFigure.image);

//DOM functionality for enemy
var enemyNameEl = document.getElementById('enemy-name');
enemyNameEl.textContent = (enemy.name);
var enemyImageEl = document.getElementById('enemy-image');
enemyImageEl.setAttribute('src', enemy.image);
var enemySpeechEl = document.getElementById('enemy-speech');
enemySpeechEl.textContent = (enemy.speech);
<|endoftext|>