<commit_before>/* global window */

var fs = require('fs-plus');
var path = require('path');
var url = require('url');

// TODO: any more correct way to do this?
var BUNDLED_MODULES = [
  'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events',
  'freelist', 'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'punycode', 'querystring', 'readline', 'repl',
  'smalloc', 'stream', 'string_decoder', 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'vm', 'zlib'
];

function findInParents(absolutePath, targetFile) {
  var addSeparator = absolutePath[0] == path.sep;
  var current = absolutePath.split(path.sep).filter(Boolean);
  var pathname;
  while (current.length) {
    pathname = current.join(path.sep);
    if (addSeparator) {
      pathname = path.sep + pathname;
    }
    console.log(path.sep, pathname);
    if (fs.readdirSync(pathname).indexOf(targetFile) >= 0) {
      return pathname;
    }

    current.pop();
  }

  return null;
}

function getModulesDir(fromDir) {
  if (!fs.existsSync(fromDir)) {
    return null;
  }
  var pathname = findInParents(fromDir, "package.json");

  if (pathname !== null) {
    return path.join(pathname, 'node_modules');
  }

  return null;
}

function resolveModule(value, fromDir, modulesDir) {
  var pathname = url.parse(value).pathname;

  if (pathname[0] === '.') { // relative
    return path.join(fromDir, pathname);
  } else if (pathname[0] === '/') { // absolute
    return value;
  } else if (modulesDir) { // node_modules
    var moduleDir = path.join(modulesDir, value);
    var packageFilename = path.join(moduleDir, 'package.json');

    if (fs.existsSync(packageFilename)) {
      var pkg;

      try {
        pkg = JSON.parse(fs.readFileSync(packageFilename));
      } catch (e) {
        pkg = false;
      }

      if (pkg && pkg.main && !Array.isArray(pkg.main) && pkg.main !== 'index.js') {
        return path.join(moduleDir, pkg.main);
      }
    }

    return moduleDir;
  }
}

function checkPath(pathname, extensions) {
  pathname = fs.resolveExtension(pathname, extensions);

  if (!pathname) {
    return false;
  }

  if (fs.isDirectorySync(pathname)) {
    pathname = fs.resolveExtension(path.join(pathname, 'index'), extensions);
    if (!pathname) {
      return false;
    }
  }

  return true;
}

function isAtom() {
  try {
    if (typeof window !== undefined && typeof window.atom !== undefined) {
      return true;
    }
  } catch (e) {
    return false;
  }

  return false;
}

function getCurrentFilePath(context) {
  if (!isAtom()) {
    return path.dirname(path.join(process.cwd(), context.getFilename()));
  }

  var editor = window.atom.workspace.getActivePaneItem();
  if (!editor) {
    return null;
  }

  return path.dirname(editor.getPath());
}

function getWebpackConfig(fromDir) {
  if (!fs.existsSync(fromDir)) {
    return {};
  }

  var pathname = findInParents(fromDir, "webpack.config.js");

  if (pathname !== null) {
    return require(path.join(pathname, 'webpack.config.js'));
  }

  return {};
}

function testModulePath(value, context, node) {
  if (BUNDLED_MODULES.indexOf(value) >= 0) {
    return;
  }

  var fileDir = getCurrentFilePath(context);
  if (!fileDir) {
    return;
  }

  var modulesDir = getModulesDir(fileDir) || '';
  var webpackConfig = getWebpackConfig(fileDir);
  var alias = {};
  var extensions = Object.keys(require.extensions);

  if (extensions.indexOf('') === -1) {
    extensions.push('');
  }

  if (typeof webpackConfig.resolve === 'object') {
    if (typeof webpackConfig.resolve.alias === 'object') {
      alias = webpackConfig.resolve.alias;
    }

    if (Array.isArray(webpackConfig.resolve.extensions)) {
      webpackConfig.resolve.extensions.forEach(function (ext) {
        if (extensions.indexOf(ext) === -1) {
          extensions.unshift(ext);
        }
      });
    }
  }

  value = alias[value] ? alias[value] : value;
  value = resolveModule(value, fileDir, modulesDir);

  if (checkPath(value, extensions)) {
    return;
  }

  value = value.replace(fileDir + (/\/$/.test(fileDir) ? '' : path.sep), './');
  value = value.replace(modulesDir + (/\/$/.test(modulesDir) ? '' : path.sep), '');

  context.report(node, "Cannot find module '" + value + "'", {});
}

module.exports = function (context) {
  return {
    ImportDeclaration: function (node) {
      testModulePath(node.source.value, context, node);
    },
    CallExpression: function (node) {
      if (node.callee.name !== 'require' || !node.arguments.length || typeof node.arguments[0].value !== 'string' || !node.arguments[0].value) {
        return;
      }

      node.arguments[0].value.split('!').forEach(function (value) {
        testModulePath(value, context, node);
      });
    }
  };
};
<commit_msg>Fixed bad char look up<commit_after>/* global window */

var fs = require('fs-plus');
var path = require('path');
var url = require('url');

// TODO: any more correct way to do this?
var BUNDLED_MODULES = [
  'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events',
  'freelist', 'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'punycode', 'querystring', 'readline', 'repl',
  'smalloc', 'stream', 'string_decoder', 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'vm', 'zlib'
];

function findInParents(absolutePath, targetFile) {
  var addSeparator = absolutePath.charAt(0) == path.sep;
  var current = absolutePath.split(path.sep).filter(Boolean);
  var pathname;
  while (current.length) {
    pathname = current.join(path.sep);
    if (addSeparator) {
      pathname = path.sep + pathname;
    }
    console.log(path.sep, pathname);
    if (fs.readdirSync(pathname).indexOf(targetFile) >= 0) {
      return pathname;
    }

    current.pop();
  }

  return null;
}

function getModulesDir(fromDir) {
  if (!fs.existsSync(fromDir)) {
    return null;
  }
  var pathname = findInParents(fromDir, "package.json");

  if (pathname !== null) {
    return path.join(pathname, 'node_modules');
  }

  return null;
}

function resolveModule(value, fromDir, modulesDir) {
  var pathname = url.parse(value).pathname;

  if (pathname[0] === '.') { // relative
    return path.join(fromDir, pathname);
  } else if (pathname[0] === '/') { // absolute
    return value;
  } else if (modulesDir) { // node_modules
    var moduleDir = path.join(modulesDir, value);
    var packageFilename = path.join(moduleDir, 'package.json');

    if (fs.existsSync(packageFilename)) {
      var pkg;

      try {
        pkg = JSON.parse(fs.readFileSync(packageFilename));
      } catch (e) {
        pkg = false;
      }

      if (pkg && pkg.main && !Array.isArray(pkg.main) && pkg.main !== 'index.js') {
        return path.join(moduleDir, pkg.main);
      }
    }

    return moduleDir;
  }
}

function checkPath(pathname, extensions) {
  pathname = fs.resolveExtension(pathname, extensions);

  if (!pathname) {
    return false;
  }

  if (fs.isDirectorySync(pathname)) {
    pathname = fs.resolveExtension(path.join(pathname, 'index'), extensions);
    if (!pathname) {
      return false;
    }
  }

  return true;
}

function isAtom() {
  try {
    if (typeof window !== undefined && typeof window.atom !== undefined) {
      return true;
    }
  } catch (e) {
    return false;
  }

  return false;
}

function getCurrentFilePath(context) {
  if (!isAtom()) {
    return path.dirname(path.join(process.cwd(), context.getFilename()));
  }

  var editor = window.atom.workspace.getActivePaneItem();
  if (!editor) {
    return null;
  }

  return path.dirname(editor.getPath());
}

function getWebpackConfig(fromDir) {
  if (!fs.existsSync(fromDir)) {
    return {};
  }

  var pathname = findInParents(fromDir, "webpack.config.js");

  if (pathname !== null) {
    return require(path.join(pathname, 'webpack.config.js'));
  }

  return {};
}

