<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var wordOperators = wordRegexp(["and", "or", "not", "is", "in"]);
  var commonKeywords = ["as", "assert", "break", "class", "continue",
                        "def", "del", "elif", "else", "except", "finally",
                        "for", "from", "global", "if", "import",
                        "lambda", "pass", "raise", "return",
                        "try", "while", "with", "yield"];
  var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
                        "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
                        "enumerate", "eval", "filter", "float", "format", "frozenset",
                        "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
                        "input", "int", "isinstance", "issubclass", "iter", "len",
                        "list", "locals", "map", "max", "memoryview", "min", "next",
                        "object", "oct", "open", "ord", "pow", "property", "range",
                        "repr", "reversed", "round", "set", "setattr", "slice",
                        "sorted", "staticmethod", "str", "sum", "super", "tuple",
                        "type", "vars", "zip", "__import__", "NotImplemented",
                        "Ellipsis", "__debug__"];
  var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
                        "file", "intern", "long", "raw_input", "reduce", "reload",
                        "unichr", "unicode", "xrange", "False", "True", "None"],
             keywords: ["exec", "print"]};
  var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
             keywords: ["nonlocal", "False", "True", "None"]};

  CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));

  function top(state) {
    return state.scopes[state.scopes.length - 1];
  }

  CodeMirror.defineMode("python", function(conf, parserConf) {
    var ERRORCLASS = "error";

    var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
    var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
    var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
    var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
    var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
    var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;

    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
    if(parserConf.extra_keywords != undefined){
      myKeywords = myKeywords.concat(parserConf.extra_keywords);
    }
    if(parserConf.extra_builtins != undefined){
      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
    }
    if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
      myKeywords = myKeywords.concat(py3.keywords);
      myBuiltins = myBuiltins.concat(py3.builtins);
      var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
    } else {
      myKeywords = myKeywords.concat(py2.keywords);
      myBuiltins = myBuiltins.concat(py2.builtins);
      var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
    }
    var keywords = wordRegexp(myKeywords);
    var builtins = wordRegexp(myBuiltins);

    // tokenizers
    function tokenBase(stream, state) {
      // Handle scope changes
      if (stream.sol() && top(state).type == "py") {
        var scopeOffset = top(state).offset;
        if (stream.eatSpace()) {
          var lineOffset = stream.indentation();
          if (lineOffset > scopeOffset)
            pushScope(stream, state, "py");
          else if (lineOffset < scopeOffset && dedent(stream, state))
            state.errorToken = true;
          return null;
        } else {
          var style = tokenBaseInner(stream, state);
          if (scopeOffset > 0 && dedent(stream, state))
            style += " " + ERRORCLASS;
          return style;
        }
      }
      return tokenBaseInner(stream, state);
    }

    function tokenBaseInner(stream, state) {
      if (stream.eatSpace()) return null;

      var ch = stream.peek();

      // Handle Comments
      if (ch == "#") {
        stream.skipToEnd();
        return "comment";
      }

      // Handle Number Literals
      if (stream.match(/^[0-9\.]/, false)) {
        var floatLiteral = false;
        // Floats
        if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
        if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
        if (floatLiteral) {
          // Float literals may be "imaginary"
          stream.eat(/J/i);
          return "number";
        }
        // Integers
        var intLiteral = false;
        // Hex
        if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
        // Binary
        if (stream.match(/^0b[01]+/i)) intLiteral = true;
        // Octal
        if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
        // Decimal
        if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
          // Decimal literals may be "imaginary"
          stream.eat(/J/i);
          // TODO - Can you have imaginary longs?
          intLiteral = true;
        }
        // Zero by itself with no other piece of number.
        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
        if (intLiteral) {
          // Integer literals may be "long"
          stream.eat(/L/i);
          return "number";
        }
      }

      // Handle Strings
      if (stream.match(stringPrefixes)) {
        state.tokenize = tokenStringFactory(stream.current());
        return state.tokenize(stream, state);
      }

      // Handle operators and Delimiters
      if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
        return null;

      if (stream.match(doubleOperators)
          || stream.match(singleOperators)
          || stream.match(wordOperators))
        return "operator";

      if (stream.match(singleDelimiters))
        return null;

      if (stream.match(keywords))
        return "keyword";

      if (stream.match(builtins))
        return "builtin";

      if (stream.match(/^(self|cls)\b/))
        return "variable-2";

      if (stream.match(identifiers)) {
        if (state.lastToken == "def" || state.lastToken == "class")
          return "def";
        return "variable";
      }

      // Handle non-detected items
      stream.next();
      return ERRORCLASS;
    }

    function tokenStringFactory(delimiter) {
      while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
        delimiter = delimiter.substr(1);

      var singleline = delimiter.length == 1;
      var OUTCLASS = "string";

      function tokenString(stream, state) {
        while (!stream.eol()) {
          stream.eatWhile(/[^'"\\]/);
          if (stream.eat("\\")) {
            stream.next();
            if (singleline && stream.eol())
              return OUTCLASS;
          } else if (stream.match(delimiter)) {
            state.tokenize = tokenBase;
            return OUTCLASS;
          } else {
            stream.eat(/['"]/);
          }
        }
        if (singleline) {
          if (parserConf.singleLineStringErrors)
            return ERRORCLASS;
          else
            state.tokenize = tokenBase;
        }
        return OUTCLASS;
      }
      tokenString.isString = true;
      return tokenString;
    }

    function pushScope(stream, state, type) {
      var offset = 0, align = null;
      if (type == "py") {
        while (top(state).type != "py")
          state.scopes.pop();
      }
      offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
      if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
        align = stream.column() + 1;
      state.scopes.push({offset: offset, type: type, align: align});
    }

    function dedent(stream, state) {
      var indented = stream.indentation();
      while (top(state).offset > indented) {
        if (top(state).type != "py") return true;
        state.scopes.pop();
      }
      return top(state).offset != indented;
    }

    function tokenLexer(stream, state) {
      var style = state.tokenize(stream, state);
      var current = stream.current();

      // Handle '.' connected identifiers
      if (current == ".") {
        style = stream.match(identifiers, false) ? null : ERRORCLASS;
        if (style == null && state.lastStyle == "meta") {
          // Apply 'meta' style to '.' connected identifiers when
          // appropriate.
          style = "meta";
        }
        return style;
      }

      // Handle decorators
      if (current == "@")
        return stream.match(identifiers, false) ? "meta" : ERRORCLASS;

      if ((style == "variable" || style == "builtin")
          && state.lastStyle == "meta")
        style = "meta";

      // Handle scope changes.
      if (current == "pass" || current == "return")
        state.dedent += 1;

      if (current == "lambda") state.lambda = true;
      if (current == ":" && !state.lambda && top(state).type == "py")
        pushScope(stream, state, "py");

      var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
      if (delimiter_index != -1)
        pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));

      delimiter_index = "])}".indexOf(current);
      if (delimiter_index != -1) {
        if (top(state).type == current) state.scopes.pop();
        else return ERRORCLASS;
      }
      if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
        if (state.scopes.length > 1) state.scopes.pop();
        state.dedent -= 1;
      }

      return style;
    }

    var external = {
      startState: function(basecolumn) {
        return {
          tokenize: tokenBase,
          scopes: [{offset: basecolumn || 0, type: "py", align: null}],
          lastStyle: null,
          lastToken: null,
          lambda: false,
          dedent: 0
        };
      },

      token: function(stream, state) {
        var addErr = state.errorToken;
        if (addErr) state.errorToken = false;
        var style = tokenLexer(stream, state);

        state.lastStyle = style;

        var current = stream.current();
        if (current && style)
          state.lastToken = current;

        if (stream.eol() && state.lambda)
          state.lambda = false;
        return addErr ? style + " " + ERRORCLASS : style;
      },

      indent: function(state, textAfter) {
        if (state.tokenize != tokenBase)
          return state.tokenize.isString ? CodeMirror.Pass : 0;

        var scope = top(state);
        var closing = textAfter && textAfter.charAt(0) == scope.type;
        if (scope.align != null)
          return scope.align - (closing ? 1 : 0);
        else if (closing && state.scopes.length > 1)
          return state.scopes[state.scopes.length - 2].offset;
        else
          return scope.offset;
      },

      lineComment: "#",
      fold: "indent"
    };
    return external;
  });

  CodeMirror.defineMIME("text/x-python", "python");

  var words = function(str) { return str.split(" "); };

  CodeMirror.defineMIME("text/x-cython", {
    name: "python",
    extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
                          "extern gil include nogil property public"+
                          "readonly struct union DEF IF ELIF ELSE")
  });

});
<commit_msg>[python mode] Make 'in' a keyword<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var wordOperators = wordRegexp(["and", "or", "not", "is"]);
  var commonKeywords = ["as", "assert", "break", "class", "continue",
                        "def", "del", "elif", "else", "except", "finally",
                        "for", "from", "global", "if", "import",
                        "lambda", "pass", "raise", "return",
                        "try", "while", "with", "yield", "in"];
  var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
                        "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
                        "enumerate", "eval", "filter", "float", "format", "frozenset",
                        "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
                        "input", "int", "isinstance", "issubclass", "iter", "len",
                        "list", "locals", "map", "max", "memoryview", "min", "next",
                        "object", "oct", "open", "ord", "pow", "property", "range",
                        "repr", "reversed", "round", "set", "setattr", "slice",
                        "sorted", "staticmethod", "str", "sum", "super", "tuple",
                        "type", "vars", "zip", "__import__", "NotImplemented",
                        "Ellipsis", "__debug__"];
  var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
                        "file", "intern", "long", "raw_input", "reduce", "reload",
                        "unichr", "unicode", "xrange", "False", "True", "None"],
             keywords: ["exec", "print"]};
  var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
             keywords: ["nonlocal", "False", "True", "None"]};

  CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));

  function top(state) {
    return state.scopes[state.scopes.length - 1];
  }

  CodeMirror.defineMode("python", function(conf, parserConf) {
    var ERRORCLASS = "error";

    var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
    var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
    var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
    var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
    var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
    var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;

    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
    if(parserConf.extra_keywords != undefined){
      myKeywords = myKeywords.concat(parserConf.extra_keywords);
    }
    if(parserConf.extra_builtins != undefined){
      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
    }
    if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
      myKeywords = myKeywords.concat(py3.keywords);
      myBuiltins = myBuiltins.concat(py3.builtins);
      var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
    } else {
      myKeywords = myKeywords.concat(py2.keywords);
      myBuiltins = myBuiltins.concat(py2.builtins);
      var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
    }
    var keywords = wordRegexp(myKeywords);
    var builtins = wordRegexp(myBuiltins);

    // tokenizers
    function tokenBase(stream, state) {
      // Handle scope changes
      if (stream.sol() && top(state).type == "py") {
        var scopeOffset = top(state).offset;
        if (stream.eatSpace()) {
          var lineOffset = stream.indentation();
          if (lineOffset > scopeOffset)
            pushScope(stream, state, "py");
          else if (lineOffset < scopeOffset && dedent(stream, state))
            state.errorToken = true;
          return null;
        } else {
          var style = tokenBaseInner(stream, state);
          if (scopeOffset > 0 && dedent(stream, state))
            style += " " + ERRORCLASS;
          return style;
        }
      }
      return tokenBaseInner(stream, state);
    }

    function tokenBaseInner(stream, state) {
      if (stream.eatSpace()) return null;

      var ch = stream.peek();

      // Handle Comments
      if (ch == "#") {
        stream.skipToEnd();
        return "comment";
      }

      // Handle Number Literals
      if (stream.match(/^[0-9\.]/, false)) {
        var floatLiteral = false;
        // Floats
        if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
        if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
        if (floatLiteral) {
          // Float literals may be "imaginary"
          stream.eat(/J/i);
          return "number";
        }
        // Integers
        var intLiteral = false;
        // Hex
        if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
        // Binary
        if (stream.match(/^0b[01]+/i)) intLiteral = true;
        // Octal
        if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
        // Decimal
        if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
          // Decimal literals may be "imaginary"
          stream.eat(/J/i);
          // TODO - Can you have imaginary longs?
          intLiteral = true;
        }
        // Zero by itself with no other piece of number.
        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
        if (intLiteral) {
          // Integer literals may be "long"
          stream.eat(/L/i);
          return "number";
        }
      }

      // Handle Strings
      if (stream.match(stringPrefixes)) {
        state.tokenize = tokenStringFactory(stream.current());
        return state.tokenize(stream, state);
      }

      // Handle operators and Delimiters
      if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
        return null;

      if (stream.match(doubleOperators)
          || stream.match(singleOperators)
          || stream.match(wordOperators))
        return "operator";

      if (stream.match(singleDelimiters))
        return null;

      if (stream.match(keywords))
        return "keyword";

      if (stream.match(builtins))
        return "builtin";

      if (stream.match(/^(self|cls)\b/))
        return "variable-2";

      if (stream.match(identifiers)) {
        if (state.lastToken == "def" || state.lastToken == "class")
          return "def";
        return "variable";
      }

      // Handle non-detected items
      stream.next();
      return ERRORCLASS;
    }

    function tokenStringFactory(delimiter) {
      while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
        delimiter = delimiter.substr(1);

      var singleline = delimiter.length == 1;
      var OUTCLASS = "string";

      function tokenString(stream, state) {
        while (!stream.eol()) {
          stream.eatWhile(/[^'"\\]/);
          if (stream.eat("\\")) {
            stream.next();
            if (singleline && stream.eol())
              return OUTCLASS;
          } else if (stream.match(delimiter)) {
            state.tokenize = tokenBase;
            return OUTCLASS;
          } else {
            stream.eat(/['"]/);
          }
        }
        if (singleline) {
          if (parserConf.singleLineStringErrors)
            return ERRORCLASS;
          else
            state.tokenize = tokenBase;
        }
        return OUTCLASS;
      }
      tokenString.isString = true;
      return tokenString;
    }

    function pushScope(stream, state, type) {
      var offset = 0, align = null;
      if (type == "py") {
        while (top(state).type != "py")
          state.scopes.pop();
      }
      offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
      if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
        align = stream.column() + 1;
      state.scopes.push({offset: offset, type: type, align: align});
    }

    function dedent(stream, state) {
      var indented = stream.indentation();
      while (top(state).offset > indented) {
        if (top(state).type != "py") return true;
        state.scopes.pop();
      }
      return top(state).offset != indented;
    }

    function tokenLexer(stream, state) {
      var style = state.tokenize(stream, state);
      var current = stream.current();

      // Handle '.' connected identifiers
      if (current == ".") {
        style = stream.match(identifiers, false) ? null : ERRORCLASS;
        if (style == null && state.lastStyle == "meta") {
          // Apply 'meta' style to '.' connected identifiers when
          // appropriate.
          style = "meta";
        }
        return style;
      }

      // Handle decorators
      if (current == "@")
        return stream.match(identifiers, false) ? "meta" : ERRORCLASS;

      if ((style == "variable" || style == "builtin")
          && state.lastStyle == "meta")
        style = "meta";

      // Handle scope changes.
      if (current == "pass" || current == "return")
        state.dedent += 1;

      if (current == "lambda") state.lambda = true;
      if (current == ":" && !state.lambda && top(state).type == "py")
        pushScope(stream, state, "py");

      var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
      if (delimiter_index != -1)
        pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));

      delimiter_index = "])}".indexOf(current);
      if (delimiter_index != -1) {
        if (top(state).type == current) state.scopes.pop();
        else return ERRORCLASS;
      }
      if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
        if (state.scopes.length > 1) state.scopes.pop();
        state.dedent -= 1;
      }

      return style;
    }

    var external = {
      startState: function(basecolumn) {
        return {
          tokenize: tokenBase,
          scopes: [{offset: basecolumn || 0, type: "py", align: null}],
          lastStyle: null,
          lastToken: null,
          lambda: false,
          dedent: 0
        };
      },

      token: function(stream, state) {
        var addErr = state.errorToken;
        if (addErr) state.errorToken = false;
        var style = tokenLexer(stream, state);

        state.lastStyle = style;

        var current = stream.current();
        if (current && style)
          state.lastToken = current;

        if (stream.eol() && state.lambda)
          state.lambda = false;
        return addErr ? style + " " + ERRORCLASS : style;
      },

      indent: function(state, textAfter) {
        if (state.tokenize != tokenBase)
          return state.tokenize.isString ? CodeMirror.Pass : 0;

        var scope = top(state);
        var closing = textAfter && textAfter.charAt(0) == scope.type;
        if (scope.align != null)
          return scope.align - (closing ? 1 : 0);
        else if (closing && state.scopes.length > 1)
          return state.scopes[state.scopes.length - 2].offset;
        else
          return scope.offset;
      },

      lineComment: "#",
      fold: "indent"
    };
    return external;
  });

  CodeMirror.defineMIME("text/x-python", "python");

  var words = function(str) { return str.split(" "); };

  CodeMirror.defineMIME("text/x-cython", {
    name: "python",
    extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
                          "extern gil include nogil property public"+
                          "readonly struct union DEF IF ELIF ELSE")
  });

});
<|endoftext|>


