<commit_before>/*!
 * jsUri
 * https://github.com/derek-watson/jsUri
 *
 * Copyright 2013, Derek Watson
 * Released under the MIT license.
 *
 * Includes parseUri regular expressions
 * http://blog.stevenlevithan.com/archives/parseuri
 * Copyright 2007, Steven Levithan
 * Released under the MIT license.
 */

 /*globals define, module */

(function(global) {

  var re = {
    starts_with_slashes: /^\/+/,
    ends_with_slashes: /\/+$/,
    pluses: /\+/g,
    query_separator: /[&;]/,
    uri_parser: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
  };

  /**
   * Define forEach for older js environments
   * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility
   */
  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fn, scope) {
      for (var i = 0, len = this.length; i < len; ++i) {
        fn.call(scope || this, this[i], i, this);
      }
    };
  }

  /**
   * unescape a query param value
   * @param  {string} s encoded value
   * @return {string}   decoded value
   */
  function decode(s) {
    if (s) {
      s = decodeURIComponent(s);
      s = s.replace(re.pluses, ' ');
    }
    return s;
  }

  /**
   * Breaks a uri string down into its individual parts
   * @param  {string} str uri
   * @return {object}     parts
   */
  function parseUri(str) {
    var parser = re.uri_parser;
    var parserKeys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"];
    var m = parser.exec(str || '');
    var parts = {};

    parserKeys.forEach(function(key, i) {
      parts[key] = m[i] || '';
    });

    return parts;
  }

  /**
   * Breaks a query string down into an array of key/value pairs
   * @param  {string} str query
   * @return {array}      array of arrays (key/value pairs)
   */
  function parseQuery(str) {
    var i, ps, p, n, k, v;
    var pairs = [];

    if (typeof(str) === 'undefined' || str === null || str === '') {
      return pairs;
    }

    if (str.indexOf('?') === 0) {
      str = str.substring(1);
    }

    ps = str.toString().split(re.query_separator);

    for (i = 0; i < ps.length; i++) {
      p = ps[i];
      n = p.indexOf('=');

      if (n !== 0) {
        k = decodeURIComponent(p.substring(0, n));
        v = decodeURIComponent(p.substring(n + 1));
        pairs.push(n === -1 ? [p, null] : [k, v]);
      }

    }
    return pairs;
  }

  /**
   * Creates a new Uri object
   * @constructor
   * @param {string} str
   */
  function Uri(str) {
    this.uriParts = parseUri(str);
    this.queryPairs = parseQuery(this.uriParts.query);
    this.hasAuthorityPrefixUserPref = null;
  }

  /**
   * Define getter/setter methods
   */
  ['protocol', 'userInfo', 'host', 'port', 'path', 'anchor'].forEach(function(key) {
    Uri.prototype[key] = function(val) {
      if (typeof val !== 'undefined') {
        this.uriParts[key] = val;
      }
      return this.uriParts[key];
    };
  });

  /**
   * if there is no protocol, the leading // can be enabled or disabled
   * @param  {Boolean}  val
   * @return {Boolean}
   */
  Uri.prototype.hasAuthorityPrefix = function(val) {
    if (typeof val !== 'undefined') {
      this.hasAuthorityPrefixUserPref = val;
    }

    if (this.hasAuthorityPrefixUserPref === null) {
      return (this.uriParts.source.indexOf('//') !== -1);
    } else {
      return this.hasAuthorityPrefixUserPref;
    }
  };

  /**
   * Serializes the internal state of the query pairs
   * @param  {string} [val]   set a new query string
   * @return {string}         query string
   */
  Uri.prototype.query = function(val) {
    var s = '', i, param;

    if (typeof val !== 'undefined') {
      this.queryPairs = parseQuery(val);
    }

    for (i = 0; i < this.queryPairs.length; i++) {
      param = this.queryPairs[i];
      if (s.length > 0) {
        s += '&';
      }
      if (param[1] === null) {
        s += param[0];
      } else {
        s += param[0];
        s += '=';
        if (param[1]) {
          s += encodeURIComponent(param[1]);
        }
      }
    }
    return s.length > 0 ? '?' + s : s;
  };

  /**
   * returns the first query param value found for the key
   * @param  {string} key query key
   * @return {string}     first value found for key
   */
  Uri.prototype.getQueryParamValue = function (key) {
    var param, i;
    for (i = 0; i < this.queryPairs.length; i++) {
      param = this.queryPairs[i];
      if (key === param[0]) {
        return param[1];
      }
    }
  };

  /**
   * returns an array of query param values for the key
   * @param  {string} key query key
   * @return {array}      array of values
   */
  Uri.prototype.getQueryParamValues = function (key) {
    var arr = [], i, param;
    for (i = 0; i < this.queryPairs.length; i++) {
      param = this.queryPairs[i];
      if (key === param[0]) {
        arr.push(param[1]);
      }
    }
    return arr;
  };

  /**
   * removes query parameters
   * @param  {string} key     remove values for key
   * @param  {val}    [val]   remove a specific value, otherwise removes all
   * @return {Uri}            returns self for fluent chaining
   */
  Uri.prototype.deleteQueryParam = function (key, val) {
    var arr = [], i, param, keyMatchesFilter, valMatchesFilter;

    for (i = 0; i < this.queryPairs.length; i++) {

      param = this.queryPairs[i];
      keyMatchesFilter = decode(param[0]) === decode(key);
      valMatchesFilter = param[1] === val;

      if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && (!keyMatchesFilter || !valMatchesFilter))) {
        arr.push(param);
      }
    }

    this.queryPairs = arr;

    return this;
  };

  /**
   * adds a query parameter
   * @param  {string}  key        add values for key
   * @param  {string}  val        value to add
   * @param  {integer} [index]    specific index to add the value at
   * @return {Uri}                returns self for fluent chaining
   */
  Uri.prototype.addQueryParam = function (key, val, index) {
    if (arguments.length === 3 && index !== -1) {
      index = Math.min(index, this.queryPairs.length);
      this.queryPairs.splice(index, 0, [key, val]);
    } else if (arguments.length > 0) {
      this.queryPairs.push([key, val]);
    }
    return this;
  };

  /**
   * replaces query param values
   * @param  {string} key         key to replace value for
   * @param  {string} newVal      new value
   * @param  {string} [oldVal]    replace only one specific value (otherwise replaces all)
   * @return {Uri}                returns self for fluent chaining
   */
  Uri.prototype.replaceQueryParam = function (key, newVal, oldVal) {
    var index = -1, i, param;

    if (arguments.length === 3) {
      for (i = 0; i < this.queryPairs.length; i++) {
        param = this.queryPairs[i];
        if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) {
          index = i;
          break;
        }
      }
      this.deleteQueryParam(key, decode(oldVal)).addQueryParam(key, newVal, index);
    } else {
      for (i = 0; i < this.queryPairs.length; i++) {
        param = this.queryPairs[i];
        if (decode(param[0]) === decode(key)) {
          index = i;
          break;
        }
      }
      this.deleteQueryParam(key);
      this.addQueryParam(key, newVal, index);
    }
    return this;
  };

  /**
   * Define fluent setter methods (setProtocol, setHasAuthorityPrefix, etc)
   */
  ['protocol', 'hasAuthorityPrefix', 'userInfo', 'host', 'port', 'path', 'query', 'anchor'].forEach(function(key) {
    var method = 'set' + key.charAt(0).toUpperCase() + key.slice(1);
    Uri.prototype[method] = function(val) {
      this[key](val);
      return this;
    };
  });

  /**
   * Scheme name, colon and doubleslash, as required
   * @return {string} http:// or possibly just //
   */
  Uri.prototype.scheme = function() {
    var s = '';

    if (this.protocol()) {
      s += this.protocol();
      if (this.protocol().indexOf(':') !== this.protocol().length - 1) {
        s += ':';
      }
      s += '//';
    } else {
      if (this.hasAuthorityPrefix() && this.host()) {
        s += '//';
      }
    }

    return s;
  };

  /**
   * Same as Mozilla nsIURI.prePath
   * @return {string} scheme://user:password@host:port
   * @see  https://developer.mozilla.org/en/nsIURI
   */
  Uri.prototype.origin = function() {
    var s = this.scheme();

    if (s == 'file://') {
      return s + this.uriParts.authority;
    }

    if (this.userInfo() && this.host()) {
      s += this.userInfo();
      if (this.userInfo().indexOf('@') !== this.userInfo().length - 1) {
        s += '@';
      }
    }

    if (this.host()) {
      s += this.host();
      if (this.port()) {
        s += ':' + this.port();
      }
    }

    return s;
  };

  /**
   * Adds a trailing slash to the path
   */
  Uri.prototype.addTrailingSlash = function() {
    var path = this.path() || '';

    if (path.substr(-1) !== '/') {
      this.path(path + '/');
    }

    return this;
  };

  /**
   * Serializes the internal state of the Uri object
   * @return {string}
   */
  Uri.prototype.toString = function() {
    var path, s = this.origin();

    if (this.path()) {
      path = this.path();
      if (!(re.ends_with_slashes.test(s) || re.starts_with_slashes.test(path))) {
        s += '/';
      } else {
        if (s) {
          s.replace(re.ends_with_slashes, '/');
        }
        path = path.replace(re.starts_with_slashes, '/');
      }
      s += path;
    } else {
      if (this.host() && (this.query().toString() || this.anchor())) {
        s += '/';
      }
    }
    if (this.query().toString()) {
      if (this.query().toString().indexOf('?') !== 0) {
        s += '?';
      }
      s += this.query().toString();
    }

    if (this.anchor()) {
      if (this.anchor().indexOf('#') !== 0) {
        s += '#';
      }
      s += this.anchor();
    }

    return s;
  };

  /**
   * Clone a Uri object
   * @return {Uri} duplicate copy of the Uri
   */
  Uri.prototype.clone = function() {
    return new Uri(this.toString());
  };

  /**
   * export via AMD or CommonJS, otherwise leak a global
   */
  if (typeof define === 'function' && define.amd) {
    define(function() {
      return Uri;
    });
  } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = Uri;
  } else {
    global.Uri = Uri;
  }
}(this));
<commit_msg>Cache for loop lengths<commit_after>/*!
 * jsUri
 * https://github.com/derek-watson/jsUri
 *
 * Copyright 2013, Derek Watson
 * Released under the MIT license.
 *
 * Includes parseUri regular expressions
 * http://blog.stevenlevithan.com/archives/parseuri
 * Copyright 2007, Steven Levithan
 * Released under the MIT license.
 */

 /*globals define, module */