function testModulePath(value, context, node) {
  if (BUNDLED_MODULES.indexOf(value) >= 0) {
    return;
  }

  var fileDir = getCurrentFilePath(context);
  if (!fileDir) {
    return;
  }

  var modulesDir = getModulesDir(fileDir) || '';
  var webpackConfig = getWebpackConfig(fileDir);
  var alias = {};
  var extensions = Object.keys(require.extensions);

  if (extensions.indexOf('') === -1) {
    extensions.push('');
  }

  if (typeof webpackConfig.resolve === 'object') {
    if (typeof webpackConfig.resolve.alias === 'object') {
      alias = webpackConfig.resolve.alias;
    }

    if (Array.isArray(webpackConfig.resolve.extensions)) {
      webpackConfig.resolve.extensions.forEach(function (ext) {
        if (extensions.indexOf(ext) === -1) {
          extensions.unshift(ext);
        }
      });
    }
  }

  value = alias[value] ? alias[value] : value;
  value = resolveModule(value, fileDir, modulesDir);

  if (checkPath(value, extensions)) {
    return;
  }

  value = value.replace(fileDir + (/\/$/.test(fileDir) ? '' : path.sep), './');
  value = value.replace(modulesDir + (/\/$/.test(modulesDir) ? '' : path.sep), '');

  context.report(node, "Cannot find module '" + value + "'", {});
}

module.exports = function (context) {
  return {
    ImportDeclaration: function (node) {
      testModulePath(node.source.value, context, node);
    },
    CallExpression: function (node) {
      if (node.callee.name !== 'require' || !node.arguments.length || typeof node.arguments[0].value !== 'string' || !node.arguments[0].value) {
        return;
      }

      node.arguments[0].value.split('!').forEach(function (value) {
        testModulePath(value, context, node);
      });
    }
  };
};
<|endoftext|>


<commit_before><commit_msg>Fixed monster rotation bug.<commit_after><|endoftext|>


<commit_before>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    //opera.postError("" + evt.keyCode + " " + evt.which )
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<commit_msg>Added ctrl-k shortcut<commit_after>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    //opera.postError("" + evt.keyCode + " " + evt.which );
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 107: // k key. ctrl-k == kill to en of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var pos = this._textarea.selectionStart;
          this._textarea.value = this._textarea.value.slice(0, pos);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<|endoftext|>


<commit_before>import proxyquire from 'proxyquire';
import chai from "chai";
import chaiAsPromised from "chai-as-promised";

chai.use(chaiAsPromised);
chai.should();

// might be useful elsewhere too...
const passportStub = {
  authenticate(name, callback) {
    return function(req) {
      if (!req)
        throw new Error("no req");
      if (!req.query)
        throw new Error("no req.query");

      const override = req.query.override;
      if (!override)
        throw new Error("no req.query.override");

      switch (override) {
        case 'error': return callback(new Error());
        case 'user': return callback(null, { id: 1 });
        case 'info': return callback(null, null, 'info');
        default: throw new Error("Invalid req.query.override: " + override);
      }
    }
  }
}

const context = {
  mapUserToUserId: user => user.id,
  createTokenFromUser: () => 'token'
};

const resolvers = proxyquire('./resolvers', { passport: passportStub }).default;

describe('resolvers', () => {

  describe('local', () => {

    describe('passportLoginEmail', () => {

      const passportLoginEmail
        = resolvers.RootMutation.passportLoginEmail.bind(context);

      it('throws on a real error', () => {
        // I don't know how to check if an async function throws
        passportLoginEmail(null, { override: 'error' }).should.be.rejected;
      });

      it('passes the user', async () => {
        const result = await passportLoginEmail(null, { override: 'user' });
        result.error.should.equal("");
        result.token.should.equal('token');
        result.userId.should.equal(1);
      });

      it('passes an error string (not a throw)', async () => {
        const result = await passportLoginEmail(null, { override: 'info' });
        result.error.should.equal("info");
        result.token.should.equal("");
        result.userId.should.equal("");
      });

    });

  });

});<commit_msg>test passportStub in resolver.  100% coverage?<commit_after>import proxyquire from 'proxyquire';
import chai from "chai";
import chaiAsPromised from "chai-as-promised";

chai.use(chaiAsPromised);
chai.should();

// might be useful elsewhere too...
const passportStub = {
  authenticate(name, callback) {
    return function(req) {
      if (!req)
        throw new Error("no req");
      if (!req.query)
        throw new Error("no req.query");

      const override = req.query.override;
      if (!override)
        throw new Error("no req.query.override");

      switch (override) {
        case 'error': return callback(new Error());
        case 'user': return callback(null, { id: 1 });
        case 'info': return callback(null, null, 'info');
        default: throw new Error("Invalid req.query.override: " + override);
      }
    }
  }
}

const context = {
  mapUserToUserId: user => user.id,
  createTokenFromUser: () => 'token'
};

const resolvers = proxyquire('./resolvers', { passport: passportStub }).default;

describe('resolvers', () => {

  describe('passportStub', () => {

    it('throws on missing or invalid values', () => {

      (function() {
        passportStub.authenticate()();
      }).should.throw();

      (function() {
        passportStub.authenticate()({});
      }).should.throw();

      (function() {
        passportStub.authenticate()({ query: {} });
      }).should.throw();

      (function() {
        passportStub.authenticate()({ query: { override: 'error' }});
      }).should.throw();

      (function() {
        passportStub.authenticate()({ query: { override: 'invalid' }});
      }).should.throw();

    });

  });

  describe('local', () => {

    describe('passportLoginEmail', () => {

      const passportLoginEmail
        = resolvers.RootMutation.passportLoginEmail.bind(context);

      it('throws on a real error', () => {
        // I don't know how to check if an async function throws
        passportLoginEmail(null, { override: 'error' }).should.be.rejected;
      });

      it('passes the user', async () => {
        const result = await passportLoginEmail(null, { override: 'user' });
        result.error.should.equal("");
        result.token.should.equal('token');
        result.userId.should.equal(1);
      });

      it('passes an error string (not a throw)', async () => {
        const result = await passportLoginEmail(null, { override: 'info' });
        result.error.should.equal("info");
        result.token.should.equal("");
        result.userId.should.equal("");
      });

    });

  });

});<|endoftext|>


<commit_before>'use strict';

// Get the path of the Quick Notes stylesheet, to be passed to CKEditor.
var styleSheets = Array.prototype.slice.call(document.styleSheets);
var styleSheet = styleSheets.filter(function(style) {
  return style.ownerNode.id === 'qnStyleSheet';
})[0];
var styleSheetPath = styleSheet.href;

module.exports = {
  // Constants
  CATEGORY_NAME_MAXLENGTH: 20,
  NOTE_TITLE_MAXLENGTH: 20,
  NOTE_BODY_MAXLENGTH: 1000,
  UNSPECIFIED_CATEGORY_NAME: 'Unspecified',
  // Vendor
  CKEDITOR: {
    contentsCss: styleSheetPath,
    bodyClass: 'qn-Editor-content',
    uiColor: '#E1D8B7'
  }
};
<commit_msg>Increase CKEditor height<commit_after>'use strict';

// Get the path of the Quick Notes stylesheet, to be passed to CKEditor.
var styleSheets = Array.prototype.slice.call(document.styleSheets);
var styleSheet = styleSheets.filter(function(style) {
  return style.ownerNode.id === 'qnStyleSheet';
})[0];
var styleSheetPath = styleSheet.href;

module.exports = {
  // Constants
  CATEGORY_NAME_MAXLENGTH: 20,
  NOTE_TITLE_MAXLENGTH: 20,
  NOTE_BODY_MAXLENGTH: 1000,
  UNSPECIFIED_CATEGORY_NAME: 'Unspecified',
  // Vendor
  CKEDITOR: {
    contentsCss: styleSheetPath,
    bodyClass: 'qn-Editor-content',
    height: '26em',
    uiColor: '#E1D8B7'
  }
};
<|endoftext|>


<commit_before>// Beautiful player using POO

// Work In Progress

class Player {
  constructor(params = { basePath, parent, id, className, link, width, height, controls, autoplay}) {

    this.parent    = document.querySelector(params.parent)
    this.link      = params.basePath + params.link
    this.width     = params.width
    this.height    = params.height
    this.autoplay  = params.autoplay
    this.id        = params.id
    this.className = params.className

    this.playerContainer = document.createElement('div')

    this.playerContainer.className = 'playerContainer'
    this.playerContainer.tabIndex  = 0;
    this.playerContainer.width     = this.width
    this.playerContainer.height    = 'auto'
    this.parent.appendChild(this.playerContainer)

    this.player = document.createElement('video')

    this.player.src       = this.link
    this.player.id        = this.id
    this.player.className = this.className
    this.player.autoplay  = this.autoplay
    this.player.controls  = false

    this.playerContainer.appendChild(this.player)

    // Check if controls are required
    if (params.controls) {
      this.createControllers()
    } else {
      console.log('To enable controllers turn controls to true')
    }
  }