<commit_before>import uniq from "lodash.uniq";
import pick from "lodash.pick";

import presenceTracker from "./presenceTracker";

import Attractor from "./Attractor";
import Cannon from "./Cannon";
import Body from "./Body";

import * as canvasDraw from "./canvasDraw";
import * as constants from "./constants";
import * as pointUtils from "./pointUtils";

function Game() {
    let canvas = document.getElementById("canvas");

    canvas.style.position = "absolute";
    canvas.width = document.body.clientWidth;
    canvas.height = document.body.clientHeight;

    // Get the drawing context.  This contains functions that let you draw to the canvas.
    let screen = canvas.getContext('2d');
    this.screen = screen;
    screen.font = "10pt Arial";

    canvasDraw.setScreen(this.screen);

    // Note down the dimensions of the canvas.  These are used to
    // place game bodies.
    this.size = { x: canvas.width, y: canvas.height };
    this.realSize = { x: constants.toReal(this.size.x), y: constants.toReal(this.size.y) };

    this.deadBodies = [];
    this.bodies = [];
    this.attractors = [];
    this.cannons = [];

    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4), y: constants.toReal(this.size.y / 4) // left left top
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4 * 3) , y: constants.toReal(this.size.y / 4) // left right top
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4), y: constants.toReal(this.size.y / 4 * 3) // left left bottom
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4 * 3), y: constants.toReal(this.size.y / 4 * 3) // right right bottom
    }));

    let spawnBody = (point, isManual = false) => {
        this.addBody(new Body(this, point, isManual));
    };

    let spawnCannon = (point) => {
        let comparePoint = pointUtils.fromReal(point);
        let toPoint = pointUtils.toReal({
            x: comparePoint.x === this.size.x ? 0 : comparePoint.x === 0 ? this.size.x : comparePoint.x,
            y: comparePoint.y === this.size.y ? 0 : comparePoint.y === 0 ? this.size.y : comparePoint.y
        });

        this.cannons.push(new Cannon(this, point, toPoint));
    };

    let currentAttractor;

    canvas.addEventListener("mousedown", (e) => {
        let {clientX, clientY} = e;

        const point = {
            x: constants.toReal(clientX),
            y: constants.toReal(clientY)
        };

        let closestCannon = this.cannonNearPoint(point);

        if (e.ctrlKey) {
            console.log("new cannon");
            spawnCannon(point);
        } else if (e.shiftKey) {
            console.log("new planet");
            let inAttractorData = this.pointInAttractor(point);
            if (inAttractorData) {
                let { attractor } = inAttractorData;

                currentAttractor = attractor;
            } else {
                this.attractors.push(new Attractor(this, point));

            }
        } else if (closestCannon && closestCannon.distance <= constants.toReal(100)) {
            let cannon = closestCannon.cannon;
            cannon.select(point);
        } else {
            console.log("d:", closestCannon && constants.fromReal(closestCannon.distance));
            spawnBody(point, true);
        }
    });

    canvas.addEventListener("mouseup", e => {
        let {clientX, clientY} = e;

        if (currentAttractor) {
            currentAttractor.center = pointUtils.toReal({ x: clientX, y: clientY });
        }

        currentAttractor = null;
    });

    let tick = () => {
        screen.clearRect(0, 0, this.size.x, this.size.y); //allows this.update() to draw vectors
        this.update();
        this.draw();
        requestAnimationFrame(tick);
    };

    this.updatedAt = Date.now();
    requestAnimationFrame(tick);
}

