<commit_before>function import_request_variables (types, prefix) {
    // http://kevin.vanzonneveld.net
    // +      original by: Jalal Berrami
    // + reimplemented by: Brett Zamir (http://brettz9.blogspot.com)
    // + improved by: Brett Zamir (http://brettz9.blogspot.com)
    // *        example 1: document.cookie = 'snack=yummy';
    // *        example 1: import_request_variables('gc', 'pr_');
    // *        results 1: pr_snack == 'yummy'

    var i = 0, current = '', url = '', vars = '', arrayBracketPos = -1, arrName='', win = this.window, targetObj = this.window;
    prefix = prefix || '';

    if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.requestVarsObj'] &&
        this.php_js.ini['phpjs.requestVarsObj'].local_value) { // Allow designated object to be used instead of window
        targetObj = this.php_js.ini['phpjs.requestVarsObj'].local_value;
    }

    if (/g/i.test(types)) { // GET
        for(i = 0, url = win.location.href, vars = url.substring(url.lastIndexOf("?") + 1, url.length).split("&"); i < vars.length; i++){
            current = vars[i].split("=");
            arrayBracketPos = current[0].indexOf('[');
            if (arrayBracketPos !== -1) {
                arrName = current[0].substring(0, arrayBracketPos);
                if (!targetObj[prefix+arrName]) {
                    targetObj[prefix+arrName] = [];
                }
                targetObj[prefix+arrName].push(current[1] || null);
            }
            else {
                targetObj[prefix+current[0]] = current[1] || null;
            }
        }
    }
    if (/c/i.test(types)) { // COOKIE
        for(i = 0, vars = win.document.cookie.split("&"); i < vars.length;i++){
            current = vars[i].split("=");
            targetObj[prefix+current[0]] = current[1].split(";")[0] || null;
        }
    }
}<commit_msg>Allow escaped characters to be unescaped<commit_after>function import_request_variables (types, prefix) {
    // http://kevin.vanzonneveld.net
    // +      original by: Jalal Berrami
    // + reimplemented by: Brett Zamir (http://brettz9.blogspot.com)
    // + improved by: Brett Zamir (http://brettz9.blogspot.com)
    // *        example 1: document.cookie = 'snack=yummy';
    // *        example 1: import_request_variables('gc', 'pr_');
    // *        results 1: pr_snack == 'yummy'

    var i = 0, current = '', url = '', vars = '', arrayBracketPos = -1, arrName='', win = this.window, targetObj = this.window;
    prefix = prefix || '';

    if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.requestVarsObj'] &&
        this.php_js.ini['phpjs.requestVarsObj'].local_value) { // Allow designated object to be used instead of window
        targetObj = this.php_js.ini['phpjs.requestVarsObj'].local_value;
    }

    if (/g/i.test(types)) { // GET
        for(i = 0, url = win.location.href, vars = url.substring(url.lastIndexOf("?") + 1, url.length).split("&"); i < vars.length; i++){
            current = vars[i].split("=");
            current[1] = decodeURIComponent(current[1]);
            arrayBracketPos = current[0].indexOf('[');
            if (arrayBracketPos !== -1) {
                arrName = current[0].substring(0, arrayBracketPos);
                arrName = decodeURIComponent(arrName);
                if (!targetObj[prefix+arrName]) {
                    targetObj[prefix+arrName] = [];
                }
                targetObj[prefix+arrName].push(current[1] || null);
            }
            else {
                current[0] = decodeURIComponent(current[0]);
                targetObj[prefix+current[0]] = current[1] || null;
            }
        }
    }
    if (/c/i.test(types)) { // COOKIE
        for(i = 0, vars = win.document.cookie.split("&"); i < vars.length;i++){
            current = vars[i].split("=");
            targetObj[prefix+current[0]] = current[1].split(";")[0] || null;
        }
    }
}<|endoftext|>


<commit_before>'use strict';

var ObjUtils = require('./../../../utilities/object');

// A BEST Application with a Famo.us Context
var EXECUTED_COMPONENTS = {};

// A component is an instantiated BEST module.
var COMPONENTS = {};

// List of the tagged dependencies of every tagged module.
var DEPENDENCIES = {};

// A module is a BEST definition.
var MODULES = {};

// Config objects
var CONFIGS = {};

// Timelines objects
var TIMELINES = {};

// Attachment objects ('raw code' wrappers)
var ATTACHMENTS = {};

var DEFAULT_TAG = 'HEAD';
var BEHAVIORS_KEY = 'behaviors';
var EVENTS_KEY = 'events';
var STATES_KEY = 'states';
var TREE_KEY = 'tree';
var EXTENSION_KEYS = 'extensions';

function getAttachments(name, tag) {
    if (ATTACHMENTS[name] && ATTACHMENTS[name][tag]) {
        return ObjUtils.clone(ATTACHMENTS[name][tag]);
    }
    else {
        return [];
    }
}

function setAttachment(name, tag, info) {
    if (!ATTACHMENTS[name]) ATTACHMENTS[name] = {};
    if (!ATTACHMENTS[name][tag]) ATTACHMENTS[name][tag] = [];
    ATTACHMENTS[name][tag].push(info);
}

function Wrapper(name, tag, mod) {
    this.name = name;
    this.tag = tag;
    this.mod = mod;
}

Wrapper.prototype.config = function(conf) {
    if (!CONFIGS[this.name]) {
        CONFIGS[this.name] = {};
    }
    CONFIGS[this.name][this.tag] = conf;
    return this;
};

function getConfig(name, tag) {
    if (CONFIGS[name] && CONFIGS[name][tag]) {
        return ObjUtils.clone(CONFIGS[name][tag]);
    }
    else {
        return {};
    }
}

Wrapper.prototype.timelines = function timelines(timelinesObject) {
    if (!TIMELINES[this.name]) {
        TIMELINES[this.name] = {};
    }
    TIMELINES[this.name][this.tag] = timelinesObject;
    return this;
};

function getTimelines(name, tag) {
    if (TIMELINES[name] && TIMELINES[name][tag]) {
        return ObjUtils.clone(TIMELINES[name][tag]);
    }
    else {
        return {};
    }
}

function wrapModule(name, tag, mod) {
    return new Wrapper(name, tag, mod);
}

function getModuleDefinition(name, tag, useClone) {
    useClone = useClone === undefined ? true : useClone;
    tag = tag ? tag : DEFAULT_TAG;
    if (MODULES[name] && MODULES[name][tag]) {
        return useClone ? ObjUtils.clone(MODULES[name][tag].definition) : MODULES[name][tag].definition;
    }
    else {
        return null;
    }
}

function saveModule(name, tag, options, definition) {
    if (!MODULES[name]) {
        MODULES[name] = {};
    }
    MODULES[name][tag] = {
        definition: definition,
        options: options
    };
    saveDependencies(name, tag, options.dependencies || {});
    return getModuleDefinition(name, tag);
}

function hasModule(name, tag) {
    return !!getModuleDefinition(name, tag);
}

var NORMAL_FACET_NAMES = {
    'behaviors': true,
    'events': true,
    'states': true,
    'tree': true
};

function extendDefintion(definition, extensions) {
    var extensionDefinition;
    for (var i = 0; i < extensions.length; i++) {
        extensionDefinition = getModuleDefinition(extensions[i].name, extensions[i].version, false);

        definition[BEHAVIORS_KEY] = definition[BEHAVIORS_KEY] ? definition[BEHAVIORS_KEY] : {};
        definition[EVENTS_KEY] = definition[EVENTS_KEY] ? definition[EVENTS_KEY] : {};
        definition[STATES_KEY] = definition[STATES_KEY] ? definition[STATES_KEY] : {};
        ObjUtils.naiveExtends(definition[BEHAVIORS_KEY], extensionDefinition[BEHAVIORS_KEY]);
        ObjUtils.naiveExtends(definition[EVENTS_KEY], extensionDefinition[EVENTS_KEY]);
        ObjUtils.naiveExtends(definition[STATES_KEY], extensionDefinition[STATES_KEY]);

        if (!definition[TREE_KEY]) {
            definition[TREE_KEY] = extensionDefinition[TREE_KEY] || '';
        }
    }
}

function validateModule(name, tag, options, definition) {
    if (/[A-Z]/.test(name)) {
        console.warn('`' + name + ' (' + tag + ')` ' + 'has an uppercase letter in its name; use lowercase only');
    }
    for (var facetName in definition) {
        if (!(facetName in NORMAL_FACET_NAMES)) {
            console.warn('`' + name + ' (' + tag + ')` ' + 'has an unrecognized property `' + facetName + '` in its definition');
        }

        if (options[EXTENSION_KEYS]) {
            extendDefintion(definition, options[EXTENSION_KEYS]);
        }
    }
}

function registerModule(name, tag, options, definition) {
    if (!hasModule(name, tag)) {
        validateModule(name, tag, options, definition);
        return wrapModule(name, tag, saveModule(name, tag, options, definition));
    }
    else {
        return wrapModule(name, tag, getModuleDefinition(name, tag));
    }
}

function getComponent(uid) {
    return COMPONENTS[uid];
}

function hasComponent(uid) {
    return !!getComponent(uid);
}

function saveComponent(uid, component) {
    COMPONENTS[uid] = component;
}

function registerComponent(uid, component) {
    if (hasComponent(uid)) {
        throw new Error('Component with UID `' + uid + '` already exists!');
    }
    else {
        saveComponent(uid, component);
    }
}

function saveExecutedComponent(selector, component) {
    EXECUTED_COMPONENTS[selector] = component;
}

function getExecutedComponent(selector) {
    return EXECUTED_COMPONENTS[selector];
}

// We need to store the dependencies that a given module depends on
// because we need to assign the version tags to all of the components
// in the tree for use when selecting elements correctly
function saveDependencies(name, tag, dependencies) {
    if (!DEPENDENCIES[name]) {
        DEPENDENCIES[name] = {};
    }
    DEPENDENCIES[name][tag] = {};
    for (var depName in dependencies) {
        var depVersion = dependencies[depName];
        DEPENDENCIES[name][tag][depName] = depVersion;
    }
}

function getDependencies(name, tag) {
    if (DEPENDENCIES[name] && DEPENDENCIES[name][tag]) {
        return DEPENDENCIES[name][tag];
    }
    else {
        throw new Error('No dependencies found for `' + name + ' (' + tag + ')`');
    }
}

module.exports = {
    getAttachments: getAttachments,
    getComponent: getComponent,
    getConfig: getConfig,
    getDependencies: getDependencies,
    getExecutedComponent: getExecutedComponent,
    getModuleDefinition: getModuleDefinition,
    getTimelines: getTimelines,
    registerComponent: registerComponent,
    registerModule: registerModule,
    saveDependencies: saveDependencies,
    saveExecutedComponent: saveExecutedComponent,
    setAttachment: setAttachment
};
<commit_msg>Check for the extension definition before trying to extend<commit_after>'use strict';

var ObjUtils = require('./../../../utilities/object');

// A BEST Application with a Famo.us Context
var EXECUTED_COMPONENTS = {};

// A component is an instantiated BEST module.
var COMPONENTS = {};

// List of the tagged dependencies of every tagged module.
var DEPENDENCIES = {};

// A module is a BEST definition.
var MODULES = {};

// Config objects
var CONFIGS = {};

// Timelines objects
var TIMELINES = {};

// Attachment objects ('raw code' wrappers)
var ATTACHMENTS = {};

var DEFAULT_TAG = 'HEAD';
var BEHAVIORS_KEY = 'behaviors';
var EVENTS_KEY = 'events';
var STATES_KEY = 'states';
var TREE_KEY = 'tree';
var EXTENSION_KEYS = 'extensions';

function getAttachments(name, tag) {
    if (ATTACHMENTS[name] && ATTACHMENTS[name][tag]) {
        return ObjUtils.clone(ATTACHMENTS[name][tag]);
    }
    else {
        return [];
    }
}

function setAttachment(name, tag, info) {
    if (!ATTACHMENTS[name]) ATTACHMENTS[name] = {};
    if (!ATTACHMENTS[name][tag]) ATTACHMENTS[name][tag] = [];
    ATTACHMENTS[name][tag].push(info);
}

function Wrapper(name, tag, mod) {
    this.name = name;
    this.tag = tag;
    this.mod = mod;
}