  createControllers() {

    let player = this.player
    let playerContainer = this.playerContainer

    // Create control bar
    let controlBar = document.createElement('div')
    controlBar.className = 'controlBar'
    playerContainer.appendChild(controlBar)

    // Create play/pause button
    let playPauseButton = document.createElement('div')
    playPauseButton.className = 'playPauseButton'
    controlBar.appendChild(playPauseButton)

    // Create Mute Button
    let muteButton = document.createElement('div')
    muteButton.className = 'muteButton'
    controlBar.appendChild(muteButton)

    // Create Volume Slider
    let volumeController = document.createElement('div')
    volumeController.className = 'volumeController'
    controlBar.appendChild(volumeController)

    // Create Volume Bar inside Volume Slider
    let volumeBar = document.createElement('div')
    volumeBar.className = 'volumeBar'
    volumeController.appendChild(volumeBar)

    let activeVolume = false

    // Create currentTimer
    let currentTimer = document.createElement('div')
    currentTimer.className = 'currentTimer'
    currentTimer.innerHTML = '0:00'
    controlBar.appendChild(currentTimer)

    // Create Timeline
    let timeline = document.createElement('div')
    timeline.className = 'timeline'
    controlBar.appendChild(timeline)
    let activeTimeline = false

    // Create vars for lerp
    let position = 0
    let amount = 0.1

    // Create TimelineLoadingBar
    let timelineLoadingBar = document.createElement('div')
    timelineLoadingBar.className = 'timelineLoadingBar'
    timeline.appendChild(timelineLoadingBar)

    // Create Timeline Bar
    let timelineBar = document.createElement('div')
    timelineBar.className = 'timelineBar'
    timeline.appendChild(timelineBar)

    // Create durationTimer
    let durationTimer = document.createElement('div')
    durationTimer.className = 'durationTimer'
    durationTimer.innerHTML = '0:00'
    controlBar.appendChild(durationTimer)

    // Create fullscreenButton
    let fullscreenButton = document.createElement('div')
    fullscreenButton.className = 'fullscreenButton'
    controlBar.appendChild(fullscreenButton)

    let fullscreenMode = false

    // Check at start

    // Check Video Status to display the right button
    if(player.autoplay) {
      playPauseButton.classList.add('isPlaying')
    } else {
      playPauseButton.classList.remove('isPlaying')
    }

    // Set volume at start
    player.volume = 0
    volumeBar.style.transform = 'scaleX('+ player.volume + ')'

    // Events

    // PlayPause Events
    let playPauseEvents = [player, playPauseButton]
    playPauseEvents.forEach(function(e) {
      e.addEventListener('click', function() {
        if(player.paused) {
          player.play()
          playPauseButton.classList.add('isPlaying')
        } else {
          player.pause()
          playPauseButton.classList.remove('isPlaying')
        }
      })
    })

    this.parent.addEventListener('keydown', function(e) {
      if (e.keyCode === 32) {
        if(player.paused) {
          player.play()
          playPauseButton.classList.add('isPlaying')
        } else {
          player.pause()
          playPauseButton.classList.remove('isPlaying')
        }
      }
    })

    // Volume Events
    muteButton.addEventListener('click', function() {
      let volumeSaved = 0;
      if (!muteButton.classList.contains('isMuted')) {
        this.volumeSaved = player.volume
        player.volume = 0;
        muteButton.classList.toggle('isMuted')
      } else {
        player.volume = this.volumeSaved
        muteButton.classList.toggle('isMuted')
      }
    })

    volumeController.addEventListener('mousedown', function(event) {
      activeVolume = true
      muteButton.classList.remove('isMuted')
        if( 0.1 <= event.offsetX / volumeController.offsetWidth && event.offsetX / volumeController.offsetWidth <= 0.9) {
          player.volume = event.offsetX / volumeController.offsetWidth
        } else if (event.offsetX / volumeController.offsetWidth < 0.1) {
          player.volume = 0
          muteButton.classList.add('isMuted')
        } else {
          player.volume = 1
        }
        volumeBar.style.transform = 'scaleX(' + player.volume + ')'
    })

    volumeController.addEventListener('mousemove', function(event) {
      if(activeVolume) {
       muteButton.classList.remove('isMuted')
        if( 0.1 <= event.offsetX / volumeController.offsetWidth && event.offsetX / volumeController.offsetWidth <= 0.9) {
          player.volume = event.offsetX / volumeController.offsetWidth
        } else if (event.offsetX / volumeController.offsetWidth < 0.1) {
          player.volume = 0
          muteButton.classList.add('isMuted')
        } else {
          player.volume = 1
        }
        volumeBar.style.transform = 'scaleX(' + player.volume + ')'
      }
    })

    window.addEventListener('mouseup', function(){
      activeVolume = false
    })

    // Timeline Events
    setInterval(function barProgression(){
      // Display bar progression
      let timelineBarProgression = player.currentTime / player.duration

      if(position === 0) {
        timelineBar.style.transform = 'scaleX(' + timelineBarProgression + ')'
        position += player.currentTime
      } else {
        position += (player.currentTime - position) * amount
        timelineBarProgression = position / player.duration
        timelineBar.style.transform = 'scaleX(' + timelineBarProgression + ')'
      }

      // Display on timeline what is already loaded
      timelineLoadingBar.style.transform = 'scaleX(' + player.buffered.end(0) / player.duration + ')'

      // console.log(fullscreenMode);

    }, 16);

    player.addEventListener('timeupdate', function() {

      // Display time formated
      let durationTimeFormated = new Date(null)
      durationTimeFormated.setSeconds(Math.floor(player.duration))
      let ds = durationTimeFormated.getSeconds()
      let dm = durationTimeFormated.getMinutes()
      let dh = durationTimeFormated.getUTCHours()

      let currentTimeFormated = new Date(null)
      currentTimeFormated.setSeconds(Math.floor(player.currentTime))

      let cs = currentTimeFormated.getSeconds()
      let cm = currentTimeFormated.getMinutes()
      let ch = currentTimeFormated.getUTCHours()

      // Checks for currentTime display
      if(cs >= 0 && cs < 10) {
        cs = '0' + cs
      }
      if(ch > 0 && (cm === 0 && cm < 10)) {
        cm = '0' + cm
      }

      // Checks for durationTime display
      if(ds >= 0 && ds < 10) {
        ds = '0' + ds
      }
      if(dh > 0 && (dm === 0 && dm < 10)) {
        dm = '0' + cm
      }

      //
      if(dh === 0 || ch === 0) {
        currentTimer.innerHTML = cm + ':' + cs
        durationTimer.innerHTML = dm + ':' + ds
      } else {
        currentTimer.innerHTML = ch + ':' + cm + ':' + cs
        durationTimer.innerHTML = dh + ':' + dm + ':' + ds
      }
    })

    timeline.addEventListener('mousedown', function(event) {
      player.pause()
      timelineBar.classList.add('isTransiting')
      activeTimeline = true
      let requestedPosition = event.offsetX / timeline.offsetWidth
      player.currentTime = player.duration * requestedPosition
      timelineBar.style.transform = 'scaleX(' + requestedPosition + ')'
      position = player.currentTime
    })

    timeline.addEventListener('mousemove', function(event) {
      if(activeTimeline) {
      let requestedPosition = event.offsetX / timeline.offsetWidth
      player.currentTime = player.duration * requestedPosition
      timelineBar.style.transform = 'scaleX(' + requestedPosition + ')'
      position = player.currentTime
      }
    })

    window.addEventListener('mouseup', function(){
      if(activeTimeline) {
        timelineBar.classList.remove('isTransiting')
        player.play()
        playPauseButton.classList.add('isPlaying')
        activeTimeline = false
      }
    })

    // Fullscreen
    fullscreenButton.addEventListener('click', function(e) {
      if(!fullscreenMode) {
        if (playerContainer.requestFullscreen) {
          playerContainer.requestFullscreen();
        } else if (playerContainer.mozRequestFullScreen) {
          playerContainer.mozRequestFullScreen();
        } else if (playerContainer.webkitRequestFullscreen) {
          playerContainer.webkitRequestFullscreen();
        }
        fullscreenMode = true
      } else {
        if (document.cancelFullScreen) {
          document.cancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen();
        }
        fullscreenMode = false
      }
    })

    let isFullscreen = false;

    const fullscreenEvents = ["fullscreenchange", "webkitfullscreenchange", "mozfullscreenchange", "msfullscreenchange"]
    fullscreenEvents.forEach( function(event) {
      document.addEventListener(event, function() {
        console.log(isFullscreen);
        isFullscreen = !isFullscreen
        if(!isFullscreen) {
          fullscreenMode = false
        }
      })
    })
  }
}