(function(global) {

  var re = {
    starts_with_slashes: /^\/+/,
    ends_with_slashes: /\/+$/,
    pluses: /\+/g,
    query_separator: /[&;]/,
    uri_parser: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
  };

  /**
   * Define forEach for older js environments
   * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility
   */
  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fn, scope) {
      for (var i = 0, len = this.length; i < len; ++i) {
        fn.call(scope || this, this[i], i, this);
      }
    };
  }

  /**
   * unescape a query param value
   * @param  {string} s encoded value
   * @return {string}   decoded value
   */
  function decode(s) {
    if (s) {
      s = decodeURIComponent(s);
      s = s.replace(re.pluses, ' ');
    }
    return s;
  }

  /**
   * Breaks a uri string down into its individual parts
   * @param  {string} str uri
   * @return {object}     parts
   */
  function parseUri(str) {
    var parser = re.uri_parser;
    var parserKeys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"];
    var m = parser.exec(str || '');
    var parts = {};

    parserKeys.forEach(function(key, i) {
      parts[key] = m[i] || '';
    });

    return parts;
  }

  /**
   * Breaks a query string down into an array of key/value pairs
   * @param  {string} str query
   * @return {array}      array of arrays (key/value pairs)
   */
  function parseQuery(str) {
    var i, ps, p, n, k, v;
    var pairs = [];

    if (typeof(str) === 'undefined' || str === null || str === '') {
      return pairs;
    }

    if (str.indexOf('?') === 0) {
      str = str.substring(1);
    }

    ps = str.toString().split(re.query_separator);

    for (i = 0, l = ps.length; i < l; i++) {
      p = ps[i];
      n = p.indexOf('=');

      if (n !== 0) {
        k = decodeURIComponent(p.substring(0, n));
        v = decodeURIComponent(p.substring(n + 1));
        pairs.push(n === -1 ? [p, null] : [k, v]);
      }

    }
    return pairs;
  }

  /**
   * Creates a new Uri object
   * @constructor
   * @param {string} str
   */
  function Uri(str) {
    this.uriParts = parseUri(str);
    this.queryPairs = parseQuery(this.uriParts.query);
    this.hasAuthorityPrefixUserPref = null;
  }

  /**
   * Define getter/setter methods
   */
  ['protocol', 'userInfo', 'host', 'port', 'path', 'anchor'].forEach(function(key) {
    Uri.prototype[key] = function(val) {
      if (typeof val !== 'undefined') {
        this.uriParts[key] = val;
      }
      return this.uriParts[key];
    };
  });

  /**
   * if there is no protocol, the leading // can be enabled or disabled
   * @param  {Boolean}  val
   * @return {Boolean}
   */
  Uri.prototype.hasAuthorityPrefix = function(val) {
    if (typeof val !== 'undefined') {
      this.hasAuthorityPrefixUserPref = val;
    }

    if (this.hasAuthorityPrefixUserPref === null) {
      return (this.uriParts.source.indexOf('//') !== -1);
    } else {
      return this.hasAuthorityPrefixUserPref;
    }
  };

  /**
   * Serializes the internal state of the query pairs
   * @param  {string} [val]   set a new query string
   * @return {string}         query string
   */
  Uri.prototype.query = function(val) {
    var s = '', i, param;

    if (typeof val !== 'undefined') {
      this.queryPairs = parseQuery(val);
    }

    for (i = 0, l = this.queryPairs.length; i < l; i++) {
      param = this.queryPairs[i];
      if (s.length > 0) {
        s += '&';
      }
      if (param[1] === null) {
        s += param[0];
      } else {
        s += param[0];
        s += '=';
        if (param[1]) {
          s += encodeURIComponent(param[1]);
        }
      }
    }
    return s.length > 0 ? '?' + s : s;
  };

  /**
   * returns the first query param value found for the key
   * @param  {string} key query key
   * @return {string}     first value found for key
   */
  Uri.prototype.getQueryParamValue = function (key) {
    var param, i;
    for (i = 0, l = this.queryPairs.length; i < l; i++) {
      param = this.queryPairs[i];
      if (key === param[0]) {
        return param[1];
      }
    }
  };

  /**
   * returns an array of query param values for the key
   * @param  {string} key query key
   * @return {array}      array of values
   */
  Uri.prototype.getQueryParamValues = function (key) {
    var arr = [], i, param;
    for (i = 0, l = this.queryPairs.length; i < l; i++) {
      param = this.queryPairs[i];
      if (key === param[0]) {
        arr.push(param[1]);
      }
    }
    return arr;
  };

  /**
   * removes query parameters
   * @param  {string} key     remove values for key
   * @param  {val}    [val]   remove a specific value, otherwise removes all
   * @return {Uri}            returns self for fluent chaining
   */
  Uri.prototype.deleteQueryParam = function (key, val) {
    var arr = [], i, param, keyMatchesFilter, valMatchesFilter;

    for (i = 0, l = this.queryPairs.length; i < l; i++) {

      param = this.queryPairs[i];
      keyMatchesFilter = decode(param[0]) === decode(key);
      valMatchesFilter = param[1] === val;

      if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && (!keyMatchesFilter || !valMatchesFilter))) {
        arr.push(param);
      }
    }

    this.queryPairs = arr;

    return this;
  };

  /**
   * adds a query parameter
   * @param  {string}  key        add values for key
   * @param  {string}  val        value to add
   * @param  {integer} [index]    specific index to add the value at
   * @return {Uri}                returns self for fluent chaining
   */
  Uri.prototype.addQueryParam = function (key, val, index) {
    if (arguments.length === 3 && index !== -1) {
      index = Math.min(index, this.queryPairs.length);
      this.queryPairs.splice(index, 0, [key, val]);
    } else if (arguments.length > 0) {
      this.queryPairs.push([key, val]);
    }
    return this;
  };

  /**
   * replaces query param values
   * @param  {string} key         key to replace value for
   * @param  {string} newVal      new value
   * @param  {string} [oldVal]    replace only one specific value (otherwise replaces all)
   * @return {Uri}                returns self for fluent chaining
   */
  Uri.prototype.replaceQueryParam = function (key, newVal, oldVal) {
    var index = -1, len = this.queryPairs.length, i, param;

    if (arguments.length === 3) {
      for (i = 0; i < len; i++) {
        param = this.queryPairs[i];
        if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) {
          index = i;
          break;
        }
      }
      this.deleteQueryParam(key, decode(oldVal)).addQueryParam(key, newVal, index);
    } else {
      for (i = 0; i < len; i++) {
        param = this.queryPairs[i];
        if (decode(param[0]) === decode(key)) {
          index = i;
          break;
        }
      }
      this.deleteQueryParam(key);
      this.addQueryParam(key, newVal, index);
    }
    return this;
  };

  /**
   * Define fluent setter methods (setProtocol, setHasAuthorityPrefix, etc)
   */
  ['protocol', 'hasAuthorityPrefix', 'userInfo', 'host', 'port', 'path', 'query', 'anchor'].forEach(function(key) {
    var method = 'set' + key.charAt(0).toUpperCase() + key.slice(1);
    Uri.prototype[method] = function(val) {
      this[key](val);
      return this;
    };
  });

  /**
   * Scheme name, colon and doubleslash, as required
   * @return {string} http:// or possibly just //
   */
  Uri.prototype.scheme = function() {
    var s = '';

    if (this.protocol()) {
      s += this.protocol();
      if (this.protocol().indexOf(':') !== this.protocol().length - 1) {
        s += ':';
      }
      s += '//';
    } else {
      if (this.hasAuthorityPrefix() && this.host()) {
        s += '//';
      }
    }

    return s;
  };

  /**
   * Same as Mozilla nsIURI.prePath
   * @return {string} scheme://user:password@host:port
   * @see  https://developer.mozilla.org/en/nsIURI
   */
  Uri.prototype.origin = function() {
    var s = this.scheme();

    if (s == 'file://') {
      return s + this.uriParts.authority;
    }

    if (this.userInfo() && this.host()) {
      s += this.userInfo();
      if (this.userInfo().indexOf('@') !== this.userInfo().length - 1) {
        s += '@';
      }
    }

    if (this.host()) {
      s += this.host();
      if (this.port()) {
        s += ':' + this.port();
      }
    }

    return s;
  };

  /**
   * Adds a trailing slash to the path
   */
  Uri.prototype.addTrailingSlash = function() {
    var path = this.path() || '';

    if (path.substr(-1) !== '/') {
      this.path(path + '/');
    }

    return this;
  };

  /**
   * Serializes the internal state of the Uri object
   * @return {string}
   */
  Uri.prototype.toString = function() {
    var path, s = this.origin();

    if (this.path()) {
      path = this.path();
      if (!(re.ends_with_slashes.test(s) || re.starts_with_slashes.test(path))) {
        s += '/';
      } else {
        if (s) {
          s.replace(re.ends_with_slashes, '/');
        }
        path = path.replace(re.starts_with_slashes, '/');
      }
      s += path;
    } else {
      if (this.host() && (this.query().toString() || this.anchor())) {
        s += '/';
      }
    }
    if (this.query().toString()) {
      if (this.query().toString().indexOf('?') !== 0) {
        s += '?';
      }
      s += this.query().toString();
    }

    if (this.anchor()) {
      if (this.anchor().indexOf('#') !== 0) {
        s += '#';
      }
      s += this.anchor();
    }

    return s;
  };

  /**
   * Clone a Uri object
   * @return {Uri} duplicate copy of the Uri
   */
  Uri.prototype.clone = function() {
    return new Uri(this.toString());
  };

  /**
   * export via AMD or CommonJS, otherwise leak a global
   */
  if (typeof define === 'function' && define.amd) {
    define(function() {
      return Uri;
    });
  } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = Uri;
  } else {
    global.Uri = Uri;
  }
}(this));
<|endoftext|>


<commit_before>// BROWSER ENVIRONMENT
var brico = require('./_brico.js')

<commit_msg>fix for kindle jalopy<commit_after>// BROWSER ENVIRONMENT
if(!Function.prototype.bind) require('./bindshim.js') //kindle jalopy doesn't have bind
var ws = require('websocket-stream');
var wss = ws('ws://192.168.1.76/');

wss.on('data', function (d) {
  var c =document.getElementById('container')
  c.innerHTML = d
})
<|endoftext|>


<commit_before>import CollectionUtils from '../CollectionUtils';
export default class ItemService {

    /*@ngInject*/
    constructor(articleService, itemRestService, listItemRestService, shoppingListRestService) {

        this.articleService = articleService;

        this.restService = itemRestService;
        this.listItemRestService = listItemRestService;
        this.shoppingListRestService = shoppingListRestService;
        this.items = this.restService.container;

    }

    /**
     * Returns all items.
     *
     * @param {Boolean} refetch If true a request to the backend will be performed. If false the last fetched lists are
     *                  returned.
     * @returns {Array} All Items
     */
    getAllItems(refetch = false) {
        if (refetch || this.itemsAlreadyFetched()) {
            this.restService.fetch();
        }
        return this.items;
    }

    getItemsOfList(list) {
        return this.listItemRestService.fetch({listId: list.entityId});
    }

    itemsAlreadyFetched() {
        return !this.items.fetching && !this.items.fetched;
    }

    addItemToList(item, list) {
        return this.articleService.createArticle(item.article)
            .then((createdArticle) => {
                item.article.entityId = createdArticle.entityId;
                return this.listItemRestService.create(item, {listId: list.entityId});
            }).then((createdItem) => {
                list.lastModified = Date.now();
                list.items.push(item);
                this.shoppingListRestService.storeInDB(list);
                return createdItem;
            });
    }