Wrapper.prototype.config = function(conf) {
    if (!CONFIGS[this.name]) {
        CONFIGS[this.name] = {};
    }
    CONFIGS[this.name][this.tag] = conf;
    return this;
};

function getConfig(name, tag) {
    if (CONFIGS[name] && CONFIGS[name][tag]) {
        return ObjUtils.clone(CONFIGS[name][tag]);
    }
    else {
        return {};
    }
}

Wrapper.prototype.timelines = function timelines(timelinesObject) {
    if (!TIMELINES[this.name]) {
        TIMELINES[this.name] = {};
    }
    TIMELINES[this.name][this.tag] = timelinesObject;
    return this;
};

function getTimelines(name, tag) {
    if (TIMELINES[name] && TIMELINES[name][tag]) {
        return ObjUtils.clone(TIMELINES[name][tag]);
    }
    else {
        return {};
    }
}

function wrapModule(name, tag, mod) {
    return new Wrapper(name, tag, mod);
}

function getModuleDefinition(name, tag, useClone) {
    useClone = useClone === undefined ? true : useClone;
    tag = tag ? tag : DEFAULT_TAG;
    if (MODULES[name] && MODULES[name][tag]) {
        return useClone ? ObjUtils.clone(MODULES[name][tag].definition) : MODULES[name][tag].definition;
    }
    else {
        return null;
    }
}

function saveModule(name, tag, options, definition) {
    if (!MODULES[name]) {
        MODULES[name] = {};
    }
    MODULES[name][tag] = {
        definition: definition,
        options: options
    };
    saveDependencies(name, tag, options.dependencies || {});
    return getModuleDefinition(name, tag);
}

function hasModule(name, tag) {
    return !!getModuleDefinition(name, tag);
}

var NORMAL_FACET_NAMES = {
    'behaviors': true,
    'events': true,
    'states': true,
    'tree': true
};

function extendDefintion(definition, extensions) {
    var extensionDefinition;
    for (var i = 0; i < extensions.length; i++) {
        extensionDefinition = getModuleDefinition(extensions[i].name, extensions[i].version, false);

        definition[BEHAVIORS_KEY] = definition[BEHAVIORS_KEY] ? definition[BEHAVIORS_KEY] : {};
        definition[EVENTS_KEY] = definition[EVENTS_KEY] ? definition[EVENTS_KEY] : {};
        definition[STATES_KEY] = definition[STATES_KEY] ? definition[STATES_KEY] : {};

        if (extensionDefinition) {
            ObjUtils.naiveExtends(definition[BEHAVIORS_KEY], extensionDefinition[BEHAVIORS_KEY]);
            ObjUtils.naiveExtends(definition[EVENTS_KEY], extensionDefinition[EVENTS_KEY]);
            ObjUtils.naiveExtends(definition[STATES_KEY], extensionDefinition[STATES_KEY]);

            if (!definition[TREE_KEY]) {
                definition[TREE_KEY] = extensionDefinition[TREE_KEY] || '';
            }
        }
    }
}

function validateModule(name, tag, options, definition) {
    if (/[A-Z]/.test(name)) {
        console.warn('`' + name + ' (' + tag + ')` ' + 'has an uppercase letter in its name; use lowercase only');
    }
    for (var facetName in definition) {
        if (!(facetName in NORMAL_FACET_NAMES)) {
            console.warn('`' + name + ' (' + tag + ')` ' + 'has an unrecognized property `' + facetName + '` in its definition');
        }

        if (options[EXTENSION_KEYS]) {
            extendDefintion(definition, options[EXTENSION_KEYS]);
        }
    }
}

function registerModule(name, tag, options, definition) {
    if (!hasModule(name, tag)) {
        validateModule(name, tag, options, definition);
        return wrapModule(name, tag, saveModule(name, tag, options, definition));
    }
    else {
        return wrapModule(name, tag, getModuleDefinition(name, tag));
    }
}

function getComponent(uid) {
    return COMPONENTS[uid];
}

function hasComponent(uid) {
    return !!getComponent(uid);
}

function saveComponent(uid, component) {
    COMPONENTS[uid] = component;
}

function registerComponent(uid, component) {
    if (hasComponent(uid)) {
        throw new Error('Component with UID `' + uid + '` already exists!');
    }
    else {
        saveComponent(uid, component);
    }
}

function saveExecutedComponent(selector, component) {
    EXECUTED_COMPONENTS[selector] = component;
}

function getExecutedComponent(selector) {
    return EXECUTED_COMPONENTS[selector];
}

// We need to store the dependencies that a given module depends on
// because we need to assign the version tags to all of the components
// in the tree for use when selecting elements correctly
function saveDependencies(name, tag, dependencies) {
    if (!DEPENDENCIES[name]) {
        DEPENDENCIES[name] = {};
    }
    DEPENDENCIES[name][tag] = {};
    for (var depName in dependencies) {
        var depVersion = dependencies[depName];
        DEPENDENCIES[name][tag][depName] = depVersion;
    }
}

function getDependencies(name, tag) {
    if (DEPENDENCIES[name] && DEPENDENCIES[name][tag]) {
        return DEPENDENCIES[name][tag];
    }
    else {
        throw new Error('No dependencies found for `' + name + ' (' + tag + ')`');
    }
}

module.exports = {
    getAttachments: getAttachments,
    getComponent: getComponent,
    getConfig: getConfig,
    getDependencies: getDependencies,
    getExecutedComponent: getExecutedComponent,
    getModuleDefinition: getModuleDefinition,
    getTimelines: getTimelines,
    registerComponent: registerComponent,
    registerModule: registerModule,
    saveDependencies: saveDependencies,
    saveExecutedComponent: saveExecutedComponent,
    setAttachment: setAttachment
};
<|endoftext|>


<commit_before><commit_msg>Added Chrome & Firefox support through selenium<commit_after><|endoftext|>


<commit_before>/**
 * @noflow
 * @private
 */

import events from 'events';
import createLog from 'silk-log/device';
import fs from 'fs';
import * as util from 'silk-sysutils';
const log = createLog('input');

//
// Note: The InputDevice class has been inspired by
// https://github.com/Bornholm/node-keyboard (MIT)
//
class InputDevice extends events.EventEmitter {
  constructor(dev) {
    super();
    log.info(`InputDevice: ${dev}`);
    this.dev = dev;
    this.bufferSize = 16; // input event size for a 32-bit kernel ONLY
    this.buf = new Buffer(this.bufferSize);
    fs.open(this.dev, 'r', (err, fd) => {
      if (err) {
        log.warn(`Unable to open ${dev}`);
        return;
      }
      this.fd = fd;
      this._read();
    });
  }

  _read() {
    fs.read(this.fd, this.buf, 0, this.bufferSize, null,
      (err, bytesRead, buffer) => this._onRead(err, bytesRead, buffer));
  }

  _onRead(err, bytesRead, buffer) {
    if (err) {
      log.warn(`Unable to read ${this.dev}: ${err}`);
      return;
    }
    let event;
    if (buffer.readUInt16LE(8) === 1 /*EV_KEY*/ ) {

      /**
       * Key event type
       *
       * @name KeyEventType
       * @typedef {Object} KeyEventType
       *
       * @property {number} timeS Timestamp (Seconds part)
       * @property {number} timeMS  Timestamp (Microseconds part)
       * @property {number} keyCode Keyboard code
       * @memberof silk-input
       */
      event = {
        timeS: buffer.readUInt32LE(0),
        timeMS: buffer.readUInt32LE(4),
        keyCode: buffer.readUInt16LE(10),
        type: ['up', 'down', 'repeat'][buffer.readUInt32LE(12)],
      };
      const keys = {
        113: 'mute',
        114: 'volumedown',
        115: 'volumeup',
        116: 'power',
        224: 'brightnessdown',
        225: 'brightnessup',
        330: 'touch',
      };
      event.keyId = keys[event.keyCode];
    }

    if (event) {
      log.debug(`input event: ${event.type}, ${event.keyId}`);
      this.emit(event.type, event);
    }
    this._read();
  }
}

let inputDevices = {};

/**
 * Silk Input
 *
 * @module silk-input
 * @example
 * 'use strict';
 *
 * const Input = require('silk-input').default;
 * const log = require('silk-alog');
 *
 * let input = new Input();
 * input.on('down', e => log.info('Key down event', JSON.stringify(e)));
 * input.on('up', e => log.info('Key up event', JSON.stringify(e)));
 */
export default class Input extends events.EventEmitter {
  constructor() {
    super();

    const devices = util.getlistprop('ro.silk.ui.inputevents', null);
    if (devices) {
      this._open(devices);
    } else {
      fs.readdir('/dev/input', (err, availDevices) => {
        if (err) {
          log.warn(`Unable to enumerate input devices: ${err}`);
          return;
        }
        this._open(availDevices.filter(i => i.substring(0, 5) === 'event'));
      });
    }
  }