// Creating new Player
let customPlayer = new Player({ basePath: './src/videos/', parent: '.container', id: '1', className: 'customPlayer', link: 'video.mp4', width: 800, height: 'auto', controls: true, autoplay: true})<commit_msg>Add fulscreen on double click<commit_after>// Beautiful player using POO

// Work In Progress

class Player {
  constructor(params = { basePath, parent, id, className, link, width, height, controls, autoplay}) {

    this.parent    = document.querySelector(params.parent)
    this.link      = params.basePath + params.link
    this.width     = params.width
    this.height    = params.height
    this.autoplay  = params.autoplay
    this.id        = params.id
    this.className = params.className

    this.playerContainer = document.createElement('div')

    this.playerContainer.className = 'playerContainer'
    this.playerContainer.tabIndex  = 0;
    this.playerContainer.width     = this.width
    this.playerContainer.height    = 'auto'
    this.parent.appendChild(this.playerContainer)

    this.player = document.createElement('video')

    this.player.src       = this.link
    this.player.id        = this.id
    this.player.className = this.className
    this.player.autoplay  = this.autoplay
    this.player.controls  = false

    this.playerContainer.appendChild(this.player)

    // Check if controls are required
    if (params.controls) {
      this.createControllers()
    } else {
      console.log('To enable controllers turn controls to true')
    }
  }

  createControllers() {

    let player = this.player
    let playerContainer = this.playerContainer

    // Create control bar
    let controlBar = document.createElement('div')
    controlBar.className = 'controlBar'
    playerContainer.appendChild(controlBar)

    // Create play/pause button
    let playPauseButton = document.createElement('div')
    playPauseButton.className = 'playPauseButton'
    controlBar.appendChild(playPauseButton)

    // Create Mute Button
    let muteButton = document.createElement('div')
    muteButton.className = 'muteButton'
    controlBar.appendChild(muteButton)

    // Create Volume Slider
    let volumeController = document.createElement('div')
    volumeController.className = 'volumeController'
    controlBar.appendChild(volumeController)

    // Create Volume Bar inside Volume Slider
    let volumeBar = document.createElement('div')
    volumeBar.className = 'volumeBar'
    volumeController.appendChild(volumeBar)

    let activeVolume = false

    // Create currentTimer
    let currentTimer = document.createElement('div')
    currentTimer.className = 'currentTimer'
    currentTimer.innerHTML = '0:00'
    controlBar.appendChild(currentTimer)

    // Create Timeline
    let timeline = document.createElement('div')
    timeline.className = 'timeline'
    controlBar.appendChild(timeline)
    let activeTimeline = false

    // Create vars for lerp
    let position = 0
    let amount = 0.1

    // Create TimelineLoadingBar
    let timelineLoadingBar = document.createElement('div')
    timelineLoadingBar.className = 'timelineLoadingBar'
    timeline.appendChild(timelineLoadingBar)

    // Create Timeline Bar
    let timelineBar = document.createElement('div')
    timelineBar.className = 'timelineBar'
    timeline.appendChild(timelineBar)

    // Create durationTimer
    let durationTimer = document.createElement('div')
    durationTimer.className = 'durationTimer'
    durationTimer.innerHTML = '0:00'
    controlBar.appendChild(durationTimer)

    // Create fullscreenButton
    let fullscreenButton = document.createElement('div')
    fullscreenButton.className = 'fullscreenButton'
    controlBar.appendChild(fullscreenButton)

    let fullscreenMode = false

    // Check at start

    // Check Video Status to display the right button
    if(player.autoplay) {
      playPauseButton.classList.add('isPlaying')
    } else {
      playPauseButton.classList.remove('isPlaying')
    }

    // Set volume at start
    player.volume = 0
    volumeBar.style.transform = 'scaleX('+ player.volume + ')'

    // Events

    // PlayPause Events
    let playPauseEvents = [player, playPauseButton]
    playPauseEvents.forEach(function(e) {
      e.addEventListener('click', function() {
        if(player.paused) {
          player.play()
          playPauseButton.classList.add('isPlaying')
        } else {
          player.pause()
          playPauseButton.classList.remove('isPlaying')
        }
      })
    })

    this.parent.addEventListener('keydown', function(e) {
      if (e.keyCode === 32) {
        if(player.paused) {
          player.play()
          playPauseButton.classList.add('isPlaying')
        } else {
          player.pause()
          playPauseButton.classList.remove('isPlaying')
        }
      }
    })

    // Volume Events
    muteButton.addEventListener('click', function() {
      let volumeSaved = 0;
      if (!muteButton.classList.contains('isMuted')) {
        this.volumeSaved = player.volume
        player.volume = 0;
        muteButton.classList.toggle('isMuted')
      } else {
        player.volume = this.volumeSaved
        muteButton.classList.toggle('isMuted')
      }
    })

    volumeController.addEventListener('mousedown', function(event) {
      activeVolume = true
      muteButton.classList.remove('isMuted')
        if( 0.1 <= event.offsetX / volumeController.offsetWidth && event.offsetX / volumeController.offsetWidth <= 0.9) {
          player.volume = event.offsetX / volumeController.offsetWidth
        } else if (event.offsetX / volumeController.offsetWidth < 0.1) {
          player.volume = 0
          muteButton.classList.add('isMuted')
        } else {
          player.volume = 1
        }
        volumeBar.style.transform = 'scaleX(' + player.volume + ')'
    })

    volumeController.addEventListener('mousemove', function(event) {
      if(activeVolume) {
       muteButton.classList.remove('isMuted')
        if( 0.1 <= event.offsetX / volumeController.offsetWidth && event.offsetX / volumeController.offsetWidth <= 0.9) {
          player.volume = event.offsetX / volumeController.offsetWidth
        } else if (event.offsetX / volumeController.offsetWidth < 0.1) {
          player.volume = 0
          muteButton.classList.add('isMuted')
        } else {
          player.volume = 1
        }
        volumeBar.style.transform = 'scaleX(' + player.volume + ')'
      }
    })

    window.addEventListener('mouseup', function(){
      activeVolume = false
    })

    // Timeline Events
    setInterval(function barProgression(){
      // Display bar progression
      let timelineBarProgression = player.currentTime / player.duration

      if(position === 0) {
        timelineBar.style.transform = 'scaleX(' + timelineBarProgression + ')'
        position += player.currentTime
      } else {
        position += (player.currentTime - position) * amount
        timelineBarProgression = position / player.duration
        timelineBar.style.transform = 'scaleX(' + timelineBarProgression + ')'
      }

      // Display on timeline what is already loaded
      timelineLoadingBar.style.transform = 'scaleX(' + player.buffered.end(0) / player.duration + ')'

      // console.log(fullscreenMode);

    }, 16);

    player.addEventListener('timeupdate', function() {

      // Display time formated
      let durationTimeFormated = new Date(null)
      durationTimeFormated.setSeconds(Math.floor(player.duration))
      let ds = durationTimeFormated.getSeconds()
      let dm = durationTimeFormated.getMinutes()
      let dh = durationTimeFormated.getUTCHours()

      let currentTimeFormated = new Date(null)
      currentTimeFormated.setSeconds(Math.floor(player.currentTime))

      let cs = currentTimeFormated.getSeconds()
      let cm = currentTimeFormated.getMinutes()
      let ch = currentTimeFormated.getUTCHours()

      // Checks for currentTime display
      if(cs >= 0 && cs < 10) {
        cs = '0' + cs
      }
      if(ch > 0 && (cm === 0 && cm < 10)) {
        cm = '0' + cm
      }

      // Checks for durationTime display
      if(ds >= 0 && ds < 10) {
        ds = '0' + ds
      }
      if(dh > 0 && (dm === 0 && dm < 10)) {
        dm = '0' + cm
      }

      //
      if(dh === 0 || ch === 0) {
        currentTimer.innerHTML = cm + ':' + cs
        durationTimer.innerHTML = dm + ':' + ds
      } else {
        currentTimer.innerHTML = ch + ':' + cm + ':' + cs
        durationTimer.innerHTML = dh + ':' + dm + ':' + ds
      }
    })

    timeline.addEventListener('mousedown', function(event) {
      player.pause()
      timelineBar.classList.add('isTransiting')
      activeTimeline = true
      let requestedPosition = event.offsetX / timeline.offsetWidth
      player.currentTime = player.duration * requestedPosition
      timelineBar.style.transform = 'scaleX(' + requestedPosition + ')'
      position = player.currentTime
    })

    timeline.addEventListener('mousemove', function(event) {
      if(activeTimeline) {
      let requestedPosition = event.offsetX / timeline.offsetWidth
      player.currentTime = player.duration * requestedPosition
      timelineBar.style.transform = 'scaleX(' + requestedPosition + ')'
      position = player.currentTime
      }
    })

    window.addEventListener('mouseup', function(){
      if(activeTimeline) {
        timelineBar.classList.remove('isTransiting')
        player.play()
        playPauseButton.classList.add('isPlaying')
        activeTimeline = false
      }
    })

    // Fullscreen
    fullscreenButton.addEventListener('click', function(e) {
      if(!fullscreenMode) {
        if (playerContainer.requestFullscreen) {
          playerContainer.requestFullscreen();
        } else if (playerContainer.mozRequestFullScreen) {
          playerContainer.mozRequestFullScreen();
        } else if (playerContainer.webkitRequestFullscreen) {
          playerContainer.webkitRequestFullscreen();
        }
        fullscreenMode = true
      } else {
        if (document.cancelFullScreen) {
          document.cancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen();
        }
        fullscreenMode = false
      }
    })

    // Fullscreen on double click
    player.addEventListener('dblclick', function(e) {
      if(!fullscreenMode) {
        if (playerContainer.requestFullscreen) {
          playerContainer.requestFullscreen();
        } else if (playerContainer.mozRequestFullScreen) {
          playerContainer.mozRequestFullScreen();
        } else if (playerContainer.webkitRequestFullscreen) {
          playerContainer.webkitRequestFullscreen();
        }
        fullscreenMode = true
      } else {
        if (document.cancelFullScreen) {
          document.cancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen();
        }
        fullscreenMode = false
      }
    })

    let isFullscreen = false;

    const fullscreenEvents = ["fullscreenchange", "webkitfullscreenchange", "mozfullscreenchange", "msfullscreenchange"]
    fullscreenEvents.forEach( function(event) {
      document.addEventListener(event, function() {
        console.log(isFullscreen);
        isFullscreen = !isFullscreen
        if(!isFullscreen) {
          fullscreenMode = false
        }
      })
    })
  }
}