    updateItemOfList(item, list) {
        return this.listItemRestService.update(item, {listId: list.entityId});
    }

    deleteItemOfList(item, list) {
        return this.listItemRestService.delete(item, {listId: list.entityId})
            .then(() => {
                CollectionUtils.remove(list.items, item);
                list.lastModified = Date.now();
                this.shoppingListRestService.storeInDB(list);
            });
    }
}<commit_msg>Save updated list in indexDB when item is updated<commit_after>import CollectionUtils from '../CollectionUtils';
export default class ItemService {

    /*@ngInject*/
    constructor(articleService, itemRestService, listItemRestService, shoppingListRestService) {

        this.articleService = articleService;

        this.restService = itemRestService;
        this.listItemRestService = listItemRestService;
        this.shoppingListRestService = shoppingListRestService;
        this.items = this.restService.container;

    }

    /**
     * Returns all items.
     *
     * @param {Boolean} refetch If true a request to the backend will be performed. If false the last fetched lists are
     *                  returned.
     * @returns {Array} All Items
     */
    getAllItems(refetch = false) {
        if (refetch || this.itemsAlreadyFetched()) {
            this.restService.fetch();
        }
        return this.items;
    }

    getItemsOfList(list) {
        return this.listItemRestService.fetch({listId: list.entityId});
    }

    itemsAlreadyFetched() {
        return !this.items.fetching && !this.items.fetched;
    }

    addItemToList(item, list) {
        return this.articleService.createArticle(item.article)
            .then((createdArticle) => {
                item.article.entityId = createdArticle.entityId;
                return this.listItemRestService.create(item, {listId: list.entityId});
            }).then((createdItem) => {
                list.lastModified = Date.now();
                list.items.push(item);
                this.shoppingListRestService.storeInDB(list);
                return createdItem;
            });
    }

    updateItemOfList(item, list) {
        return this.listItemRestService.update(item, {listId: list.entityId})
            .then(() => {
                list.lastModified = Date.now();
                this.shoppingListRestService.storeInDB(list);
            });
    }

    deleteItemOfList(item, list) {
        return this.listItemRestService.delete(item, {listId: list.entityId})
            .then(() => {
                CollectionUtils.remove(list.items, item);
                list.lastModified = Date.now();
                this.shoppingListRestService.storeInDB(list);
            });
    }
}<|endoftext|>


<commit_before>
// SmoothScroll v0.9.2
// Licensed under the terms of the MIT license.
// Balázs Galambosi (c) 2011

/**
 * A module for middle mouse scrolling.
 */
(function(window){

// local settings
var img = document.createElement("div"); // img at the reference point
var scrolling = false; // guards one phase
var enabled   = false; // from settings
var framerate = 200;

// get global settings
chrome.extension.connect({ name: "smoothscroll"}).
onMessage.addListener(function (settings) {
    enabled = (settings.middlemouse == "true");
});

/**
 * Initializes the image at the reference point.
 */
function init() {
    var url = chrome.extension.getURL("../img/cursor.png");
    var style = img.style;
    style.background = "url("+url+")";
    style.position   = "fixed";
    style.zIndex     = "1000";
    style.width      = "20px";
    style.height     = "20px";
    new Image().src  = url; // force download
}

/**
 * Shows the reference image, and binds event listeners for scrolling.
 * It also manages the animation.
 * @param {Object} event
 */
function mousedown(e) {

    var isLink = false;
    var elem   = e.target;

    do {
        isLink = isNodeName(elem, "a");
        if (isLink) break;
    } while (elem = elem.parentNode)

    // if it's not the middle button, or
    // it's being used on an <a> element
    // take the default action
    if (!enabled || e.button !== 1 || isLink) {
        return true;
    }

    // we don't want the default by now
    e.preventDefault();

    // quit if there's an ongoing scrolling
    if (scrolling) {
        return false;
    }

    // set up a new scrolling phase
    scrolling = true;

    // reference point
    img.style.left = e.clientX - 10 + "px";
    img.style.top  = e.clientY - 10 + "px";
    document.body.appendChild(img);

    var refereceX = e.clientX;
    var refereceY = e.clientY;

    var speedX = 0;
    var speedY = 0;

    var elem = overflowingAncestor(e.target);

    // animation loop
    var delay = 1000 / framerate;
    var interval = setInterval(function(){
        elem.scrollLeft += (speedX * delay) << 0;
        elem.scrollTop  += (speedY * delay) << 0;
    }, delay);

    var first = true;

    function mousemove(e) {
        if (first) {
            addEvent("mouseup", remove);
            first = false;
        }
        speedX = (e.clientX - refereceX) * 10 / 1000;
        speedY = (e.clientY - refereceY) * 10 / 1000;
    }

    function remove(e) {
        removeEvent("mousemove", mousemove);
        removeEvent("mousedown", remove);
        removeEvent("mouseup", remove);
        removeEvent("keydown", remove);
        clearInterval(interval);
        document.body.removeChild(img);
        scrolling = false;
    }

    addEvent("mousemove", mousemove);
    addEvent("mousedown", remove);
    addEvent("keydown", remove);
}

addEvent("mousedown", mousedown);
addEvent("DOMContentLoaded", init);

})(window);
<commit_msg>default framerate back to 100<commit_after>
// SmoothScroll v0.9.2
// Licensed under the terms of the MIT license.
// Balázs Galambosi (c) 2011

/**
 * A module for middle mouse scrolling.
 */
(function(window){

// local settings
var img = document.createElement("div"); // img at the reference point
var scrolling = false; // guards one phase
var enabled   = false; // from settings
var framerate = 100;

// get global settings
chrome.extension.connect({ name: "smoothscroll"}).
onMessage.addListener(function (settings) {
    enabled = (settings.middlemouse == "true");
});

/**
 * Initializes the image at the reference point.
 */
function init() {
    var url = chrome.extension.getURL("../img/cursor.png");
    var style = img.style;
    style.background = "url("+url+")";
    style.position   = "fixed";
    style.zIndex     = "1000";
    style.width      = "20px";
    style.height     = "20px";
    new Image().src  = url; // force download
}

/**
 * Shows the reference image, and binds event listeners for scrolling.
 * It also manages the animation.
 * @param {Object} event
 */
function mousedown(e) {

    var isLink = false;
    var elem   = e.target;

    do {
        isLink = isNodeName(elem, "a");
        if (isLink) break;
    } while (elem = elem.parentNode)

    // if it's not the middle button, or
    // it's being used on an <a> element
    // take the default action
    if (!enabled || e.button !== 1 || isLink) {
        return true;
    }

    // we don't want the default by now
    e.preventDefault();

    // quit if there's an ongoing scrolling
    if (scrolling) {
        return false;
    }

    // set up a new scrolling phase
    scrolling = true;

    // reference point
    img.style.left = e.clientX - 10 + "px";
    img.style.top  = e.clientY - 10 + "px";
    document.body.appendChild(img);

    var refereceX = e.clientX;
    var refereceY = e.clientY;

    var speedX = 0;
    var speedY = 0;

    var elem = overflowingAncestor(e.target);

    // animation loop
    var delay = 1000 / framerate;
    var interval = setInterval(function(){
        elem.scrollLeft += (speedX * delay) << 0;
        elem.scrollTop  += (speedY * delay) << 0;
    }, delay);

    var first = true;

    function mousemove(e) {
        if (first) {
            addEvent("mouseup", remove);
            first = false;
        }
        speedX = (e.clientX - refereceX) * 10 / 1000;
        speedY = (e.clientY - refereceY) * 10 / 1000;
    }

    function remove(e) {
        removeEvent("mousemove", mousemove);
        removeEvent("mousedown", remove);
        removeEvent("mouseup", remove);
        removeEvent("keydown", remove);
        clearInterval(interval);
        document.body.removeChild(img);
        scrolling = false;
    }

    addEvent("mousemove", mousemove);
    addEvent("mousedown", remove);
    addEvent("keydown", remove);
}

addEvent("mousedown", mousedown);
addEvent("DOMContentLoaded", init);

})(window);
<|endoftext|>


<commit_before>import _ from 'lodash'
import mware from 'mware'
import path from 'path'
import fs from 'fs'

import licensing from './licensing'

const createMiddleware = function(bp, middlewareName) {
  const _use = mware()
  const _error = mware()

  const use = function(middleware) {
    if (typeof(middleware) !== 'function') {
      throw new TypeError('Expected all middleware arguments to be functions')
    }

    if (middleware.length === 2) {
      _use(middleware)
    } else if (middleware.length === 3) {
      _error(middleware)
    }
  }

  const dispatch = function(event) {
    if (!_.isPlainObject(event)) {
      throw new TypeError('Expected all dispatch arguments to be plain event objects')
    }

    const conformity = {
      type: function(value) { return typeof(value) === 'string' },
      platform: function(value) { return typeof(value) === 'string' },
      text: function(value) { return typeof(value) === 'string' },
      raw: function() { return true }
    }

    if (!_.conformsTo(event, conformity)) {
      throw new TypeError('Expected event to contain (type: string), ' +
        '(platform: string), (text: string), (raw: any)')
    }

    // Provide botpress to the event handlers
    event.bp = bp

    _use.run(event, function(err) {
      if (err) {
        _error.run(err, event, () => {
          bp.logger.error(`[BOTPRESS] Unhandled error in middleware (${middlewareName}). Error: ${err.message}`)
        })
      }
    })
  }

  return { use, dispatch }
}