  _open(devices) {
    this._inputDevices = devices.map(device => {
      const devicePath = `/dev/input/${device}`;
      let d;
      d = inputDevices[devicePath];
      if (!d) {
        d = new InputDevice(devicePath);
        inputDevices[devicePath] = d;
      }

      /**
       * This event is emitted when a key is released
       *
       * @event up
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('up', e => this.emit('up', e));

      /**
       * This event is emitted when a key is pressed
       *
       * @event down
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('down', e => this.emit('down', e));

      /**
       * This event is emitted when key is pressed and held down
       *
       * @event repeat
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('repeat', e => this.emit('repeat', e));
      return d;
    });
  }
}
<commit_msg>Flowify silk-input<commit_after>/**
 * @flow
 * @private
 */

import events from 'events';
import createLog from 'silk-log/device';
import fs from 'fs';
import * as util from 'silk-sysutils';
const log = createLog('input');

//
// Note: The InputDevice class has been inspired by
// https://github.com/Bornholm/node-keyboard (MIT)
//
class InputDevice extends events.EventEmitter {
  buf: Buffer;
  bufferSize: number;
  dev: string;
  fd: number;

  constructor(dev: string) {
    super();
    log.info(`InputDevice: ${dev}`);
    this.dev = dev;
    this.bufferSize = 16; // input event size for a 32-bit kernel ONLY
    this.buf = new Buffer(this.bufferSize);
    fs.open(this.dev, 'r', 0, (err, fd) => {
      if (err) {
        log.warn(`Unable to open ${dev}`);
        return;
      }
      this.fd = fd;
      this._read();
    });
  }

  _read() {
    fs.read(this.fd, this.buf, 0, this.bufferSize, null,
      (err, bytesRead, buffer) => this._onRead(err, bytesRead, buffer));
  }

  _onRead(err, bytesRead, buffer) {
    if (err) {
      log.warn(`Unable to read ${this.dev}: ${err.toString()}`);
      return;
    }
    let event;
    if (buffer.readUInt16LE(8) === 1 /*EV_KEY*/ ) {

      /**
       * Key event type
       *
       * @name KeyEventType
       * @typedef {Object} KeyEventType
       *
       * @property {number} timeS Timestamp (Seconds part)
       * @property {number} timeMS  Timestamp (Microseconds part)
       * @property {number} keyCode Keyboard code
       * @property {string} keyId String key id
       * @memberof silk-input
       */
      event = {
        timeS: buffer.readUInt32LE(0),
        timeMS: buffer.readUInt32LE(4),
        keyCode: buffer.readUInt16LE(10),
        keyId: 'unknown',
        type: ['up', 'down', 'repeat'][buffer.readUInt32LE(12)],
      };
      const keys = {
        '113': 'mute',
        '114': 'volumedown',
        '115': 'volumeup',
        '116': 'power',
        '224': 'brightnessdown',
        '225': 'brightnessup',
        '330': 'touch',
      };
      if (keys[event.keyCode]) {
        event.keyId = keys[event.keyCode];
      }
    }

    if (event) {
      log.debug(`input event: ${event.type}, ${event.keyId}`);
      this.emit(event.type, event);
    }
    this._read();
  }
}

let inputDevices = {};

/**
 * Silk Input
 *
 * @module silk-input
 * @example
 * 'use strict';
 *
 * const Input = require('silk-input').default;
 * const log = require('silk-alog');
 *
 * let input = new Input();
 * input.on('down', e => log.info('Key down event', JSON.stringify(e)));
 * input.on('up', e => log.info('Key up event', JSON.stringify(e)));
 */
export default class Input extends events.EventEmitter {
  _inputDevices: ?Array<InputDevice>;

  constructor() {
    super();

    const devices = util.getlistprop('ro.silk.ui.inputevents');
    if (devices.length > 0) {
      this._open(devices);
    } else {
      fs.readdir('/dev/input', (err, availDevices) => {
        if (err) {
          log.warn(`Unable to enumerate input devices: ${err.toString()}`);
          return;
        }
        this._open(availDevices.filter(i => i.substring(0, 5) === 'event'));
      });
    }
  }

  _open(devices: Array<string>) {
    this._inputDevices = devices.map(device => {
      const devicePath = `/dev/input/${device}`;
      let d;
      d = inputDevices[devicePath];
      if (!d) {
        d = new InputDevice(devicePath);
        inputDevices[devicePath] = d;
      }

      /**
       * This event is emitted when a key is released
       *
       * @event up
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('up', e => this.emit('up', e));

      /**
       * This event is emitted when a key is pressed
       *
       * @event down
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('down', e => this.emit('down', e));

      /**
       * This event is emitted when key is pressed and held down
       *
       * @event repeat
       * @memberof silk-input
       * @instance
       * @property {KeyEventType} e
       */
      d.on('repeat', e => this.emit('repeat', e));
      return d;
    });
  }
}
<|endoftext|>


<commit_before>(function() {
  var eyex;
  var eyegaze_edge;
  var pending_edge;
  var available = {};

  try {
    eyex = require('eyex');
  } catch(e) { }
  try {
    pending_edge = require('eyegaze_edge');
  } catch(e) { }
  if(eyex) {
    try {
      eyex.setup();
      available.eyex = true;
    } catch(e) {
      eyex = null;
    }
  }
  if(pending_edge) {
    try {
      pending_edge.setup(function(res) {
        if(res && res.ready) {
          eyegaze_edge = pending_edge;
          available.eyegaze_edge = true;
        }
      });
    } catch(e) {
      pending_edge = null;
    }
  }

  var poll = null;
  var lasts = {};
  var gaze_history = [];
  var callbacks = [];
  var any_averaged = false;
  var run_callbacks = function(message) {
    callbacks.forEach(function(obj) {
      if(message.type == 'over' && obj.level == 'noisy') {
        obj.callback(message);
      } else if(message.type == 'linger' && obj.level == 'averaged') {
        obj.callback(message);
      }
    });
  };
  var gazelinger = {
    statuses: {
    },
    available: available,
    listen: function(callback, level) {
      level = level || 'noisy';
      if(level == 'averaged') { any_averaged = true; }
      if(eyex && available.eyex) {
        try { eyex.teardown(); } catch(e) { }
        setTimeout(function() {
          try {
            eyex.setup();
            lasts.give_up_eyex = false;
          } catch(e) { }

        }, 500);
      }
      if(!poll) {
        var poll = function() {
          if(callbacks.length == 0) { return; }
          // Race condition since eyegaze edge setup check takes a few second, if you start listening
          // too soon then you'll miss this listen call unless it's part of the polling loop.
          if(eyegaze_edge && eyegaze_edge.listen && available.eyegaze_edge && !eyegaze_edge.listening) {
            // idempotent
            eyegaze_edge.listen();
          }
          var data = {};
          var now = (new Date()).getTime();
          if(eyex && eyex.ping) {
            data.eyex = eyex.ping();
            if(data.eyex && data.eyex.status) {
              gazelinger.statuses.eyex = {code: data.eyex.status};
              // If EyeX has disconnected or failed, try reconnecting a few times
              if(!lasts.give_up_eyex && (data.eyex.status == 5 || data.eyex.status == -1)) {
                lasts.eyex_attempts = lasts.eyex_attempts || [];
                var attempts = [];
                // After 5 failed attempts within a 2-minute window, go ahead and give up
                for(var idx = 0; idx < lasts.eyex_attempts.length; idx++) {
                  if(lasts.eyex_attempts[idx] > (now - (120 * 1000))) {
                    attempts.push(lasts.eyex_attempts[idx]);
                  }
                }
                lasts.eyex_attempts.push(now);
                if(lasts.eyex_attempts.length > 5) {
                  lasts.give_up_eyex = true;
                } else {
                  try { eyex.teardown(); } catch(e) { }
                  setTimeout(function() {
                    try {
                      eyex.setup();
                      lasts.give_up_eyex = false;
                    } catch(e) { }
                  }, 500);
                }
              }
            }
            if(data.eyex && (data.eyex.gaze_ts == 0 || lasts.eyex == data.eyex.gaze_ts)) {
              data.eyex = null;
            } else if(data.eyex) {
              lasts.eyex = data.eyex.gaze_ts;
              lasts.eyex_js = now;
            }
            if(lasts.eyex_js && gazelinger.statuses.eyex && lasts.eyex_js < (now - (30 * 1000))) {
              gazelinger.statuses.eyex.dormant = true;
            }
          }
          if(eyegaze_edge && eyegaze_edge.ping) {
            data.eyegaze_edge = eyegaze_edge.ping();
            if(data.eyegaze_edge && data.eyegaze_edge.status) {
              gazelinger.statuses.eyegaze_edge = {code: data.eyegaze_edge.status};
            }
            if(data.eyegaze_edge && (data.eyegaze_edge.gaze_ts == 0 || lasts.eyegaze_edge == data.eyegaze_edge.gaze_ts)) {
              data.eyegaze_edge = null;
            } else if(data.eyegaze_edge) {
              lasts.eyegaze_edge = data.eyegaze_edge.gaze_ts;
              lasts.eyegaze_edge_js = now;
            }
            if(lasts.eyegaze_edge_js && gazelinger.statuses.eyegaze_edge && lasts.eyegaze_edge_js < (now - (30 * 1000))) {
              gazelinger.statuses.eyegaze_edge.dormant = true;
            }
          }
          data.result = {};
          var keys = ['eyex', 'eyegaze_edge'];
          for(var idx = 0; idx < keys.length; idx++) {
            if(data[keys[idx]]) {
              data.result = data[keys[idx]];
              data.result.hardware = keys[idx];
            }
          }
          data = data.result;

          var message = { raw: data };
          message.ts = (new Date()).getTime();

          if(data.end_ts && data.begin_ts && data.end_ts > data.begin_ts && data.end_ts != lasts.linger) {
//             console.log("linger duration " + (data.end_ts - data.begin_ts));
//             console.log("time since last " + (data.end_ts - lasts.linger));
            lasts.linger = data.end_ts;

            message = { raw: data};
            message.ts = (new Date()).getTime();
            message.screenX = data.data_x; //(data.data_x / ratio) - (window.screenInnerOffsetX || window.screenX);
            message.screenY = data.data_y; // = (data.data_y / ratio) - (window.screenInnerOffsetY || window.screenY);
            message.duration = (data.end_ts - data.begin_ts);
            message.scaled = data.scaled !== false;
            message.type = 'linger';
//            run_callbacks(message);
          }
          if(data.gaze_ts && data.gaze_ts != lasts.gaze) {
            lasts.gaze = data.gaze_ts;
            message = { raw: data};
            message.ts = (new Date()).getTime();
            message.screenX = data.gaze_x; //(data.gaze_x / ratio) - (window.screenInnerOffsetX || window.screenX);
            message.screenY = data.gaze_y; //(data.gaze_y / ratio) - (window.screenInnerOffsetY || window.screenY);
            message.duration = 50;
            message.scaled = data.scaled !== false;
            message.type = 'over';
            message.ts = data.gaze_ts;
            run_callbacks(message);

            if(any_averaged) {
              gaze_history.push({
                x: message.screenX,
                y: message.screenY,
                ts: message.ts
              });

              // prune based on distance from latest timestamp
              var new_history = [];
              gaze_history.forEach(function (e) { if (lasts.gaze - e.ts < 200) { new_history.push(e); } });
              gaze_history = new_history;

              // find a quick median
              var xs = gaze_history.sort(function (a, b) { return b.x - a.x });
              var midx = xs[Math.ceil(xs.length / 2)];
              var ys = gaze_history.sort(function (a, b) { return b.y - a.y });
              var midy = ys[Math.ceil(ys.length / 2)];

              // won't enter this until there are at least 2 events
              if (midx && midy) {
                midx = midx.x;
                midy = midy.y;

                // temporarily remove outliers
                var filtered_history = gaze_history.filter(function (e) { return (Math.abs(e.x - midx) < 50) && (Math.abs(e.y - midy) < 50); });
                // wait until there are at least 3 clustered events, which means a minimum of 150ms
                if (filtered_history.length > 2) {
                  var biggest_dist = 0;
                  lasts.history = gaze_history[0].ts;
                  filtered_history.forEach(function (e) { biggest_dist = Math.max(biggest_dist, e.ts - lasts.history); lasts.history - e.ts; });

                  // if there are no significant time gaps, compute a new middle and trigger a linger event
                  if (biggest_dist <= 60) {
                    var mean_x = 0;
                    var mean_y = 0;
                    filtered_history.forEach(function (e) { mean_x = mean_x + e.x; mean_y = mean_y + e.y; });
                    mean_x = mean_x / filtered_history.length;
                    mean_y = mean_y / filtered_history.length;

                    message = { raw: data};
                    message.ts = (new Date()).getTime();
                    message.screenX = mean_x;
                    message.screenY = mean_y;
                    message.type = 'linger';
                    message.duration = filtered_history[filtered_history.length - 1].ts - filtered_history[0].ts;
                    message.scaled = data.scaled !== false;
                    run_callbacks(message);
                    // slicing at index 3 means you will get at most a linger event every 150ms
                    gaze_history = gaze_history.slice(3, 50);
                  }
                }
              }
            }
          }
          // I was using setInterval, but it was causing a queue backlog, I thought this would help.
          setTimeout(poll, 45);
        };
        setTimeout(poll, 45);
      }
      callbacks.push({callback: callback, level: level});
    },
    stop_listening: function() {
      // TODO: support multiple listeners
      callbacks = [];
      any_averaged = false;
      clearTimeout(poll);
      poll = null;
      if(eyegaze_edge && eyegaze_edge.stop_listening) {
        eyegaze_edge.stop_listening();
      }
    },
    can_calibrate: function() {
      return !!eyegaze_edge;
    },
    calibrate: function() {
      if(eyegaze_edge) {
        eyegaze_edge.calibrate();
      }
    }
  };
  module.exports = gazelinger;
})();
<commit_msg>prevent truncating all new events if too many arrive<commit_after>(function() {
  var eyex;
  var eyegaze_edge;
  var pending_edge;
  var available = {};

  try {
    eyex = require('eyex');
  } catch(e) { }
  try {
    pending_edge = require('eyegaze_edge');
  } catch(e) { }
  if(eyex) {
    try {
      eyex.setup();
      available.eyex = true;
    } catch(e) {
      eyex = null;
    }
  }
  if(pending_edge) {
    try {
      pending_edge.setup(function(res) {
        if(res && res.ready) {
          eyegaze_edge = pending_edge;
          available.eyegaze_edge = true;
        }
      });
    } catch(e) {
      pending_edge = null;
    }
  }

  var poll = null;
  var lasts = {};
  var gaze_history = [];
  var callbacks = [];
  var any_averaged = false;
  var run_callbacks = function(message) {
    callbacks.forEach(function(obj) {
      if(message.type == 'over' && obj.level == 'noisy') {
        obj.callback(message);
      } else if(message.type == 'linger' && obj.level == 'averaged') {
        obj.callback(message);
      }
    });
  };
  var gazelinger = {
    statuses: {
    },
    available: available,
    listen: function(callback, level) {
      level = level || 'noisy';
      if(level == 'averaged') { any_averaged = true; }
      if(eyex && available.eyex) {
        try { eyex.teardown(); } catch(e) { }
        setTimeout(function() {
          try {
            eyex.setup();
            lasts.give_up_eyex = false;
          } catch(e) { }

        }, 500);
      }
      if(!poll) {
        var poll = function() {
          if(callbacks.length == 0) { return; }
          // Race condition since eyegaze edge setup check takes a few second, if you start listening
          // too soon then you'll miss this listen call unless it's part of the polling loop.
          if(eyegaze_edge && eyegaze_edge.listen && available.eyegaze_edge && !eyegaze_edge.listening) {
            // idempotent
            eyegaze_edge.listen();
          }
          var data = {};
          var now = (new Date()).getTime();
          if(eyex && eyex.ping) {
            data.eyex = eyex.ping();
            if(data.eyex && data.eyex.status) {
              gazelinger.statuses.eyex = {code: data.eyex.status};
              // If EyeX has disconnected or failed, try reconnecting a few times
              if(!lasts.give_up_eyex && (data.eyex.status == 5 || data.eyex.status == -1)) {
                lasts.eyex_attempts = lasts.eyex_attempts || [];
                var attempts = [];
                // After 5 failed attempts within a 2-minute window, go ahead and give up
                for(var idx = 0; idx < lasts.eyex_attempts.length; idx++) {
                  if(lasts.eyex_attempts[idx] > (now - (120 * 1000))) {
                    attempts.push(lasts.eyex_attempts[idx]);
                  }
                }
                lasts.eyex_attempts.push(now);
                if(lasts.eyex_attempts.length > 5) {
                  lasts.give_up_eyex = true;
                } else {
                  try { eyex.teardown(); } catch(e) { }
                  setTimeout(function() {
                    try {
                      eyex.setup();
                      lasts.give_up_eyex = false;
                    } catch(e) { }
                  }, 500);
                }
              }
            }
            if(data.eyex && (data.eyex.gaze_ts == 0 || lasts.eyex == data.eyex.gaze_ts)) {
              data.eyex = null;
            } else if(data.eyex) {
              lasts.eyex = data.eyex.gaze_ts;
              lasts.eyex_js = now;
            }
            if(lasts.eyex_js && gazelinger.statuses.eyex && lasts.eyex_js < (now - (30 * 1000))) {
              gazelinger.statuses.eyex.dormant = true;
            }
          }
          if(eyegaze_edge && eyegaze_edge.ping) {
            data.eyegaze_edge = eyegaze_edge.ping();
            if(data.eyegaze_edge && data.eyegaze_edge.status) {
              gazelinger.statuses.eyegaze_edge = {code: data.eyegaze_edge.status};
            }
            if(data.eyegaze_edge && (data.eyegaze_edge.gaze_ts == 0 || lasts.eyegaze_edge == data.eyegaze_edge.gaze_ts)) {
              data.eyegaze_edge = null;
            } else if(data.eyegaze_edge) {
              lasts.eyegaze_edge = data.eyegaze_edge.gaze_ts;
              lasts.eyegaze_edge_js = now;
            }
            if(lasts.eyegaze_edge_js && gazelinger.statuses.eyegaze_edge && lasts.eyegaze_edge_js < (now - (30 * 1000))) {
              gazelinger.statuses.eyegaze_edge.dormant = true;
            }
          }
          data.result = {};
          var keys = ['eyex', 'eyegaze_edge'];
          for(var idx = 0; idx < keys.length; idx++) {
            if(data[keys[idx]]) {
              data.result = data[keys[idx]];
              data.result.hardware = keys[idx];
            }
          }
          data = data.result;

          var message = { raw: data };
          message.ts = (new Date()).getTime();

          if(data.end_ts && data.begin_ts && data.end_ts > data.begin_ts && data.end_ts != lasts.linger) {
//             console.log("linger duration " + (data.end_ts - data.begin_ts));
//             console.log("time since last " + (data.end_ts - lasts.linger));
            lasts.linger = data.end_ts;

            message = { raw: data};
            message.ts = (new Date()).getTime();
            message.screenX = data.data_x; //(data.data_x / ratio) - (window.screenInnerOffsetX || window.screenX);
            message.screenY = data.data_y; // = (data.data_y / ratio) - (window.screenInnerOffsetY || window.screenY);
            message.duration = (data.end_ts - data.begin_ts);
            message.scaled = data.scaled !== false;
            message.type = 'linger';
//            run_callbacks(message);
          }
          if(data.gaze_ts && data.gaze_ts != lasts.gaze) {
            lasts.gaze = data.gaze_ts;
            message = { raw: data};
            message.ts = (new Date()).getTime();
            message.screenX = data.gaze_x; //(data.gaze_x / ratio) - (window.screenInnerOffsetX || window.screenX);
            message.screenY = data.gaze_y; //(data.gaze_y / ratio) - (window.screenInnerOffsetY || window.screenY);
            message.duration = 50;
            message.scaled = data.scaled !== false;
            message.type = 'over';
            message.ts = data.gaze_ts;
            run_callbacks(message);

            if(any_averaged) {
              gaze_history.push({
                x: message.screenX,
                y: message.screenY,
                ts: message.ts
              });

              // prune based on distance from latest timestamp
              var new_history = [];
              gaze_history.forEach(function (e) { if (lasts.gaze - e.ts < 200) { new_history.push(e); } });
              gaze_history = new_history;

              // find a quick median
              var xs = gaze_history.sort(function (a, b) { return b.x - a.x });
              var midx = xs[Math.ceil(xs.length / 2)];
              var ys = gaze_history.sort(function (a, b) { return b.y - a.y });
              var midy = ys[Math.ceil(ys.length / 2)];

              // won't enter this until there are at least 2 events
              if (midx && midy) {
                midx = midx.x;
                midy = midy.y;

                // temporarily remove outliers
                var filtered_history = gaze_history.filter(function (e) { return (Math.abs(e.x - midx) < 50) && (Math.abs(e.y - midy) < 50); });
                // wait until there are at least 3 clustered events, which means a minimum of 150ms
                if (filtered_history.length > 2) {
                  var biggest_dist = 0;
                  lasts.history = gaze_history[0].ts;
                  filtered_history.forEach(function (e) { biggest_dist = Math.max(biggest_dist, e.ts - lasts.history); lasts.history - e.ts; });

                  // if there are no significant time gaps, compute a new middle and trigger a linger event
                  if (biggest_dist <= 60) {
                    var mean_x = 0;
                    var mean_y = 0;
                    filtered_history.forEach(function (e) { mean_x = mean_x + e.x; mean_y = mean_y + e.y; });
                    mean_x = mean_x / filtered_history.length;
                    mean_y = mean_y / filtered_history.length;

                    message = { raw: data};
                    message.ts = (new Date()).getTime();
                    message.screenX = mean_x;
                    message.screenY = mean_y;
                    message.type = 'linger';
                    message.duration = filtered_history[filtered_history.length - 1].ts - filtered_history[0].ts;
                    message.scaled = data.scaled !== false;
                    run_callbacks(message);
                    // slicing at index 3 means you will get at most a linger event every 150ms
                    if(gaze_history.length > 45) {
                      gaze_history = gaze_history.slice(0 - (gaze_history.length - 3));
                    } else {
                      gaze_history = gaze_history.slice(3, 50);
                    }
                  }
                }
              }
            }
          }
          // I was using setInterval, but it was causing a queue backlog, I thought this would help.
          setTimeout(poll, 45);
        };
        setTimeout(poll, 45);
      }
      callbacks.push({callback: callback, level: level});
    },
    stop_listening: function() {
      // TODO: support multiple listeners
      callbacks = [];
      any_averaged = false;
      clearTimeout(poll);
      poll = null;
      if(eyegaze_edge && eyegaze_edge.stop_listening) {
        eyegaze_edge.stop_listening();
      }
    },
    can_calibrate: function() {
      return !!eyegaze_edge;
    },
    calibrate: function() {
      if(eyegaze_edge) {
        eyegaze_edge.calibrate();
      }
    }
  };
  module.exports = gazelinger;
})();
<|endoftext|>


<commit_before><commit_msg>updated to latest meteor<commit_after><|endoftext|>


<commit_before><commit_msg>random commit<commit_after><|endoftext|>


<commit_before>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Main entry point for PhET translation web app.
 */

// modules
var cookieParser = require( 'cookie-parser' );
var express = require( 'express' );
var https = require( 'https' );
var doT = require( 'express-dot' );
var LocaleInfo = require( __dirname + '/js/LocaleInfo' );
var TranslatableSimInfo = require( __dirname + '/js/TranslatableSimInfo' );

// constants
var LISTEN_PORT = 16372;
var REQUIRE_LOGIN = true;

// Create and configure the ExpressJS app
var app = express();
app.set( 'views', __dirname + '/html/views' );
app.set( 'view engine', 'dot' );
app.engine( 'html', doT.__express );
app.use( '/translate/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/sim/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/img', express.static( __dirname + '/public/img' ) );
app.use( '/translate/js', express.static( __dirname + '/public/js' ) );
app.use( '/translate/sim/js', express.static( __dirname + '/public/js' ) );
app.use( '/translate/fonts', express.static( __dirname + '/public/fonts' ) );

//----------------------------------------------------------------------------
// Set up the routes
//----------------------------------------------------------------------------

// convenience function for presenting escaped HTML
function escapeHTML( s ) {
  return s.replace( /&/g, '&' )
    .replace( /"/g, '"' )
    .replace( /</g, '<' )
    .replace( />/g, '>' );
}

// need cookieParser middleware before we can do anything with cookies
app.use( express.cookieParser() );

// check for the presence of the login cookie
if ( REQUIRE_LOGIN ) {
  app.get( '/translate/*', function( req, res, next ) {
    console.log( 'all cookies :' );
    for ( var k in req.cookies ) {
      if ( req.cookies.hasOwnProperty( k ) ) {
        alert( "Key is " + k + ", value is" + req.cookies[ k ] );
      }
    }
    console.log( '------------' );
    console.log( 'Checking for login cookie' );
    var cookie = req.cookies.JSESSIONID;
    if ( cookie === undefined ) {
      // no: the user must log in
      console.log( 'session cookie not found, sending to login page' );
      res.render( 'login-required.html', { title: 'Login Required' } );
    }
    else {
      // yes, cookie was present, go to the next route
      console.log( 'session cookie found, moving to next step' );
      next(); // send to next route
    }
  } );
}

// Initial page for the translation utility
app.get( '/translate/', function( req, res, next ) {
  res.render( 'translate-home.html', { simInfoArray: TranslatableSimInfo.simInfoArray } );
} );

// route for translating a specific sim to a specific language
app.get( '/translate/sim/:simName?/:targetLocale?', function( req, res ) {
  var simName = req.param( 'simName' );
  var targetLocale = req.param( 'targetLocale' );
  var path = '/phetsims/' + simName + '/master/strings/' + simName + '-strings_en.json';

  // Pull the English string data from github.
  https.get( { host: 'raw.githubusercontent.com', path: path }, function( response ) {
    response.on( 'data', function( data ) {

      if ( response.statusCode === 200 ) {
        // Parse the returned JSON data into a JavaScript object.
        strings = JSON.parse( data );

        // Create a table for the strings to be translated.
        var stringsTable = '';
        for ( var key in strings ) {
          if ( strings.hasOwnProperty( key ) ) {
            stringsTable += '
';
            stringsTable += '' + escapeHTML( strings[ key ] ) + '	';
            stringsTable += '<input type=\"text\" 	';
            stringsTable += '
';
          }
        }

        // Assemble the data that will be supplied to the template.
        var templateData = {
          title: "PhET Translation Utility",
          subtitle: "Please enter a translation for each English string:",
          destinationLanguage: LocaleInfo.localeToLanguageString( targetLocale ),
          stringsTable: stringsTable,
          simName: simName,
          simUrl: TranslatableSimInfo.getSimInfoByProjectName( simName ).testUrl
        };

        // Render the page.
        res.render( 'translate-sim.html', templateData );
      }
      else {
        res.send( 'Error: Sim data not found' );
      }
    } );
  } );
} );

app.get( '/*', function( req, res ) {
  res.send( '
Error: Page not found.  URL = ' + req.url + '
' );
} );

app.listen( LISTEN_PORT, function() { console.log( 'Listening on port ' + LISTEN_PORT ) } );
<commit_msg>Changed an alert to a console.log.<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Main entry point for PhET translation web app.
 */

// modules
var cookieParser = require( 'cookie-parser' );
var express = require( 'express' );
var https = require( 'https' );
var doT = require( 'express-dot' );
var LocaleInfo = require( __dirname + '/js/LocaleInfo' );
var TranslatableSimInfo = require( __dirname + '/js/TranslatableSimInfo' );

// constants
var LISTEN_PORT = 16372;
var REQUIRE_LOGIN = true;

// Create and configure the ExpressJS app
var app = express();
app.set( 'views', __dirname + '/html/views' );
app.set( 'view engine', 'dot' );
app.engine( 'html', doT.__express );
app.use( '/translate/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/sim/css', express.static( __dirname + '/public/css' ) );
app.use( '/translate/img', express.static( __dirname + '/public/img' ) );
app.use( '/translate/js', express.static( __dirname + '/public/js' ) );
app.use( '/translate/sim/js', express.static( __dirname + '/public/js' ) );
app.use( '/translate/fonts', express.static( __dirname + '/public/fonts' ) );

//----------------------------------------------------------------------------
// Set up the routes
//----------------------------------------------------------------------------

// convenience function for presenting escaped HTML
function escapeHTML( s ) {
  return s.replace( /&/g, '&' )
    .replace( /"/g, '"' )
    .replace( /</g, '<' )
    .replace( />/g, '>' );
}

// need cookieParser middleware before we can do anything with cookies
app.use( express.cookieParser() );

// check for the presence of the login cookie
if ( REQUIRE_LOGIN ) {
  app.get( '/translate/*', function( req, res, next ) {
    console.log( 'all cookies :' );
    for ( var k in req.cookies ) {
      if ( req.cookies.hasOwnProperty( k ) ) {
        console.log( "Key is " + k + ", value is" + req.cookies[ k ] );
      }
    }
    console.log( '------------' );
    console.log( 'Checking for login cookie' );
    var cookie = req.cookies.JSESSIONID;
    if ( cookie === undefined ) {
      // no: the user must log in
      console.log( 'session cookie not found, sending to login page' );
      res.render( 'login-required.html', { title: 'Login Required' } );
    }
    else {
      // yes, cookie was present, go to the next route
      console.log( 'session cookie found, moving to next step' );
      next(); // send to next route
    }
  } );
}

// Initial page for the translation utility
app.get( '/translate/', function( req, res, next ) {
  res.render( 'translate-home.html', { simInfoArray: TranslatableSimInfo.simInfoArray } );
} );

// route for translating a specific sim to a specific language
app.get( '/translate/sim/:simName?/:targetLocale?', function( req, res ) {
  var simName = req.param( 'simName' );
  var targetLocale = req.param( 'targetLocale' );
  var path = '/phetsims/' + simName + '/master/strings/' + simName + '-strings_en.json';

  // Pull the English string data from github.
  https.get( { host: 'raw.githubusercontent.com', path: path }, function( response ) {
    response.on( 'data', function( data ) {

      if ( response.statusCode === 200 ) {
        // Parse the returned JSON data into a JavaScript object.
        strings = JSON.parse( data );

        // Create a table for the strings to be translated.
        var stringsTable = '';
        for ( var key in strings ) {
          if ( strings.hasOwnProperty( key ) ) {
            stringsTable += '
';
            stringsTable += '' + escapeHTML( strings[ key ] ) + '	';
            stringsTable += '<input type=\"text\" 	';
            stringsTable += '
';
          }
        }

        // Assemble the data that will be supplied to the template.
        var templateData = {
          title: "PhET Translation Utility",
          subtitle: "Please enter a translation for each English string:",
          destinationLanguage: LocaleInfo.localeToLanguageString( targetLocale ),
          stringsTable: stringsTable,
          simName: simName,
          simUrl: TranslatableSimInfo.getSimInfoByProjectName( simName ).testUrl
        };

        // Render the page.
        res.render( 'translate-sim.html', templateData );
      }
      else {
        res.send( 'Error: Sim data not found' );
      }
    } );
  } );
} );

app.get( '/*', function( req, res ) {
  res.send( '
Error: Page not found.  URL = ' + req.url + '
' );
} );

app.listen( LISTEN_PORT, function() { console.log( 'Listening on port ' + LISTEN_PORT ) } );
<|endoftext|>


<commit_before><commit_msg>build with Search capability<commit_after><|endoftext|>


<commit_before><commit_msg>Capture third party requests from non-visible tabs.<commit_after><|endoftext|>


<commit_before><commit_msg>Clear the class dialog on open<commit_after><|endoftext|>


<commit_before>
function checkScaling() {
    var matches = {
        'pixel-ratio__1_25': "screen and (-webkit-min-device-pixel-ratio: 1.25) and (-webkit-max-device-pixel-ratio: 1.49), " +
                                "screen and (min-resolution: 1.25dppx) and (max-resolution: 1.49dppx)",
        'pixel-ratio__1_5': "screen and (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 1.74), " +
                                "screen and (min-resolution: 1.5dppx) and (max-resolution: 1.74dppx)",
        'pixel-ratio__1_75': "screen and (-webkit-min-device-pixel-ratio: 1.75) and (-webkit-max-device-pixel-ratio: 1.99), " +
                                "screen and (min-resolution: 1.75dppx) and (max-resolution: 1.99dppx)",
    };

    for (var c in matches) {
        if ( window.matchMedia(matches[c]).matches ) {
            document.body.classList.add(c);
            break;
        }
    }

    if ( !window.matchMedia("screen and (-webkit-device-pixel-ratio: 1.5)," +
                            "screen and (-webkit-device-pixel-ratio: 1)," +
                            "screen and (-webkit-device-pixel-ratio: 2)").matches )
    {
        // don't add zoom for mobile devices
        // if (!(/android|avantgo|playbook|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent || navigator.vendor || window.opera)))
        //     document.getElementsByTagName('html')[0].setAttribute('style', 'zoom: ' + (1 / window.devicePixelRatio) + ';');
    }
}

checkScaling();

var params = (function() {
    var e,
        a = /\+/g,  // Regex for replacing addition symbol with a space
        r = /([^&=]+)=?([^&]*)/g,
        d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
        q = window.location.search.substring(1),
        urlParams = {};

    while (e = r.exec(q))
        urlParams[d(e[1])] = d(e[2]);

    return urlParams;
})();

var checkLocalStorage = (function () {
    try {
        var storage = window['localStorage'];
        return true;
    }
    catch(e) {
        return false;
    }
})();

if ( window.desktop && window.desktop.theme && (window.desktop.theme.id || window.desktop.theme.type)) {
    // params.uitheme = undefined;
    localStorage.setItem("ui-theme-id", window.desktop.theme.id);
}

if ( !!params.uitheme && checkLocalStorage && !localStorage.getItem("ui-theme-id") ) {
    // const _t = params.uitheme.match(/([\w-]+)/g);

    if ( params.uitheme == 'default-dark' )
        params.uitheme = 'theme-dark';
    else
    if ( params.uitheme == 'default-light' )
        params.uitheme = 'theme-classic-light';

    localStorage.setItem("ui-theme-id", params.uitheme);
}

var ui_theme_name = checkLocalStorage ? localStorage.getItem("ui-theme-id") : undefined;
if ( !ui_theme_name ) {
    if ( window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ) {
        ui_theme_name = 'theme-dark';
        checkLocalStorage && localStorage.setItem("ui-theme-id", ui_theme_name);
    }
}
if ( !!ui_theme_name ) {
    document.body.classList.add(ui_theme_name);
}
<commit_msg>[desktop] apply theme's type if available<commit_after>
function checkScaling() {
    var matches = {
        'pixel-ratio__1_25': "screen and (-webkit-min-device-pixel-ratio: 1.25) and (-webkit-max-device-pixel-ratio: 1.49), " +
                                "screen and (min-resolution: 1.25dppx) and (max-resolution: 1.49dppx)",
        'pixel-ratio__1_5': "screen and (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 1.74), " +
                                "screen and (min-resolution: 1.5dppx) and (max-resolution: 1.74dppx)",
        'pixel-ratio__1_75': "screen and (-webkit-min-device-pixel-ratio: 1.75) and (-webkit-max-device-pixel-ratio: 1.99), " +
                                "screen and (min-resolution: 1.75dppx) and (max-resolution: 1.99dppx)",
    };

    for (var c in matches) {
        if ( window.matchMedia(matches[c]).matches ) {
            document.body.classList.add(c);
            break;
        }
    }

    if ( !window.matchMedia("screen and (-webkit-device-pixel-ratio: 1.5)," +
                            "screen and (-webkit-device-pixel-ratio: 1)," +
                            "screen and (-webkit-device-pixel-ratio: 2)").matches )
    {
        // don't add zoom for mobile devices
        // if (!(/android|avantgo|playbook|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent || navigator.vendor || window.opera)))
        //     document.getElementsByTagName('html')[0].setAttribute('style', 'zoom: ' + (1 / window.devicePixelRatio) + ';');
    }
}

checkScaling();

var params = (function() {
    var e,
        a = /\+/g,  // Regex for replacing addition symbol with a space
        r = /([^&=]+)=?([^&]*)/g,
        d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
        q = window.location.search.substring(1),
        urlParams = {};

    while (e = r.exec(q))
        urlParams[d(e[1])] = d(e[2]);

    return urlParams;
})();

var checkLocalStorage = (function () {
    try {
        var storage = window['localStorage'];
        return true;
    }
    catch(e) {
        return false;
    }
})();

if ( window.desktop && window.desktop.theme ) {
    if ( window.desktop.theme.id ) {
        // params.uitheme = undefined;
        localStorage.setItem("ui-theme-id", window.desktop.theme.id);
    } else
    if ( window.desktop.theme.type ) {
        if ( window.desktop.theme.type == 'dark' ) params.uitheme == 'default-dark'; else
        if ( window.desktop.theme.type == 'light' ) params.uitheme == 'default-light';
    }
}

if ( !!params.uitheme && checkLocalStorage && !localStorage.getItem("ui-theme-id") ) {
    // const _t = params.uitheme.match(/([\w-]+)/g);

    if ( params.uitheme == 'default-dark' )
        params.uitheme = 'theme-dark';
    else
    if ( params.uitheme == 'default-light' )
        params.uitheme = 'theme-classic-light';

    localStorage.setItem("ui-theme-id", params.uitheme);
}

var ui_theme_name = checkLocalStorage ? localStorage.getItem("ui-theme-id") : undefined;
if ( !ui_theme_name ) {
    if ( window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ) {
        ui_theme_name = 'theme-dark';
        checkLocalStorage && localStorage.setItem("ui-theme-id", ui_theme_name);
    }
}
if ( !!ui_theme_name ) {
    document.body.classList.add(ui_theme_name);
}
<|endoftext|>


<commit_before><commit_msg>Stabilize wire cleanup by just recreating them on wire table changes<commit_after><|endoftext|>


<commit_before>/**
 * Created by joe on 2/6/15.
 * Classes used to generate several major parts of the UI
 */

function updateList(id,preprocessedFiles,func) {
    var rowContainer = $(id).empty();

    preprocessedFiles.forEach(function (item) {
        $(rowContainer).append(func(item));
    });
}

function createRow(obj) {
    return $('
').text(JSON.stringify(obj));
}

function filterById(id,elem)
{
    return elem.api_id==id;
}

function findById(id,elems)
{
    return elems.filter(filterById.bind(null,id))[0];
}

Config = new function(){
    this.Sort = {
        name:"ID",
        dir:1
    };
};

Resource = new function(){
    this.field = ["石油","弹药","钢","铝","快速建造材","快速修复材","建造材料"];
    this.update = function() {
        var list = $('#resources-list').empty();
        for (var i = 0;i<Resource.field.length;++i) {
            $(list).append($('
').text(Resource.field[i]+":"+window.rawsvd.port.api_material[i].api_value));
        }
    }
};

MapArea = new function(){
    this.field = {
        NAME:"name"
    };
    this.fieldS = {
        NAME:function(mapA){return mapA.api_name;}
    };
};

Mission = new function(){
    this.field = {
        NAME:"name",
        AREA:"area",
        LNAME:"longname"
    };
    this.fieldS = {
        NAME:function(mission){return mission.api_name;},
        AREA:function(mission){return MapArea.fieldS.NAME(findById(mission.api_maparea_id,window.mst.api_maparea_id));}
    };
};

Ship = new function(){
    // Defining fields of ship and their description
    this.field = {
        ID:"ID",
        NAME:"名字",
        STYPE:"舰种",
        RARE:'稀有',
        HP:"HP",
        LV:"LV",
        AFTERLV:"改造等级", //lv
        EXP2:"升级经验",
        COND:"状态",
        KARY:"火力",
        RAIS:"雷装",
        TAIK:"对空",
        SOUK:"装甲",
        KAIH:"回避",
        TAIS:"对潜",
        SAKU:"索敌",
        LUCK:"幸运"
    };

    // Return a value for sorting and reuse purposes
    this.fieldVal = {
        ID:function(ship){return ship.api_id;},
        NAME:function(ship){return ship.api_ship_id;}, //name
        HP:function(ship){return ship.api_nowhp/ship.api_maxhp;}, //hp
        STYPE:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_stype;},
        RARE:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_backs;},
        LV:function(ship){return ship.api_lv;}, //lv
        AFTERLV:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_afterlv;}, //hp
        EXP2:function(ship){return ship.api_exp[1];}, //exp2nxt
        COND:function(ship){return ship.api_cond;}, //Cond
        KARY:function(ship){return ship.api_karyoku[0];}, //karyoku
        RAIS:function(ship){return ship.api_raisou[0];}, //raisou
        TAIK:function(ship){return ship.api_taiku[0];}, //taiku
        SOUK:function(ship){return ship.api_soukou[0];}, //soukou
        KAIH:function(ship){return ship.api_kaihi[0];}, //kaihi
        TAIS:function(ship){return ship.api_taisen[0];}, //taisen
        SAKU:function(ship){return ship.api_sakuteki[0];}, //sakuteki
        LUCK:function(ship){return ship.api_lucky[0];} //lucky
    };

    // Return a value for outputing as an jQuery Element
    this.fieldS = {
        ID:function(ship){return ship.api_id;},
        NAME:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_name;}, //name
        HP:function(ship){ return healthBarGen(ship.api_nowhp,ship.api_maxhp);},//return ship.api_nowhp + "/" + ship.api_maxhp;}, //hp
        STYPE:function(ship){return findById(Ship.fieldVal.STYPE(ship),window.mst.api_mst_stype).api_name;},
        RARE:function(ship){return Ship.fieldVal.RARE(ship);},
        LV:function(ship){return ship.api_lv;}, //lv
        AFTERLV:function(ship){var lv=Ship.fieldVal.AFTERLV(ship); return lv==0?"-":lv;}, //lv
        EXP2:function(ship){return ship.api_exp[1];}, //exp2nxt
        COND:function(ship){return ship.api_cond;}, //Cond
        KARY:function(ship){return getRem(ship.api_karyoku);}, //karyoku
        RAIS:function(ship){return getRem(ship.api_raisou);}, //raisou
        TAIK:function(ship){return getRem(ship.api_taiku);}, //taiku
        SOUK:function(ship){return getRem(ship.api_soukou);}, //soukou
        KAIH:function(ship){return getRem(ship.api_kaihi);}, //kaihi
        TAIS:function(ship){return getRem(ship.api_taisen);}, //taisen
        SAKU:function(ship){return getRem(ship.api_sakuteki);}, //sakuteki
        LUCK:function(ship){return getRem(ship.api_lucky);} //lucky
    };

    // Comparing ship a, b with field name name and order ord(+1/-1)
    function cmpShip(name,ord,a,b)
    {
        return ord*(Ship.fieldVal[name](a)-Ship.fieldVal[name](b));
    }

    // Filters implemented if can support
    this.filter = {
        LOCK:function(args,ship){ return args == 0? true:(ship.api_locked>0);},
        LVRNG:function(args,ship){
            if (args[0]>0 && ship.api_lv<args[0])return false;
            if (args[1]>0 && ship.api_lv>args[1])return false;
            return true},
        STYPE:function(args,ship){
            return args.indexOf(Ship.fieldVal.STYPE(ship))>=0;}
    };

    function getRem(inp)
    {
        // todo affected by equipments?
        if (inp[0]>=inp[1])
            return inp[0]+"[M]";
        return inp[0]+"["+(inp[1]-inp[0])+"]";
    }

    function healthBarGen(a,b)
    {
        const color=["red","orange","yellow","green"];
        var col = color[Math.floor((a*4-1)/b)];
        var txt = a+"/"+b;
        var par = Math.floor(100*a/b);
        return $('

').append($('

').text(txt));
    }

    this.filterShipList = function(ships,filters)
    {
        for (var i=0;i<filters.length;++i)
            ships = ships.filter(filters[i][0].bind(null,filters[i][1]));
        return ships;
    };

    this.createShipRow = function (cols,ship)
    {
        var tr = $('
');
        for (var i = 0;i<cols.length;++i)
            tr.append($('').append(Ship.fieldS[cols[i]](ship)));
        return tr;
    };

    this.init = function()
    {
        // update ship type names
        var shipsel = $("#ships-type");
        window.mst.api_mst_stype.forEach(function(stype){
                var item=$('').append('<input type="checkbox" name="stype" value="'+stype.api_id+'" checked>')
                    .append($("

").text(stype.api_name));
                shipsel.append(item);
            }
        );

        // ship form init
        $("#ships-selector").on('change',function(event) {
            Ship.update();
        });
        var tmp = $("#ships-cols").empty();

        Object.getOwnPropertyNames(Ship.field).forEach(function(val){
            tmp.append($('').append('<input type="checkbox" name="cols" value="'+val+'" checked>'+Ship.field[val]));
        });
    };

    this.update = function()
    {
        var cols = [];
        var stypes = [];
        var lock = 0;
        var lvmin = 0;
        var lvmax = 0;
        var shipsel = $("#ships-selector");
        shipsel.find("input[name=cols]:checkbox:checked").each(function() {
            cols.push($(this).val());
        });

        shipsel.find("input[name=stype]:checkbox:checked").each(function() {
            stypes.push(eval($(this).val()));
        });

        shipsel.find("input[name=locked]:checkbox:checked").each(function() {
            lock=1;
        });

        shipsel.find("input[name=lvmin]").each(function() {
            lvmin = eval($(this).val());
        });

        shipsel.find("input[name=lvmax]").each(function() {
            lvmax = eval($(this).val());
        });

        var th = $("#ships-head");
        var tr = $('

');
        for (var i = 0;i<cols.length;++i)
        {
            var col =$('	').text(Ship.field[cols[i]]);
            $(col).click((function(name)
            {
                if (Config.Sort.name!=name)
                {
                    Config.Sort.name=name;
                    Config.Sort.dir=1;
                }
                else Config.Sort.dir=-Config.Sort.dir;
                Ship.update();
            }).bind(null,cols[i]));
            $(tr).append(col);
        }
        $(th).empty().append(tr);

        var sortf = cmpShip.bind(null,Config.Sort.name,Config.Sort.dir);

        // filtering
        //var ship=window.rawsvd.port.api_ship;
        var ship = Ship.filterShipList(window.rawsvd.port.api_ship,[
            [Ship.filter.STYPE,stypes],
            [Ship.filter.LOCK,lock],
            [Ship.filter.LVRNG,[lvmin,lvmax]]]);
        ship = ship.sort(sortf);
        updateList('#ships-list',ship,Ship.createShipRow.bind(null,cols));
    };
};

Timer = new function(){
    this.init = function(){
        setInterval(function(){
            try{
                Timer.update();
            }catch(err){
                // discard everything
            }
        },1000);
    };

    function updateTimerList(tag,times)
    {
        $(tag).each(function()
        {
            $(this).empty();
            var rowContainer = this;
            times.forEach(function(item){
                $(rowContainer).append($("

").text(new Mytimer(item).toString()));
            });
        })
    }

    this.update = function()
    {
        var times = [];
        window.rawsvd.port.api_deck_port.forEach(function(item){
            if (item.api_mission[0] != 0)
                times.push(item.api_mission[2]);
        });
        updateTimerList('.mission_timers',times);
        times = [];
        window.rawsvd.kdock.forEach(function(item){
            if (item.api_state > 0)
                times.push(item.api_complete_time);
        });
        updateTimerList('.build_timers',times);
        times = [];
        window.rawsvd.port.api_ndock.forEach(function(item){
            if (item.api_state > 0)
                times.push(item.api_complete_time);
        });
        updateTimerList('.repair_timers',times);
    };
};

Deck = new function(){
    this.field = {
        NAME:"名称",
        MISS:"任务",
        SHIP:"舰船信息"
    };

    this.fieldVal = {
        NAME:function(deck){return deck.api_id;},
        MISS:function(deck){return deck.api_mission[1];},
        SHIP:function(deck){return 0;}
    };
    this.shipField=["NAME","STYPE","LV","EXP2","HP","COND"];
    function genShipList(ships)
    {
        var tmp = $('
');
        ships.forEach(function(id) {
            if (id<0)
                return;
            var ship = findById(id,window.rawsvd.port.api_ship);
            var row = $('
');
            Deck.shipField.forEach(function(field){
                $(row).append($('').append(Ship.fieldS[field](ship)));
            });
            $(tmp).append(row);
        });
        return tmp;
    }

    function genMission(mission)
    {
        if (mission[1] == 0)
            return "无";
        var tmp=$('
');
        var mis = findById(mission[1],window.mst.api_mst_mission);
        $(tmp).append($('
').append(Mission.fieldS.AREA(mis)));
        $(tmp).append($('
').append(Mission.fieldS.NAME(mis)));
        $(tmp).append($('
').append(new Date(mission[2]).toLocaleTimeString()));
        return tmp;
    }
    this.fieldS = {
        NAME:function(deck){return deck.api_name;},
        MISS:function(deck){return genMission(deck.api_mission);},
        SHIP:function(deck){return genShipList(deck.api_ship);}
    };

    this.init = function()
    {
        var list=$('#decks-list').find(' thead').empty();
        var tmp=$('
');
        Object.getOwnPropertyNames(Deck.field).forEach(function(val){
            tmp.append($('').append(Deck.field[val]));
        });
        list.append(tmp);
        $(list).find("th:not(:last-child)").attr("rowspan",2);
        tmp=$('
');
        Deck.shipField.forEach(function(field){
            $(tmp).append($('').append(Ship.field[field]));
        });
        list.append(tmp);

    };
    function createDeckRow(deck,args)
    {
        var tmp = $('
');
        Object.getOwnPropertyNames(Deck.field).forEach(function(val){
            tmp.append($('').append(Deck.fieldS[val](deck)));
        });
        return tmp;
    }
    this.update = function()
    {
        var list=$('#decks-list').find(' tbody').empty();
        var args = 0;
        window.rawsvd.port.api_deck_port.forEach(function(deck){
            list.append(createDeckRow(deck,args));
        });
    };
};<commit_msg>fixed typo maparea<commit_after>/**
 * Created by joe on 2/6/15.
 * Classes used to generate several major parts of the UI
 */

function updateList(id,preprocessedFiles,func) {
    var rowContainer = $(id).empty();

    preprocessedFiles.forEach(function (item) {
        $(rowContainer).append(func(item));
    });
}

function createRow(obj) {
    return $('
').text(JSON.stringify(obj));
}

function filterById(id,elem)
{
    return elem.api_id==id;
}

function findById(id,elems)
{
    return elems.filter(filterById.bind(null,id))[0];
}

Config = new function(){
    this.Sort = {
        name:"ID",
        dir:1
    };
};

Resource = new function(){
    this.field = ["石油","弹药","钢","铝","快速建造材","快速修复材","建造材料"];
    this.update = function() {
        var list = $('#resources-list').empty();
        for (var i = 0;i<Resource.field.length;++i) {
            $(list).append($('
').text(Resource.field[i]+":"+window.rawsvd.port.api_material[i].api_value));
        }
    }
};

MapArea = new function(){
    this.field = {
        NAME:"name"
    };
    this.fieldS = {
        NAME:function(mapA){return mapA.api_name;}
    };
};

Mission = new function(){
    this.field = {
        NAME:"name",
        AREA:"area",
        LNAME:"longname"
    };
    this.fieldS = {
        NAME:function(mission){return mission.api_name;},
        AREA:function(mission){return MapArea.fieldS.NAME(findById(mission.api_maparea_id,window.mst.api_mst_maparea));}
    };
};

Ship = new function(){
    // Defining fields of ship and their description
    this.field = {
        ID:"ID",
        NAME:"名字",
        STYPE:"舰种",
        RARE:'稀有',
        HP:"HP",
        LV:"LV",
        AFTERLV:"改造等级", //lv
        EXP2:"升级经验",
        COND:"状态",
        KARY:"火力",
        RAIS:"雷装",
        TAIK:"对空",
        SOUK:"装甲",
        KAIH:"回避",
        TAIS:"对潜",
        SAKU:"索敌",
        LUCK:"幸运"
    };

    // Return a value for sorting and reuse purposes
    this.fieldVal = {
        ID:function(ship){return ship.api_id;},
        NAME:function(ship){return ship.api_ship_id;}, //name
        HP:function(ship){return ship.api_nowhp/ship.api_maxhp;}, //hp
        STYPE:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_stype;},
        RARE:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_backs;},
        LV:function(ship){return ship.api_lv;}, //lv
        AFTERLV:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_afterlv;}, //hp
        EXP2:function(ship){return ship.api_exp[1];}, //exp2nxt
        COND:function(ship){return ship.api_cond;}, //Cond
        KARY:function(ship){return ship.api_karyoku[0];}, //karyoku
        RAIS:function(ship){return ship.api_raisou[0];}, //raisou
        TAIK:function(ship){return ship.api_taiku[0];}, //taiku
        SOUK:function(ship){return ship.api_soukou[0];}, //soukou
        KAIH:function(ship){return ship.api_kaihi[0];}, //kaihi
        TAIS:function(ship){return ship.api_taisen[0];}, //taisen
        SAKU:function(ship){return ship.api_sakuteki[0];}, //sakuteki
        LUCK:function(ship){return ship.api_lucky[0];} //lucky
    };

    // Return a value for outputing as an jQuery Element
    this.fieldS = {
        ID:function(ship){return ship.api_id;},
        NAME:function(ship){return findById(Ship.fieldVal.NAME(ship),window.mst.api_mst_ship).api_name;}, //name
        HP:function(ship){ return healthBarGen(ship.api_nowhp,ship.api_maxhp);},//return ship.api_nowhp + "/" + ship.api_maxhp;}, //hp
        STYPE:function(ship){return findById(Ship.fieldVal.STYPE(ship),window.mst.api_mst_stype).api_name;},
        RARE:function(ship){return Ship.fieldVal.RARE(ship);},
        LV:function(ship){return ship.api_lv;}, //lv
        AFTERLV:function(ship){var lv=Ship.fieldVal.AFTERLV(ship); return lv==0?"-":lv;}, //lv
        EXP2:function(ship){return ship.api_exp[1];}, //exp2nxt
        COND:function(ship){return ship.api_cond;}, //Cond
        KARY:function(ship){return getRem(ship.api_karyoku);}, //karyoku
        RAIS:function(ship){return getRem(ship.api_raisou);}, //raisou
        TAIK:function(ship){return getRem(ship.api_taiku);}, //taiku
        SOUK:function(ship){return getRem(ship.api_soukou);}, //soukou
        KAIH:function(ship){return getRem(ship.api_kaihi);}, //kaihi
        TAIS:function(ship){return getRem(ship.api_taisen);}, //taisen
        SAKU:function(ship){return getRem(ship.api_sakuteki);}, //sakuteki
        LUCK:function(ship){return getRem(ship.api_lucky);} //lucky
    };

    // Comparing ship a, b with field name name and order ord(+1/-1)
    function cmpShip(name,ord,a,b)
    {
        return ord*(Ship.fieldVal[name](a)-Ship.fieldVal[name](b));
    }

    // Filters implemented if can support
    this.filter = {
        LOCK:function(args,ship){ return args == 0? true:(ship.api_locked>0);},
        LVRNG:function(args,ship){
            if (args[0]>0 && ship.api_lv<args[0])return false;
            if (args[1]>0 && ship.api_lv>args[1])return false;
            return true},
        STYPE:function(args,ship){
            return args.indexOf(Ship.fieldVal.STYPE(ship))>=0;}
    };

    function getRem(inp)
    {
        // todo affected by equipments?
        if (inp[0]>=inp[1])
            return inp[0]+"[M]";
        return inp[0]+"["+(inp[1]-inp[0])+"]";
    }

    function healthBarGen(a,b)
    {
        const color=["red","orange","yellow","green"];
        var col = color[Math.floor((a*4-1)/b)];
        var txt = a+"/"+b;
        var par = Math.floor(100*a/b);
        return $('

').append($('

').text(txt));
    }

    this.filterShipList = function(ships,filters)
    {
        for (var i=0;i<filters.length;++i)
            ships = ships.filter(filters[i][0].bind(null,filters[i][1]));
        return ships;
    };

    this.createShipRow = function (cols,ship)
    {
        var tr = $('
');
        for (var i = 0;i<cols.length;++i)
            tr.append($('').append(Ship.fieldS[cols[i]](ship)));
        return tr;
    };

    this.init = function()
    {
        // update ship type names
        var shipsel = $("#ships-type");
        window.mst.api_mst_stype.forEach(function(stype){
                var item=$('').append('<input type="checkbox" name="stype" value="'+stype.api_id+'" checked>')
                    .append($("

").text(stype.api_name));
                shipsel.append(item);
            }
        );

        // ship form init
        $("#ships-selector").on('change',function(event) {
            Ship.update();
        });
        var tmp = $("#ships-cols").empty();

        Object.getOwnPropertyNames(Ship.field).forEach(function(val){
            tmp.append($('').append('<input type="checkbox" name="cols" value="'+val+'" checked>'+Ship.field[val]));
        });
    };

    this.update = function()
    {
        var cols = [];
        var stypes = [];
        var lock = 0;
        var lvmin = 0;
        var lvmax = 0;
        var shipsel = $("#ships-selector");
        shipsel.find("input[name=cols]:checkbox:checked").each(function() {
            cols.push($(this).val());
        });

        shipsel.find("input[name=stype]:checkbox:checked").each(function() {
            stypes.push(eval($(this).val()));
        });

        shipsel.find("input[name=locked]:checkbox:checked").each(function() {
            lock=1;
        });

        shipsel.find("input[name=lvmin]").each(function() {
            lvmin = eval($(this).val());
        });

        shipsel.find("input[name=lvmax]").each(function() {
            lvmax = eval($(this).val());
        });

        var th = $("#ships-head");
        var tr = $('

');
        for (var i = 0;i<cols.length;++i)
        {
            var col =$('	').text(Ship.field[cols[i]]);
            $(col).click((function(name)
            {
                if (Config.Sort.name!=name)
                {
                    Config.Sort.name=name;
                    Config.Sort.dir=1;
                }
                else Config.Sort.dir=-Config.Sort.dir;
                Ship.update();
            }).bind(null,cols[i]));
            $(tr).append(col);
        }
        $(th).empty().append(tr);

        var sortf = cmpShip.bind(null,Config.Sort.name,Config.Sort.dir);

        // filtering
        //var ship=window.rawsvd.port.api_ship;
        var ship = Ship.filterShipList(window.rawsvd.port.api_ship,[
            [Ship.filter.STYPE,stypes],
            [Ship.filter.LOCK,lock],
            [Ship.filter.LVRNG,[lvmin,lvmax]]]);
        ship = ship.sort(sortf);
        updateList('#ships-list',ship,Ship.createShipRow.bind(null,cols));
    };
};

Timer = new function(){
    this.init = function(){
        setInterval(function(){
            try{
                Timer.update();
            }catch(err){
                // discard everything
            }
        },1000);
    };

    function updateTimerList(tag,times)
    {
        $(tag).each(function()
        {
            $(this).empty();
            var rowContainer = this;
            times.forEach(function(item){
                $(rowContainer).append($("

").text(new Mytimer(item).toString()));
            });
        })
    }

    this.update = function()
    {
        var times = [];
        window.rawsvd.port.api_deck_port.forEach(function(item){
            if (item.api_mission[0] != 0)
                times.push(item.api_mission[2]);
        });
        updateTimerList('.mission_timers',times);
        times = [];
        window.rawsvd.kdock.forEach(function(item){
            if (item.api_state > 0)
                times.push(item.api_complete_time);
        });
        updateTimerList('.build_timers',times);
        times = [];
        window.rawsvd.port.api_ndock.forEach(function(item){
            if (item.api_state > 0)
                times.push(item.api_complete_time);
        });
        updateTimerList('.repair_timers',times);
    };
};

Deck = new function(){
    this.field = {
        NAME:"名称",
        MISS:"任务",
        SHIP:"舰船信息"
    };

    this.fieldVal = {
        NAME:function(deck){return deck.api_id;},
        MISS:function(deck){return deck.api_mission[1];},
        SHIP:function(deck){return 0;}
    };
    this.shipField=["NAME","STYPE","LV","EXP2","HP","COND"];
    function genShipList(ships)
    {
        var tmp = $('
');
        ships.forEach(function(id) {
            if (id<0)
                return;
            var ship = findById(id,window.rawsvd.port.api_ship);
            var row = $('
');
            Deck.shipField.forEach(function(field){
                $(row).append($('').append(Ship.fieldS[field](ship)));
            });
            $(tmp).append(row);
        });
        return tmp;
    }

    function genMission(mission)
    {
        if (mission[1] == 0)
            return "无";
        var tmp=$('
');
        var mis = findById(mission[1],window.mst.api_mst_mission);
        $(tmp).append($('
').append(Mission.fieldS.AREA(mis)));
        $(tmp).append($('
').append(Mission.fieldS.NAME(mis)));
        $(tmp).append($('
').append(new Date(mission[2]).toLocaleTimeString()));
        return tmp;
    }
    this.fieldS = {
        NAME:function(deck){return deck.api_name;},
        MISS:function(deck){return genMission(deck.api_mission);},
        SHIP:function(deck){return genShipList(deck.api_ship);}
    };

    this.init = function()
    {
        var list=$('#decks-list').find(' thead').empty();
        var tmp=$('
');
        Object.getOwnPropertyNames(Deck.field).forEach(function(val){
            tmp.append($('').append(Deck.field[val]));
        });
        list.append(tmp);
        $(list).find("th:not(:last-child)").attr("rowspan",2);
        tmp=$('
');
        Deck.shipField.forEach(function(field){
            $(tmp).append($('').append(Ship.field[field]));
        });
        list.append(tmp);

    };
    function createDeckRow(deck,args)
    {
        var tmp = $('
');
        Object.getOwnPropertyNames(Deck.field).forEach(function(val){
            tmp.append($('').append(Deck.fieldS[val](deck)));
        });
        return tmp;
    }
    this.update = function()
    {
        var list=$('#decks-list').find(' tbody').empty();
        var args = 0;
        window.rawsvd.port.api_deck_port.forEach(function(deck){
            list.append(createDeckRow(deck,args));
        });
    };
};<|endoftext|>


<commit_before>Upload = {
    // Simple upload plugin

    init: function(fileTypes) {
	// Use either as standalone upload plugin
	//    =Upload('doc,docx')
	// Or as upload response plugin
	//    Answer: Upload/ipynb;...
	this.confirmMsgElem = document.getElementById(this.pluginId+'-uploadconfirm-msg');
	this.confirmLoadElem = document.getElementById(this.pluginId+'-uploadconfirm-load');
	this.uploadElem = document.getElementById(this.pluginId+'-uploadbutton');
	this.uploadElem.addEventListener('change', this.fileUpload.bind(this), false);

	this.viewer = {};
	if (!this.qattributes || Slidoc.PluginManager.answered(this.qattributes.qnumber) || Slidoc.PluginManager.lateSession()) {
	    // Upload only works with questions
	    this.uploadElem.style.display = 'none';
	    document.getElementById(this.pluginId+'-uploadlabel').style.display = 'none';
	    return;
	}

	var fTypes = (fileTypes || this.qattributes.qtype.split('/')[1]).trim().split(',');
	Slidoc.log('Slidoc.Plugins.Upload.init:', fTypes);
	this.fileTypes = [];
	for (var j=0; j<fTypes.length; j++)
	    this.fileTypes.push(fTypes[j].trim().toLowerCase());

	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	Slidoc.log('Slidoc.Plugins.Upload.init2:', fileInfo);
	if (fileInfo && fileInfo.loadURL)
	    this.viewer.initURL = fileInfo.loadURL;
    },

    display: function (response, pluginResp) {
	Slidoc.log('Slidoc.Plugins.Upload.display:', this, response, pluginResp);
	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	if (fileInfo) {
	    this.confirmMsgElem.textContent = 'Successfully uploaded '+fileInfo.origName+' at '+(new Date(fileInfo.uploadTime));
	    this.confirmLoadElem.href = fileInfo.loadURL;
	} else {
	    this.confirmMsgElem.textContent = 'Nothing uploaded';
	    this.confirmLoadElem.href = '';
	}
	if (this.viewer.displayURL)
	    this.viewer.displayURL(fileInfo ? fileInfo.loadURL : '');
    },

    uploaded: function(value) {
	Slidoc.log('Slidoc.Plugins.Upload.uploaded:', value);
	var loadURL = location.host + value.url;
	if (this.fileType == 'ipynb') {
	    // Append "query" using "%3F" as nbviewer chomps up normal queries
	    loadURL += '%3F' + value.fileKey;
	    if (location.protocol == 'https:')
		loadURL = 'https://nbviewer.jupyter.org/urls/'+loadURL;
	    else
		loadURL = 'http://nbviewer.jupyter.org/url/'+loadURL;
	} else {
	    loadURL = '//'+loadURL;
	}
	this.persist[this.qattributes.qnumber] = {origName: this.origName, fileType: this.fileType, upload: value, loadURL: loadURL,
						  uploadTime: Date.now()};
	this.display(value.name);
	Slidoc.PluginManager.saveSession(); // Save upload info
    },

    disable: function (displayCorrect) {
	Slidoc.log('Slidoc.Plugins.Upload.disable:', displayCorrect);
	this.uploadElem.disabled = 'disabled';
    },

    fileUpload: function (evt) {
	Slidoc.log('Slidoc.Plugins.Upload.fileUpload:', evt);
	if (!evt.target || !evt.target.files)
	    return;
	var files = evt.target.files; // FileList object
	if (files.length != 1) {
	    alert("Please select a single file");
	    return;
	}

	var file = files[0];
	this.origName = file.name;
	this.fileType = ((file.type ? fileTypeMap[file.type]:'') || file.name.split('.').slice(-1)[0]).toLowerCase();
	if (this.fileTypes.indexOf(this.fileType) < 0) {
	    alert('Invalid file type '+this.fileType+'; expecting one of '+this.fileTypes);
	    return;
	}

	var fileDesc = file.name+', '+file.size+' bytes';
	if (file.lastModifiedDate)
	    fileDesc += ', last modified: '+file.lastModifiedDate.toLocaleDateString();

	var loadCallback = function(result) {
	    if (!result || result.error) {
		alert('Error in uploading file: '+( (result && result.error) ? result.error : ''));
		return;
	    }
	    this.uploaded(result.value);
	}

	var filePrefix = '';
	var qno = this.qattributes ? 'q'+this.qattributes.qnumber : '';
	if (qno)
	    filePrefix += qno + '/' + this.sessionName + '-' + qno;
	else
	    filePrefix += this.sessionName;

	var match = /^([- \w]+)(,\s*([A-Z]).*)$/i.exec(this.displayName||'');
	if (match)
	    filePrefix += '-' + match[1].trim().replace(' ','-').toLowerCase() + (match[3] ? '-'+match[3].toLowerCase() : '');

	var dataParams = {filename: file.name, mimeType: file.type, filePrefix: filePrefix}
	var loadHandler = function(loadEvt) {
	    var arrBuffer = loadEvt.target.result;
	    Slidoc.log('Slidoc.Plugins.Upload.fileUpload.loadHandler:', file.name, file.type, arrBuffer.byteLength);
	    this.remoteCall('_uploadData', loadCallback.bind(this), dataParams, arrBuffer.byteLength);
	    if (!window.GService)
		alert('No upload service');
	    GService.rawWS(arrBuffer);
	};

	var reader = new FileReader();
	reader.onload = loadHandler.bind(this);
	reader.onerror = function(loadEvt) {
	    alert("Failed to read file "+file.name+" (code="+loadEvt.target.error.code+")");
	};

	reader.readAsArrayBuffer(file);
    },

    response: function (retry, callback) {
	Slidoc.log('Slidoc.Plugins.Upload.response:', retry, !!callback);
	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	if (!fileInfo && !window.confirm('Answer file not uploaded. Do you want to give up on trying to upload it?'))
	    return;

	if (fileInfo) {
	    var response = fileInfo.origName;
	    var pluginResp = {name: fileInfo.upload.name, score: 1, correctAnswer: '', time: fileInfo.uploadTime,
			      fileType: this.fileType, url: fileInfo.upload.url, fileKey: fileInfo.upload.fileKey};
	    this.remoteCall('lockFile', null, fileInfo.upload.url);
	} else {
	    var response = '';
	    var pluginResp = {name: '', score: null, correctAnswer: ''};
	}

	if (callback)
	    callback(response, pluginResp);
    }
};

var fileTypeMap = {
    'application/pdf': 'pdf'
};

/* PluginHead: ^(,?ipynb|,?pdf)+$

   PluginBody:
   Select file or drag-and-drop over button:
   <input type="file" id="%(pluginId)s-uploadbutton"
   class="slidoc-clickable slidoc-button slidoc-plugin-Upload-button %(pluginId)s-uploadbutton"
   onclick="Slidoc.Plugins['%(pluginName)s']['%(pluginSlideId)s'].fileUpload(this);"></input>

     Click here to view/download

*/
<commit_msg>Fixes2<commit_after>Upload = {
    // Simple upload plugin

    init: function(fileTypes) {
	// Use either as standalone upload plugin
	//    =Upload('doc,docx')
	// Or as upload response plugin
	//    Answer: Upload/ipynb;...
	this.confirmMsgElem = document.getElementById(this.pluginId+'-uploadconfirm-msg');
	this.confirmLoadElem = document.getElementById(this.pluginId+'-uploadconfirm-load');
	this.uploadElem = document.getElementById(this.pluginId+'-uploadbutton');
	this.uploadElem.addEventListener('change', this.fileUpload.bind(this), false);

	this.viewer = {};
	if (!this.qattributes || Slidoc.PluginManager.answered(this.qattributes.qnumber) || Slidoc.PluginManager.lateSession()) {
	    // Upload only works with questions
	    this.uploadElem.style.display = 'none';
	    document.getElementById(this.pluginId+'-uploadlabel').style.display = 'none';
	    return;
	}

	var fTypes = (fileTypes || this.qattributes.qtype.split('/')[1]).trim().split(',');
	Slidoc.log('Slidoc.Plugins.Upload.init:', fTypes);
	this.fileTypes = [];
	for (var j=0; j<fTypes.length; j++)
	    this.fileTypes.push(fTypes[j].trim().toLowerCase());

	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	Slidoc.log('Slidoc.Plugins.Upload.init2:', fileInfo);
	if (fileInfo && fileInfo.loadURL) {
	    this.display();
	    this.viewer.initURL = fileInfo.loadURL;
	}
    },

    display: function (response, pluginResp) {
	Slidoc.log('Slidoc.Plugins.Upload.display:', this, response, pluginResp);
	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	if (fileInfo) {
	    this.confirmMsgElem.textContent = 'Successfully uploaded '+fileInfo.origName+' on '+(new Date(fileInfo.uploadTime));
	    this.confirmLoadElem.href = fileInfo.loadURL;
	} else {
	    this.confirmMsgElem.textContent = 'Nothing uploaded';
	    this.confirmLoadElem.href = '';
	}
	if (this.viewer.displayURL)
	    this.viewer.displayURL(fileInfo ? fileInfo.loadURL : '');
    },

    uploaded: function(value) {
	Slidoc.log('Slidoc.Plugins.Upload.uploaded:', value);
	var loadURL = location.host + value.url;
	if (this.fileType == 'ipynb') {
	    // Append "query" using "%3F" as nbviewer chomps up normal queries
	    loadURL += '%3F' + value.fileKey;
	    if (location.protocol == 'https:')
		loadURL = 'https://nbviewer.jupyter.org/urls/'+loadURL;
	    else
		loadURL = 'http://nbviewer.jupyter.org/url/'+loadURL;
	} else {
	    loadURL = '//'+loadURL;
	}
	this.persist[this.qattributes.qnumber] = {origName: this.origName, fileType: this.fileType, upload: value, loadURL: loadURL,
						  uploadTime: Date.now()};
	this.display(value.name);
	Slidoc.PluginManager.saveSession(); // Save upload info
    },

    disable: function (displayCorrect) {
	Slidoc.log('Slidoc.Plugins.Upload.disable:', displayCorrect);
	this.uploadElem.disabled = 'disabled';
    },

    fileUpload: function (evt) {
	Slidoc.log('Slidoc.Plugins.Upload.fileUpload:', evt);
	if (!evt.target || !evt.target.files)
	    return;
	var files = evt.target.files; // FileList object
	if (files.length != 1) {
	    alert("Please select a single file");
	    return;
	}

	var file = files[0];
	this.origName = file.name;
	this.fileType = ((file.type ? fileTypeMap[file.type]:'') || file.name.split('.').slice(-1)[0]).toLowerCase();
	if (this.fileTypes.indexOf(this.fileType) < 0) {
	    alert('Invalid file type '+this.fileType+'; expecting one of '+this.fileTypes);
	    return;
	}

	var fileDesc = file.name+', '+file.size+' bytes';
	if (file.lastModifiedDate)
	    fileDesc += ', last modified: '+file.lastModifiedDate.toLocaleDateString();

	var loadCallback = function(result) {
	    if (!result || result.error) {
		alert('Error in uploading file: '+( (result && result.error) ? result.error : ''));
		return;
	    }
	    this.uploaded(result.value);
	}

	var filePrefix = '';
	var qno = this.qattributes ? 'q'+this.qattributes.qnumber : '';
	if (qno)
	    filePrefix += qno + '/' + this.sessionName + '-' + qno;
	else
	    filePrefix += this.sessionName;

	var match = /^([- \w]+)(,\s*([A-Z]).*)$/i.exec(this.displayName||'');
	if (match)
	    filePrefix += '-' + match[1].trim().replace(' ','-').toLowerCase() + (match[3] ? '-'+match[3].toLowerCase() : '');

	var dataParams = {filename: file.name, mimeType: file.type, filePrefix: filePrefix}
	var loadHandler = function(loadEvt) {
	    var arrBuffer = loadEvt.target.result;
	    Slidoc.log('Slidoc.Plugins.Upload.fileUpload.loadHandler:', file.name, file.type, arrBuffer.byteLength);
	    this.remoteCall('_uploadData', loadCallback.bind(this), dataParams, arrBuffer.byteLength);
	    if (!window.GService)
		alert('No upload service');
	    GService.rawWS(arrBuffer);
	};

	var reader = new FileReader();
	reader.onload = loadHandler.bind(this);
	reader.onerror = function(loadEvt) {
	    alert("Failed to read file "+file.name+" (code="+loadEvt.target.error.code+")");
	};

	reader.readAsArrayBuffer(file);
    },

    response: function (retry, callback) {
	Slidoc.log('Slidoc.Plugins.Upload.response:', retry, !!callback);
	var fileInfo = this.qattributes && this.persist[this.qattributes.qnumber];
	if (!fileInfo && !window.confirm('Answer file not uploaded. Do you want to give up on trying to upload it?'))
	    return;

	if (fileInfo) {
	    var response = fileInfo.origName;
	    var pluginResp = {name: fileInfo.upload.name, score: 1, correctAnswer: '', time: fileInfo.uploadTime,
			      fileType: this.fileType, url: fileInfo.upload.url, fileKey: fileInfo.upload.fileKey};
	    this.remoteCall('lockFile', null, fileInfo.upload.url);
	} else {
	    var response = '';
	    var pluginResp = {name: '', score: null, correctAnswer: ''};
	}

	if (callback)
	    callback(response, pluginResp);
    }
};

var fileTypeMap = {
    'application/pdf': 'pdf'
};

/* PluginHead: ^(,?ipynb|,?pdf)+$

   PluginBody:
   Select file or drag-and-drop over button:
   <input type="file" id="%(pluginId)s-uploadbutton"
   class="slidoc-clickable slidoc-button slidoc-plugin-Upload-button %(pluginId)s-uploadbutton"
   onclick="Slidoc.Plugins['%(pluginName)s']['%(pluginSlideId)s'].fileUpload(this);"></input>

     Click here to view/download

*/
<|endoftext|>