Game.prototype.pointInAttractor = function (point) {
    let distanceTo = attractor => Math.ceil(pointUtils.distanceBetween(point, attractor.center));

    let minumumData = null;

    this.attractors.forEach(attractor => {
        let distance = distanceTo(attractor);
        if (minumumData === null || distance < minumumData.distance) {
            minumumData = { distance, attractor };
        }
    });

    console.log("data", minumumData);

    if (minumumData && minumumData.attractor.radius >= minumumData.distance) {
        return minumumData;
    }

    return null;
};

Game.prototype.cannonNearPoint = function (point) {
    let distanceTo = (cannon) => Math.ceil(pointUtils.distanceBetween(point, cannon.center));

    let distances = this.cannons.map(cannon => distanceTo(cannon));

    let minumumDistance = Math.min.apply(Math, distances);

    let returnVal = null;
    this.cannons.some(cannon => {
        if (distanceTo(cannon) === minumumDistance) {
            returnVal = { cannon, distance: minumumDistance };
        }
    });

    return returnVal;
};

Game.prototype.update = function() {
    const now = Date.now();
    let timeSinceUpdate = (now - this.updatedAt) * constants.TimeScale;
    timeSinceUpdate = Math.min(500, timeSinceUpdate);
    this.updatedAt = now;

    this.attractors.forEach(attractor => attractor.update());

    this.bodies.forEach(body => body.update(timeSinceUpdate)); // Update must run before cleanup of dead bodies
    this.bodies = this.bodies.filter(body => body.isAlive);

    this.cannons.forEach(cannon => cannon.update());
};

Game.prototype.draw = function() {
    let screen = this.screen;

    this.deadBodies.forEach(deadBody => {
        canvasDraw.drawBody(deadBody);
    });

    this.bodies
        // .filter(body => body.isManual)
        .forEach(canvasDraw.drawBody);

    this.attractors.forEach(canvasDraw.drawBody);
    this.cannons.forEach(canvasDraw.drawBody);

    canvasDraw.setColor("black");
    screen.fillText("Bodies: " + this.bodies.length, 1, this.size.y);
};

Game.prototype.addBody = function(body) {
    this.bodies.push(body);
};

window.addEventListener("DOMContentLoaded", () => { global.game = new Game(); });<commit_msg>Attractors should now move dynamically with the mouse (mousemove handler).<commit_after>import uniq from "lodash.uniq";
import pick from "lodash.pick";

import presenceTracker from "./presenceTracker";

import Attractor from "./Attractor";
import Cannon from "./Cannon";
import Body from "./Body";

import * as canvasDraw from "./canvasDraw";
import * as constants from "./constants";
import * as pointUtils from "./pointUtils";

function Game() {
    let canvas = document.getElementById("canvas");

    canvas.style.position = "absolute";
    canvas.width = document.body.clientWidth;
    canvas.height = document.body.clientHeight;

    // Get the drawing context.  This contains functions that let you draw to the canvas.
    let screen = canvas.getContext('2d');
    this.screen = screen;
    screen.font = "10pt Arial";

    canvasDraw.setScreen(this.screen);

    // Note down the dimensions of the canvas.  These are used to
    // place game bodies.
    this.size = { x: canvas.width, y: canvas.height };
    this.realSize = { x: constants.toReal(this.size.x), y: constants.toReal(this.size.y) };

    this.deadBodies = [];
    this.bodies = [];
    this.attractors = [];
    this.cannons = [];

    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4), y: constants.toReal(this.size.y / 4) // left left top
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4 * 3) , y: constants.toReal(this.size.y / 4) // left right top
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4), y: constants.toReal(this.size.y / 4 * 3) // left left bottom
    }));
    this.attractors.push(new Attractor(this, {
        x: constants.toReal(this.size.x / 4 * 3), y: constants.toReal(this.size.y / 4 * 3) // right right bottom
    }));

    let spawnBody = (point, isManual = false) => {
        this.addBody(new Body(this, point, isManual));
    };

    let spawnCannon = (point) => {
        let comparePoint = pointUtils.fromReal(point);
        let toPoint = pointUtils.toReal({
            x: comparePoint.x === this.size.x ? 0 : comparePoint.x === 0 ? this.size.x : comparePoint.x,
            y: comparePoint.y === this.size.y ? 0 : comparePoint.y === 0 ? this.size.y : comparePoint.y
        });

        this.cannons.push(new Cannon(this, point, toPoint));
    };

    let currentAttractor;

    canvas.addEventListener("mousedown", (e) => {
        let {clientX, clientY} = e;

        const point = {
            x: constants.toReal(clientX),
            y: constants.toReal(clientY)
        };

        let closestCannon = this.cannonNearPoint(point);

        if (e.ctrlKey) {
            console.log("new cannon");
            spawnCannon(point);
        } else if (e.shiftKey) {
            console.log("new planet");
            let inAttractorData = this.pointInAttractor(point);
            if (inAttractorData) {
                let { attractor } = inAttractorData;

                currentAttractor = attractor;
            } else {
                this.attractors.push(new Attractor(this, point));

            }
        } else if (closestCannon && closestCannon.distance <= constants.toReal(100)) {
            let cannon = closestCannon.cannon;
            cannon.select(point);
        } else {
            spawnBody(point, true);
        }
    });

    canvas.addEventListener("mousemove", e => {
        let {clientX, clientY} = e;

        if (currentAttractor) {
            currentAttractor.center = pointUtils.toReal({ x: clientX, y: clientY });
        }
    });

    canvas.addEventListener("mouseup", e => {
        let {clientX, clientY} = e;

        if (currentAttractor) {
            currentAttractor.center = pointUtils.toReal({ x: clientX, y: clientY });
        }

        currentAttractor = null;
    });

    let tick = () => {
        screen.clearRect(0, 0, this.size.x, this.size.y); //allows this.update() to draw vectors
        this.update();
        this.draw();
        requestAnimationFrame(tick);
    };

    this.updatedAt = Date.now();
    requestAnimationFrame(tick);
}

Game.prototype.pointInAttractor = function (point) {
    let distanceTo = attractor => Math.ceil(pointUtils.distanceBetween(point, attractor.center));

    let minumumData = null;

    this.attractors.forEach(attractor => {
        let distance = distanceTo(attractor);
        if (minumumData === null || distance < minumumData.distance) {
            minumumData = { distance, attractor };
        }
    });

    if (minumumData && minumumData.attractor.radius >= minumumData.distance) {
        return minumumData;
    }

    return null;
};

Game.prototype.cannonNearPoint = function (point) {
    let distanceTo = (cannon) => Math.ceil(pointUtils.distanceBetween(point, cannon.center));

    let distances = this.cannons.map(cannon => distanceTo(cannon));

    let minumumDistance = Math.min.apply(Math, distances);

    let returnVal = null;
    this.cannons.some(cannon => {
        if (distanceTo(cannon) === minumumDistance) {
            returnVal = { cannon, distance: minumumDistance };
        }
    });

    return returnVal;
};

Game.prototype.update = function() {
    const now = Date.now();
    let timeSinceUpdate = (now - this.updatedAt) * constants.TimeScale;
    timeSinceUpdate = Math.min(500, timeSinceUpdate);
    this.updatedAt = now;

    this.attractors.forEach(attractor => attractor.update());

    this.bodies.forEach(body => body.update(timeSinceUpdate)); // Update must run before cleanup of dead bodies
    this.bodies = this.bodies.filter(body => body.isAlive);

    this.cannons.forEach(cannon => cannon.update());
};