module.exports = function(bp, dataLocation, projectLocation, logger) {
  const middlewaresFilePath = path.join(dataLocation, 'middlewares.json')
  let incoming, outgoing, middlewares, customizations

  const noopChain = function() {
    let message = 'Middleware called before middlewares have been loaded. This is a no-op.'
     + ' Have you forgotten to call `bp.loadMiddlewares()` in your bot?'

    if (arguments && typeof(arguments[0]) === 'object') {
      message += '\nCalled with: ' + JSON.stringify(arguments[0], null, 2)
    }

    logger.warn(message)
  }

  const readCustomizations = () => {
    if (!fs.existsSync(middlewaresFilePath)) {
      fs.writeFileSync(middlewaresFilePath, '{}')
    }
    return JSON.parse(fs.readFileSync(middlewaresFilePath))
  }

  const writeCustomizations = () => {
    fs.writeFileSync(middlewaresFilePath, JSON.stringify(customizations))
  }

  const setCustomizations = middlewares => {
    _.each(middlewares, (middleware => {
      const { name, order, enabled } = middleware
      customizations[name] = { order, enabled }
    }))
    writeCustomizations()
  }

  const resetCustomizations = () => {
    customizations = {}
    writeCustomizations()
  }

  const register = (middleware) => {
    if (!middleware || !middleware.name) {
      logger.error('A unique middleware name is mandatory')
      return false
    }

    if (!middleware.handler) {
      logger.error('A middleware handler is mandatory')
      return false
    }

    if (!middleware.type || (middleware.type !== 'incoming' && middleware.type !== 'outgoing')) {
      logger.error('A middleware type (incoming or outgoing) is required')
      return false
    }

    middleware.order = middleware.order || 0
    middleware.enabled = typeof middleware.enabled === 'undefined' ? true: !!middleware.enabled

    if (_.some(middlewares, m => m.name === middleware.name)) {
      logger.error('Another middleware with the same name has already been registered')
      return false
    }

    middlewares.push(middleware)
  }

  const list = () => {
    return _.orderBy(middlewares.map(middleware => {
      const customization = customizations[middleware.name]
      if (customization) {
        return Object.assign({}, middleware, customization)
      }
      return middleware
    }), 'order')
  }

  const load = () => {
    incoming = createMiddleware(bp, 'incoming')
    outgoing = createMiddleware(bp, 'outgoing')

    const { middleware: licenseMiddleware } = licensing(projectLocation)
    incoming.use(licenseMiddleware)

    _.each(list(), (m => {
      if (!m.enabled) {
        return logger.debug('SKIPPING middleware:', m.name, ' [Reason=disabled]')
      }

      logger.debug('Loading middleware:', m.name)

      if (m.type === 'incoming') {
        incoming.use(m.handler)
      } else {
        outgoing.use(m.handler)
      }
    }))
  }

  const sendToMiddleware = type => event => {
    let mw = type === 'incoming' ? incoming : outgoing
    mw.dispatch ? mw.dispatch(event) : mw(event)
  }

  const sendIncoming = sendToMiddleware('incoming')
  const sendOutgoing = sendToMiddleware('outgoing')

  incoming = outgoing = noopChain
  middlewares = []
  customizations = readCustomizations()

  return {
    load,
    list,
    register,
    sendIncoming,
    sendOutgoing,
    getCustomizations: () => customizations,
    setCustomizations,
    resetCustomizations
  }
}
<commit_msg>sendOutgoing and sendIncoming return a promise<commit_after>import _ from 'lodash'
import mware from 'mware'
import path from 'path'
import fs from 'fs'
import Promise from 'bluebird'

import licensing from './licensing'

const createMiddleware = function(bp, middlewareName) {
  const _use = mware()
  const _error = mware()

  const use = function(middleware) {
    if (typeof(middleware) !== 'function') {
      throw new TypeError('Expected all middleware arguments to be functions')
    }

    if (middleware.length === 2) {
      _use(middleware)
    } else if (middleware.length === 3) {
      _error(middleware)
    }
  }

  const dispatch = function(event) {
    if (!_.isPlainObject(event)) {
      throw new TypeError('Expected all dispatch arguments to be plain event objects')
    }

    const conformity = {
      type: function(value) { return typeof(value) === 'string' },
      platform: function(value) { return typeof(value) === 'string' },
      text: function(value) { return typeof(value) === 'string' },
      raw: function() { return true }
    }

    if (!_.conformsTo(event, conformity)) {
      throw new TypeError('Expected event to contain (type: string), ' +
        '(platform: string), (text: string), (raw: any)')
    }

    // Provide botpress to the event handlers
    event.bp = bp

    _use.run(event, function(err) {
      if (err) {
        _error.run(err, event, () => {
          bp.logger.error(`[BOTPRESS] Unhandled error in middleware (${middlewareName}). Error: ${err.message}`)
        })
      }
    })

    return event._promise || Promise.resolve()
  }

  return { use, dispatch }
}