// Creating new Player
let customPlayer = new Player({ basePath: './src/videos/', parent: '.container', id: '1', className: 'customPlayer', link: 'video.mp4', width: 800, height: 'auto', controls: true, autoplay: true})<|endoftext|>


<commit_before>import React from 'react'
import Reflux from 'reflux'
import Route from 'react-route'

import Card from 'material-ui/lib/card/card'
import CardActions from 'material-ui/lib/card/card-actions'
import CardHeader from 'material-ui/lib/card/card-header'
import CardMedia from 'material-ui/lib/card/card-media'
import CardTitle from 'material-ui/lib/card/card-title'
import CardText from 'material-ui/lib/card/card-text'
import List from 'material-ui/lib/lists/list'
import ListItem from 'material-ui/lib/lists/list-item'
import colors from 'material-ui/lib/styles/colors'
import DescriptionIcon from 'material-ui/lib/svg-icons/action/description'
import RaisedButton from 'material-ui/lib/raised-button'
import FlatButton from 'material-ui/lib/flat-button'
import Avatar from 'material-ui/lib/avatar'
import ActionDescription from 'material-ui/lib/svg-icons/action/description'
import CircularProgress from 'material-ui/lib/circular-progress'

import { actions as searchActions } from './search_store'
import PaperAvatar from './paper_avatar'

const TYPE_MEETING = "https://oparl.org/schema/1.0/Meeting"
const TYPE_PAPER = "https://oparl.org/schema/1.0/Paper"
const TYPE_FILE = "https://oparl.org/schema/1.0/File"

export default React.createClass({
  mixins: [
    Reflux.listenTo(searchActions.search, 'onSearch'),
    Reflux.listenTo(searchActions.search.completed, 'onSearchCompleted'),
    Reflux.listenTo(searchActions.search.failed, 'onSearchFailed')
  ],

  getInitialState() {
    return {
      results: []
    }
  },

  componentDidMount() {
    // Start an empty search on startup
    searchActions.search("")
  },

  onSearch() {
    this.setState({
      loading: true
    })
  },

  onSearchFailed() {
    this.setState({
      loading: false
    })
  },

  onSearchCompleted(results) {
    this.setState({
      loading: false,
      results: results
    })
  },

  render() {
    return (

        {this.state.loading ?
          <CircularProgress size={2}/> :
          this.state.results.map((result, i) =>
            <SearchResult key={i} {...result}/>
          )}

    )
  },
})

class SearchResult extends React.Component {
  render() {
    switch(this.props.type) {
    case TYPE_MEETING:
      return <Meeting {...this.props}/>
      break
    case TYPE_PAPER:
      return <Paper {...this.props}/>
      break
    case TYPE_FILE:
      return <File {...this.props}/>
      break
    default:
      return

    }
  }
}

class Meeting extends React.Component {
  render() {
    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            title={this.props.name}
            subtitle={`${this.props.shortName} ${this.props.start}`}
            style={{ backgroundColor: colors.lime500 }}
            />
        <CardText>
          {findFilesInObject(this.props).map(id =>
            <FileItem key={id} id={id}/>
          )}
          <List>
            {this.props.agendaItem ?
              this.props.agendaItem.map((item, i) =>
                <ListItem
                    key={i}
                    disabled={!(item.consultation && item.consultation.parentID)}
                    onClick={() => Route.go(`/paper/${item.consultation.parentID}`)}
                    innerDivStyle={{ paddingRight: "0" }}
                    leftIcon={(item.number && item.number.length <= 2) ?
                      <Avatar size={24}>{item.number}</Avatar> :
                      {item.number}
                    }>

                    {item.name}

                  {(findFilesInObject(item).length > 0) ? (
                    <List>
                      {findFilesInObject(item).map(id =>
                        <FileItem key={id} id={id}/>
                      )}
                    </List>
                  ) : ""}
                </ListItem>
              ) : ""}
          </List>
        </CardText>
      </Card>
    )
  }
}

class Paper extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      meetings: []
    }
  }

  render() {
    let paper = this.props

    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            avatar={<PaperAvatar paper={paper}/>}
            title={paper.name}
            titleStyle={{
              whiteSpace: 'nowrap',
              overflow: 'hidden',
              textOverflow: 'ellipsis',
              // Vorlagen names tend to get really long, though
              // they're included in the @title in full
              maxWidth: '56em'
            }}
            subtitle={`${paper.shortName} ${iso8601ToDate(paper.publishedDate)}`}
            style={{ backgroundColor: colors.lime700 }}
            />
        <CardText>
          {findFilesInObject(paper).map(id =>
            <FileItem key={id} id={id}/>
          )}
          <List>
            {(paper.consultation || [])
              .filter(consultation => !!consultation.meeting)
              .map((consultation, i) =>
                <PaperMeetingItem key={i} id={consultation.meeting} filesOf={paper.id}/>
            )}
          </List>
        </CardText>
        <CardActions style={{ textAlign: 'right' }}>
          <RaisedButton label="Vorlage lesen" primary={true}
              onClick={() => Route.go(`/paper/${encodeURIComponent(paper.id)}`)}
              />
        </CardActions>
      </Card>
    )
  }
}

class File extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      meetings: [],
      papers: []
    }
  }

  componentDidMount() {
    fetch(`/api/oparl/file/${encodeURIComponent(this.props.id)}/context`)
      .then(res => res.json())
      .then(results => {
        let meetings = []
        let papers = []
        for(let result of results) {
          if (result.type == TYPE_MEETING) {
            meetings.push(result)
          } else if (result.type == TYPE_PAPER) {
            papers.push(result)
          }
        }
        this.setState({
          meetings: meetings,
          papers: papers
        })
      })
  }

  render() {
    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            title={<Avatar backgroundColor="white" size={36}><ActionDescription/></Avatar> {this.props.name}}
            style={{ backgroundColor: colors.lime300 }}
            />
        <CardText>
          <List>
            {this.state.meetings.map(meeting =>
              <MeetingItem key={meeting.id} {...meeting}/>
            )}
            {this.state.papers.map(paper =>
              <PaperItem key={paper.id} {...paper}/>
            )}
          </List>
        </CardText>
        <CardActions style={{ textAlign: 'right' }}>
          <RaisedButton label="Text Annotieren" primary={true}
              style={{ verticalAlign: 'top' }}
              onClick={ev => Route.go(`/file/${encodeURIComponent(this.props.id)}`)}
              />
          <RaisedButton label="Original-PDF" secondary={true}
              linkButton={true} href={this.props.downloadUrl}
              />
        </CardActions>
      </Card>
    )
  }
}

class MeetingItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/meeting/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let meeting = this.state || this.props

    return (
      <ListItem disabled={true}
          primaryText={meeting.name}
          secondaryText={iso8601ToDate(meeting.start)}
          />
    )
  }
}

class PaperMeetingItem extends MeetingItem {
  render() {
    let meeting = this.state || this.props

    return (
      <ListItem disabled={true} innerDivStyle={{ paddingRight: "0" }}>
        <List subheader={meeting.name}>
          {findFilesInObject(meeting).map(id =>
            <FileItem key={id} id={id}/>
          )}
          {meeting.agendaItem ?
            <ListItem innerDivStyle={{ paddingRight: "0" }}>
              {meeting.agendaItem
                .filter(item =>
                  item.consultation &&
                  item.consultation.parentID === this.props.filesOf
                )
                .map((item, i) =>
                  <List key={i} subheader={item.name}>
                    {findFilesInObject(item).map(id =>
                      <FileItem key={id} id={id}/>
                    )}
                  </List>
                )}
            </ListItem> : ""
          }
        </List>
      </ListItem>
    )
  }
}

class PaperItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/paper/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let paper = this.state || this.props

    return <ListItem
        leftIcon={<PaperAvatar paper={paper}/>}
        primaryText={paper.name}
        secondaryText={iso8601ToDate(paper.publishedDate)}
        onClick={() => Route.go(`/paper/${encodeURIComponent(paper.id)}`)}
        />
  }
}

class FileItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/file/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let file = this.state || this.props

    return (
      <ListItem
          innerDivStyle={{ paddingRight: "0" }}
          primaryText={file.name}
          leftIcon={<ActionDescription/>}
          onClick={ev => Route.go(`/file/${encodeURIComponent(this.props.id)}`)}
          />
    )
  }
}

const FILES_KEYS = [
  'invitation',
  'masterFile',
  'mainFile',
  'derivativeFile',
  'verbatimProtocol',
  'resultsProtocol',
  'resolutionFile',
  'auxiliaryFile'
]

function findFilesInObject(obj) {
  let results = []
  for(let k of FILES_KEYS) {
    let v = obj[k]
    if (typeof v == 'string') {
      results.push(v)
    } else if (v) {
      results.push(...v)
    }
  }
  return results
}

function iso8601ToDate(iso8601) {
  let m
  if (iso8601 && (m = iso8601.match(/(\d{4})-(\d\d)-(\d\d)/))) {
    return `${m[3]}.${m[2]}.${m[1]}`
  } else {
    return "?"
  }
}
<commit_msg>search_results: prettify PaperAvatars<commit_after>import React from 'react'
import Reflux from 'reflux'
import Route from 'react-route'

import Card from 'material-ui/lib/card/card'
import CardActions from 'material-ui/lib/card/card-actions'
import CardHeader from 'material-ui/lib/card/card-header'
import CardMedia from 'material-ui/lib/card/card-media'
import CardTitle from 'material-ui/lib/card/card-title'
import CardText from 'material-ui/lib/card/card-text'
import List from 'material-ui/lib/lists/list'
import ListItem from 'material-ui/lib/lists/list-item'
import colors from 'material-ui/lib/styles/colors'
import DescriptionIcon from 'material-ui/lib/svg-icons/action/description'
import RaisedButton from 'material-ui/lib/raised-button'
import FlatButton from 'material-ui/lib/flat-button'
import Avatar from 'material-ui/lib/avatar'
import ActionDescription from 'material-ui/lib/svg-icons/action/description'
import CircularProgress from 'material-ui/lib/circular-progress'

import { actions as searchActions } from './search_store'
import PaperAvatar from './paper_avatar'

const TYPE_MEETING = "https://oparl.org/schema/1.0/Meeting"
const TYPE_PAPER = "https://oparl.org/schema/1.0/Paper"
const TYPE_FILE = "https://oparl.org/schema/1.0/File"

export default React.createClass({
  mixins: [
    Reflux.listenTo(searchActions.search, 'onSearch'),
    Reflux.listenTo(searchActions.search.completed, 'onSearchCompleted'),
    Reflux.listenTo(searchActions.search.failed, 'onSearchFailed')
  ],

  getInitialState() {
    return {
      results: []
    }
  },

  componentDidMount() {
    // Start an empty search on startup
    searchActions.search("")
  },

  onSearch() {
    this.setState({
      loading: true
    })
  },

  onSearchFailed() {
    this.setState({
      loading: false
    })
  },

  onSearchCompleted(results) {
    this.setState({
      loading: false,
      results: results
    })
  },

  render() {
    return (

        {this.state.loading ?
          <CircularProgress size={2}/> :
          this.state.results.map((result, i) =>
            <SearchResult key={i} {...result}/>
          )}

    )
  },
})

class SearchResult extends React.Component {
  render() {
    switch(this.props.type) {
    case TYPE_MEETING:
      return <Meeting {...this.props}/>
      break
    case TYPE_PAPER:
      return <Paper {...this.props}/>
      break
    case TYPE_FILE:
      return <File {...this.props}/>
      break
    default:
      return

    }
  }
}

class Meeting extends React.Component {
  render() {
    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            title={this.props.name}
            subtitle={`${this.props.shortName} ${this.props.start}`}
            style={{ backgroundColor: colors.lime500 }}
            />
        <CardText>
          {findFilesInObject(this.props).map(id =>
            <FileItem key={id} id={id}/>
          )}
          <List>
            {this.props.agendaItem ?
              this.props.agendaItem.map((item, i) =>
                <ListItem
                    key={i}
                    disabled={!(item.consultation && item.consultation.parentID)}
                    onClick={() => Route.go(`/paper/${item.consultation.parentID}`)}
                    innerDivStyle={{ paddingRight: "0" }}
                    leftIcon={(item.number && item.number.length <= 2) ?
                      <Avatar size={24}>{item.number}</Avatar> :
                      {item.number}
                    }>

                    {item.name}

                  {(findFilesInObject(item).length > 0) ? (
                    <List>
                      {findFilesInObject(item).map(id =>
                        <FileItem key={id} id={id}/>
                      )}
                    </List>
                  ) : ""}
                </ListItem>
              ) : ""}
          </List>
        </CardText>
      </Card>
    )
  }
}

class Paper extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      meetings: []
    }
  }

  render() {
    let paper = this.props

    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            avatar={<PaperAvatar paper={paper} size={32}/>}
            title={paper.name}
            titleStyle={{
              whiteSpace: 'nowrap',
              overflow: 'hidden',
              textOverflow: 'ellipsis',
              // Vorlagen names tend to get really long, though
              // they're included in the @title in full
              maxWidth: '56em'
            }}
            subtitle={`${paper.shortName} ${iso8601ToDate(paper.publishedDate)}`}
            style={{ backgroundColor: colors.lime700 }}
            />
        <CardText>
          {findFilesInObject(paper).map(id =>
            <FileItem key={id} id={id}/>
          )}
          <List>
            {(paper.consultation || [])
              .filter(consultation => !!consultation.meeting)
              .map((consultation, i) =>
                <PaperMeetingItem key={i} id={consultation.meeting} filesOf={paper.id}/>
            )}
          </List>
        </CardText>
        <CardActions style={{ textAlign: 'right' }}>
          <RaisedButton label="Vorlage lesen" primary={true}
              onClick={() => Route.go(`/paper/${encodeURIComponent(paper.id)}`)}
              />
        </CardActions>
      </Card>
    )
  }
}