Game.prototype.draw = function() {
    let screen = this.screen;

    this.deadBodies.forEach(deadBody => {
        canvasDraw.drawBody(deadBody);
    });

    this.bodies
        // .filter(body => body.isManual)
        .forEach(canvasDraw.drawBody);

    this.attractors.forEach(canvasDraw.drawBody);
    this.cannons.forEach(canvasDraw.drawBody);

    canvasDraw.setColor("black");
    screen.fillText("Bodies: " + this.bodies.length, 1, this.size.y);
};

Game.prototype.addBody = function(body) {
    this.bodies.push(body);
};

window.addEventListener("DOMContentLoaded", () => { global.game = new Game(); });<|endoftext|>


<commit_before>var gulp = require('gulp');
var del = require('del');
var config = require('../config');
var htmlConfig = require('../config/html');
var iconFontConfig = require('../config/iconFont');

gulp.task('clean', function( cb ) {
    return del([
        config.publicAssets,
        htmlConfig.dest,
        iconFontConfig.sassDest
    ]);
});
<commit_msg>remove iconFon<commit_after>var gulp = require('gulp');
var del = require('del');
var config = require('../config');
var htmlConfig = require('../config/html');

gulp.task('clean', function (cb) {
    return del([
        config.publicAssets,
        htmlConfig.dest
    ]);
});
<|endoftext|>


<commit_before>/**
 * ## Image list
 *
 * Displays images belonging to a given user on a given service
 *
 * ### Example
 *
 *

 *
 * ### Options
 *
 * - `id`: Optional, the id of the user whose images we want to list. By default, it will list the images of the current user.
 * - `format`: Optional, the format of the thumbnail you wish to get. Can be `thumb`, `square`, `small`, `medium`, `large`, `original`. Will map to the closest existing preset when used on external networks. Default:  `small`
 * - `provider`: Optional, service from which we will fetch images. Can be `hull`, `instagram`, or `facebook`, by default it will list images from `hull`.
 * - `limit`: Optional, the number of images to display. Be default it will display 10 images.
 * - `scope` : Optional, a Facebook permission you need to ask the user before being able to show data. - If this permission is not given, a button will be shown to ask for it.
 *
 * ### Template
 *
 * - `images`: Displays the list of the user's images.
 *
 * ### Actions
 *
 * - `authorize`: Pops up a permissions dialog or a login dialog.
 *
 * ### Datasource
 *
 * - `images`: The user's images.
 * - `authorized` : A hash of permissions showing if the user can view the images.
 * Contains `provider`, `permissions` : Booleans showing if the provider and permissions are right,
 * and `provider_name` containing the name of the asked provider
 *
 */
Hull.define(['underscore'], {
  type: 'Hull',

  templates: [
    'images'
  ],

  options: {
    id:'me',
    provider:'hull',
    limit: 10,
    format: 'small',
    scope: 'user_photos'
  },

  refreshEvents: ['model.hull.me.change'],

  initialize: function() {
    this.me = this.sandbox.data.api.model('me');

    this.provider = this.options.provider;

    this.id = this.options.id || "me";
  },

  actions: {
    authorize: function(){
      if(this.provider==="facebook"){
        this.sandbox.login('facebook', {scope:this.options.scope}).then(this.sandbox.util._.bind(function(){
          this.render();
        },this));
      } else {
        this.sandbox.login(this.provider,{}).then(this.sandbox.util._.bind(function(){
          // this.render();
        },this));
      }
    }
  },

  datasources: {
    authorized: function() {
      return this.isAuthorized(this.provider);
    },

    images: function() {

      var deferred = this.sandbox.data.deferred();
      var self = this;

      // map identities by name
      var identities = this.sandbox.util._.reduce(this.me.get('identities'), function(m, i) {
        m[i.provider] = i;
        return m;
      }, {});

      //Are we logged in to provider, or is provider hull. if Provider is hull, are we asking "me" without being loggedin ?
      if( this.loggedIn()[this.provider] || (this.provider==="hull" && (this.loggedIn() || this.id!=="me"))){
        this.request(this.provider, identities, this.options).then(this.sandbox.util._.bind(function(res) {

          var serialized = this.sandbox.util._.bind(this.serializers[self.provider],this,res,this.options)
          var images = serialized().slice(0, this.options.limit)
          deferred.resolve(images);

        }, this));
      } else{
        deferred.resolve([]);
      }

      return deferred.promise();
    }

  },

  isAuthorized: function(provider){
    var deferred = this.sandbox.data.deferred();
    var self = this;
    var auth = {
      provider_name: provider,
      provider:false,
      permissions:false
    };

    if(provider==="hull"){
      auth.permissions = true;

      var valid = (this.loggedIn() || this.id!=="me");
      auth.provider = valid;

      var authProviders = this.sandbox.config.services.types.auth;
      if (!authProviders || !authProviders.length) {
        deferred.reject(new Error('No auth provider for this app'));
      } else {
        auth.provider_name = authProviders[0].replace(/_app$/,'');
        deferred.resolve(auth);
      }
    } else {
      if (this.loggedIn()[provider]){
        auth.provider=true;
        if(provider==='facebook'){
          this.hasFacebookPermissions(self.options.scope, auth, deferred);
        } else {
          auth.permissions=true;
          deferred.resolve(auth);
        }
      } else {
        auth.provider=false;
        deferred.resolve(auth);
      }

    }

    return deferred.promise();
  },
  hasFacebookPermissions: function(scope, authorization, deferred){
    "use strict";
    var sandbox = this.sandbox;
    this.api({provider: "facebook", path: "me/permissions"}).then(function(res) {

      //Convert scope to array if given as a string.
      if(this.sandbox.util._.isString(scope)){
        scope = scope.replace(' ','').split(',');
      }

      if(this.sandbox.util._.isArray(scope) && (this.sandbox.util._.intersection(this.sandbox.util._.keys(res.data[0]), scope).length==scope.length)){
        //we have all the perms we need.
        authorization.permissions=true;
      }

      deferred.resolve(authorization);
    });
  },

  request: function(provider, identities, options) {
    "use strict";
    var path, params;

    switch (provider) {
      case 'hull':
        path = this.id + '/images';
        params = { per_page: this.options.limit };
        break;
      case 'facebook':
        path = {provider: 'facebook', path: this.id+'/photos/uploaded'};
        // path = 'facebook/' + ((this.id==='me')?identities.facebook.uid:this.id) + '/photos';
        params = { };
        break;
      case 'instagram':
        path = {provider: 'instagram', path: 'users/'+((this.id==='me')?'self':this.id)+'/media/recent'};
        params = { per_page: this.options.limit };
        break;
    }

    return this.api(path, params);
  },

  serializers: {
    hull: function(res, options) {
      "use strict";
      var sandbox = this.sandbox
      return this.sandbox.util._.map(res, function(f) {
        return {
          provider: 'hull',
          name: f.name,
          picture: sandbox.helpers.imageUrl(f.id, options.format)
        };
      });
    },

    facebook: function(res, options) {
      "use strict";
      var format='source';
      switch (options.format){
        case 'small' :
        case 'thumb' :
        case 'square' :
          format = 'picture';
          break;
        case 'medium' :
        case 'large' :
        case 'original' :
          format = 'source';
          break;
      }

      return this.sandbox.util._.map(res.data, function(f) {
        return {
          provider: 'facebook',
          name: f.from.name,
          picture: f[format],
          uid: f.id
        };
      });
    },

    instagram: function(res, options){
      var format = 'low_resolution';
      switch (options.format){
        case 'small' :
        case 'thumb' :
        case 'square' :
          format = 'thumbnail';
          break;
        case 'medium' :
          format = 'standard_resolution';
          break;
        case 'large' :
        case 'original' :
          format = 'standard_resolution';
          break;
      }

      return this.sandbox.util._.map(res, function(f){
        var t = "";
        if(f && f.caption){t = f.caption.text;}
        return {
          provider: 'instagram',
          name: t,
          picture: f.images[format].url,
          uid: f.id
        };
      });
    }
  }
});
<commit_msg>Fixes widget images<commit_after>/**
 * ## Image list
 *
 * Displays images belonging to a given user on a given service
 *
 * ### Example
 *
 *

 *
 * ### Options
 *
 * - `id`: Optional, the id of the user whose images we want to list. By default, it will list the images of the current user.
 * - `format`: Optional, the format of the thumbnail you wish to get. Can be `thumb`, `square`, `small`, `medium`, `large`, `original`. Will map to the closest existing preset when used on external networks. Default:  `small`
 * - `provider`: Optional, service from which we will fetch images. Can be `hull`, `instagram`, or `facebook`, by default it will list images from `hull`.
 * - `limit`: Optional, the number of images to display. Be default it will display 10 images.
 * - `scope` : Optional, a Facebook permission you need to ask the user before being able to show data. - If this permission is not given, a button will be shown to ask for it.
 *
 * ### Template
 *
 * - `images`: Displays the list of the user's images.
 *
 * ### Actions
 *
 * - `authorize`: Pops up a permissions dialog or a login dialog.
 *
 * ### Datasource
 *
 * - `images`: The user's images.
 * - `authorized` : A hash of permissions showing if the user can view the images.
 * Contains `provider`, `permissions` : Booleans showing if the provider and permissions are right,
 * and `provider_name` containing the name of the asked provider
 *
 */