module.exports = function(bp, dataLocation, projectLocation, logger) {
  const middlewaresFilePath = path.join(dataLocation, 'middlewares.json')
  let incoming, outgoing, middlewares, customizations

  const noopChain = function() {
    let message = 'Middleware called before middlewares have been loaded. This is a no-op.'
     + ' Have you forgotten to call `bp.loadMiddlewares()` in your bot?'

    if (arguments && typeof(arguments[0]) === 'object') {
      message += '\nCalled with: ' + JSON.stringify(arguments[0], null, 2)
    }

    logger.warn(message)
  }

  const readCustomizations = () => {
    if (!fs.existsSync(middlewaresFilePath)) {
      fs.writeFileSync(middlewaresFilePath, '{}')
    }
    return JSON.parse(fs.readFileSync(middlewaresFilePath))
  }

  const writeCustomizations = () => {
    fs.writeFileSync(middlewaresFilePath, JSON.stringify(customizations))
  }

  const setCustomizations = middlewares => {
    _.each(middlewares, (middleware => {
      const { name, order, enabled } = middleware
      customizations[name] = { order, enabled }
    }))
    writeCustomizations()
  }

  const resetCustomizations = () => {
    customizations = {}
    writeCustomizations()
  }

  const register = (middleware) => {
    if (!middleware || !middleware.name) {
      logger.error('A unique middleware name is mandatory')
      return false
    }

    if (!middleware.handler) {
      logger.error('A middleware handler is mandatory')
      return false
    }

    if (!middleware.type || (middleware.type !== 'incoming' && middleware.type !== 'outgoing')) {
      logger.error('A middleware type (incoming or outgoing) is required')
      return false
    }

    middleware.order = middleware.order || 0
    middleware.enabled = typeof middleware.enabled === 'undefined' ? true: !!middleware.enabled

    if (_.some(middlewares, m => m.name === middleware.name)) {
      logger.error('Another middleware with the same name has already been registered')
      return false
    }

    middlewares.push(middleware)
  }

  const list = () => {
    return _.orderBy(middlewares.map(middleware => {
      const customization = customizations[middleware.name]
      if (customization) {
        return Object.assign({}, middleware, customization)
      }
      return middleware
    }), 'order')
  }

  const load = () => {
    incoming = createMiddleware(bp, 'incoming')
    outgoing = createMiddleware(bp, 'outgoing')

    const { middleware: licenseMiddleware } = licensing(projectLocation)
    incoming.use(licenseMiddleware)

    _.each(list(), (m => {
      if (!m.enabled) {
        return logger.debug('SKIPPING middleware:', m.name, ' [Reason=disabled]')
      }

      logger.debug('Loading middleware:', m.name)

      if (m.type === 'incoming') {
        incoming.use(m.handler)
      } else {
        outgoing.use(m.handler)
      }
    }))
  }

  const sendToMiddleware = type => event => {
    let mw = type === 'incoming' ? incoming : outgoing
    return mw.dispatch ? mw.dispatch(event) : mw(event)
  }

  const sendIncoming = sendToMiddleware('incoming')
  const sendOutgoing = sendToMiddleware('outgoing')

  incoming = outgoing = noopChain
  middlewares = []
  customizations = readCustomizations()

  return {
    load,
    list,
    register,
    sendIncoming,
    sendOutgoing,
    getCustomizations: () => customizations,
    setCustomizations,
    resetCustomizations
  }
}
<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])

  .provider('$modal', function() {

    var defaults = this.defaults = {
      animation: 'am-fade',
      backdropAnimation: 'am-fade',
      prefixClass: 'modal',
      placement: 'top',
      template: 'modal/modal.tpl.html',
      contentTemplate: false,
      container: false,
      element: null,
      backdrop: true,
      keyboard: true,
      html: false,
      show: true
    };

    this.$get = function($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, dimensions) {

      var forEach = angular.forEach;
      var trim = String.prototype.trim;
      var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
      var bodyElement = angular.element($window.document.body);
      var htmlReplaceRegExp = /ng-bind="/ig;

      function ModalFactory(config) {

        var $modal = {};

        // Common vars
        var options = angular.extend({}, defaults, config);
        $modal.$promise = fetchTemplate(options.template);
        var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
        if(!options.element && !options.container) {
          options.container = 'body';
        }

        // Support scope as string options
        forEach(['title', 'content'], function(key) {
          if(options[key]) scope[key] = options[key];
        });

        // Provide scope helpers
        scope.$hide = function() {
          scope.$$postDigest(function() {
            $modal.hide();
          });
        };
        scope.$show = function() {
          scope.$$postDigest(function() {
            $modal.show();
          });
        };
        scope.$toggle = function() {
          scope.$$postDigest(function() {
            $modal.toggle();
          });
        };

        // Support contentTemplate option
        if(options.contentTemplate) {
          $modal.$promise = $modal.$promise.then(function(template) {
            var templateEl = angular.element(template);
            return fetchTemplate(options.contentTemplate)
            .then(function(contentTemplate) {
              var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(contentTemplate);
              // Drop the default footer as you probably don't want it if you use a custom contentTemplate
              if(!config.template) contentEl.next().remove();
              return templateEl[0].outerHTML;
            });
          });
        }

        // Fetch, compile then initialize modal
        var modalLinker, modalElement;
        var backdropElement = angular.element('
');
        $modal.$promise.then(function(template) {
          if(angular.isObject(template)) template = template.data;
          if(options.html) template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
          template = trim.apply(template);
          modalLinker = $compile(template);
          $modal.init();
        });

        $modal.init = function() {

          // Options: show
          if(options.show) {
            scope.$$postDigest(function() {
              $modal.show();
            });
          }

        };

        $modal.destroy = function() {

          // Remove element
          if(modalElement) {
            modalElement.remove();
            modalElement = null;
          }
          if(backdropElement) {
            backdropElement.remove();
            backdropElement = null;
          }

          // Destroy scope
          scope.$destroy();

        };

        $modal.show = function() {

          var parent = options.container ? findElement(options.container) : null;
          var after = options.container ? null : options.element;

          // Fetch a cloned element linked from template
          modalElement = $modal.$element = modalLinker(scope, function(clonedElement, scope) {});

          // Set the initial positioning.
          modalElement.css({display: 'block'}).addClass(options.placement);

          // Options: animation
          if(options.animation) {
            if(options.backdrop) {
              backdropElement.addClass(options.backdropAnimation);
            }
            modalElement.addClass(options.animation);
          }

          if(options.backdrop) {
            $animate.enter(backdropElement, bodyElement, null, function() {});
          }
          $animate.enter(modalElement, parent, after, function() {});
          scope.$isShown = true;
          scope.$$phase || scope.$digest();
          // Focus once the enter-animation has started
          // Weird PhantomJS bug hack
          var el = modalElement[0];
          requestAnimationFrame(function() {
            el.focus();
          });

          bodyElement.addClass(options.prefixClass + '-open');
          if(options.animation) {
            bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
          }

          // Bind events
          if(options.backdrop) {
            modalElement.on('click', hideOnBackdropClick);
            backdropElement.on('click', hideOnBackdropClick);
          }
          if(options.keyboard) {
            modalElement.on('keyup', $modal.$onKeyUp);
          }

        };

        $modal.hide = function() {

          $animate.leave(modalElement, function() {
            bodyElement.removeClass(options.prefixClass + '-open');
            if(options.animation) {
              bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
            }
          });
          if(options.backdrop) {
            $animate.leave(backdropElement, function() {});
          }
          scope.$isShown = false;
          scope.$$phase || scope.$digest();

          // Unbind events
          if(options.backdrop) {
            modalElement.off('click', hideOnBackdropClick);
            backdropElement.off('click', hideOnBackdropClick);
          }
          if(options.keyboard) {
            modalElement.off('keyup', $modal.$onKeyUp);
          }

        };

        $modal.toggle = function() {

          scope.$isShown ? $modal.hide() : $modal.show();

        };

        $modal.focus = function() {
          modalElement[0].focus();
        };

        // Protected methods

        $modal.$onKeyUp = function(evt) {

          evt.which === 27 && $modal.hide();

        };

        // Private methods

        function hideOnBackdropClick(evt) {
          if(evt.target !== evt.currentTarget) return;
          options.backdrop === 'static' ? $modal.focus() : $modal.hide();
        }

        return $modal;

      }

      // Helper functions

      function findElement(query, element) {
        return angular.element((element || document).querySelectorAll(query));
      }

      function fetchTemplate(template) {
        return $q.when($templateCache.get(template) || $http.get(template))
        .then(function(res) {
          if(angular.isObject(res)) {
            $templateCache.put(template, res.data);
            return res.data;
          }
          return res;
        });
      }

      return ModalFactory;

    };

  })

  .directive('bsModal', function($window, $location, $sce, $modal) {

    return {
      restrict: 'EAC',
      scope: true,
      link: function postLink(scope, element, attr, transclusion) {

        // Directive options
        var options = {scope: scope, element: element, show: false};
        angular.forEach(['template', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        // Support scope as data-attrs
        angular.forEach(['title', 'content'], function(key) {
          attr[key] && attr.$observe(key, function(newValue, oldValue) {
            scope[key] = $sce.trustAsHtml(newValue);
          });
        });

        // Support scope as an object
        attr.bsModal && scope.$watch(attr.bsModal, function(newValue, oldValue) {
          if(angular.isObject(newValue)) {
            angular.extend(scope, newValue);
          } else {
            scope.content = newValue;
          }
        }, true);

        // Initialize modal
        var modal = $modal(options);

        // Trigger
        element.on(attr.trigger || 'click', modal.toggle);

        // Garbage collection
        scope.$on('$destroy', function() {
          modal.destroy();
          options = null;
          modal = null;
        });

      }
    };

  });
<commit_msg>feat(modal): expose options object<commit_after>'use strict';

angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])

  .provider('$modal', function() {

    var defaults = this.defaults = {
      animation: 'am-fade',
      backdropAnimation: 'am-fade',
      prefixClass: 'modal',
      placement: 'top',
      template: 'modal/modal.tpl.html',
      contentTemplate: false,
      container: false,
      element: null,
      backdrop: true,
      keyboard: true,
      html: false,
      show: true
    };

    this.$get = function($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, dimensions) {

      var forEach = angular.forEach;
      var trim = String.prototype.trim;
      var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
      var bodyElement = angular.element($window.document.body);
      var htmlReplaceRegExp = /ng-bind="/ig;

      function ModalFactory(config) {

        var $modal = {};

        // Common vars
        var options = $modal.$options = angular.extend({}, defaults, config);
        $modal.$promise = fetchTemplate(options.template);
        var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
        if(!options.element && !options.container) {
          options.container = 'body';
        }

        // Support scope as string options
        forEach(['title', 'content'], function(key) {
          if(options[key]) scope[key] = options[key];
        });

        // Provide scope helpers
        scope.$hide = function() {
          scope.$$postDigest(function() {
            $modal.hide();
          });
        };
        scope.$show = function() {
          scope.$$postDigest(function() {
            $modal.show();
          });
        };
        scope.$toggle = function() {
          scope.$$postDigest(function() {
            $modal.toggle();
          });
        };

        // Support contentTemplate option
        if(options.contentTemplate) {
          $modal.$promise = $modal.$promise.then(function(template) {
            var templateEl = angular.element(template);
            return fetchTemplate(options.contentTemplate)
            .then(function(contentTemplate) {
              var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(contentTemplate);
              // Drop the default footer as you probably don't want it if you use a custom contentTemplate
              if(!config.template) contentEl.next().remove();
              return templateEl[0].outerHTML;
            });
          });
        }

        // Fetch, compile then initialize modal
        var modalLinker, modalElement;
        var backdropElement = angular.element('
');
        $modal.$promise.then(function(template) {
          if(angular.isObject(template)) template = template.data;
          if(options.html) template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
          template = trim.apply(template);
          modalLinker = $compile(template);
          $modal.init();
        });

        $modal.init = function() {

          // Options: show
          if(options.show) {
            scope.$$postDigest(function() {
              $modal.show();
            });
          }

        };

        $modal.destroy = function() {

          // Remove element
          if(modalElement) {
            modalElement.remove();
            modalElement = null;
          }
          if(backdropElement) {
            backdropElement.remove();
            backdropElement = null;
          }

          // Destroy scope
          scope.$destroy();

        };

        $modal.show = function() {

          var parent = options.container ? findElement(options.container) : null;
          var after = options.container ? null : options.element;

          // Fetch a cloned element linked from template
          modalElement = $modal.$element = modalLinker(scope, function(clonedElement, scope) {});

          // Set the initial positioning.
          modalElement.css({display: 'block'}).addClass(options.placement);

          // Options: animation
          if(options.animation) {
            if(options.backdrop) {
              backdropElement.addClass(options.backdropAnimation);
            }
            modalElement.addClass(options.animation);
          }

          if(options.backdrop) {
            $animate.enter(backdropElement, bodyElement, null, function() {});
          }
          $animate.enter(modalElement, parent, after, function() {});
          scope.$isShown = true;
          scope.$$phase || scope.$digest();
          // Focus once the enter-animation has started
          // Weird PhantomJS bug hack
          var el = modalElement[0];
          requestAnimationFrame(function() {
            el.focus();
          });

          bodyElement.addClass(options.prefixClass + '-open');
          if(options.animation) {
            bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
          }

          // Bind events
          if(options.backdrop) {
            modalElement.on('click', hideOnBackdropClick);
            backdropElement.on('click', hideOnBackdropClick);
          }
          if(options.keyboard) {
            modalElement.on('keyup', $modal.$onKeyUp);
          }

        };

        $modal.hide = function() {

          $animate.leave(modalElement, function() {
            bodyElement.removeClass(options.prefixClass + '-open');
            if(options.animation) {
              bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
            }
          });
          if(options.backdrop) {
            $animate.leave(backdropElement, function() {});
          }
          scope.$isShown = false;
          scope.$$phase || scope.$digest();

          // Unbind events
          if(options.backdrop) {
            modalElement.off('click', hideOnBackdropClick);
            backdropElement.off('click', hideOnBackdropClick);
          }
          if(options.keyboard) {
            modalElement.off('keyup', $modal.$onKeyUp);
          }

        };

        $modal.toggle = function() {

          scope.$isShown ? $modal.hide() : $modal.show();

        };

        $modal.focus = function() {
          modalElement[0].focus();
        };

        // Protected methods

        $modal.$onKeyUp = function(evt) {

          evt.which === 27 && $modal.hide();

        };

        // Private methods

        function hideOnBackdropClick(evt) {
          if(evt.target !== evt.currentTarget) return;
          options.backdrop === 'static' ? $modal.focus() : $modal.hide();
        }

        return $modal;

      }

      // Helper functions

      function findElement(query, element) {
        return angular.element((element || document).querySelectorAll(query));
      }

      function fetchTemplate(template) {
        return $q.when($templateCache.get(template) || $http.get(template))
        .then(function(res) {
          if(angular.isObject(res)) {
            $templateCache.put(template, res.data);
            return res.data;
          }
          return res;
        });
      }

      return ModalFactory;

    };

  })

  .directive('bsModal', function($window, $location, $sce, $modal) {

    return {
      restrict: 'EAC',
      scope: true,
      link: function postLink(scope, element, attr, transclusion) {

        // Directive options
        var options = {scope: scope, element: element, show: false};
        angular.forEach(['template', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        // Support scope as data-attrs
        angular.forEach(['title', 'content'], function(key) {
          attr[key] && attr.$observe(key, function(newValue, oldValue) {
            scope[key] = $sce.trustAsHtml(newValue);
          });
        });

        // Support scope as an object
        attr.bsModal && scope.$watch(attr.bsModal, function(newValue, oldValue) {
          if(angular.isObject(newValue)) {
            angular.extend(scope, newValue);
          } else {
            scope.content = newValue;
          }
        }, true);

        // Initialize modal
        var modal = $modal(options);

        // Trigger
        element.on(attr.trigger || 'click', modal.toggle);

        // Garbage collection
        scope.$on('$destroy', function() {
          modal.destroy();
          options = null;
          modal = null;
        });

      }
    };

  });
<|endoftext|>


<commit_before>import moment from 'moment'
const today = new moment()

export default [
	{
		start_date: {
			year: 2015,
			month: 6,
			day: 16
		},
		end_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		text: {
			headline: "2016 Presidential Campaign"
		},
	},
	{
		start_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		end_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		text: {
			headline: "Transition Period"
		},
	},
	{
		start_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		end_date: {
			year: today.year(),
			month: today.month() + 1,
			day: today.day() + 1
		},
		text: {
			headline: "Trump Administration"
		}
	}
]
<commit_msg>Fix administration era end day<commit_after>import moment from 'moment'
const today = new moment()

export default [
	{
		start_date: {
			year: 2015,
			month: 6,
			day: 16
		},
		end_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		text: {
			headline: "2016 Presidential Campaign"
		},
	},
	{
		start_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		end_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		text: {
			headline: "Transition Period"
		},
	},
	{
		start_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		end_date: {
			year: today.year(),
			month: today.month() + 1,
			day: today.date() + 1
		},
		text: {
			headline: "Trump Administration"
		}
	}
]
<|endoftext|>


<commit_before>var R = require('ramda');
var bookshelf = require('../common/bookshelf_init');
var mergeExtents = require('../common/merge_extents.js');
var sumCheck = require('../badges/sum_check');
var dateSequentialCheck = require('../badges/date_check_sequential');
var dateTotalCheck = require('../badges/date_check_total.js');
var Badge = require('./Badge.js');
var Promise = require('bluebird');

require('./Changeset');
require('./Badge');
require('./Hashtag');

// Returns User model
var User = bookshelf.Model.extend({
  jsonColumns: ['geo_extent'],
  tableName: 'users',

  changesets: function () {
    return this.hasMany('Changeset');
  },
  badges: function () {
    return this.belongsToMany('Badge', 'badges_users');
  },
  getNumCountries: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    // Subquery to find the relevant changesets
    var subquery = qb.select('id')
    .from('changesets')
    .where('user_id', this.attributes.id);

    // Get the country ids from the junction table
    return qb.select('country_id')
    .from('changesets_countries')
    .where('changeset_id', 'in', subquery)
    .then(function (results) {
      // Pluck the country_id from results and check uniqueness
      var ids = R.uniq(R.map(R.prop('country_id'), results));
      return ids.length;
    });
  },
  getHashtags: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    // Subquery to find the relevant changesets
    var subquery = qb.select('id')
    .from('changesets')
    .where('user_id', this.attributes.id);

    // Get the hashtags by joining on the junction table
    return qb.select('hashtag')
    .from('changesets_hashtags')
    .join('hashtags', 'changesets_hashtags.hashtag_id', 'hashtags.id')
    .where('changeset_id', 'in', subquery)
    .then(function (results) {
      return R.uniq(R.map(R.prop('hashtag'), results));
    });
  },
  getTimestamps: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    return qb.select('created_at')
    .from('changesets')
    .where('user_id', this.attributes.id)
    .then(function (results) {
      return R.map(R.prop('created_at'), results);
    });
  },
  updateUserMetrics: function (metrics, newExtent, transaction) {
    var user = this;
    var opts = {method: 'update'};
    var editor = metrics.editor;
    metrics = metrics.metrics;
    var jsomEdit = function (editorStr) {
      return editorStr.toLowerCase().match(/^josm/) ? 1 : 0;
    };
    if (transaction) {
      opts.transacting = transaction;
    }
    return user.save({
      geo_extent:
        mergeExtents(user.attributes.geo_extent, newExtent),
      total_road_count_add:
        Number(user.attributes.total_road_count_add) + Number(metrics.road_count),
      total_road_count_mod:
        Number(user.attributes.total_road_count_mod) + Number(metrics.road_count_mod),
      total_building_count_add:
        Number(user.attributes.total_building_count_add) + Number(metrics.building_count),
      total_building_count_mod:
        Number(user.attributes.total_building_count_mod) + Number(metrics.building_count_mod),
      total_waterway_count_add:
        Number(user.attributes.total_waterway_count_add) + Number(metrics.waterway_count),
      total_poi_count_add:
        Number(user.attributes.total_poi_count_add) + Number(metrics.poi_count),
      // GPS trace not yet implemented
      total_gpstrace_count_add:
        0,
      total_road_km_add:
        Number(user.attributes.total_road_km_add) + Number(metrics.road_km),
      total_road_km_mod:
        Number(user.attributes.total_road_km_mod) + Number(metrics.road_km_mod),
      total_waterway_km_add:
        Number(user.attributes.total_waterway_km_add) + Number(metrics.waterway_km),
      // GPS trace not yet implemented
      total_gpstrace_km_add:
        0,
      total_josm_edit_count:
         Number(user.attributes.total_josm_edit_count) + jsomEdit(editor)
    }, opts);
  },
  updateBadges: function (metrics, transaction) {
    var user = this;
    var opts = {};
    if (transaction) {
      opts.transacting = transaction;
    }
    var sumBadges = sumCheck({
      // note: road counts and road mod counts were removed
      // roads: user.attributes.total_road_count_add,
      // roadMods: user.attributes.total_road_count_mod,
      buildings: user.attributes.total_building_count_add,
      pois: user.attributes.total_poi_count_add,
      roadKms: user.attributes.total_road_km_add,
      roadKmMods: user.attributes.total_road_km_mod,
      waterways: user.attributes.total_waterway_km_add,
      josm: user.attributes.total_josm_edit_count
    });
    var consistencyBadge = dateSequentialCheck(metrics.timestamps);
    var historyBadge = dateTotalCheck(metrics.timestamps);
    var earnedBadges = R.mergeAll([
      sumBadges, consistencyBadge, historyBadge
    ]);

    var pickerFunction = R.pick(['category', 'level']);
    var pickFromArray = R.map(pickerFunction);

    var currentBadges = user.related('badges').toJSON();
    var earnedBadgeLevels = pickFromArray(R.values(earnedBadges));
    var currentBadgeLevels = pickFromArray(currentBadges);
    var newBadges = R.difference(earnedBadgeLevels, currentBadgeLevels);

    return Promise.map(newBadges, function (badge) {
      return Badge.where({category: badge.category, level: badge.level}).fetch({transacting: transaction})
      .then(function (badge) {
        return user.badges().attach(badge, opts);
      });
    });
  }
}, {
  createUserIfNotExists: function (user, transaction) {
    return User.where({id: user.id}).fetch({withRelated: 'badges', transacting: transaction}).then(function (result) {
      if (!result) {
        return User.forge({
          id: user.id,
          name: user.name,
          geo_extent: user.geo_extent,
          avatar: user.avatar,
          total_road_count_add: 0,
          total_road_count_mod: 0,
          total_building_count_add: 0,
          total_building_count_mod: 0,
          total_waterway_count_add: 0,
          total_poi_count_add: 0,
          // GPS trace not yet implemented
          total_gpstrace_count_add: 0,
          total_road_km_add: 0,
          total_road_km_mod: 0,
          total_waterway_km_add: 0,
          // GPS trace not yet implemented
          total_gpstrace_km_add: 0,
          created_at: new Date(),
          total_josm_edit_count: 0
        }).save(null, {method: 'insert', transacting: transaction});
      } else {
        return result;
      }
    });
  }
});

module.exports = bookshelf.model('User', User);
<commit_msg>spellcheck<commit_after>var R = require('ramda');
var bookshelf = require('../common/bookshelf_init');
var mergeExtents = require('../common/merge_extents.js');
var sumCheck = require('../badges/sum_check');
var dateSequentialCheck = require('../badges/date_check_sequential');
var dateTotalCheck = require('../badges/date_check_total.js');
var Badge = require('./Badge.js');
var Promise = require('bluebird');

require('./Changeset');
require('./Badge');
require('./Hashtag');

// Returns User model
var User = bookshelf.Model.extend({
  jsonColumns: ['geo_extent'],
  tableName: 'users',

  changesets: function () {
    return this.hasMany('Changeset');
  },
  badges: function () {
    return this.belongsToMany('Badge', 'badges_users');
  },
  getNumCountries: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    // Subquery to find the relevant changesets
    var subquery = qb.select('id')
    .from('changesets')
    .where('user_id', this.attributes.id);

    // Get the country ids from the junction table
    return qb.select('country_id')
    .from('changesets_countries')
    .where('changeset_id', 'in', subquery)
    .then(function (results) {
      // Pluck the country_id from results and check uniqueness
      var ids = R.uniq(R.map(R.prop('country_id'), results));
      return ids.length;
    });
  },
  getHashtags: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    // Subquery to find the relevant changesets
    var subquery = qb.select('id')
    .from('changesets')
    .where('user_id', this.attributes.id);

    // Get the hashtags by joining on the junction table
    return qb.select('hashtag')
    .from('changesets_hashtags')
    .join('hashtags', 'changesets_hashtags.hashtag_id', 'hashtags.id')
    .where('changeset_id', 'in', subquery)
    .then(function (results) {
      return R.uniq(R.map(R.prop('hashtag'), results));
    });
  },
  getTimestamps: function (trx) {
    // Check if we're in a transaction
    var qb = trx || bookshelf.knex;

    return qb.select('created_at')
    .from('changesets')
    .where('user_id', this.attributes.id)
    .then(function (results) {
      return R.map(R.prop('created_at'), results);
    });
  },
  updateUserMetrics: function (metrics, newExtent, transaction) {
    var user = this;
    var opts = {method: 'update'};
    var editor = metrics.editor;
    metrics = metrics.metrics;
    var josmEdit = function (editorStr) {
      return editorStr.toLowerCase().match(/^josm/) ? 1 : 0;
    };
    if (transaction) {
      opts.transacting = transaction;
    }
    return user.save({
      geo_extent:
        mergeExtents(user.attributes.geo_extent, newExtent),
      total_road_count_add:
        Number(user.attributes.total_road_count_add) + Number(metrics.road_count),
      total_road_count_mod:
        Number(user.attributes.total_road_count_mod) + Number(metrics.road_count_mod),
      total_building_count_add:
        Number(user.attributes.total_building_count_add) + Number(metrics.building_count),
      total_building_count_mod:
        Number(user.attributes.total_building_count_mod) + Number(metrics.building_count_mod),
      total_waterway_count_add:
        Number(user.attributes.total_waterway_count_add) + Number(metrics.waterway_count),
      total_poi_count_add:
        Number(user.attributes.total_poi_count_add) + Number(metrics.poi_count),
      // GPS trace not yet implemented
      total_gpstrace_count_add:
        0,
      total_road_km_add:
        Number(user.attributes.total_road_km_add) + Number(metrics.road_km),
      total_road_km_mod:
        Number(user.attributes.total_road_km_mod) + Number(metrics.road_km_mod),
      total_waterway_km_add:
        Number(user.attributes.total_waterway_km_add) + Number(metrics.waterway_km),
      // GPS trace not yet implemented
      total_gpstrace_km_add:
        0,
      total_josm_edit_count:
         Number(user.attributes.total_josm_edit_count) + josmEdit(editor)
    }, opts);
  },
  updateBadges: function (metrics, transaction) {
    var user = this;
    var opts = {};
    if (transaction) {
      opts.transacting = transaction;
    }
    var sumBadges = sumCheck({
      // note: road counts and road mod counts were removed
      // roads: user.attributes.total_road_count_add,
      // roadMods: user.attributes.total_road_count_mod,
      buildings: user.attributes.total_building_count_add,
      pois: user.attributes.total_poi_count_add,
      roadKms: user.attributes.total_road_km_add,
      roadKmMods: user.attributes.total_road_km_mod,
      waterways: user.attributes.total_waterway_km_add,
      josm: user.attributes.total_josm_edit_count
    });
    var consistencyBadge = dateSequentialCheck(metrics.timestamps);
    var historyBadge = dateTotalCheck(metrics.timestamps);
    var earnedBadges = R.mergeAll([
      sumBadges, consistencyBadge, historyBadge
    ]);

    var pickerFunction = R.pick(['category', 'level']);
    var pickFromArray = R.map(pickerFunction);

    var currentBadges = user.related('badges').toJSON();
    var earnedBadgeLevels = pickFromArray(R.values(earnedBadges));
    var currentBadgeLevels = pickFromArray(currentBadges);
    var newBadges = R.difference(earnedBadgeLevels, currentBadgeLevels);

    return Promise.map(newBadges, function (badge) {
      return Badge.where({category: badge.category, level: badge.level}).fetch({transacting: transaction})
      .then(function (badge) {
        return user.badges().attach(badge, opts);
      });
    });
  }
}, {
  createUserIfNotExists: function (user, transaction) {
    return User.where({id: user.id}).fetch({withRelated: 'badges', transacting: transaction}).then(function (result) {
      if (!result) {
        return User.forge({
          id: user.id,
          name: user.name,
          geo_extent: user.geo_extent,
          avatar: user.avatar,
          total_road_count_add: 0,
          total_road_count_mod: 0,
          total_building_count_add: 0,
          total_building_count_mod: 0,
          total_waterway_count_add: 0,
          total_poi_count_add: 0,
          // GPS trace not yet implemented
          total_gpstrace_count_add: 0,
          total_road_km_add: 0,
          total_road_km_mod: 0,
          total_waterway_km_add: 0,
          // GPS trace not yet implemented
          total_gpstrace_km_add: 0,
          created_at: new Date(),
          total_josm_edit_count: 0
        }).save(null, {method: 'insert', transacting: transaction});
      } else {
        return result;
      }
    });
  }
});