class File extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      meetings: [],
      papers: []
    }
  }

  componentDidMount() {
    fetch(`/api/oparl/file/${encodeURIComponent(this.props.id)}/context`)
      .then(res => res.json())
      .then(results => {
        let meetings = []
        let papers = []
        for(let result of results) {
          if (result.type == TYPE_MEETING) {
            meetings.push(result)
          } else if (result.type == TYPE_PAPER) {
            papers.push(result)
          }
        }
        this.setState({
          meetings: meetings,
          papers: papers
        })
      })
  }

  render() {
    return (
      <Card style={{ marginBottom: "1em" }}>
        <CardHeader
            title={<Avatar backgroundColor="white" size={36}><ActionDescription/></Avatar> {this.props.name}}
            style={{ backgroundColor: colors.lime300 }}
            />
        <CardText>
          <List>
            {this.state.meetings.map(meeting =>
              <MeetingItem key={meeting.id} {...meeting}/>
            )}
            {this.state.papers.map(paper =>
              <PaperItem key={paper.id} {...paper}/>
            )}
          </List>
        </CardText>
        <CardActions style={{ textAlign: 'right' }}>
          <RaisedButton label="Text Annotieren" primary={true}
              style={{ verticalAlign: 'top' }}
              onClick={ev => Route.go(`/file/${encodeURIComponent(this.props.id)}`)}
              />
          <RaisedButton label="Original-PDF" secondary={true}
              linkButton={true} href={this.props.downloadUrl}
              />
        </CardActions>
      </Card>
    )
  }
}

class MeetingItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/meeting/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let meeting = this.state || this.props

    return (
      <ListItem disabled={true}
          primaryText={meeting.name}
          secondaryText={iso8601ToDate(meeting.start)}
          />
    )
  }
}

class PaperMeetingItem extends MeetingItem {
  render() {
    let meeting = this.state || this.props

    return (
      <ListItem disabled={true} innerDivStyle={{ paddingRight: "0" }}>
        <List subheader={meeting.name}>
          {findFilesInObject(meeting).map(id =>
            <FileItem key={id} id={id}/>
          )}
          {meeting.agendaItem ?
            <ListItem innerDivStyle={{ paddingRight: "0" }}>
              {meeting.agendaItem
                .filter(item =>
                  item.consultation &&
                  item.consultation.parentID === this.props.filesOf
                )
                .map((item, i) =>
                  <List key={i} subheader={item.name}>
                    {findFilesInObject(item).map(id =>
                      <FileItem key={id} id={id}/>
                    )}
                  </List>
                )}
            </ListItem> : ""
          }
        </List>
      </ListItem>
    )
  }
}

class PaperItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/paper/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let paper = this.state || this.props

    return <ListItem
        leftIcon={<PaperAvatar paper={paper} size={24} style={{ color: 'white' }}/>}
        primaryText={paper.name}
        secondaryText={iso8601ToDate(paper.publishedDate)}
        onClick={() => Route.go(`/paper/${encodeURIComponent(paper.id)}`)}
        />
  }
}

class FileItem extends React.Component {
  componentDidMount() {
    if (!this.props.name) {
      fetch(`/api/oparl/file/${encodeURIComponent(this.props.id)}`)
        .then(res => res.json())
        .then(result => {
          this.setState(result)
        })
    }
  }

  render() {
    let file = this.state || this.props

    return (
      <ListItem
          innerDivStyle={{ paddingRight: "0" }}
          primaryText={file.name}
          leftIcon={<ActionDescription/>}
          onClick={ev => Route.go(`/file/${encodeURIComponent(this.props.id)}`)}
          />
    )
  }
}

const FILES_KEYS = [
  'invitation',
  'masterFile',
  'mainFile',
  'derivativeFile',
  'verbatimProtocol',
  'resultsProtocol',
  'resolutionFile',
  'auxiliaryFile'
]

function findFilesInObject(obj) {
  let results = []
  for(let k of FILES_KEYS) {
    let v = obj[k]
    if (typeof v == 'string') {
      results.push(v)
    } else if (v) {
      results.push(...v)
    }
  }
  return results
}

function iso8601ToDate(iso8601) {
  let m
  if (iso8601 && (m = iso8601.match(/(\d{4})-(\d\d)-(\d\d)/))) {
    return `${m[3]}.${m[2]}.${m[1]}`
  } else {
    return "?"
  }
}
<|endoftext|>


<commit_before>/* @flow */

"use strict"

import url from "url"
import path from "path"
import request from "request"
import express from "express"
import sh from "shelljs"
import handlebars from "handlebars"
import bodyParser from "body-parser"
import prepare from "./prepare"
import FileDb from "./db.js"
import sha1 from "../shared/sha1"
import * as utils from "../shared/utils"

export default class ChartedServer {
  address: any;
  env: t_ENV;
  staticRoot: string;
  store: FileDb;

  static start(port: number, staticRoot: string, db: FileDb) {
    return new Promise((resolve) => {
      let app = express()
      let charted = new ChartedServer(db, staticRoot)

      app.use(bodyParser.json())
      app.use(express.static(staticRoot))
      app.get('/c/:id', charted.getChart.bind(charted))
      app.get('/embed/:id', charted.getChart.bind(charted))
      app.post('/c/:id', charted.saveChart.bind(charted))
      app.get('/load', charted.loadChart.bind(charted))
      app.get('/oembed', charted.getOembed.bind(charted))

      let server = app.listen(port, () => {
        charted.address = server.address()
        resolve(charted)
      })
    })
  }

  constructor(store: FileDb, staticRoot: string) {
    this.store = store
    this.staticRoot = staticRoot
    this.env = {dev: false}
  }

  getChart(req: express$Request, res: express$Response) {
    this.store.get(req.params.id).then((params) => {
      if (!params) {
        this.notFound(res, `chart ${req.params.id} was not found.`)
        return
      }

      let code = sh.cat(path.join(__dirname, '..', 'templates', 'index.html'))
      let html = handlebars.compile(code)({
        ENV: this.env,
        dataUrl: params.dataUrl
      })

      res.status(200).send(html)
    })
  }

  loadChart(req: express$Request, res: express$Response) {
    if (req.query.url) {
      let parsed = url.parse(req.query.url, /* parse query string */ true)
      let chartUrl = url.format(prepare(parsed))
      let params = {dataUrl: chartUrl}

      this.store.set(utils.getChartId(params), params)
        .then(() => this.respondWithChart(res, params))

      return
    }

    if (req.query.id) {
      this.store.get(req.query.id)
        .then((params) => {
          if (!params) {
            this.notFound(res, `chart ${req.query.id} was not found.`)
            return
          }

          this.respondWithChart(res, params)
        })

      return
    }

    this.badRequest(res, 'either url or id is required')
    return
  }

  saveChart(req: express$Request, res: express$Response) {
    let id = req.params.id
    if (utils.getChartId(req.body) != id) {
      this.badRequest(res, 'id and params are out of sync.')
      return
    }

    this.store.set(id, req.body)
    res.setHeader('Content-Type', 'application/json')
    res.status(200)
    res.end(JSON.stringify({status: 'ok'}))
  }

  getOembed(req: express$Request, res: express$Response) {
    // oEmbed requires a URL.
    if (!req.query.url) {
      this.badRequest(res, 'URL Required.')
      return
    }

    // Grab the id from the url.
    const id = utils.parseChartId(req.query.url)

    if (!id) {
      this.badRequest(res, 'Could not parse ID from url')
      return
    }

    // get the chart.
    this.store.get(id)
      .then((params) => {
        if (!params) {
          this.notFound(res, `chart ${id} was not found.`)
          return
        }

        res.setHeader('Content-Type', 'application/json')
        res.status(200)

        res.end(JSON.stringify({
          type: 'rich',
          version: '1.0',
          width: 1280,
          height: 600,
          title: "Charted",
          html: `<iframe src="https://www.charted.co/embed/${id}" width="1280" height="600" scrolling="no" frameborder="0"></iframe>`
        }))
      })
  }

  respondWithChart(res: express$Response, params: t_CHART_PARAM) {
    request(params.dataUrl, (err, resp, body) => {
      if (err) {
        this.badRequest(res, err)
        return
      }

      if (resp.statusCode != 200) {
        this.badRequest(res, `Received HTTP-${resp.statusCode} status code from ${params.dataUrl}`)
        return
      }

      res.setHeader('Content-Type', 'application/json')
      res.status(200)
      res.end(JSON.stringify({params: params, data: body}))
    })
  }

  notFound(res: express$Response, message: string) {
    res.status(404)
    res.end(`Not Found: ${message}`)
  }

  badRequest(res: express$Response, message: string) {
    res.status(400)
    res.end(`Bad Request: ${message}`)
  }
}
<commit_msg>Allow Charted to be run behind proxies<commit_after>/* @flow */

"use strict"

import url from "url"
import path from "path"
import request from "request"
import express from "express"
import sh from "shelljs"
import handlebars from "handlebars"
import bodyParser from "body-parser"
import prepare from "./prepare"
import FileDb from "./db.js"
import sha1 from "../shared/sha1"
import * as utils from "../shared/utils"

export default class ChartedServer {
  address: any;
  env: t_ENV;
  staticRoot: string;
  store: FileDb;