Hull.define(['underscore'], {
  type: 'Hull',

  templates: [
    'images'
  ],

  options: {
    id:'me',
    provider:'hull',
    limit: 10,
    format: 'small',
    scope: 'user_photos'
  },

  refreshEvents: ['model.hull.me.change'],

  initialize: function() {
    this.me = this.sandbox.data.api.model('me');

    this.provider = this.options.provider;

    this.id = this.options.id || "me";
  },

  actions: {
    authorize: function(){
      if(this.provider==="facebook"){
        this.sandbox.login('facebook', {scope:this.options.scope}).then(this.sandbox.util._.bind(function(){
          this.render();
        },this));
      } else {
        this.sandbox.login(this.provider,{}).then(this.sandbox.util._.bind(function(){
          // this.render();
        },this));
      }
    }
  },

  datasources: {
    authorized: function() {
      return this.isAuthorized(this.provider);
    },

    images: function() {

      var deferred = this.sandbox.data.deferred();
      var self = this;

      // map identities by name
      var identities = this.sandbox.util._.reduce(this.me.get('identities'), function(m, i) {
        m[i.provider] = i;
        return m;
      }, {});

      //Are we logged in to provider, or is provider hull. if Provider is hull, are we asking "me" without being loggedin ?
      if( this.loggedIn()[this.provider] || (this.provider==="hull" && (this.loggedIn() || this.id!=="me"))){
        this.request(this.provider, identities, this.options).then(this.sandbox.util._.bind(function(res) {

          var serialized = this.sandbox.util._.bind(this.serializers[self.provider],this,res,this.options)
          var images = serialized().slice(0, this.options.limit)
          deferred.resolve(images);

        }, this));
      } else{
        deferred.resolve([]);
      }

      return deferred.promise();
    }

  },

  isAuthorized: function(provider){
    var deferred = this.sandbox.data.deferred();
    var self = this;
    var auth = {
      provider_name: provider,
      provider:false,
      permissions:false
    };

    if(provider==="hull"){
      auth.permissions = true;

      var valid = (this.loggedIn() || this.id!=="me");
      auth.provider = valid;

      var authProviders = this.sandbox.config.services.types.auth;
      if (!authProviders || !authProviders.length) {
        deferred.reject(new Error('No auth provider for this app'));
      } else {
        auth.provider_name = authProviders[0].replace(/_app$/,'');
        deferred.resolve(auth);
      }
    } else {
      if (this.loggedIn()[provider]){
        auth.provider=true;
        if(provider==='facebook'){
          this.hasFacebookPermissions(self.options.scope, auth, deferred);
        } else {
          auth.permissions=true;
          deferred.resolve(auth);
        }
      } else {
        auth.provider=false;
        deferred.resolve(auth);
      }

    }

    return deferred.promise();
  },
  hasFacebookPermissions: function(scope, authorization, deferred){
    "use strict";
    var _ = this.sandbox.util._;
    this.api({provider: "facebook", path: "me/permissions"}).then(function(res) {

      //Convert scope to array if given as a string.
      if(_.isString(scope)){
        scope = scope.replace(' ','').split(',');
      }

      if(_.isArray(scope) && (_.intersection(_.keys(res.data[0]), scope).length==scope.length)){
        //we have all the perms we need.
        authorization.permissions=true;
      }

      deferred.resolve(authorization);
    });
  },

  request: function(provider, identities, options) {
    "use strict";
    var path, params;

    switch (provider) {
      case 'hull':
        path = this.id + '/images';
        params = { per_page: this.options.limit };
        break;
      case 'facebook':
        path = {provider: 'facebook', path: this.id+'/photos/uploaded'};
        // path = 'facebook/' + ((this.id==='me')?identities.facebook.uid:this.id) + '/photos';
        params = { };
        break;
      case 'instagram':
        path = {provider: 'instagram', path: 'users/'+((this.id==='me')?'self':this.id)+'/media/recent'};
        params = { per_page: this.options.limit };
        break;
    }

    return this.api(path, params);
  },

  serializers: {
    hull: function(res, options) {
      "use strict";
      var sandbox = this.sandbox;
      return this.sandbox.util._.map(res, function(f) {
        return {
          provider: 'hull',
          name: f.name,
          picture: sandbox.helpers.imageUrl(f.id, options.format)
        };
      });
    },

    facebook: function(res, options) {
      "use strict";
      var format='source';
      switch (options.format){
        case 'small' :
        case 'thumb' :
        case 'square' :
          format = 'picture';
          break;
        case 'medium' :
        case 'large' :
        case 'original' :
          format = 'source';
          break;
      }

      return this.sandbox.util._.map(res.data, function(f) {
        return {
          provider: 'facebook',
          name: f.from.name,
          picture: f[format],
          uid: f.id
        };
      });
    },

    instagram: function(res, options){
      var format = 'low_resolution';
      switch (options.format){
        case 'small' :
        case 'thumb' :
        case 'square' :
          format = 'thumbnail';
          break;
        case 'medium' :
          format = 'standard_resolution';
          break;
        case 'large' :
        case 'original' :
          format = 'standard_resolution';
          break;
      }

      return this.sandbox.util._.map(res, function(f){
        var t = "";
        if(f && f.caption){t = f.caption.text;}
        return {
          provider: 'instagram',
          name: t,
          picture: f.images[format].url,
          uid: f.id
        };
      });
    }
  }
});
<|endoftext|>


<commit_before>import fs from "fs";
import path from "path";

import { parse } from "babel-core";

import escodegen from "escodegen";

import * as Pluggable from "../../pluggable";

const parseModule = Pluggable.sync(function parseModule (asset) {
  const tokens = [];
  const comments = [];

  asset.ast = parse(asset.rawSource, {
    locations: true,
    ranges: true,
    sourceFile: path.join(asset.ns, asset.nsPath),
    onToken: tokens,
    onComment: comments
  });
  asset.ast = escodegen.attachComments(asset.ast, comments, tokens);

  return asset;
});

function loadModule (asset) {
  asset = Object.assign({}, asset);
  asset.rawSource = fs.readFileSync(asset.path, "utf-8");
  return this.parseModule(asset);
}

export { parse, escodegen };
export default Pluggable.sync(loadModule, { parseModule });
<commit_msg>Make `load-ast.js` module more friendly to extensibility.<commit_after>import fs from "fs";
import path from "path";

import { parse } from "babel-core";
import escodegen from "escodegen";

import * as Pluggable from "../../pluggable";

const parseSourceToAst = Pluggable.sync(function parseSourceToAst (raw, sourceFile) {
  if (!sourceFile) { return parse(raw); }

  const tokens = [];
  const comments = [];

  const ast = parse(raw, {
    locations: true,
    ranges: true,
    sourceFile: sourceFile,
    onToken: tokens,
    onComment: comments
  });

  return escodegen.attachComments(ast, comments, tokens);
});

const parseModule = Pluggable.sync(function parseModule (asset) {
  return Object.assign({}, asset, {
    ast: this.parseSourceToAst(asset.rawSource, path.join(asset.ns, asset.nsPath))
  });
}, { parseSourceToAst });

function loadModule (asset) {
  asset = Object.assign({}, asset);
  asset.rawSource = fs.readFileSync(asset.path, "utf-8");
  return this.parseModule(asset);
}

export default Pluggable.sync(loadModule, { parseModule });
<|endoftext|>


<commit_before>jQuery(document).ready(function($) {

  $.fn.chat = function() {
    this.each(function() {
      var container = this;
      var lastUpdate = $('.timestamp', container).text();
      var chatUpdateUrl = $('form.new_chat_update', container).attr('action');

      document.location = '#chat_end';

      function updateChat() {
        $.getJSON(chatUpdateUrl, {last_update: lastUpdate}, function(data) {
          $.each(data.feeds, function(i, feed) {
            if(document.getElementById(feed.id) == null) {
              $(".chat_updates", container).append(feed.update);
              $('a.chat_end', container).remove();
              $(".chat_updates", container).append("");
              document.location = '#chat_end';
            };
          });

          lastUpdate = data.timestamp;
          setTimeout(updateChat, 1000);

        });

        $.get(chatUpdateUrl.replace('chat_updates', 'chatting_users'), function(data) {
          $('.chatting_users', container).replaceWith(data);
        });
      };

      $('form.new_chat_update', container).ajaxForm(function() {
        $('#chat_update_message').val('');
      });

      $('#chat_update_message').focus();

      setTimeout(updateChat, 1000);
    });
  };

  $('#chat').chat();
});