module.exports = bookshelf.model('User', User);
<|endoftext|>


<commit_before>class World {

	constructor(width, height) {
		this.width = width;
		this.height = height;
		this.entities = [];	// entities in this world
		this.killents = [];	// entities that are to be removed from this world
		this.scoreCallback = function(){};

		this.time = new Date().getTime();
	}

	think() {
		// update time
		var now = new Date().getTime();
		this.timedelta = (now - this.time) * 0.001;
		this.time = now;

		// remove entities marked for removal
		for(let i = 0; i < this.killents.length; ++i) {
			let idx = this.entities.indexOf(this.killents[i]);
			if(idx >= 0) {
				this.entities.splice(idx, 1);
			}
		}
		this.killents = [];

		// ensure all remaining entities think
		for(let i = 0; i < this.entities.length; ++i) {
			let ent = this.entities[i];
			if("think" in ent) {
				this.entities[i].think();
			}
		}
	}

	findRadius(ent, radius, type) {
		var radius_squared = radius * radius;
		var result = [];
		var x = ent.x;
		var y = ent.y;

		for(let i = 0; i < this.entities.length; ++i) {
			let e2 = this.entities[i];
			if(e2 === ent || (type && e2.type != type)) {
				continue;
			}
			let xdelta = x - e2.x;
			let ydelta = y - e2.y;
			let dist_squared = (xdelta*xdelta)+(ydelta*ydelta);
			if(dist_squared <= radius_squared) {
				result.push(e2);
			}
		}

		return result;
	}

	findClosest(ent, radius, type) {
		var radius_squared = radius * radius;
		var result = null;
		var best = radius_squared + 9;
		var x = ent.x;
		var y = ent.y;

		for(let i = 0; i < this.entities.length; ++i) {
			let e2 = this.entities[i];
			if(e2 === ent || (type && e2.type != type)) {
				continue;
			}

			let xdelta = x - e2.x;
			let ydelta = y - e2.y;
			let dist_squared = (xdelta*xdelta)+(ydelta*ydelta);
			if(dist_squared <= radius_squared && dist_squared < best) {
				result = e2;
				best = dist_squared;
			}
		}

		return result;
	}

	findType(type) {
		var result = [];
		for(var i = 0; i < this.entities.length; ++i) {
			let ent = this.entities[i];
			if(ent.type === type) {
				result.push(ent);
			}
		}

		return result;
	}

	getNetMsg() {
		var msg = [];
		for(let i = 0; i < this.entities.length; ++i) {
			let ent = this.entities[i];
			if("getNetMsg" in ent) {
				msg.push(ent.getNetMsg());
			}
		}
		return msg;
	}

	add(ent) {
		if(this.entities.indexOf(ent) < 0) {
			this.entities.push(ent);
		}
	}

	remove(ent) {
		if(this.killents.indexOf(ent) < 0) {
			this.killents.push(ent);
		}
	}

	setScoreCallback(callback) {
		this.scoreCallback = callback;
	}

	score(points, player, other) {
		this.scoreCallback(points, player, other);
	}
}