  static start(port: number, staticRoot: string, db: FileDb) {
    return new Promise((resolve) => {
      let app = express()
      let charted = new ChartedServer(db, staticRoot)

      app.enable('trust proxy')
      app.use(bodyParser.json())
      app.use(express.static(staticRoot))
      app.get('/c/:id', charted.getChart.bind(charted))
      app.get('/embed/:id', charted.getChart.bind(charted))
      app.post('/c/:id', charted.saveChart.bind(charted))
      app.get('/load', charted.loadChart.bind(charted))
      app.get('/oembed', charted.getOembed.bind(charted))

      let server = app.listen(port, () => {
        charted.address = server.address()
        resolve(charted)
      })
    })
  }

  constructor(store: FileDb, staticRoot: string) {
    this.store = store
    this.staticRoot = staticRoot
    this.env = {dev: false}
  }

  getChart(req: express$Request, res: express$Response) {
    this.store.get(req.params.id).then((params) => {
      if (!params) {
        this.notFound(res, `chart ${req.params.id} was not found.`)
        return
      }

      let code = sh.cat(path.join(__dirname, '..', 'templates', 'index.html'))
      let html = handlebars.compile(code)({
        ENV: this.env,
        dataUrl: params.dataUrl
      })

      res.status(200).send(html)
    })
  }

  loadChart(req: express$Request, res: express$Response) {
    if (req.query.url) {
      let parsed = url.parse(req.query.url, /* parse query string */ true)
      let chartUrl = url.format(prepare(parsed))
      let params = {dataUrl: chartUrl}

      this.store.set(utils.getChartId(params), params)
        .then(() => this.respondWithChart(res, params))

      return
    }

    if (req.query.id) {
      this.store.get(req.query.id)
        .then((params) => {
          if (!params) {
            this.notFound(res, `chart ${req.query.id} was not found.`)
            return
          }

          this.respondWithChart(res, params)
        })

      return
    }

    this.badRequest(res, 'either url or id is required')
    return
  }

  saveChart(req: express$Request, res: express$Response) {
    let id = req.params.id
    if (utils.getChartId(req.body) != id) {
      this.badRequest(res, 'id and params are out of sync.')
      return
    }

    this.store.set(id, req.body)
    res.setHeader('Content-Type', 'application/json')
    res.status(200)
    res.end(JSON.stringify({status: 'ok'}))
  }

  getOembed(req: express$Request, res: express$Response) {
    // oEmbed requires a URL.
    if (!req.query.url) {
      this.badRequest(res, 'URL Required.')
      return
    }

    // Grab the id from the url.
    const id = utils.parseChartId(req.query.url)

    if (!id) {
      this.badRequest(res, 'Could not parse ID from url')
      return
    }

    // get the chart.
    this.store.get(id)
      .then((params) => {
        if (!params) {
          this.notFound(res, `chart ${id} was not found.`)
          return
        }

        res.setHeader('Content-Type', 'application/json')
        res.status(200)

        res.end(JSON.stringify({
          type: 'rich',
          version: '1.0',
          width: 1280,
          height: 600,
          title: "Charted",
          html: `<iframe src="https://www.charted.co/embed/${id}" width="1280" height="600" scrolling="no" frameborder="0"></iframe>`
        }))
      })
  }

  respondWithChart(res: express$Response, params: t_CHART_PARAM) {
    request(params.dataUrl, (err, resp, body) => {
      if (err) {
        this.badRequest(res, err)
        return
      }

      if (resp.statusCode != 200) {
        this.badRequest(res, `Received HTTP-${resp.statusCode} status code from ${params.dataUrl}`)
        return
      }

      res.setHeader('Content-Type', 'application/json')
      res.status(200)
      res.end(JSON.stringify({params: params, data: body}))
    })
  }

  notFound(res: express$Response, message: string) {
    res.status(404)
    res.end(`Not Found: ${message}`)
  }

  badRequest(res: express$Response, message: string) {
    res.status(400)
    res.end(`Bad Request: ${message}`)
  }
}
<|endoftext|>


<commit_before>/* global self, caches, fetch */
// Based on https://serviceworke.rs/strategy-network-or-cache_service-worker_doc.html

const VERSION = require('../package.json').version
var CACHE = `cache-${VERSION}`
const SimpleLogger = require('./SimpleLogger')
const logger = new SimpleLogger()

const cachedResources = [
  '/',
  '/offline',
  '/css/style-mithril.css',
  '/bin/client-mithril.js',
  '/timesync/timesync.js',
  '/icons/iconfont/MaterialIcons-Regular.woff2',
  '/fonts/roboto/Roboto-Regular.woff2',
  '/api/error'
]

async function fromCache (request) {
  var cache = await caches.open(CACHE)
  var matching = await cache.match(request)
  if (!matching) {
    throw new Error(`No cached data for ${request.method}: ${request.url}`)
  }
  return matching
}

function fromNetwork (request, timeout) {
  return new Promise((resolve, reject) => {
    var timeoutId = setTimeout(reject, timeout)
    fetch(request).then(function (response) {
      clearTimeout(timeoutId)
      if (request.method === 'GET') {
        caches.open(CACHE).then(cache => {
          cache.put(request, response.clone())
        })
      }
      resolve(response.clone())
    }, reject)
  })
}

self.addEventListener('install', evt => {
  logger.log(`New ServiceWorker ${VERSION} is being installed`, 'ServiceWorker')
  self.skipWaiting()
  evt.waitUntil(caches.open(CACHE).then(cache => {
    cache.addAll(cachedResources)
  }))
})
self.addEventListener('activate', evt => {
  evt.waitUntil(
    caches.keys()
      .then(keys => Promise.all(keys.map(key => {
        if (key !== CACHE) {
          return caches.delete(key)
        }
      })))
      .then(() => {
        logger.log(`Ready to use cache ${CACHE}`, 'ServiceWorker')
      })
  )
})
self.addEventListener('fetch', function (evt) {
  evt.respondWith(
    fromNetwork(evt.request, 4000)
      .catch(function (e) {
        logger.log(`${evt.request.url} serving from cached`, 'ServiceWorker')
        return fromCache(evt.request)
      })
      .catch(function (e) {
        return fromCache('/api/error')
      })
  )
})
<commit_msg>Increase service worker timeout<commit_after>/* global self, caches, fetch */
// Based on https://serviceworke.rs/strategy-network-or-cache_service-worker_doc.html

const VERSION = require('../package.json').version
var CACHE = `cache-${VERSION}`
const SimpleLogger = require('./SimpleLogger')
const logger = new SimpleLogger()

const cachedResources = [
  '/',
  '/offline',
  '/css/style-mithril.css',
  '/bin/client-mithril.js',
  '/timesync/timesync.js',
  '/icons/iconfont/MaterialIcons-Regular.woff2',
  '/fonts/roboto/Roboto-Regular.woff2',
  '/api/error'
]

async function fromCache (request) {
  var cache = await caches.open(CACHE)
  var matching = await cache.match(request)
  if (!matching) {
    throw new Error(`No cached data for ${request.method}: ${request.url}`)
  }
  return matching
}

function fromNetwork (request, timeout) {
  return new Promise((resolve, reject) => {
    var timeoutId = setTimeout(reject, timeout)
    fetch(request).then(function (response) {
      clearTimeout(timeoutId)
      if (request.method === 'GET') {
        caches.open(CACHE).then(cache => {
          cache.put(request, response.clone())
        })
      }
      resolve(response.clone())
    }, reject)
  })
}

self.addEventListener('install', evt => {
  logger.log(`New ServiceWorker ${VERSION} is being installed`, 'ServiceWorker')
  self.skipWaiting()
  evt.waitUntil(caches.open(CACHE).then(cache => {
    cache.addAll(cachedResources)
  }))
})
self.addEventListener('activate', evt => {
  evt.waitUntil(
    caches.keys()
      .then(keys => Promise.all(keys.map(key => {
        if (key !== CACHE) {
          return caches.delete(key)
        }
      })))
      .then(() => {
        logger.log(`Ready to use cache ${CACHE}`, 'ServiceWorker')
      })
  )
})
self.addEventListener('fetch', function (evt) {
  evt.respondWith(
    fromNetwork(evt.request, 8000)
      .catch(function (e) {
        logger.log(`${evt.request.url} serving from cached`, 'ServiceWorker')
        return fromCache(evt.request)
      })
      .catch(function (e) {
        return fromCache('/api/error')
      })
  )
})
<|endoftext|>