<commit_msg>Chat is scrolled properly which fixes http://github.com/wolfgangihloff/peergroupsupervision/issues#issue/3<commit_after>jQuery(document).ready(function($) {

  $.fn.chat = function() {
    this.each(function() {
      var container = this;
      var lastUpdate = $('.timestamp', container).text();
      var chatUpdateUrl = $('form.new_chat_update', container).attr('action');
      var chatUpdates = $(".chat_updates", container);

      function scrollUpdates() {
        chatUpdates.animate({ scrollTop: chatUpdates.attr("scrollHeight") }, 500);
      };

      scrollUpdates();

      function updateChat() {
        $.getJSON(chatUpdateUrl, {last_update: lastUpdate}, function(data) {
          $.each(data.feeds, function(i, feed) {
            if(document.getElementById(feed.id) == null) {
              chatUpdates.append(feed.update);
              $('#' + feed.id, container).hide().fadeIn(500);
              scrollUpdates();
            };
          });

          lastUpdate = data.timestamp;
          setTimeout(updateChat, 1000);

        });

        $.get(chatUpdateUrl.replace('chat_updates', 'chatting_users'), function(data) {
          $('.chatting_users', container).replaceWith(data);
        });
      };

      $('form.new_chat_update', container).ajaxForm(function() {
        $('#chat_update_message').val('');
      });

      $('#chat_update_message').focus();

      setTimeout(updateChat, 1000);
    });
  };

  $('#chat').chat();
});

<|endoftext|>


<commit_before>/**
 * @author R.Akhtyamov
 * https://github.com/Heterotroph/StreamingChart
 */

var charts = {};

(function() {

    /**
     * Example of arguments:
     *  size = {width: 1000, height: 400}
     *  point = {width: 50, height: 1}
     *  axis = {offset: 0, dynamicSpace: {top: 10, bottom: 0}, isDynamic: false}
     *  style = {
     *      background: {color: "#000000", alpha: 0.5},
     *      grid: {thickness: 1, color: "#00FFFF", alpha: 1, width: 1, height: 20, dash: [1, 0]},
     *      axisX:  {thickness: 1, color: "#000000", alpha: 1, offset: 0},
     *      chart: {
     *          lines: {thickness: 3, color: "#000000", alpha: 0.8, bounds: true},
     *          points:  {thickness: 3, radius: 2, lineColor: "#0000FF", fillColor: "#FF0000", alpha: 0.8, bounds: true}
     *      }
     *  }
     */
    function StreamingChart(size, point, axis, style) {
        this.Container_constructor();

        //chart points data
        this._data = [];

        //configuration
        this._size = size;
        this._point = point;
        this._axis = axis;
        this._style = style;

        //dynamic values
        this._widthCapacity = 0;
        this._heightCapacity = 0;
        this._dynamicOffset = axis.offset;
        this._dynamicPoint = {width: point.width, height: point.height};
        this._extremeMax = {value: -Number.MAX_VALUE, age: Number.MAX_VALUE};
        this._extremeMin = {value: Number.MAX_VALUE, age: Number.MAX_VALUE};

        //views
        this._backgroundShape = this.addChild(new createjs.Shape());
        this._gridShape = this.addChild(new createjs.Shape());
        this._axisXShape = this.addChild(new createjs.Shape());
        this._chartShape = this.addChild(new createjs.Shape());
        this._pointShape = this.addChild(new createjs.Shape());

        //apply style configuration
        this._calculateCapacity();
        this.redraw();
    }

    var p = createjs.extend(StreamingChart, createjs.Container);

    //
    //  PUBLIC METHODS
    //

    p.append = function(data) {
        if (data.length === 0) return;
        var totalData = this._data.concat(data);
        this._data = totalData.slice(-this._widthCapacity);

        this._searchExtreme(data);
        this._processExtreme();

        this._clearChartAndPoints();
        this._drawChart(0, this._dynamicPoint.width, this._data, this._style.chart);
    };

    p.clear = function() {
        this._data = [];
        this._clearChartAndPoints();
        this._extremeMax = {value: -Number.MAX_VALUE, age: this._widthCapacity};
        this._extremeMin = {value: Number.MAX_VALUE, age: this._widthCapacity};
        this._moveAxisX(this._style.axisX.offset);
    };

    p.setStyle = function(style) {
        this._style = style;
        this.redraw();
    };

    p.redraw = function() {
        this.append(this._data.splice(0, this._data.length));
        this._drawBackgroundShape(this._size, this._style.background);
        this._updateGrid(this._style.grid);
        this._drawAxisX(this._style.axisX);
        this._updateMask(this._style.chart.lines.bounds, this._size.width, this._size.height);
    };

    p.setComplexSize = function(width, height) {
        var widthSegments = this._size.width /  this._dynamicPoint.width;
        widthSegments = Math.ceil(widthSegments * 1000) / 1000; //TODO: Убрать этот костыль и найти красивое решение
        var heightSegments = this._size.height / this._dynamicPoint.height;
        heightSegments = Math.ceil(heightSegments * 1000) / 1000; //TODO: ...
        this.setSize(width, height);
        this.setPoint(this._size.width / widthSegments, this._size.height / heightSegments);
    };

    p.setSize = function(width, height) {
        this._size.width = width;
        this._size.height = height;
        this._calculateCapacity();
    };

    p.getSize = function() {
        return {width: this._size.width, height: this._size.height};
    };

    p.setPoint = function(width, height) {
        this._point.width = width;
        this._point.height = height;
        this._dynamicPoint = {width: this._point.width, height: this._point.height};
        this._calculateCapacity();
    };

    p.getPoint = function() {
        return {width: this._dynamicPoint.width, height: this._dynamicPoint.height};
    };

    p.setOffset = function(value) {
        this._axis.offset = value;
        this._dynamicOffset = value;
        this._calculateCapacity();
    };

    p.getOffset = function() {
        return this._dynamicOffset;
    };

    p.setGrid = function(width, height) {
        this._style.grid.width = width;
        this._style.grid.height = height;
        this._updateGrid(this._style.grid);
    };

    p.getGrid = function() {
        return {width: this._style.grid.width, height: this._style.grid.height};
    };

    p.getData = function() {
        return this._data.slice();
    };

    p.getCapacity = function() {
        return this._widthCapacity;
    };

    p.getExtreme = function() {
        var indexCapacity = Math.min(this._data.length, this._widthCapacity) - 1;
        var min = {
            value: this._extremeMin.value,
            index: indexCapacity - this._extremeMin.age
        };
        var max = {
            value: this._extremeMax.value,
            index: indexCapacity - this._extremeMax.age
        };
        return {min: min, max: max};
    };

    p.getInterpolatedValue = function(index) {
        if (this._data.length === 0) return 0;
        index = Math.round(index * 100) / 100;
        index = Math.min(index, this._data.length - 1);
        index = Math.max(index, 0);
        var intIndex = Math.floor(index);
        if (intIndex === index) return this._data[index];
        var delta = this._data[intIndex + 1] - this._data[intIndex];
        return this._data[intIndex] + delta * (index - intIndex);
    };

    p.getInterpolatedValueByLocalX = function(localX) {
        var index = this.getIndexByLocalX(localX);
        return this.getInterpolatedValue(index);
    };

    p.getIndexByLocalX = function(localX) {
        return localX / this._dynamicPoint.width;
    };

    p.getLocalXByIndex = function(index) {
        var localX = index * this._dynamicPoint.width;
        return Math.round(localX);
    };

    p.getValueByLocalY = function(localY) {
        return (this._size.height - localY) / this._dynamicPoint.height + this._dynamicOffset;
    };

    p.getLocalYByValue = function(value) {
        var localY = this._size.height - (this._applyOffset(value) * this._dynamicPoint.height);
        return Math.round(localY);
    };

    //
    //  PRIVATE METHODS (VIEW)
    //

    p._drawChart = function(offsetX, stepX, data, style) {
        var aX, aY, bX, bY;
        aX = offsetX;
        aY = this._applyOffset(data[0]) * this._dynamicPoint.height;
        this._chartShape.graphics.setStrokeStyle(style.lines.thickness).beginStroke(style.lines.color);
        this._pointShape.graphics.setStrokeStyle(style.points.thickness).beginStroke(style.points.lineColor);
        if (offsetX === 0) this._drawPoint(0, Math.round(aY), style.points);
        for (var i = 0; i < data.length - 1; i++) {
            bX = Math.round(offsetX + stepX * (i + 1));
            bY = Math.round(this._applyOffset(data[i + 1]) * this._dynamicPoint.height);
            this._drawSegment(aX, aY, bX, bY, style.lines);
            this._drawPoint(bX, bY, style.points);
            aX = bX;
            aY = bY;
        }
        this._chartShape.graphics.endStroke();
        this._pointShape.graphics.endStroke();
    };

    p._drawSegment = function(aX, aY, bX, bY, style) {
        var graphics = this._chartShape.graphics;
        graphics.moveTo(aX, this._size.height - aY).lineTo(bX, this._size.height - bY);
    };

    p._drawPoint = function(x, y, style) {
        var graphics = this._pointShape.graphics;
        if (style.radius === 0) return;
        if (style.bounds && !this._isInsideBounds(x, y)) return;
        graphics.beginFill(style.fillColor);
        graphics.drawCircle(x, this._size.height - y, style.radius);
        graphics.endFill();
    };

    p._drawBackgroundShape = function(size, style) {
        var graphics = this._backgroundShape.graphics.clear();
        if (style.alpha === 0) return;
        graphics.beginFill(style.color);
        graphics.drawRoundRect(0, 0, size.width, size.height, 3);
        this._backgroundShape.alpha = style.alpha;
    };

    p._updateGrid = function(style) {
        var stepX = this._dynamicPoint.width * this._style.grid.width;
        var stepY = this._dynamicPoint.height * this._style.grid.height;
        this._drawGridShape(stepX, stepY, style);
    };

    p._drawGridShape = function(stepX, stepY, style) {
        var graphics = this._gridShape.graphics.clear();
        if (style.alpha === 0) return;
        graphics.setStrokeDash(style.dash);
        graphics.setStrokeStyle(style.thickness).beginStroke(style.color);
        if (stepX) {
            for (var x = stepX; x < this._size.width; x += stepX) {
                graphics.moveTo(x, 0).lineTo(x, this._size.height);
            }
        }
        var gridOffset = (-this._dynamicOffset * this._dynamicPoint.height) % stepY;
        gridOffset = gridOffset < 0 ? gridOffset + stepY : gridOffset;
        if (stepY) {
            for (var y = this._size.height - gridOffset; y >= 0; y -= stepY) {
                graphics.moveTo(0, y).lineTo(this._size.width, y);
            }
        }
        graphics.endStroke();
        this._gridShape.alpha = style.alpha;
    };

    p._updateMask = function(bounds, width, height) {
        if (bounds) {
            this._drawMaskShape(0, 0, width, height);
        } else {
            this._chartShape.mask = null;
        }
    };

    p._drawMaskShape = function(x, y, width, height) {
        this._chartShape.mask = new createjs.Shape();
        this._chartShape.mask.graphics.beginFill("#000000");
        this._chartShape.mask.graphics.drawRect(x, y, width, height);
    };

    p._drawLevelLine = function(shape, thickness, color) {
        var graphics = shape.graphics.clear();
        graphics.setStrokeStyle(thickness).beginStroke(color);
        graphics.moveTo(0, 0).lineTo(this._size.width, 0).endStroke();
    };

    p._drawAxisX = function(style) {
        if (style.alpha === 0) return;
        this._axisXShape.alpha = style.alpha;
        this._drawLevelLine(this._axisXShape, style.thickness, style.color);
        this._moveAxisX(style.offset);
    };

    p._moveAxisX = function(offset) {
        this._axisXShape.y = this._size.height - this._applyOffset(offset) * this._dynamicPoint.height;
        this._axisXShape.visible = this._isInsideBounds(0, this._axisXShape.y);
    };

    p._processExtreme = function() {
        if (!this._axis.isDynamic) return;
        var isDynamicChanged = this._calculateDynamic();
        if (!isDynamicChanged) return;
        this._updateGrid(this._style.grid);
        this._moveAxisX(this._style.axisX.offset);
    };

    p._clearChartAndPoints = function() {
        this._chartShape.graphics.clear();
        this._pointShape.graphics.clear();
    };

    //
    //  PRIVATE METHODS (UTILS)
    //

    p._calculateDynamic = function() {
        var workHeight = this._size.height - this._axis.dynamicSpace.top - this._axis.dynamicSpace.bottom;
        var workDelta = this._extremeMax.value - this._extremeMin.value  - this._axis.offset;
        var tempPointHeight = workHeight / Math.max(workDelta, 0.001);

        var tempAxisOffset = this._extremeMin.value - this._axis.dynamicSpace.bottom / tempPointHeight;

        var result = this._dynamicPoint.height != tempPointHeight || this._dynamicOffset != tempAxisOffset;
        this._dynamicOffset = tempAxisOffset;
        this._dynamicPoint.height = tempPointHeight;
        if (result) this._calculateCapacity();

        return result;
    };

    p._calculateCapacity = function() {
        this._widthCapacity = Math.floor(this._size.width /  this._dynamicPoint.width) + 1;
        this._heightCapacity = Math.floor(this._size.height / this._dynamicPoint.height) + 1;
    };

    p._applyOffset = function(y) {
        return y - this._dynamicOffset;
    };

    p._isInsideBounds = function(x, y) {
        return x >= 0 && x <= this._size.width
            && y >= 0 && y <= this._size.height;
    };

    p._searchExtreme = function(data) {
        var newExtreme;
        this._extremeMax.age += data.length;
        this._extremeMin.age += data.length;
        var isOld = this._extremeMax.age >= this._widthCapacity;
        isOld = isOld || this._extremeMin.age >= this._widthCapacity;
        if (isOld || !data.length) {
            newExtreme = this._getExtreme(Number.MAX_VALUE, -Number.MAX_VALUE, this._data);
        } else {
            newExtreme = this._getExtreme(this._extremeMin.value, this._extremeMax.value, data);
        }
        this._extremeMin = newExtreme.min;
        this._extremeMax = newExtreme.max;
    };

    p._getExtreme = function(min, max, data) {
        var currentMax = {value: max, age: this._widthCapacity};
        var currentMin = {value: min, age: this._widthCapacity};
        for (var i = 0; i < data.length; i++) {
            currentMax.value = Math.max(currentMax.value, data[i]);
            if (currentMax.value === data[i]) currentMax.age = data.length - i - 1;
            currentMin.value = Math.min(currentMin.value, data[i]);
            if (currentMin.value === data[i]) currentMin.age = data.length - i - 1;
        }
        return {min: currentMin, max: currentMax};
    };

    //  ---
    charts.StreamingChart = createjs.promote(StreamingChart, "Container");
}());<commit_msg>Fixed getLocalXByIndex<commit_after>/**
 * @author R.Akhtyamov
 * https://github.com/Heterotroph/StreamingChart
 */