class MovingEntity {

	updateAngle(direction, rotspeed) {
		var angle = this.angle;
		angle += direction * (rotspeed * this.world.timedelta);
		angle = angle < -Math.PI ? angle + (2 * Math.PI) : angle;
		angle = angle > Math.PI ? angle - (2 * Math.PI) : angle;
		this.angle = angle;
	}

	updatePosition(speed) {
		var x = this.x;
		var y = this.y;
		x += this.world.timedelta * (Math.cos(this.angle) * speed);
		y -= this.world.timedelta * (Math.sin(this.angle) * speed);

		// clamp position to world dimensions
		var width = this.world.width;
		var height = this.world.height;
		x = x < 0 ? x + width : x;
		x = x > width ? x - width : x;

		y = y < 0 ? y + height : y;
		y = y > height ? y - height : y;

		this.x = x;
		this.y = y;
	}
}

class Plane extends MovingEntity {

	constructor(world, x, y, player) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.player = player;
		this.type = 0; // this is a plane!
		this.health = 100;

		this.angle = 0;	 			// plane is horizontal, flies right ->
		this.dir = -1;				// 1: turn counter-clockwise, 0: don't turn, -1: turn clockwise

		this.fire = false;
		this.lastfire = 0;			// time of firing last
		this.firedelay = 700;

		this.boost = false;
		this.boostfuel = 100;
		this.boostrecovery = 10; 	// boost fuel recovered per second
		this.boostconsumption = 40;	// boost fuel consumed per second

		this.brake = false;

		this.recovertime = 0;
		this.recoverdelay = 1333;

		this.multishottime = 0;
		this.multishotduration = 15000;
		this.multishottype = 0;

		this.rapidfiretime = 0;
		this.rapidfireduration = 20000;
	}

	// Quake-style think function :)
	think() {
		var timedelta = this.world.timedelta;

		var direction = this.dir;
		var rotspeed = 1.9;
		var speed = 100; 			// pixels per second!

		if(this.brake) {
			speed -= 40;
			rotspeed *= 1.2;
		}

		if(this.boost) {
			this.boostfuel -= this.boostconsumption * timedelta;
			if(this.boostfuel > 0) {
				speed += 75;
				rotspeed *= 1.2;
			}
		} else {
			this.boostfuel += this.boostrecovery * timedelta;
		}
		this.boostfuel = Math.min(100, Math.max(0, this.boostfuel));

		// rotate if dead
		if(this.health <= 0) {
			direction = 1;
			rotspeed = 15;
			speed = 0;
			if(this.world.time > this.recovertime) {
				this.health = 100;
			}
		}

		this.updateAngle(direction, rotspeed);
		this.updatePosition(speed);

		var rapidfire = this.world.time < this.rapidfiretime;

		// fire if ready
		if(this.fire && this.health > 0 && this.world.time > this.lastfire + (this.firedelay * (rapidfire ? 0.33 : 1.0))) {
			this.doFire();
		}
	}

	doFire() {
		this.lastfire = this.world.time;
		new Bullet(this.world, this.x, this.y, this.angle, this.player);
		if(this.multishottime > this.world.time) {
			if(this.multishottype == 0) {
				new Bullet(this.world, this.x, this.y, this.angle - 0.5 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + 0.5 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + Math.PI, this.player);
			} else {
				new Bullet(this.world, this.x, this.y, this.angle - 0.125 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + 0.125 * Math.PI, this.player);
			}
		}
		new Sound(this.world, 0); // Firing sound
	}

	receiveDamage(dmg) {
		this.health -= dmg;
		if(this.health <= 0) {
			this.recovertime = this.world.time + this.recoverdelay;
		}
	}

	setDir(newdir) {
		this.dir = newdir;
		this.dir = this.dir < -1 ? -1 : this.dir;
		this.dir = this.dir > 1 ? 1 : this.dir;
	}

	setFire(newfire) {
		this.fire = newfire;
	}

	setBoost(newboost) {
		this.boost = newboost;
	}

	setBrake(newbrake) {
		this.brake = newbrake;
	}

	enableMultiShot(type) {
		this.multishottype = type;
		this.multishottime = this.world.time + this.multishotduration;
	}

	enableRapidFire() {
		this.rapidfiretime = this.world.time + this.rapidfireduration;
	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.player|0),
			(this.x|0),
			(this.y|0),
			((this.angle * 200)|0)
		];
	}
}

class Bullet extends MovingEntity{

	constructor(world, x, y, angle, player) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.player = player;
		this.angle = angle;
		this.type = 1;			 // this is a bullet!

		this.speed = 300; 		// pixels per second!
		this.lifeTime = 700;	// milliseconds before removal
		this.endTime = this.world.time + this.lifeTime;
	}

	// Quake-style think function :)
	think() {
		this.updatePosition(this.speed);

		// find close planes!
		var other = this.world.findClosest(this, 20, 0);
		if(other && other.player != this.player && other.health > 0) {
			other.receiveDamage(200);
			new Sound(this.world, 1); // Hit sound
			this.world.score(1, this.player, other.player);
			this.world.remove(this);
		}

		// this bullet expired
		if(this.world.time > this.endTime) {
			this.world.remove(this);
		}
	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.x|0),
			(this.y|0)
		];
	}
}

class Bonus extends MovingEntity {

	constructor(world, x, y) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.type = 3; // this is a bonus item

		this.angle = Math.random() * 2.0;
		this.speed = 40;
		this.expiretime = this.world.time + 10000;
	}

	think() {
		this.updatePosition(this.speed);

		// find close planes!
		var other = this.world.findClosest(this, 20, 0);
		if(other && other.health > 0) {
			var r = Math.random();
			if(r < 0.33) {
				other.enableMultiShot(0);
			} else if(r < 0.66) {
				other.enableMultiShot(1);
			} else {
				other.enableRapidFire();
			}
			new Sound(this.world, 3); // Bonus pickup
			this.world.remove(this);
		}

		if(this.world.time > this.expiretime) {
			this.world.remove(this);
		}

	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.x|0),
			(this.y|0)
		];
	}
}

class BonusSpawner {

	constructor(world) {
		this.world = world;
		this.world.add(this);
		this.nextspawn = 0;
	}

	think() {
		if(this.world.time < this.nextspawn) return;
		this.nextspawn = this.world.time + 15000 + Math.random() * 45000;

		// find planes
		var planes = this.world.findType(0);
		if(planes.length > 0) {
			new Bonus(this.world, this.world.width * Math.random(), this.world.height * Math.random());
			new Sound(this.world, 2); // Bonus spawn sound
		}
	}
}

class Bot {

	constructor(world, plane) {
		this.world = world;
		this.world.add(this);
		this.plane = plane;

		this.nextTurn = this.world.time;
		this.nextTarget = this.world.time;
		this.target = null;
	}

	think() {
		if(this.world.time > this.nextTarget) {
			// find close plane
			this.target = this.world.findClosest(this.plane, 200, 0);
			this.nextTarget = this.world.time + 100;
		}

		if(this.target) {
			// Arguments for Math.atan2 are "backwards": atan2(y, x)!
			// Also note that the y-axis is "downwards", the larger the "more down"
			var angle2 = Math.atan2(this.plane.y - this.target.y, this.target.x - this.plane.x)
			var diff = this.plane.angle - angle2;
			this.plane.setFire(Math.abs(diff) < 0.5);
		} else {
			this.plane.setFire(false);
			if(this.world.time > this.nextTurn) {
				var r = Math.random();
				var dir = 0;
				if(r < 0.4) {
					dir = 1;
				} else if(r > 0.6) {
					dir = -1;
				}
				this.plane.setDir(dir);
				this.nextTurn = this.world.time + (Math.random() * 2000);
			}
		}
	}
}

class Sound {

	constructor(world, sound) {
		this.world = world;
		this.world.add(this);
		this.sound = sound;
		this.type = 2; // this is a sound effect
	}

	getNetMsg() {
		var msg = [this.type, this.sound];
		this.world.remove(this);
		return msg;
	}
}