var charts = {};

(function() {

    /**
     * Example of arguments:
     *  size = {width: 1000, height: 400}
     *  point = {width: 50, height: 1}
     *  axis = {offset: 0, dynamicSpace: {top: 10, bottom: 0}, isDynamic: false}
     *  style = {
     *      background: {color: "#000000", alpha: 0.5},
     *      grid: {thickness: 1, color: "#00FFFF", alpha: 1, width: 1, height: 20, dash: [1, 0]},
     *      axisX:  {thickness: 1, color: "#000000", alpha: 1, offset: 0},
     *      chart: {
     *          lines: {thickness: 3, color: "#000000", alpha: 0.8, bounds: true},
     *          points:  {thickness: 3, radius: 2, lineColor: "#0000FF", fillColor: "#FF0000", alpha: 0.8, bounds: true}
     *      }
     *  }
     */
    function StreamingChart(size, point, axis, style) {
        this.Container_constructor();

        //chart points data
        this._data = [];

        //configuration
        this._size = size;
        this._point = point;
        this._axis = axis;
        this._style = style;

        //dynamic values
        this._widthCapacity = 0;
        this._heightCapacity = 0;
        this._dynamicOffset = axis.offset;
        this._dynamicPoint = {width: point.width, height: point.height};
        this._extremeMax = {value: -Number.MAX_VALUE, age: Number.MAX_VALUE};
        this._extremeMin = {value: Number.MAX_VALUE, age: Number.MAX_VALUE};

        //views
        this._backgroundShape = this.addChild(new createjs.Shape());
        this._gridShape = this.addChild(new createjs.Shape());
        this._axisXShape = this.addChild(new createjs.Shape());
        this._chartShape = this.addChild(new createjs.Shape());
        this._pointShape = this.addChild(new createjs.Shape());

        //apply style configuration
        this._calculateCapacity();
        this.redraw();
    }

    var p = createjs.extend(StreamingChart, createjs.Container);

    //
    //  PUBLIC METHODS
    //

    p.append = function(data) {
        if (data.length === 0) return;
        var totalData = this._data.concat(data);
        this._data = totalData.slice(-this._widthCapacity);

        this._searchExtreme(data);
        this._processExtreme();

        this._clearChartAndPoints();
        this._drawChart(0, this._dynamicPoint.width, this._data, this._style.chart);
    };

    p.clear = function() {
        this._data = [];
        this._clearChartAndPoints();
        this._extremeMax = {value: -Number.MAX_VALUE, age: this._widthCapacity};
        this._extremeMin = {value: Number.MAX_VALUE, age: this._widthCapacity};
        this._moveAxisX(this._style.axisX.offset);
    };

    p.setStyle = function(style) {
        this._style = style;
        this.redraw();
    };

    p.redraw = function() {
        this.append(this._data.splice(0, this._data.length));
        this._drawBackgroundShape(this._size, this._style.background);
        this._updateGrid(this._style.grid);
        this._drawAxisX(this._style.axisX);
        this._updateMask(this._style.chart.lines.bounds, this._size.width, this._size.height);
    };

    p.setComplexSize = function(width, height) {
        var widthSegments = this._size.width /  this._dynamicPoint.width;
        widthSegments = Math.ceil(widthSegments * 1000) / 1000; //TODO: Убрать этот костыль и найти красивое решение
        var heightSegments = this._size.height / this._dynamicPoint.height;
        heightSegments = Math.ceil(heightSegments * 1000) / 1000; //TODO: ...
        this.setSize(width, height);
        this.setPoint(this._size.width / widthSegments, this._size.height / heightSegments);
    };

    p.setSize = function(width, height) {
        this._size.width = width;
        this._size.height = height;
        this._calculateCapacity();
    };

    p.getSize = function() {
        return {width: this._size.width, height: this._size.height};
    };

    p.setPoint = function(width, height) {
        this._point.width = width;
        this._point.height = height;
        this._dynamicPoint = {width: this._point.width, height: this._point.height};
        this._calculateCapacity();
    };

    p.getPoint = function() {
        return {width: this._dynamicPoint.width, height: this._dynamicPoint.height};
    };

    p.setOffset = function(value) {
        this._axis.offset = value;
        this._dynamicOffset = value;
        this._calculateCapacity();
    };

    p.getOffset = function() {
        return this._dynamicOffset;
    };

    p.setGrid = function(width, height) {
        this._style.grid.width = width;
        this._style.grid.height = height;
        this._updateGrid(this._style.grid);
    };

    p.getGrid = function() {
        return {width: this._style.grid.width, height: this._style.grid.height};
    };

    p.getData = function() {
        return this._data.slice();
    };

    p.getCapacity = function() {
        return this._widthCapacity;
    };

    p.getExtreme = function() {
        var indexCapacity = Math.min(this._data.length, this._widthCapacity) - 1;
        var min = {
            value: this._extremeMin.value,
            index: indexCapacity - this._extremeMin.age
        };
        var max = {
            value: this._extremeMax.value,
            index: indexCapacity - this._extremeMax.age
        };
        return {min: min, max: max};
    };

    p.getInterpolatedValue = function(index) {
        if (this._data.length === 0) return 0;
        index = Math.round(index * 100) / 100;
        index = Math.min(index, this._data.length - 1);
        index = Math.max(index, 0);
        var intIndex = Math.floor(index);
        if (intIndex === index) return this._data[index];
        var delta = this._data[intIndex + 1] - this._data[intIndex];
        return this._data[intIndex] + delta * (index - intIndex);
    };

    p.getInterpolatedValueByLocalX = function(localX) {
        var index = this.getIndexByLocalX(localX);
        return this.getInterpolatedValue(index);
    };

    p.getIndexByLocalX = function(localX) {
        return localX / this._dynamicPoint.width;
    };

    p.getLocalXByIndex = function(index) {
        var localX = index * this._dynamicPoint.width;
        return localX;
    };

    p.getValueByLocalY = function(localY) {
        return (this._size.height - localY) / this._dynamicPoint.height + this._dynamicOffset;
    };

    p.getLocalYByValue = function(value) {
        var localY = this._size.height - (this._applyOffset(value) * this._dynamicPoint.height);
        return Math.round(localY);
    };

    //
    //  PRIVATE METHODS (VIEW)
    //

    p._drawChart = function(offsetX, stepX, data, style) {
        var aX, aY, bX, bY;
        aX = offsetX;
        aY = this._applyOffset(data[0]) * this._dynamicPoint.height;
        this._chartShape.graphics.setStrokeStyle(style.lines.thickness).beginStroke(style.lines.color);
        this._pointShape.graphics.setStrokeStyle(style.points.thickness).beginStroke(style.points.lineColor);
        if (offsetX === 0) this._drawPoint(0, Math.round(aY), style.points);
        for (var i = 0; i < data.length - 1; i++) {
            bX = Math.round(offsetX + stepX * (i + 1));
            bY = Math.round(this._applyOffset(data[i + 1]) * this._dynamicPoint.height);
            this._drawSegment(aX, aY, bX, bY, style.lines);
            this._drawPoint(bX, bY, style.points);
            aX = bX;
            aY = bY;
        }
        this._chartShape.graphics.endStroke();
        this._pointShape.graphics.endStroke();
    };

    p._drawSegment = function(aX, aY, bX, bY, style) {
        var graphics = this._chartShape.graphics;
        graphics.moveTo(aX, this._size.height - aY).lineTo(bX, this._size.height - bY);
    };

    p._drawPoint = function(x, y, style) {
        var graphics = this._pointShape.graphics;
        if (style.radius === 0) return;
        if (style.bounds && !this._isInsideBounds(x, y)) return;
        graphics.beginFill(style.fillColor);
        graphics.drawCircle(x, this._size.height - y, style.radius);
        graphics.endFill();
    };

    p._drawBackgroundShape = function(size, style) {
        var graphics = this._backgroundShape.graphics.clear();
        if (style.alpha === 0) return;
        graphics.beginFill(style.color);
        graphics.drawRoundRect(0, 0, size.width, size.height, 3);
        this._backgroundShape.alpha = style.alpha;
    };

    p._updateGrid = function(style) {
        var stepX = this._dynamicPoint.width * this._style.grid.width;
        var stepY = this._dynamicPoint.height * this._style.grid.height;
        this._drawGridShape(stepX, stepY, style);
    };

    p._drawGridShape = function(stepX, stepY, style) {
        var graphics = this._gridShape.graphics.clear();
        if (style.alpha === 0) return;
        graphics.setStrokeDash(style.dash);
        graphics.setStrokeStyle(style.thickness).beginStroke(style.color);
        if (stepX) {
            for (var x = stepX; x < this._size.width; x += stepX) {
                graphics.moveTo(x, 0).lineTo(x, this._size.height);
            }
        }
        var gridOffset = (-this._dynamicOffset * this._dynamicPoint.height) % stepY;
        gridOffset = gridOffset < 0 ? gridOffset + stepY : gridOffset;
        if (stepY) {
            for (var y = this._size.height - gridOffset; y >= 0; y -= stepY) {
                graphics.moveTo(0, y).lineTo(this._size.width, y);
            }
        }
        graphics.endStroke();
        this._gridShape.alpha = style.alpha;
    };

    p._updateMask = function(bounds, width, height) {
        if (bounds) {
            this._drawMaskShape(0, 0, width, height);
        } else {
            this._chartShape.mask = null;
        }
    };

    p._drawMaskShape = function(x, y, width, height) {
        this._chartShape.mask = new createjs.Shape();
        this._chartShape.mask.graphics.beginFill("#000000");
        this._chartShape.mask.graphics.drawRect(x, y, width, height);
    };

    p._drawLevelLine = function(shape, thickness, color) {
        var graphics = shape.graphics.clear();
        graphics.setStrokeStyle(thickness).beginStroke(color);
        graphics.moveTo(0, 0).lineTo(this._size.width, 0).endStroke();
    };

    p._drawAxisX = function(style) {
        if (style.alpha === 0) return;
        this._axisXShape.alpha = style.alpha;
        this._drawLevelLine(this._axisXShape, style.thickness, style.color);
        this._moveAxisX(style.offset);
    };

    p._moveAxisX = function(offset) {
        this._axisXShape.y = this._size.height - this._applyOffset(offset) * this._dynamicPoint.height;
        this._axisXShape.visible = this._isInsideBounds(0, this._axisXShape.y);
    };

    p._processExtreme = function() {
        if (!this._axis.isDynamic) return;
        var isDynamicChanged = this._calculateDynamic();
        if (!isDynamicChanged) return;
        this._updateGrid(this._style.grid);
        this._moveAxisX(this._style.axisX.offset);
    };

    p._clearChartAndPoints = function() {
        this._chartShape.graphics.clear();
        this._pointShape.graphics.clear();
    };

    //
    //  PRIVATE METHODS (UTILS)
    //

    p._calculateDynamic = function() {
        var workHeight = this._size.height - this._axis.dynamicSpace.top - this._axis.dynamicSpace.bottom;
        var workDelta = this._extremeMax.value - this._extremeMin.value  - this._axis.offset;
        var tempPointHeight = workHeight / Math.max(workDelta, 0.001);

        var tempAxisOffset = this._extremeMin.value - this._axis.dynamicSpace.bottom / tempPointHeight;

        var result = this._dynamicPoint.height != tempPointHeight || this._dynamicOffset != tempAxisOffset;
        this._dynamicOffset = tempAxisOffset;
        this._dynamicPoint.height = tempPointHeight;
        if (result) this._calculateCapacity();

        return result;
    };

    p._calculateCapacity = function() {
        this._widthCapacity = Math.floor(this._size.width /  this._dynamicPoint.width) + 1;
        this._heightCapacity = Math.floor(this._size.height / this._dynamicPoint.height) + 1;
    };

    p._applyOffset = function(y) {
        return y - this._dynamicOffset;
    };

    p._isInsideBounds = function(x, y) {
        return x >= 0 && x <= this._size.width
            && y >= 0 && y <= this._size.height;
    };

    p._searchExtreme = function(data) {
        var newExtreme;
        this._extremeMax.age += data.length;
        this._extremeMin.age += data.length;
        var isOld = this._extremeMax.age >= this._widthCapacity;
        isOld = isOld || this._extremeMin.age >= this._widthCapacity;
        if (isOld || !data.length) {
            newExtreme = this._getExtreme(Number.MAX_VALUE, -Number.MAX_VALUE, this._data);
        } else {
            newExtreme = this._getExtreme(this._extremeMin.value, this._extremeMax.value, data);
        }
        this._extremeMin = newExtreme.min;
        this._extremeMax = newExtreme.max;
    };

    p._getExtreme = function(min, max, data) {
        var currentMax = {value: max, age: this._widthCapacity};
        var currentMin = {value: min, age: this._widthCapacity};
        for (var i = 0; i < data.length; i++) {
            currentMax.value = Math.max(currentMax.value, data[i]);
            if (currentMax.value === data[i]) currentMax.age = data.length - i - 1;
            currentMin.value = Math.min(currentMin.value, data[i]);
            if (currentMin.value === data[i]) currentMin.age = data.length - i - 1;
        }
        return {min: currentMin, max: currentMax};
    };

    //  ---
    charts.StreamingChart = createjs.promote(StreamingChart, "Container");
}());<|endoftext|>