module.exports = {
	"FlyWorld" : World,
	"FlyPlane" : Plane,
	"FlyBullet": Bullet,
	"FlyBonus" : Bonus,
	"FlyBonusSpawner" : BonusSpawner,
	"FlySound" : Sound,
	"FlyBot"   : Bot
}
<commit_msg>further cleanup, use ES6-style loops<commit_after>class World {

	constructor(width, height) {
		this.width = width;
		this.height = height;
		this.entities = [];	// entities in this world
		this.killents = [];	// entities that are to be removed from this world
		this.scoreCallback = function(){};

		this.time = new Date().getTime();
	}

	think() {
		// update time
		var now = new Date().getTime();
		this.timedelta = (now - this.time) * 0.001;
		this.time = now;

		// remove entities marked for removal
		for(let kent of this.killents) {
			let idx = this.entities.indexOf(kent);
			if(idx >= 0) {
				this.entities.splice(idx, 1);
			}
		}
		// clear array of entities to be removed
		this.killents.length = 0;

		// ensure all remaining entities think
		for(let ent of this.entities) {
			if("think" in ent) {
				ent.think();
			}
		}
	}

	findRadius(ent, radius, type) {
		var radius_squared = radius * radius;
		var result = [];
		var x = ent.x;
		var y = ent.y;

		for(let e2 of this.entities) {
			if(e2 === ent || (type && e2.type != type)) {
				continue;
			}
			let xdelta = x - e2.x;
			let ydelta = y - e2.y;
			let dist_squared = (xdelta*xdelta)+(ydelta*ydelta);
			if(dist_squared <= radius_squared) {
				result.push(e2);
			}
		}

		return result;
	}

	findClosest(ent, radius, type) {
		var radius_squared = radius * radius;
		var result = null;
		var best = radius_squared + 9;
		var x = ent.x;
		var y = ent.y;

		for(let e2 of this.entities) {
			if(e2 === ent || (type && e2.type != type)) {
				continue;
			}

			let xdelta = x - e2.x;
			let ydelta = y - e2.y;
			let dist_squared = (xdelta*xdelta)+(ydelta*ydelta);
			if(dist_squared <= radius_squared && dist_squared < best) {
				result = e2;
				best = dist_squared;
			}
		}

		return result;
	}

	findType(type) {
		var result = [];
		for(let ent of this.entities) {
			if(ent.type === type) {
				result.push(ent);
			}
		}
		return result;
	}

	getNetMsg() {
		var msg = [];
		for(let ent of this.entities) {
			if("getNetMsg" in ent) {
				msg.push(ent.getNetMsg());
			}
		}
		return msg;
	}

	add(ent) {
		if(this.entities.indexOf(ent) < 0) {
			this.entities.push(ent);
		}
	}

	remove(ent) {
		if(this.killents.indexOf(ent) < 0) {
			this.killents.push(ent);
		}
	}

	setScoreCallback(callback) {
		this.scoreCallback = callback;
	}

	score(points, player, other) {
		this.scoreCallback(points, player, other);
	}
}

class MovingEntity {

	updateAngle(direction, rotspeed) {
		var angle = this.angle;
		angle += direction * (rotspeed * this.world.timedelta);
		angle = angle < -Math.PI ? angle + (2 * Math.PI) : angle;
		angle = angle > Math.PI ? angle - (2 * Math.PI) : angle;
		this.angle = angle;
	}

	updatePosition(speed) {
		var x = this.x;
		var y = this.y;
		x += this.world.timedelta * (Math.cos(this.angle) * speed);
		y -= this.world.timedelta * (Math.sin(this.angle) * speed);

		// clamp position to world dimensions
		var width = this.world.width;
		var height = this.world.height;
		x = x < 0 ? x + width : x;
		x = x > width ? x - width : x;

		y = y < 0 ? y + height : y;
		y = y > height ? y - height : y;

		this.x = x;
		this.y = y;
	}
}

class Plane extends MovingEntity {

	constructor(world, x, y, player) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.player = player;
		this.type = 0; // this is a plane!
		this.health = 100;

		this.angle = 0;	 			// plane is horizontal, flies right ->
		this.dir = -1;				// 1: turn counter-clockwise, 0: don't turn, -1: turn clockwise

		this.fire = false;
		this.lastfire = 0;			// time of firing last
		this.firedelay = 700;

		this.boost = false;
		this.boostfuel = 100;
		this.boostrecovery = 10; 	// boost fuel recovered per second
		this.boostconsumption = 40;	// boost fuel consumed per second

		this.brake = false;

		this.recovertime = 0;
		this.recoverdelay = 1333;

		this.multishottime = 0;
		this.multishotduration = 15000;
		this.multishottype = 0;

		this.rapidfiretime = 0;
		this.rapidfireduration = 20000;
	}

	// Quake-style think function :)
	think() {
		var timedelta = this.world.timedelta;

		var direction = this.dir;
		var rotspeed = 1.9;
		var speed = 100; 			// pixels per second!

		if(this.brake) {
			speed -= 40;
			rotspeed *= 1.2;
		}

		if(this.boost) {
			this.boostfuel -= this.boostconsumption * timedelta;
			if(this.boostfuel > 0) {
				speed += 75;
				rotspeed *= 1.2;
			}
		} else {
			this.boostfuel += this.boostrecovery * timedelta;
		}
		this.boostfuel = Math.min(100, Math.max(0, this.boostfuel));

		// rotate if dead
		if(this.health <= 0) {
			direction = 1;
			rotspeed = 15;
			speed = 0;
			if(this.world.time > this.recovertime) {
				this.health = 100;
			}
		}

		this.updateAngle(direction, rotspeed);
		this.updatePosition(speed);

		var rapidfire = this.world.time < this.rapidfiretime;

		// fire if ready
		if(this.fire && this.health > 0 && this.world.time > this.lastfire + (this.firedelay * (rapidfire ? 0.33 : 1.0))) {
			this.doFire();
		}
	}

	doFire() {
		this.lastfire = this.world.time;
		new Bullet(this.world, this.x, this.y, this.angle, this.player);
		if(this.multishottime > this.world.time) {
			if(this.multishottype == 0) {
				new Bullet(this.world, this.x, this.y, this.angle - 0.5 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + 0.5 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + Math.PI, this.player);
			} else {
				new Bullet(this.world, this.x, this.y, this.angle - 0.125 * Math.PI, this.player);
				new Bullet(this.world, this.x, this.y, this.angle + 0.125 * Math.PI, this.player);
			}
		}
		new Sound(this.world, 0); // Firing sound
	}

	receiveDamage(dmg) {
		this.health -= dmg;
		if(this.health <= 0) {
			this.recovertime = this.world.time + this.recoverdelay;
		}
	}

	setDir(newdir) {
		this.dir = newdir;
		this.dir = this.dir < -1 ? -1 : this.dir;
		this.dir = this.dir > 1 ? 1 : this.dir;
	}

	setFire(newfire) {
		this.fire = newfire;
	}

	setBoost(newboost) {
		this.boost = newboost;
	}

	setBrake(newbrake) {
		this.brake = newbrake;
	}

	enableMultiShot(type) {
		this.multishottype = type;
		this.multishottime = this.world.time + this.multishotduration;
	}

	enableRapidFire() {
		this.rapidfiretime = this.world.time + this.rapidfireduration;
	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.player|0),
			(this.x|0),
			(this.y|0),
			((this.angle * 200)|0)
		];
	}
}

class Bullet extends MovingEntity{

	constructor(world, x, y, angle, player) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.player = player;
		this.angle = angle;
		this.type = 1;			 // this is a bullet!

		this.speed = 300; 		// pixels per second!
		this.lifeTime = 700;	// milliseconds before removal
		this.endTime = this.world.time + this.lifeTime;
	}

	// Quake-style think function :)
	think() {
		this.updatePosition(this.speed);

		// find close planes!
		var other = this.world.findClosest(this, 20, 0);
		if(other && other.player != this.player && other.health > 0) {
			other.receiveDamage(200);
			new Sound(this.world, 1); // Hit sound
			this.world.score(1, this.player, other.player);
			this.world.remove(this);
		}

		// this bullet expired
		if(this.world.time > this.endTime) {
			this.world.remove(this);
		}
	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.x|0),
			(this.y|0)
		];
	}
}

class Bonus extends MovingEntity {

	constructor(world, x, y) {
		super();
		this.world = world;
		this.world.add(this);
		this.x = x;
		this.y = y;
		this.type = 3; // this is a bonus item

		this.angle = Math.random() * 2.0;
		this.speed = 40;
		this.expiretime = this.world.time + 10000;
	}

	think() {
		this.updatePosition(this.speed);

		// find close planes!
		var other = this.world.findClosest(this, 20, 0);
		if(other && other.health > 0) {
			var r = Math.random();
			if(r < 0.33) {
				other.enableMultiShot(0);
			} else if(r < 0.66) {
				other.enableMultiShot(1);
			} else {
				other.enableRapidFire();
			}
			new Sound(this.world, 3); // Bonus pickup
			this.world.remove(this);
		}

		if(this.world.time > this.expiretime) {
			this.world.remove(this);
		}

	}

	getNetMsg() {
		return [
			(this.type|0),
			(this.x|0),
			(this.y|0)
		];
	}
}

class BonusSpawner {

	constructor(world) {
		this.world = world;
		this.world.add(this);
		this.nextspawn = 0;
	}

	think() {
		if(this.world.time < this.nextspawn) return;
		this.nextspawn = this.world.time + 15000 + Math.random() * 45000;

		// find planes
		var planes = this.world.findType(0);
		if(planes.length > 0) {
			new Bonus(this.world, this.world.width * Math.random(), this.world.height * Math.random());
			new Sound(this.world, 2); // Bonus spawn sound
		}
	}
}

class Bot {

	constructor(world, plane) {
		this.world = world;
		this.world.add(this);
		this.plane = plane;

		this.nextTurn = this.world.time;
		this.nextTarget = this.world.time;
		this.target = null;
	}

	think() {
		if(this.world.time > this.nextTarget) {
			// find close plane
			this.target = this.world.findClosest(this.plane, 200, 0);
			this.nextTarget = this.world.time + 100;
		}

		if(this.target) {
			// Arguments for Math.atan2 are "backwards": atan2(y, x)!
			// Also note that the y-axis is "downwards", the larger the "more down"
			var angle2 = Math.atan2(this.plane.y - this.target.y, this.target.x - this.plane.x)
			var diff = this.plane.angle - angle2;
			this.plane.setFire(Math.abs(diff) < 0.5);
		} else {
			this.plane.setFire(false);
			if(this.world.time > this.nextTurn) {
				var r = Math.random();
				var dir = 0;
				if(r < 0.4) {
					dir = 1;
				} else if(r > 0.6) {
					dir = -1;
				}
				this.plane.setDir(dir);
				this.nextTurn = this.world.time + (Math.random() * 2000);
			}
		}
	}
}

class Sound {

	constructor(world, sound) {
		this.world = world;
		this.world.add(this);
		this.sound = sound;
		this.type = 2; // this is a sound effect
	}

	getNetMsg() {
		var msg = [this.type, this.sound];
		this.world.remove(this);
		return msg;
	}
}

module.exports = {
	"FlyWorld" : World,
	"FlyPlane" : Plane,
	"FlyBullet": Bullet,
	"FlyBonus" : Bonus,
	"FlyBonusSpawner" : BonusSpawner,
	"FlySound" : Sound,
	"FlyBot"   : Bot
}
<|endoftext|>