<commit_before>/*global require, process, console*/

/*
 * lk build-libs --lk-dir .
 *
 */

var args  = require('./helper/args'),
    fs    = require('fs'),
    async = require('async'),
    path  = require('path'),
    http  = require('http'),
    https = require('https'),
    url   = require('url'),
    env   = process.env;

// -=-=-=-=-=-=-=-=-=-=-
// script options
// -=-=-=-=-=-=-=-=-=-=-

var options = args.options([
    ['-h', '--help', 'Show this help'],
    ['--lk-dir DIR', 'Path to Lively Kernel core directory']],
    {},
    "Used for assembling / compiling / minimizing the necessary lib files for Lively.");

if (!options.lkDir && env.WORKSPACE_LK_EXISTS) {
    options.lkDir = env.WORKSPACE_LK;
}

if (!options.lkDir) {
    options.showHelpAndExit();
}

// -=-=-=-=-=-=-=-=-=-=-
// the real thing
// -=-=-=-=-=-=-=-=-=-=-
function store(filePath, data, thenDo) {
    fs.writeFile(filePath, data, function() {
        console.log("storing all libs in %s (%skb)",
                    filePath, Math.round((data.length / 1024) * 100) / 100);
        thenDo && thenDo();
    });
}

function download(urlString, thenDo) {
    var data = '',
        fileName = urlString.substring(urlString.lastIndexOf('/') + 1),
        urlParsed = url.parse(urlString),
        get = urlParsed.protocol === 'https:' ? https.get : http.get;

    console.log('Downloading %s...', fileName);
    comment += ' * ' + fileName + '\n';
    get({host: urlParsed.host, path: urlParsed.path}, function(res) {
        res.on('data', function (chunk) { data += chunk; })
        res.on('end', function() { thenDo(null, '// ' + urlString + '\n' + data) })
    }).on('error', function(e) {
        console.error("Error when trying to download %s: %s", urlString, e.message); })
}

function copyLocal(relativePath, thenDo) {
    var fullPath = path.join(env.LK_SCRIPTS_ROOT, relativePath),
        name = path.basename(fullPath);
    console.log('Reading %s...', name);
    comment += ' * ' + name + '\n';
    fs.readFile(fullPath, function(err, data) { thenDo(err, '// ' + relativePath + '\n' + data) });
}

function downloadAndModify(urlString, modifyFunc, thenDo) {
    download(urlString, function(err, data) { thenDo(null, modifyFunc(data)); });
}

var comment = "/*\n"
            + " * This file was compiled on " + new Date().toUTCString() + " with the libs:\n",
    downloads = [
        download.bind(global, "http://code.jquery.com/jquery-1.7.2.min.js"),
        download.bind(global, "https://raw.github.com/kriskowal/es5-shim/v1.2.10/es5-shim.min.js")],
    localLibs = [copyLocal.bind(global, "resources/pre-lib/requestAnimationFrame.js"),
                 copyLocal.bind(global, "resources/pre-lib/IE-fixes.js")];

async.series(
    downloads.concat(localLibs),
    function(err, allData) {
        comment += " */";
        store(
            path.join(options.lkDir, 'core/lib/lively-libs.js'),
            [comment].concat(allData).join('\n\n'));
    });<commit_msg>more detailed comment<commit_after>/*global require, process, console*/

/*
 * lk build-libs --lk-dir .
 *
 */

var args  = require('./helper/args'),
    fs    = require('fs'),
    async = require('async'),
    path  = require('path'),
    http  = require('http'),
    https = require('https'),
    url   = require('url'),
    env   = process.env;

// -=-=-=-=-=-=-=-=-=-=-
// script options
// -=-=-=-=-=-=-=-=-=-=-

var options = args.options([
    ['-h', '--help', 'Show this help'],
    ['--lk-dir DIR', 'Path to Lively Kernel core directory']],
    {},
    "Used for assembling / compiling / minimizing the necessary lib files for Lively.");

if (!options.lkDir && env.WORKSPACE_LK_EXISTS) {
    options.lkDir = env.WORKSPACE_LK;
}

if (!options.lkDir) {
    options.showHelpAndExit();
}

// -=-=-=-=-=-=-=-=-=-=-
// the real thing
// -=-=-=-=-=-=-=-=-=-=-
function store(filePath, data, thenDo) {
    fs.writeFile(filePath, data, function() {
        console.log("storing all libs in %s (%skb)",
                    filePath, Math.round((data.length / 1024) * 100) / 100);
        thenDo && thenDo();
    });
}

function download(urlString, thenDo) {
    var data = '',
        fileName = urlString.substring(urlString.lastIndexOf('/') + 1),
        urlParsed = url.parse(urlString),
        get = urlParsed.protocol === 'https:' ? https.get : http.get;

    console.log('Downloading %s...', fileName);
    comment += ' * ' + fileName + '\n';
    get({host: urlParsed.host, path: urlParsed.path}, function(res) {
        res.on('data', function (chunk) { data += chunk; })
        res.on('end', function() { thenDo(null, '// ' + urlString + '\n' + data) })
    }).on('error', function(e) {
        console.error("Error when trying to download %s: %s", urlString, e.message); })
}

function copyLocal(relativePath, thenDo) {
    var fullPath = path.join(env.LK_SCRIPTS_ROOT, relativePath),
        name = path.basename(fullPath);
    console.log('Reading %s...', name);
    comment += ' * ' + name + '\n';
    fs.readFile(fullPath, function(err, data) { thenDo(err, '// ' + relativePath + '\n' + data) });
}

function downloadAndModify(urlString, modifyFunc, thenDo) {
    download(urlString, function(err, data) { thenDo(null, modifyFunc(data)); });
}

var comment = "/*\n"
            + " * This file was compiled with \"lk build-libs\" on "
            + new Date().toUTCString() + " with the libs:\n",
    downloads = [
        download.bind(global, "http://code.jquery.com/jquery-1.7.2.min.js"),
        download.bind(global, "https://raw.github.com/kriskowal/es5-shim/v1.2.10/es5-shim.min.js")],
    localLibs = [copyLocal.bind(global, "resources/pre-lib/requestAnimationFrame.js"),
                 copyLocal.bind(global, "resources/pre-lib/IE-fixes.js")];

async.series(
    downloads.concat(localLibs),
    function(err, allData) {
        comment += " */";
        store(
            path.join(options.lkDir, 'core/lib/lively-libs.js'),
            [comment].concat(allData).join('\n\n'));
    });<|endoftext|>


<commit_before>import React from 'react';
import Tooltip from './../../../components/tooltip';
import chainFunctions from './../../helpers/chain-functions';
import ReactDOM from 'react-dom';

/**
 * ToolTipIDecorator.
 *
 * This decorator attaches a tooltip to a component.
 *
 * == How to use ToolTip decorator in a component:
 *
 * In your file:
 *
 *   import ToolTip from 'carbon/lib/utils/decorators/tooltip-decorator';
 *
 * To use the decorator, wrap your component with it:
 *
 *   const MyComponent = ToolTipIDecorator(
 *   class MyComponent extends React.Component {
 *     ...
 *   });
 *
 * You must also output the tooltip HTML in your component's render method:
 *
 * e.g.
 *
 * render() {
 *   return (
 *

 *       { this.tooltipHTML }
 *       ...your components JSX
 *

 *   );
 * }
 *
 * For the tooltip to position correctly, you must supply a ref of '_target' to your component.
 * You must also give the surrounding div of the component a position of 'relative'
 *
 * e.g.
 *
 * render() {
 *   return (
 *
 this._tooltip = comp } className='relative-class'>
 *       { this.tooltipHTML }
 *       ...your components JSX
 *

 *   );
 * }
 *
 *
 * @method ToolTipIDecorator
 * @param {Class} ComposedComponent class to decorate
 * @return {Object} Decorated Component
 */
let TooltipIDecorator = (ComposedComponent) => class Component extends ComposedComponent {

  constructor(...args) {
    super(...args);
  }

  state = {
    /**
     * Whether tooltip currently showing
     *
     * @property showTooltip
     * @type {Boolean}
     * @default false
     */
    showTooltip: false
  };

  /**
   * Shows tooltip
   *
   * @method onShow
   * @return {void}
   */
  onShow = () => {
    setTimeout(() => {
      this.setState({ showTooltip: true });
      this.positionTooltip();
    }, 300 );
  };

  /**
   * Hides tooltip
   *
   * @method onHide
   * @return {void}
   */
  onHide = () => {
    this.setState({ showTooltip: false });
  };

  /**
   * Returns the DOM node of the target.
   *
   * @method getTarget
   * @return {DOM node}
   */
  getTarget = () => {
    return ReactDOM.findDOMNode(this._target);
  }

  /**
   * Returns the DOM node of the tooltip.
   *
   * @method getTooltip
   * @return {DOM node}
   */
  getTooltip = () => {
    return ReactDOM.findDOMNode(this._tooltip);
  }

  /**
   * Positions tooltip relative to target
   *
   * @method positionTooltip
   * @return {Void}
   */
  positionTooltip = () => {
    if (this.state.showTooltip) {
      let target  = this.getTarget(),
          tooltip = this.getTooltip(),
          pointer = tooltip.children[1];

      let position      = this.props.tooltipPosition || 'top',
          tooltipWidth  = tooltip.offsetWidth,
          tooltipHeight = tooltip.offsetHeight,
          pointerHeight = pointer.offsetHeight,
          targetWidth   = target.offsetWidth,
          targetHeight  = target.offsetHeight;

      switch (position) {
        case "top":
          tooltip.style.top = String(-tooltipHeight - pointerHeight / 2) + 'px';
          tooltip.style.left = String(-tooltipWidth / 2 + targetWidth / 2) + 'px';
          break;

        case "bottom":
          tooltip.style.bottom = String(-tooltipHeight - pointerHeight / 2) + 'px';
          tooltip.style.left = String(-tooltipWidth / 2 + targetWidth / 2) + 'px';
          break;

        case "left":
          // hardcode 7px for pointerWidth since span has no width
          tooltip.style.left = String(-tooltipWidth - 7) + 'px';
          tooltip.style.top = String((targetHeight / 2) - (tooltipHeight / 2)) + 'px';
          break;

        case "right":
        // hardcode 7px for pointerWidth since span has no width
          tooltip.style.left = String(targetWidth + 7) + 'px';
          tooltip.style.top = String((targetHeight / 2) - (tooltipHeight / 2)) + 'px';
      }
    }
  };

  /**
   * Additional Props for decorated component
   *
   * @method componentProps
   * @return {Object} props
   */
  get componentProps() {
    let props = super.componentProps || super.inputProps;

    if (this.props.tooltipMessage) {
      props.onMouseEnter = chainFunctions(this.onShow, props.onMouseEnter);
      props.onMouseLeave = chainFunctions(this.onHide, props.onMouseLeave);
      props.onFocus = chainFunctions(this.onShow, props.onFocus);
      props.onBlur = chainFunctions(this.onHide, props.onBlur);
      props.onTouchEnd = this.state.showTooltip ? this.onHide : this.onShow;
    }
    return props;
  }

  /**
   * Alias for componentProps if component is an input
   *
   * @method inputProps
   * @return {Function} componentProps
   */
  get inputProps() {
    return this.componentProps;
  }

  /**
   * Calculated props for pointer
   *
   * @method pointerProps
   * @return {Object} props
   */
  get pointerProps() {
    if (this.props.tooltipMessage) {
      let props = {};

      switch (this.props.tooltipPosition) {
        case 'bottom':
          props.pointerPosition = 'top';
          break;
        case 'right':
          props.pointerPosition = 'left';
          break;
        case 'left':
          props.pointerPosition = 'right';
          break;
        default:
          props.pointerPosition = 'bottom';
      }

      props.pointerAlign = this.props.pointerAlign;

      return props;
    }
  }

  /**
   * Supplies the HTML for tooltip
   *
   * @method tooltipHTML
   * @return {Object} JSX for tooltip
   */
  get tooltipHTML() {
    if (this.props.tooltipMessage) {
      return (
        <Tooltip
          ref={ (comp) => this._tooltip = comp }
          showTooltip={ this.state.showTooltip }
          { ...this.pointerProps }>
          { this.props.tooltipMessage }
        </Tooltip>
      );
    }
  }
};

export default TooltipIDecorator;
<commit_msg>settimeout to 0 to fix toggle<commit_after>import React from 'react';
import Tooltip from './../../../components/tooltip';
import chainFunctions from './../../helpers/chain-functions';
import ReactDOM from 'react-dom';

/**
 * ToolTipIDecorator.
 *
 * This decorator attaches a tooltip to a component.
 *
 * == How to use ToolTip decorator in a component:
 *
 * In your file:
 *
 *   import ToolTip from 'carbon/lib/utils/decorators/tooltip-decorator';
 *
 * To use the decorator, wrap your component with it:
 *
 *   const MyComponent = ToolTipIDecorator(
 *   class MyComponent extends React.Component {
 *     ...
 *   });
 *
 * You must also output the tooltip HTML in your component's render method:
 *
 * e.g.
 *
 * render() {
 *   return (
 *

 *       { this.tooltipHTML }
 *       ...your components JSX
 *

 *   );
 * }
 *
 * For the tooltip to position correctly, you must supply a ref of '_target' to your component.
 * You must also give the surrounding div of the component a position of 'relative'
 *
 * e.g.
 *
 * render() {
 *   return (
 *
 this._tooltip = comp } className='relative-class'>
 *       { this.tooltipHTML }
 *       ...your components JSX
 *

 *   );
 * }
 *
 *
 * @method ToolTipIDecorator
 * @param {Class} ComposedComponent class to decorate
 * @return {Object} Decorated Component
 */
let TooltipIDecorator = (ComposedComponent) => class Component extends ComposedComponent {

  constructor(...args) {
    super(...args);
  }

  state = {
    /**
     * Whether tooltip currently showing
     *
     * @property showTooltip
     * @type {Boolean}
     * @default false
     */
    showTooltip: false
  };

  /**
   * Shows tooltip
   *
   * @method onShow
   * @return {void}
   */
  onShow = () => {
    setTimeout(() => {
      this.setState({ showTooltip: true });
      this.positionTooltip();
    }, 0 );
  };

  /**
   * Hides tooltip
   *
   * @method onHide
   * @return {void}
   */
  onHide = () => {
    this.setState({ showTooltip: false });
  };

  /**
   * Returns the DOM node of the target.
   *
   * @method getTarget
   * @return {DOM node}
   */
  getTarget = () => {
    return ReactDOM.findDOMNode(this._target);
  }

  /**
   * Returns the DOM node of the tooltip.
   *
   * @method getTooltip
   * @return {DOM node}
   */
  getTooltip = () => {
    return ReactDOM.findDOMNode(this._tooltip);
  }

  /**
   * Positions tooltip relative to target
   *
   * @method positionTooltip
   * @return {Void}
   */
  positionTooltip = () => {
    if (this.state.showTooltip) {
      let target  = this.getTarget(),
          tooltip = this.getTooltip(),
          pointer = tooltip.children[1];

      let position      = this.props.tooltipPosition || 'top',
          tooltipWidth  = tooltip.offsetWidth,
          tooltipHeight = tooltip.offsetHeight,
          pointerHeight = pointer.offsetHeight,
          targetWidth   = target.offsetWidth,
          targetHeight  = target.offsetHeight;

      switch (position) {
        case "top":
          tooltip.style.top = String(-tooltipHeight - pointerHeight / 2) + 'px';
          tooltip.style.left = String(-tooltipWidth / 2 + targetWidth / 2) + 'px';
          break;

        case "bottom":
          tooltip.style.bottom = String(-tooltipHeight - pointerHeight / 2) + 'px';
          tooltip.style.left = String(-tooltipWidth / 2 + targetWidth / 2) + 'px';
          break;

        case "left":
          // hardcode 7px for pointerWidth since span has no width
          tooltip.style.left = String(-tooltipWidth - 7) + 'px';
          tooltip.style.top = String((targetHeight / 2) - (tooltipHeight / 2)) + 'px';
          break;

        case "right":
        // hardcode 7px for pointerWidth since span has no width
          tooltip.style.left = String(targetWidth + 7) + 'px';
          tooltip.style.top = String((targetHeight / 2) - (tooltipHeight / 2)) + 'px';
      }
    }
  };

  /**
   * Additional Props for decorated component
   *
   * @method componentProps
   * @return {Object} props
   */
  get componentProps() {
    let props = super.componentProps || super.inputProps;

    if (this.props.tooltipMessage) {
      props.onMouseEnter = chainFunctions(this.onShow, props.onMouseEnter);
      props.onMouseLeave = chainFunctions(this.onHide, props.onMouseLeave);
      props.onFocus = chainFunctions(this.onShow, props.onFocus);
      props.onBlur = chainFunctions(this.onHide, props.onBlur);
      props.onTouchEnd = this.state.showTooltip ? this.onHide : this.onShow;
    }
    return props;
  }

  /**
   * Alias for componentProps if component is an input
   *
   * @method inputProps
   * @return {Function} componentProps
   */
  get inputProps() {
    return this.componentProps;
  }

  /**
   * Calculated props for pointer
   *
   * @method pointerProps
   * @return {Object} props
   */
  get pointerProps() {
    if (this.props.tooltipMessage) {
      let props = {};

      switch (this.props.tooltipPosition) {
        case 'bottom':
          props.pointerPosition = 'top';
          break;
        case 'right':
          props.pointerPosition = 'left';
          break;
        case 'left':
          props.pointerPosition = 'right';
          break;
        default:
          props.pointerPosition = 'bottom';
      }

      props.pointerAlign = this.props.pointerAlign;

      return props;
    }
  }

  /**
   * Supplies the HTML for tooltip
   *
   * @method tooltipHTML
   * @return {Object} JSX for tooltip
   */
  get tooltipHTML() {
    if (this.props.tooltipMessage) {
      return (
        <Tooltip
          ref={ (comp) => this._tooltip = comp }
          showTooltip={ this.state.showTooltip }
          { ...this.pointerProps }>
          { this.props.tooltipMessage }
        </Tooltip>
      );
    }
  }
};

export default TooltipIDecorator;
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const Pouchy = require('pouchy');
// Pouchy.PouchDB.debug.enable('*');
const url = require('url');
const assign = require('lodash/assign');
const cloneDeep = require('lodash/cloneDeep');
const defaultsDeep = require('lodash/defaultsDeep');
const get = require('lodash/get');
const set = require('lodash/set');
const isString = require('lodash/isString');
const without = require('lodash/without');
const result = require('lodash/result');

/**
 * @private
 * @function arrHasEmbeddedMatch
 * @description tests if an array has a match embedded in its items
 * @param {array} arr
 * @param {string} str
 * @return {boolean}
 */
const arrHasEmbeddedMatch = (arr, str) => arr.some(toMatch => str.match(toMatch));

/**
 * @class DBRegistry
 * @private
 * @note use the db-registry module/service to obtain an env specific
 * db-registry instance.  this class is for _internal_ consumption only
 *
 * @example
 * const registry = DBRegistry({
 *    isRemote: true, // configure for compute/server instance
 *    dir: appDirectory,
 *    remoteStoresSyncIn: ['local-results'],
 *    remoteStoresSyncBoth: ['remote-results']
 * });
 *
 * @param {object} opts
 * @param {string} opts.path path to fs dir where dbs will be stored
 * @param {object} opts.remote Pouchy defaults applied to remote db instantiations
 * @param {object=} opts.local Pouchy defaults applied to local db instantiations
 * @param {array=} opts.localStores strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncBoth strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncIn strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncOut strings/regexes used to configure Pouch syncronization
 * @param {object=} opts.pouchConfig default PouchDB config applied to all pouchy instances
 * @param {boolean} opts.noURLPrefix option to disable prefixing remote db urls [default false]
 * @param {Function|object} [opts.ajax] object or function to be applied to each db request
 * @property {object} dbs
 * @property {array} dbs.all all db instances in the registry
 * @property {object} dbs.registry all db instances indexed by name/url
 * @property {array} names convenience prop to access list of current db names
 */
class DBRegistry {

  constructor(opts) {
    assign(this, opts);
    this.all = [];
    this.registry = {};
    if (!this.remote || !this.remote.db) {
      throw new ReferenceError([
        'DBRegistry `remote.db` as',
        '`url.format(opts.remote.db)`\'able configuration',
      ].join(' '));
    }
  }

  /**
   * gets an existing or new instance of a db
   * @param  {string} nameOrUrl name of db or url to remote db
   * @param {object=} opts passed directly to `.register`.  see `.register` for more
   * @return {Pouchy}
   */
  get(nameOrUrl, _opts) {
    const opts = Object.assign({}, _opts);
    /* istanbul ignore if */
    if (nameOrUrl.match(/^http/) || nameOrUrl.match(/^\//)) {
      opts.url = nameOrUrl;
    } else {
      opts.name = nameOrUrl;
    }

    if (this.registry[nameOrUrl]) {
      return this.registry[nameOrUrl];
    }

    return this.register(opts);
  }

  /**
   * @private
   */
  isBidirectionalSyncStore(connStr) {
    return (
      this.remoteStoresSyncBoth &&
      arrHasEmbeddedMatch(this.remoteStoresSyncBoth, connStr)
    );
  }

  /**
   * @private
   */
  isLocalStore(connStr) {
    return this.localStores && arrHasEmbeddedMatch(this.localStores, connStr);
  }

  /**
   * @private
   */
  isSyncInStore(connStr) {
    return (
      this.remoteStoresSyncIn &&
      arrHasEmbeddedMatch(this.remoteStoresSyncIn, connStr)
    );
  }

  /**
   * @private
   */
  isSyncOutStore(connStr) {
    return (
      this.remoteStoresSyncOut &&
      arrHasEmbeddedMatch(this.remoteStoresSyncOut, connStr)
    );
  }

  /**
   * Register an app-level datastore.
   * @note remote url configurations stored on `this.remote` are not purely used
   * to generate Couch/Pouch request endpoints with the db name.  The pathname is
   * prefixed with an `'up/'` or `'down/' prior to `url.format(...)` such that
   * the coinstac-storage-proxy may intercept requests and perform auth/validation.
   * this prefixing occurs _only_ in `isLocal` mode.  `isRemote` mode implies
   * that the registry is working securely behind COINS infrastructure, and can
   * hit the Couch API directly, bypassing the proxy
   * @param  {object} opts      Required options for a Pouchy instance
   * @param  {string=} opts.name Store's registered name.
   *                             Should match a store array passed to constructor)
   * @param  {string=} opts.url  Store's URL
   * @returns {Pouchy}           Database instance
   */
  register(opts) {
    const conf = cloneDeep(opts);
    const rootRemoteDBPathname = get(this, 'remote.db.pathname') || '';
    const connStr = conf.name || conf.url;
    const syncDefaults = { live: true, retry: true, heartbeat: 5000 };
    conf.path = conf.path || this.path;
    conf.pouchConfig = conf.pouchConfig || {};
    // assert that db can register, and configure its domain
    if (this.isLocalStore(connStr)) {
      defaultsDeep(conf, this.local);
    } else if (this.isBidirectionalSyncStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { sync: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign(
          {},
          this.remote.db,
          { pathname: path.join(rootRemoteDBPathname, conf.name) }
        ) // never up/down prefix dual-dir stores
      );
    } else if (this.isSyncInStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { in: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign({}, this.remote.db, {
          pathname: `${rootRemoteDBPathname}/${conf.name}`,
        })
      );
    } else if (this.isSyncOutStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { out: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign({}, this.remote.db, {
          pathname: `${rootRemoteDBPathname}/${conf.name}`,
        })
      );
    } else {
      /* istanbul ignore next */
      throw new ReferenceError(
        `database ${connStr} does not fit local or remote database variants`
      );
    }
    if (this.pouchConfig) {
      conf.pouchConfig = defaultsDeep(this.pouchConfig, conf.pouchConfig);
    }

    conf.pouchConfig.ajax = result(this, 'ajax');

    // convert string declared PouchDB adapter to adapter instance
    const requestedAdapter = get(conf, 'pouchConfig.db');
    /* istanbul ignore if */
    if (isString(requestedAdapter)) {
      set(conf, 'pouchConfig.db', require(requestedAdapter)); // eslint-disable-line
    }

    // build db and cache it
    const db = new Pouchy(conf);
    this.registry[conf.name || conf.url] = db;
    this.all.push(db);
    return db;
  }

  /**
   * Removes database from registry
   * @param  {Pouchy|string} db database or database name
   * @param  {function=} cb
   * @return {Promise}
   */
  unregister(db) {
    /* istanbul ignore if */
    if (typeof db === 'string') {
      db = this.all.find(pouchy => pouchy.name === db);
    }
    /* istanbul ignore if */
    if (!db) {
      throw new ReferenceError(`db "${name}" not found. unable to remove`);
    }
    /* istanbul ignore else */
    if (db.syncEmitter) { db.syncEmitter.cancel(); }
    return db.destroy()
    .then((rslt) => {
      this.all = without(this.all, db);
      delete this.registry[db.name];
      return rslt;
    });
  }

  destroy() {
    return Promise.all(this.all.map(this.unregister.bind(this)))
    .then((destroyed) => {
      const notDestroyed = destroyed.filter((confirmation) => !confirmation.ok);
      /* istanbul ignore if */
      if (notDestroyed.length) {
        throw new Error('unable to destroy all dbs');
      }
      return destroyed;
    });
  }
}

/**
 * @property {array} names returns an array of db names from the registry
 * @example `dbs.names;` ==> ['db1', 'db2']
 */
Object.defineProperty(DBRegistry.prototype, 'names', {
  get() {
    /* istanbul ignore next */
    return this.all.map((db) => db.name);
  },
  enumerable: true,
});

DBRegistry.Pouchy = Pouchy;

module.exports = DBRegistry;
<commit_msg>Store all client external databases in memory.<commit_after>'use strict';

const path = require('path');
const Pouchy = require('pouchy');
// Pouchy.PouchDB.debug.enable('*');
const url = require('url');
const assign = require('lodash/assign');
const cloneDeep = require('lodash/cloneDeep');
const defaultsDeep = require('lodash/defaultsDeep');
const get = require('lodash/get');
const set = require('lodash/set');
const without = require('lodash/without');
const result = require('lodash/result');

/**
 * @private
 * @function arrHasEmbeddedMatch
 * @description tests if an array has a match embedded in its items
 * @param {array} arr
 * @param {string} str
 * @return {boolean}
 */
const arrHasEmbeddedMatch = (arr, str) => arr.some(toMatch => str.match(toMatch));

/**
 * @class DBRegistry
 * @private
 * @note use the db-registry module/service to obtain an env specific
 * db-registry instance.  this class is for _internal_ consumption only
 *
 * @example
 * const registry = DBRegistry({
 *    isRemote: true, // configure for compute/server instance
 *    dir: appDirectory,
 *    remoteStoresSyncIn: ['local-results'],
 *    remoteStoresSyncBoth: ['remote-results']
 * });
 *
 * @param {object} opts
 * @param {string} opts.path path to fs dir where dbs will be stored
 * @param {object} opts.remote Pouchy defaults applied to remote db instantiations
 * @param {object=} opts.local Pouchy defaults applied to local db instantiations
 * @param {array=} opts.localStores strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncBoth strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncIn strings/regexes used to configure Pouch syncronization
 * @param {array=} opts.remoteStoresSyncOut strings/regexes used to configure Pouch syncronization
 * @param {object=} opts.pouchConfig default PouchDB config applied to all pouchy instances
 * @param {boolean} opts.noURLPrefix option to disable prefixing remote db urls [default false]
 * @param {Function|object} [opts.ajax] object or function to be applied to each db request
 * @property {object} dbs
 * @property {array} dbs.all all db instances in the registry
 * @property {object} dbs.registry all db instances indexed by name/url
 * @property {array} names convenience prop to access list of current db names
 */
class DBRegistry {

  constructor(opts) {
    assign(this, opts);
    this.all = [];
    this.registry = {};
    if (!this.remote || !this.remote.db) {
      throw new ReferenceError([
        'DBRegistry `remote.db` as',
        '`url.format(opts.remote.db)`\'able configuration',
      ].join(' '));
    }
  }

  /**
   * gets an existing or new instance of a db
   * @param  {string} nameOrUrl name of db or url to remote db
   * @param {object=} opts passed directly to `.register`.  see `.register` for more
   * @return {Pouchy}
   */
  get(nameOrUrl, _opts) {
    const opts = Object.assign({}, _opts);
    /* istanbul ignore if */
    if (nameOrUrl.match(/^http/) || nameOrUrl.match(/^\//)) {
      opts.url = nameOrUrl;
    } else {
      opts.name = nameOrUrl;
    }

    if (this.registry[nameOrUrl]) {
      return this.registry[nameOrUrl];
    }

    return this.register(opts);
  }

  /**
   * @private
   */
  isBidirectionalSyncStore(connStr) {
    return (
      this.remoteStoresSyncBoth &&
      arrHasEmbeddedMatch(this.remoteStoresSyncBoth, connStr)
    );
  }

  /**
   * @private
   */
  isLocalStore(connStr) {
    return this.localStores && arrHasEmbeddedMatch(this.localStores, connStr);
  }

  /**
   * @private
   */
  isSyncInStore(connStr) {
    return (
      this.remoteStoresSyncIn &&
      arrHasEmbeddedMatch(this.remoteStoresSyncIn, connStr)
    );
  }

  /**
   * @private
   */
  isSyncOutStore(connStr) {
    return (
      this.remoteStoresSyncOut &&
      arrHasEmbeddedMatch(this.remoteStoresSyncOut, connStr)
    );
  }

  /**
   * Register an app-level datastore.
   * @note remote url configurations stored on `this.remote` are not purely used
   * to generate Couch/Pouch request endpoints with the db name.  The pathname is
   * prefixed with an `'up/'` or `'down/' prior to `url.format(...)` such that
   * the coinstac-storage-proxy may intercept requests and perform auth/validation.
   * this prefixing occurs _only_ in `isLocal` mode.  `isRemote` mode implies
   * that the registry is working securely behind COINS infrastructure, and can
   * hit the Couch API directly, bypassing the proxy
   * @param  {object} opts      Required options for a Pouchy instance
   * @param  {string=} opts.name Store's registered name.
   *                             Should match a store array passed to constructor)
   * @param  {string=} opts.url  Store's URL
   * @returns {Pouchy}           Database instance
   */
  register(opts) {
    const conf = cloneDeep(opts);
    const rootRemoteDBPathname = get(this, 'remote.db.pathname') || '';
    const connStr = conf.name || conf.url;
    const syncDefaults = { live: true, retry: true, heartbeat: 5000 };
    conf.path = conf.path || this.path;
    conf.pouchConfig = conf.pouchConfig || {};
    // assert that db can register, and configure its domain
    if (this.isLocalStore(connStr)) {
      defaultsDeep(conf, this.local);
    } else if (this.isBidirectionalSyncStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { sync: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign(
          {},
          this.remote.db,
          { pathname: path.join(rootRemoteDBPathname, conf.name) }
        ) // never up/down prefix dual-dir stores
      );
    } else if (this.isSyncInStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { in: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign({}, this.remote.db, {
          pathname: `${rootRemoteDBPathname}/${conf.name}`,
        })
      );
    } else if (this.isSyncOutStore(connStr)) {
      defaultsDeep(conf, this.remote);
      conf.replicate = { out: defaultsDeep({}, this.replicationOpts, syncDefaults) };
      conf.url = conf.url || url.format(
        assign({}, this.remote.db, {
          pathname: `${rootRemoteDBPathname}/${conf.name}`,
        })
      );
    } else {
      /* istanbul ignore next */
      throw new ReferenceError(
        `database ${connStr} does not fit local or remote database variants`
      );
    }
    if (this.pouchConfig) {
      conf.pouchConfig = defaultsDeep(this.pouchConfig, conf.pouchConfig);
    }

    conf.pouchConfig.ajax = result(this, 'ajax');

    /**
     * Ensure local (client) instances' non-projects databases are stored in
     * memory.
     *
     * {@link https://github.com/MRN-Code/coinstac/issues/155}
     */
    if (this.localStores && this.localStores.indexOf('projects') > -1) {
      conf.pouchConfig.adapter = connStr.includes('projects') ?
        'leveldb' :
        'memory';
    } else {
      // convert string declared PouchDB adapter to adapter instance
      const requestedAdapter = get(conf, 'pouchConfig.db');
      /* istanbul ignore if */
      if (typeof requestedAdapter === 'string') {
        set(conf, 'pouchConfig.db', require(requestedAdapter)); // eslint-disable-line
      }
    }

    // build db and cache it
    const db = new Pouchy(conf);
    this.registry[conf.name || conf.url] = db;
    this.all.push(db);
    return db;
  }

  /**
   * Removes database from registry
   * @param  {Pouchy|string} db database or database name
   * @param  {function=} cb
   * @return {Promise}
   */
  unregister(db) {
    /* istanbul ignore if */
    if (typeof db === 'string') {
      db = this.all.find(pouchy => pouchy.name === db);
    }
    /* istanbul ignore if */
    if (!db) {
      throw new ReferenceError(`db "${name}" not found. unable to remove`);
    }
    /* istanbul ignore else */
    if (db.syncEmitter) { db.syncEmitter.cancel(); }
    return db.destroy()
    .then((rslt) => {
      this.all = without(this.all, db);
      delete this.registry[db.name];
      return rslt;
    });
  }

  destroy() {
    return Promise.all(this.all.map(this.unregister.bind(this)))
    .then((destroyed) => {
      const notDestroyed = destroyed.filter((confirmation) => !confirmation.ok);
      /* istanbul ignore if */
      if (notDestroyed.length) {
        throw new Error('unable to destroy all dbs');
      }
      return destroyed;
    });
  }
}

/**
 * @property {array} names returns an array of db names from the registry
 * @example `dbs.names;` ==> ['db1', 'db2']
 */
Object.defineProperty(DBRegistry.prototype, 'names', {
  get() {
    /* istanbul ignore next */
    return this.all.map((db) => db.name);
  },
  enumerable: true,
});

DBRegistry.Pouchy = Pouchy;

module.exports = DBRegistry;
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import filesize from "filesize";
import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker';

export default class MessageComposer extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.onCallClick = this.onCallClick.bind(this);
        this.onHangupClick = this.onHangupClick.bind(this);
        this.onUploadClick = this.onUploadClick.bind(this);
        this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
        this.uploadFiles = this.uploadFiles.bind(this);
        this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
        this.onInputContentChanged = this.onInputContentChanged.bind(this);
        this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
        this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
        this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
        this.onInputStateChanged = this.onInputStateChanged.bind(this);
        this.onEvent = this.onEvent.bind(this);
        this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);

        this.state = {
            autocompleteQuery: '',
            selection: null,
            inputState: {
                style: [],
                blockType: null,
                isRichtextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
                wordCount: 0,
            },
            showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
            isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
        };
    }

    componentDidMount() {
        // N.B. using 'event' rather than 'RoomEvents' otherwise the crypto handler
        // for 'event' fires *after* 'RoomEvent', and our room won't have yet been
        // marked as encrypted.
        // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
        MatrixClientPeg.get().on("event", this.onEvent);
        this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);

        MatrixClientPeg.get().getMediaLimits().then((limits) => {
            this._uploadLimits = limits;
        }).catch(() => {
            // HS can't or won't provide limits, so don't give any.
            this._uploadLimits = {};
        })
    }

    componentWillUnmount() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("event", this.onEvent);
        }
        if (this._roomStoreToken) {
            this._roomStoreToken.remove();
        }
    }

    onEvent(event) {
        if (event.getType() !== 'm.room.encryption') return;
        if (event.getRoomId() !== this.props.room.roomId) return;
        this.forceUpdate();
    }

    _onRoomViewStoreUpdate() {
        const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
        if (this.state.isQuoting === isQuoting) return;
        this.setState({ isQuoting });
    }

    onUploadClick(ev) {
        if (MatrixClientPeg.get().isGuest()) {
            dis.dispatch({action: 'view_set_mxid'});
            return;
        }

        this.refs.uploadInput.click();
    }

    onUploadFileSelected(files) {
        const tfiles = files.target.files;
        this.uploadFiles(tfiles);
    }

    isFileUploadAllowed(file) {
        if (this._uploadLimits.upload_size != null && file.size > this._uploadLimits.upload_size) {
            return _t("File is too big. Maximum file size is %(fileSize)s", {fileSize: filesize(this._uploadLimits.upload_size)});
        }
        return true;
    }

    uploadFiles(files, limits) {
        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
        const TintableSvg = sdk.getComponent("elements.TintableSvg");

        const fileList = [];
        const acceptedFiles = [];
        const failedFiles = [];

        for (let i=0; i<files.length; i++) {
            const fileAcceptedOrError = this.isFileUploadAllowed(files[i]);
            if (fileAcceptedOrError === true) {
                acceptedFiles.push(

                    <TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }

);
                fileList.push(files[i]);
            } else {
                failedFiles.push(

                    <TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') } { _t('Reason') + ": " + fileAcceptedOrError}

);
            }
        }

        const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
        let replyToWarning = null;
        if (isQuoting) {
            replyToWarning =
{
                _t('At this time it is not possible to reply with a file so this will be sent without being a reply.')
            }
;
        }

        const acceptedFilesPart = acceptedFiles.length === 0 ? null : (

{ _t('Are you sure you want to upload the following files?') }

                    { acceptedFiles }

        );

        const failedFilesPart = failedFiles.length === 0 ? null : (

{ _t('The following files cannot be uploaded:') }

                    { failedFiles }

        );

        Modal.createTrackedDialog('Upload Files confirmation', '', QuestionDialog, {
            title: _t('Upload Files'),
            description: (

                    { acceptedFilesPart }
                    { failedFilesPart }
                    { replyToWarning }

            ),
            onFinished: (shouldUpload) => {
                if (shouldUpload) {
                    // MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
                    if (fileList) {
                        for (let i=0; i<fileList.length; i++) {
                            this.props.uploadFile(fileList[i]);
                        }
                    }
                }

                this.refs.uploadInput.value = null;
            },
        });
    }

    onHangupClick() {
        const call = CallHandler.getCallForRoom(this.props.room.roomId);
        //var call = CallHandler.getAnyActiveCall();
        if (!call) {
            return;
        }
        dis.dispatch({
            action: 'hangup',
            // hangup the call for this room, which may not be the room in props
            // (e.g. conferences which will hangup the 1:1 room instead)
            room_id: call.roomId,
        });
    }

    onCallClick(ev) {
        dis.dispatch({
            action: 'place_call',
            type: ev.shiftKey ? "screensharing" : "video",
            room_id: this.props.room.roomId,
        });
    }

    onVoiceCallClick(ev) {
        dis.dispatch({
            action: 'place_call',
            type: "voice",
            room_id: this.props.room.roomId,
        });
    }

    onInputContentChanged(content: string, selection: {start: number, end: number}) {
        this.setState({
            autocompleteQuery: content,
            selection,
        });
    }

    onInputStateChanged(inputState) {
        this.setState({inputState});
    }

    _onAutocompleteConfirm(range, completion) {
        if (this.messageComposerInput) {
            this.messageComposerInput.setDisplayedCompletion(range, completion);
        }
    }

    onFormatButtonClicked(name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", event) {
        event.preventDefault();
        this.messageComposerInput.onFormatButtonClicked(name, event);
    }

    onToggleFormattingClicked() {
        SettingsStore.setValue("MessageComposer.showFormatting", null, SettingLevel.DEVICE, !this.state.showFormatting);
        this.setState({showFormatting: !this.state.showFormatting});
    }

    onToggleMarkdownClicked(e) {
        e.preventDefault(); // don't steal focus from the editor!
        this.messageComposerInput.enableRichtext(!this.state.inputState.isRichtextEnabled);
    }

    render() {
        const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
        const uploadInputStyle = {display: 'none'};
        const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
        const TintableSvg = sdk.getComponent("elements.TintableSvg");
        const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");

        const controls = [];

        controls.push(

                <MemberAvatar member={me} width={24} height={24} />

,
        );

        let e2eImg, e2eTitle, e2eClass;
        const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
        if (roomIsEncrypted) {
            // FIXME: show a /!\ if there are untrusted devices in the room...
            e2eImg = 'img/e2e-verified.svg';
            e2eTitle = _t('Encrypted room');
            e2eClass = 'mx_MessageComposer_e2eIcon';
        } else {
            e2eImg = 'img/e2e-unencrypted.svg';
            e2eTitle = _t('Unencrypted room');
            e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
        }

        controls.push(
            ,
        );

        let callButton;
        let videoCallButton;
        let hangupButton;

        // Call buttons
        if (this.props.callState && this.props.callState !== 'ended') {
            hangupButton =

;
        } else {
            callButton =

                    <TintableSvg src="img/icon-call.svg" width="35" height="35" />

;
            videoCallButton =

                    <TintableSvg src="img/icons-video.svg" width="35" height="35" />

;
        }

        const canSendMessages = this.props.room.currentState.maySendMessage(
            MatrixClientPeg.get().credentials.userId);

        if (canSendMessages) {
            // This also currently includes the call buttons. Really we should
            // check separately for whether we can call, but this is slightly
            // complex because of conference calls.
            const uploadButton = (

                    <TintableSvg src="img/icons-upload.svg" width="35" height="35" />
                    <input ref="uploadInput" type="file"
                        style={uploadInputStyle}
                        multiple
                        onChange={this.onUploadFileSelected} />

            );

            const formattingButton = (

            );

            let placeholderText;
            if (this.state.isQuoting) {
                if (roomIsEncrypted) {
                    placeholderText = _t('Send an encrypted reply…');
                } else {
                    placeholderText = _t('Send a reply (unencrypted)…');
                }
            } else {
                if (roomIsEncrypted) {
                    placeholderText = _t('Send an encrypted message…');
                } else {
                    placeholderText = _t('Send a message (unencrypted)…');
                }
            }

            const stickerpickerButton = <Stickerpicker key='stickerpicker_controls_button' room={this.props.room} />;

            controls.push(
                <MessageComposerInput
                    ref={(c) => this.messageComposerInput = c}
                    key="controls_input"
                    onResize={this.props.onResize}
                    room={this.props.room}
                    placeholder={placeholderText}
                    onFilesPasted={this.uploadFiles}
                    onContentChanged={this.onInputContentChanged}
                    onInputStateChanged={this.onInputStateChanged} />,
                formattingButton,
                stickerpickerButton,
                uploadButton,
                hangupButton,
                callButton,
                videoCallButton,
            );
        } else {
            controls.push(

                    { _t('You do not have permission to post to this room') }

,
            );
        }

        const {style, blockType} = this.state.inputState;
        const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
            (name) => {
                const active = style.includes(name) || blockType === name;
                const suffix = active ? '-o-n' : '';
                const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
                const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
                return <img className={className}
                            title={_t(name)}
                            onMouseDown={onFormatButtonClicked}
                            key={name}
                            src={`img/button-text-${name}${suffix}.svg`}
                            height="17" />;
            },
        );

        return (

                        { controls }

                        { formatButtons }

                        <img title={this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off")}
                             onMouseDown={this.onToggleMarkdownClicked}
                            className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
                            src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />

        );
    }
}

MessageComposer.propTypes = {
    // a callback which is called when the height of the composer is
    // changed due to a change in content.
    onResize: PropTypes.func,

    // js-sdk Room object
    room: PropTypes.object.isRequired,

    // string representing the current voip call state
    callState: PropTypes.string,

    // callback when a file to upload is chosen
    uploadFile: PropTypes.func.isRequired,

    // string representing the current room app drawer state
    showApps: PropTypes.bool,
};
<commit_msg>Fix nitpicks<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import filesize from 'filesize';
import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker';

export default class MessageComposer extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.onCallClick = this.onCallClick.bind(this);
        this.onHangupClick = this.onHangupClick.bind(this);
        this.onUploadClick = this.onUploadClick.bind(this);
        this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
        this.uploadFiles = this.uploadFiles.bind(this);
        this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
        this.onInputContentChanged = this.onInputContentChanged.bind(this);
        this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
        this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
        this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
        this.onInputStateChanged = this.onInputStateChanged.bind(this);
        this.onEvent = this.onEvent.bind(this);
        this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);

        this.state = {
            autocompleteQuery: '',
            selection: null,
            inputState: {
                style: [],
                blockType: null,
                isRichtextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
                wordCount: 0,
            },
            showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
            isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
        };
    }

    componentDidMount() {
        // N.B. using 'event' rather than 'RoomEvents' otherwise the crypto handler
        // for 'event' fires *after* 'RoomEvent', and our room won't have yet been
        // marked as encrypted.
        // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
        MatrixClientPeg.get().on("event", this.onEvent);
        this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);

        MatrixClientPeg.get().getMediaLimits().then((limits) => {
            this._uploadLimits = limits;
        }).catch(() => {
            // HS can't or won't provide limits, so don't give any.
            this._uploadLimits = {};
        })
    }

    componentWillUnmount() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("event", this.onEvent);
        }
        if (this._roomStoreToken) {
            this._roomStoreToken.remove();
        }
    }

    onEvent(event) {
        if (event.getType() !== 'm.room.encryption') return;
        if (event.getRoomId() !== this.props.room.roomId) return;
        this.forceUpdate();
    }

    _onRoomViewStoreUpdate() {
        const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
        if (this.state.isQuoting === isQuoting) return;
        this.setState({ isQuoting });
    }

    onUploadClick(ev) {
        if (MatrixClientPeg.get().isGuest()) {
            dis.dispatch({action: 'view_set_mxid'});
            return;
        }

        this.refs.uploadInput.click();
    }

    onUploadFileSelected(files) {
        const tfiles = files.target.files;
        this.uploadFiles(tfiles);
    }

    isFileUploadAllowed(file) {
        if (this._uploadLimits.upload_size !== undefined && file.size > this._uploadLimits.upload_size) {
            return _t("File is too big. Maximum file size is %(fileSize)s", {fileSize: filesize(this._uploadLimits.upload_size)});
        }
        return true;
    }

    uploadFiles(files) {
        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
        const TintableSvg = sdk.getComponent("elements.TintableSvg");

        const fileList = [];
        const acceptedFiles = [];
        const failedFiles = [];

        for (let i=0; i<files.length; i++) {
            const fileAcceptedOrError = this.isFileUploadAllowed(files[i]);
            if (fileAcceptedOrError === true) {
                acceptedFiles.push(

                    <TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }

);
                fileList.push(files[i]);
            } else {
                failedFiles.push(

                    <TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') } { _t('Reason') + ": " + fileAcceptedOrError}

);
            }
        }

        const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
        let replyToWarning = null;
        if (isQuoting) {
            replyToWarning =
{
                _t('At this time it is not possible to reply with a file so this will be sent without being a reply.')
            }
;
        }

        const acceptedFilesPart = acceptedFiles.length === 0 ? null : (

{ _t('Are you sure you want to upload the following files?') }

                    { acceptedFiles }

        );

        const failedFilesPart = failedFiles.length === 0 ? null : (

{ _t('The following files cannot be uploaded:') }

                    { failedFiles }

        );

        Modal.createTrackedDialog('Upload Files confirmation', '', QuestionDialog, {
            title: _t('Upload Files'),
            description: (

                    { acceptedFilesPart }
                    { failedFilesPart }
                    { replyToWarning }

            ),
            onFinished: (shouldUpload) => {
                if (shouldUpload) {
                    // MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
                    if (fileList) {
                        for (let i=0; i<fileList.length; i++) {
                            this.props.uploadFile(fileList[i]);
                        }
                    }
                }

                this.refs.uploadInput.value = null;
            },
        });
    }

    onHangupClick() {
        const call = CallHandler.getCallForRoom(this.props.room.roomId);
        //var call = CallHandler.getAnyActiveCall();
        if (!call) {
            return;
        }
        dis.dispatch({
            action: 'hangup',
            // hangup the call for this room, which may not be the room in props
            // (e.g. conferences which will hangup the 1:1 room instead)
            room_id: call.roomId,
        });
    }

    onCallClick(ev) {
        dis.dispatch({
            action: 'place_call',
            type: ev.shiftKey ? "screensharing" : "video",
            room_id: this.props.room.roomId,
        });
    }

    onVoiceCallClick(ev) {
        dis.dispatch({
            action: 'place_call',
            type: "voice",
            room_id: this.props.room.roomId,
        });
    }

    onInputContentChanged(content: string, selection: {start: number, end: number}) {
        this.setState({
            autocompleteQuery: content,
            selection,
        });
    }

    onInputStateChanged(inputState) {
        this.setState({inputState});
    }

    _onAutocompleteConfirm(range, completion) {
        if (this.messageComposerInput) {
            this.messageComposerInput.setDisplayedCompletion(range, completion);
        }
    }

    onFormatButtonClicked(name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", event) {
        event.preventDefault();
        this.messageComposerInput.onFormatButtonClicked(name, event);
    }

    onToggleFormattingClicked() {
        SettingsStore.setValue("MessageComposer.showFormatting", null, SettingLevel.DEVICE, !this.state.showFormatting);
        this.setState({showFormatting: !this.state.showFormatting});
    }

    onToggleMarkdownClicked(e) {
        e.preventDefault(); // don't steal focus from the editor!
        this.messageComposerInput.enableRichtext(!this.state.inputState.isRichtextEnabled);
    }

    render() {
        const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
        const uploadInputStyle = {display: 'none'};
        const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
        const TintableSvg = sdk.getComponent("elements.TintableSvg");
        const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");

        const controls = [];

        controls.push(

                <MemberAvatar member={me} width={24} height={24} />

,
        );

        let e2eImg, e2eTitle, e2eClass;
        const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
        if (roomIsEncrypted) {
            // FIXME: show a /!\ if there are untrusted devices in the room...
            e2eImg = 'img/e2e-verified.svg';
            e2eTitle = _t('Encrypted room');
            e2eClass = 'mx_MessageComposer_e2eIcon';
        } else {
            e2eImg = 'img/e2e-unencrypted.svg';
            e2eTitle = _t('Unencrypted room');
            e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
        }

        controls.push(
            ,
        );

        let callButton;
        let videoCallButton;
        let hangupButton;

        // Call buttons
        if (this.props.callState && this.props.callState !== 'ended') {
            hangupButton =

;
        } else {
            callButton =

                    <TintableSvg src="img/icon-call.svg" width="35" height="35" />

;
            videoCallButton =

                    <TintableSvg src="img/icons-video.svg" width="35" height="35" />

;
        }

        const canSendMessages = this.props.room.currentState.maySendMessage(
            MatrixClientPeg.get().credentials.userId);

        if (canSendMessages) {
            // This also currently includes the call buttons. Really we should
            // check separately for whether we can call, but this is slightly
            // complex because of conference calls.
            const uploadButton = (

                    <TintableSvg src="img/icons-upload.svg" width="35" height="35" />
                    <input ref="uploadInput" type="file"
                        style={uploadInputStyle}
                        multiple
                        onChange={this.onUploadFileSelected} />

            );

            const formattingButton = (

            );

            let placeholderText;
            if (this.state.isQuoting) {
                if (roomIsEncrypted) {
                    placeholderText = _t('Send an encrypted reply…');
                } else {
                    placeholderText = _t('Send a reply (unencrypted)…');
                }
            } else {
                if (roomIsEncrypted) {
                    placeholderText = _t('Send an encrypted message…');
                } else {
                    placeholderText = _t('Send a message (unencrypted)…');
                }
            }

            const stickerpickerButton = <Stickerpicker key='stickerpicker_controls_button' room={this.props.room} />;

            controls.push(
                <MessageComposerInput
                    ref={(c) => this.messageComposerInput = c}
                    key="controls_input"
                    onResize={this.props.onResize}
                    room={this.props.room}
                    placeholder={placeholderText}
                    onFilesPasted={this.uploadFiles}
                    onContentChanged={this.onInputContentChanged}
                    onInputStateChanged={this.onInputStateChanged} />,
                formattingButton,
                stickerpickerButton,
                uploadButton,
                hangupButton,
                callButton,
                videoCallButton,
            );
        } else {
            controls.push(

                    { _t('You do not have permission to post to this room') }

,
            );
        }

        const {style, blockType} = this.state.inputState;
        const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
            (name) => {
                const active = style.includes(name) || blockType === name;
                const suffix = active ? '-o-n' : '';
                const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
                const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
                return <img className={className}
                            title={_t(name)}
                            onMouseDown={onFormatButtonClicked}
                            key={name}
                            src={`img/button-text-${name}${suffix}.svg`}
                            height="17" />;
            },
        );

        return (

                        { controls }

                        { formatButtons }

                        <img title={this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off")}
                             onMouseDown={this.onToggleMarkdownClicked}
                            className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
                            src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />

        );
    }
}

MessageComposer.propTypes = {
    // a callback which is called when the height of the composer is
    // changed due to a change in content.
    onResize: PropTypes.func,

    // js-sdk Room object
    room: PropTypes.object.isRequired,

    // string representing the current voip call state
    callState: PropTypes.string,

    // callback when a file to upload is chosen
    uploadFile: PropTypes.func.isRequired,

    // string representing the current room app drawer state
    showApps: PropTypes.bool,
};
<|endoftext|>


<commit_before>var play = require('../engine/play');
var Plate = require('../model/Plate');

var Player = function(game, name, id, sprite) {
  this.sprite = sprite;
  this.game = game;
  this.name = name;
  this.id = id;
  this.inventory = null;
  this.plate = null;
  this.completedPlates = [];
  this.score = 0;
  this.chosen = false;
}

Player.prototype.addPlate = function () {
  var sprite = this.game.add.sprite(this.game.width / 2, 0, 'plate');

  // position the plate
  sprite.y = this.game.height - sprite.height;
  if (this.id === 1) {
    sprite.x = this.game.width / 4;
  } else {
    sprite.x = (this.game.width / 4) * 3;
  }
  sprite.anchor.setTo(0.5, 0);

  // Current ingredients on the plate
  var label = this.game.add.text(-sprite.width/2 + 130, 0, '');
  sprite.addChild(label);

  var plate = new Plate(play.chooseFoods(2), sprite);
  this.plate = plate;
}

Player.prototype.populateInventory = function () {
  var self = this;
  this.inventory = _.map(play.chooseFoods(4), function(name) {
    return {
      name : name,
      sprite : function (text) {
        /*var sprite = self.game.add.text(0, 0, text);*/

        var sprite = self.game.add.sprite(0, 0, text);
        sprite.width = 200;
        sprite.height = 50;
        sprite.anchor.setTo(self.id - 1,.5);
        return sprite;
      }(name),
      selected : false
    }
  });
}

Player.prototype.completePlate = function () {
  this.completedPlates.push(this.plate);
  this.score += play.ratePlate(this.plate);
  this.plate.kill();
  this.plate = null;
}

Player.prototype.addIngredientToPlate = function (food) {
  this.removeFromInventory(food);
  this.plate.foodItems.unshift(food.name); // put the food on top
  this.plate.update();
}

Player.prototype.removeFromInventory = function (food) {
  food.sprite.alpha = .1;
  food.sprite.children[0].alpha = .1;
  food.selected = true;
  // var index = _(this.inventory).findIndex({name : food.name});
  // this.inventory.splice(index, 1);
}

Player.prototype.choose = function (index) {
  var food = this.inventory[index];
  if (!food.selected) {
    console.log('%s selected %s', this.name, food.name);
    this.addIngredientToPlate(food);
    this.chosen = true;
  } else {
    console.log('%s already chosen!', food.name);
  }
  /*var tween = this.game.add.tween(newFood, this.game, this.game.tweens);
  //   tween.to({
  //     x: 200,
  //     y: 0
  //   });
  //   tween.start();*/

  //   /*tween.onComplete = function(target, tween) {
  //     target.kill();
  //   }*/
  // }
}

Player.prototype.reset = function () {
  this.populateInventory();
  this.addPlate();
}

module.exports = Player;

<commit_msg>Display score when plates are scored<commit_after>var play = require('../engine/play');
var Plate = require('../model/Plate');

var Player = function(game, name, id, sprite) {
  this.sprite = sprite;
  this.game = game;
  this.name = name;
  this.id = id;
  this.inventory = null;
  this.plate = null;
  this.completedPlates = [];
  this.score = 0;
  this.chosen = false;
  this.color = '#ff00ff';
}

Player.prototype.addPlate = function () {
  var sprite = this.game.add.sprite(this.game.width / 2, 0, 'plate');

  // position the plate
  sprite.y = this.game.height - sprite.height;
  if (this.id === 1) {
    sprite.x = this.game.width / 4;
  } else {
    sprite.x = (this.game.width / 4) * 3;
  }
  sprite.anchor.setTo(0.5, 0);

  // Current ingredients on the plate
  var label = this.game.add.text(-sprite.width/2 + 130, 0, '');
  sprite.addChild(label);

  var plate = new Plate(play.chooseFoods(2), sprite);
  this.plate = plate;
}

Player.prototype.populateInventory = function () {
  var self = this;
  this.inventory = _.map(play.chooseFoods(4), function(name) {
    return {
      name : name,
      sprite : function (text) {
        /*var sprite = self.game.add.text(0, 0, text);*/

        var sprite = self.game.add.sprite(0, 0, text);
        sprite.width = 200;
        sprite.height = 50;
        sprite.anchor.setTo(self.id - 1,.5);
        return sprite;
      }(name),
      selected : false
    }
  });
}

Player.prototype.completePlate = function () {
  var game = this.game;

  this.completedPlates.push(this.plate);
  var plateScore = play.ratePlate(this.plate);
  this.score += plateScore;

  //TODO make this actually align with the player/plate
  var scoreText = game.add.text( game.width/4 * this.id, 500, plateScore + 'pts!', {fill : this.color, font: "65px Arial"});
  game.add.tween(scoreText).to({y: 0}, 3000, Phaser.Easing.Linear.None, true);
  game.add.tween(scoreText).to({alpha: 0}, 3000, Phaser.Easing.Linear.None, true);
  //TODO actually delete the score text. right now this just sets alpha to 0 effectively making it invisible.

  this.plate.kill();
  this.plate = null;
}

Player.prototype.addIngredientToPlate = function (food) {
  this.removeFromInventory(food);
  this.plate.foodItems.unshift(food.name); // put the food on top
  this.plate.update();
}

Player.prototype.removeFromInventory = function (food) {
  food.sprite.alpha = .1;
  food.sprite.children[0].alpha = .1;
  food.selected = true;
  // var index = _(this.inventory).findIndex({name : food.name});
  // this.inventory.splice(index, 1);
}

Player.prototype.choose = function (index) {
  var food = this.inventory[index];
  if (!food.selected) {
    console.log('%s selected %s', this.name, food.name);
    this.addIngredientToPlate(food);
    this.chosen = true;
  } else {
    console.log('%s already chosen!', food.name);
  }
  /*var tween = this.game.add.tween(newFood, this.game, this.game.tweens);
  //   tween.to({
  //     x: 200,
  //     y: 0
  //   });
  //   tween.start();*/

  //   /*tween.onComplete = function(target, tween) {
  //     target.kill();
  //   }*/
  // }
}

Player.prototype.reset = function () {
  this.populateInventory();
  this.addPlate();
}

module.exports = Player;

<|endoftext|>


<commit_before>function mipsInstructionExecutor(ME) {
    var BITS_PER_SHAMT = 5;
    var BITS_PER_IMMEDIATE = 16;
	var instructions = {
        /////////////////////////////////////////////
        // Mips Arithmetic Instructions
        /////////////////////////////////////////////
        'ADD': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rs) + ME.getRegisterVal(namedArgs.$rt));
            	ME.incerementPC();
            }
        },
        'ADDI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm);
            	ME.incerementPC();
            }
        },
        'ADDU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterUnsignedVal(namedArgs.$rs) + ME.getRegisterUnsignedVal(namedArgs.$rt));
                ME.incerementPC();
            }
        },
        'ADDIU': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        },
        'SUB': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SUBU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterUnsignedVal(namedArgs.$rs) - ME.getRegisterUnsignedVal(namedArgs.$rt));
                ME.incerementPC();
            }
        },
        'LUI': {
            parseMethod: parse_$RT_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt, (namedArgs.imm << 16));
                ME.incerementPC();
            } // TODO: make some tests
        },
        /////////////////////////////////////////////
        // Mips Logical Instructions
        /////////////////////////////////////////////
        'AND': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    ME.getRegisterVal(namedArgs.rs) & ME.getRegisterVal(namedArgs.rt)
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'ANDI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt,
                    ME.getRegisterVal(namedArgs.rs) & namedArgs.imm
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'NOR': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    ~(ME.getRegisterVal(namedArgs.rs) | ME.getRegisterVal(namedArgs.rt))
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'OR': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    (ME.getRegisterVal(namedArgs.rs) | ME.getRegisterVal(namedArgs.rt))
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'ORI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt,
                    (ME.getRegisterVal(namedArgs.rs) | namedArgs.imm)
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'SLL': {
            parseMethod: parse_$RD_$rt_shamt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rt) << namedArgs.shamt);
                ME.incerementPC();
            }
        },
        'SRL': {
            parseMethod: parse_$RD_$rt_shamt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rt) >> namedArgs.shamt);
                ME.incerementPC();
            } // TODO: make some tests
        },
        /////////////////////////////////////////////
        // Mips Branch and Jump Instructions
        /////////////////////////////////////////////
        'BEQ': {
            parseMethod: parse_$rs_$rt_label,
            runMethod: function(namedArgs) {
                if (ME.getRegisterVal(namedArgs.$rs) === ME.getRegisterVal(namedArgs.$rt))
                    ME.goToLabel(namedArgs.label);
                else
                    ME.incerementPC();
            }
        },
        'BNE': {
            parseMethod: parse_$rs_$rt_label,
            runMethod: function(namedArgs) {
                if (ME.getRegisterVal(namedArgs.$rs) !== ME.getRegisterVal(namedArgs.$rt))
                    ME.goToLabel(namedArgs.label);
                else
                    ME.incerementPC();
            }
        },
        'J': {
            parseMethod: parse_label,
            runMethod: function(namedArgs) {
                ME.goToLabel(namedArgs.label);
            }
        },
        'JAL': {
            parseMethod: parse_label,
            runMethod: function(namedArgs) {
                ME.goToLabel(namedArgs.label);
                ME.setRegisterVal('$ra', ME.getLineNumber() + 1);

            } // TODO: make some tests
        },
        'JR': {
            parseMethod: parse_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        /////////////////////////////////////////////
        // Mips Memory Access Instructions
        /////////////////////////////////////////////
        'LW': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'SW': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LH': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LHU': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'SH': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LB': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.stack.getByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm));
            	ME.incerementPC();
            }
        },
        'LBU': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.stack.getUnsignedByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm));
            	ME.incerementPC();
            }
        },
        'SB': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.stack.setByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm, ME.getRegisterVal(namedArgs.$rt));
            	ME.incerementPC();
            }
        },
        /////////////////////////////////////////////
        // Mips Comparison Instructions
        /////////////////////////////////////////////
        'SLT': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTIU': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        }
    };

    ////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////
    // these parse methods simply convert the 1-3 arguments of the instruction into an object that makes them more meaningful (i.e. $rd, $rs, $rt)
    // these methods are snake_case on purpose, for clarity
    // uppercase means the register is writable
    function parse_$RD_$rs_$rt(args) {
    	return {
    		'expectedArgCount': 3,
    		'$rd': parseWritableRegister(args[0]),
    		'$rs': parseRegister(args[1]),
    		'$rt': parseRegister(args[2])
    	};
    }
    function parse_$RT_$rs_imm(args) {
    	return {
    		'expectedArgCount': 3,
    		'$rt': parseWritableRegister(args[0]),
    		'$rs': parseRegister(args[1]),
    		'imm': parseImmediate(args[2])
    	};
    }
    function parse_$RT_imm(args){
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseWritableRegister(args[0]),
    		'imm': parseImmediate(args[1])
    	};
    };
    function parse_$RD_$rt_shamt(args){
    	return {
    		'expectedArgCount': 3,
    		'$rd': parseWritableRegister(args[0]),
    		'$rt': parseRegister(args[1]),
    		'shamt': parseImmediate(args[2], BITS_PER_SHAMT)
    	};
    };
    function parse_$rs_$rt_label(args){
    	return {
    		'expectedArgCount': 3,
    		'$rs': parseRegister(args[0]),
    		'$rt': parseRegister(args[1]),
    		'label': parseLabel(args[2])
    	};
    };
    function parse_label(args){
    	return {
    		'expectedArgCount': 1,
    		'label': parseLabel(args[0])
    	};
    };
    function parse_$rs(args){
    	return {
    		'expectedArgCount': 1,
    		'$rs': parseRegister(args[0])
    	};
    };
    function parse_$RT_imm_$rs(args){
    	var immediateAndRegister = parseImmAnd$rs(args[1]);
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseWritableRegister(args[0]),
    		'imm': parseImmediate(immediateAndRegister.imm),
    		'$rs': parseRegister(immediateAndRegister.$rs)
    	};
    };
    function parse_$rt_imm_$rs(args){
    	var immediateAndRegister = parseImmAnd$rs(args[1]);
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseRegister(args[0]),
    		'imm': parseImmediate(immediateAndRegister.imm),
    		'$rs': parseRegister(immediateAndRegister.$rs)
    	};
    };

    function parseImmAnd$rs(string) {
    	var match = /^((?:[-+]\s*)?\d+)\(\s*(\$\w+)\s*\)$/.exec(string);
    	return {
    		'imm': match[1],
    		'$rs': match[2]
    	};
    }
    function parseRegister(reg) {
    	if (ME.isValidRegister(reg))
    		return reg;
    	else
    		return null;
    }
    function parseLabel(label) {
    	if (/^[a-zA-Z]\w*$/.test(label))
    		return label;
    	else
    		return null;
    }
    function parseWritableRegister(reg) {
    	if (ME.isValidWritableRegister(reg))
    		return reg;
    	else
    		return null;
    }
    /**
     * check if argument is an immediate, parse, and return the results.
     * @member mips_emulator
     * @private
     * @param  {String} arg
     * @return {Number}
     */
    function parseImmediate(arg, bits) {
        bits = bits || BITS_PER_IMMEDIATE;
        var isNumber = /^([-+]\s*)?\d+$/.test(arg);

        if (!isNumber) {
            return null;
        }

        var number = parseInt(arg, 10);

        var minValue = -Math.pow(2, bits-1);
        var maxValue = Math.pow(2, bits) - 1;
        if (number < minValue || maxValue < number) {
            return null; // TODO: return that it was out of range?
        }

        return number;
    };

    var result = {
    	parseInstruction: function(instructionName, args, outArgs, outError) {
    		var instruction = instructions[instructionName];
	        if (!instruction) {
	            if (outError) outError.message = "Unknown instruction: " + instructionName;
	            return false;
	        }

	        var parseMethod = instruction.parseMethod;
	        assert(parseMethod, "If this is a valid instruction, then it must have a parseMethod.");

	        // ensure that each of the keys (e.g. $rd, $rs, $rt) contain a non-null value in order to determine if we were successful or not
        	var validArgs = true;
        	var namedArgs = parseMethod(args); // e.g. namedArgs has $rd, $rs, imm, etc.
        	if (namedArgs['expectedArgCount'] !== args.length) {
	            if (outError) outError.message = "Incorrect number of arguments {0}, should be {1}.".format(args.length, namedArgs['expectedArgCount']);
	            return false;
        	}
			for (var key in namedArgs) {
				// important check that this is objects own property not from prototype prop inherited
				if (!namedArgs.hasOwnProperty(key)) continue;

				if (namedArgs[key] === null) {
					validArgs = false;
					break;
				}

				if (outArgs) outArgs[key] = namedArgs[key];
			}

			if (!validArgs) {
				if (outError) outError.message = "Invalid arguments: " + args;
				return false;
			}

	        return true;
	    },
	    runInstruction: function(instructionName, args) {
	    	var namedArgs = {};
	    	var parsed = this.parseInstruction(instructionName, args, namedArgs, null);
	    	assert(parsed, "Instruction did not parse correctly");

	    	var runMethod = instructions[instructionName].runMethod;
	    	assert(runMethod);
	    	runMethod(namedArgs);
    	}
    };

    return result;
}<commit_msg>fixed $ referecnes<commit_after>function mipsInstructionExecutor(ME) {
    var BITS_PER_SHAMT = 5;
    var BITS_PER_IMMEDIATE = 16;
	var instructions = {
        /////////////////////////////////////////////
        // Mips Arithmetic Instructions
        /////////////////////////////////////////////
        'ADD': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rs) + ME.getRegisterVal(namedArgs.$rt));
            	ME.incerementPC();
            }
        },
        'ADDI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm);
            	ME.incerementPC();
            }
        },
        'ADDU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterUnsignedVal(namedArgs.$rs) + ME.getRegisterUnsignedVal(namedArgs.$rt));
                ME.incerementPC();
            }
        },
        'ADDIU': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        },
        'SUB': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SUBU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterUnsignedVal(namedArgs.$rs) - ME.getRegisterUnsignedVal(namedArgs.$rt));
                ME.incerementPC();
            }
        },
        'LUI': {
            parseMethod: parse_$RT_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt, (namedArgs.imm << 16));
                ME.incerementPC();
            } // TODO: make some tests
        },
        /////////////////////////////////////////////
        // Mips Logical Instructions
        /////////////////////////////////////////////
        'AND': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    ME.getRegisterVal(namedArgs.$rs) & ME.getRegisterVal(namedArgs.$rt)
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'ANDI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt,
                    ME.getRegisterVal(namedArgs.$rs) & namedArgs.imm
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'NOR': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    ~(ME.getRegisterVal(namedArgs.$rs) | ME.getRegisterVal(namedArgs.$rt))
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'OR': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rd,
                    (ME.getRegisterVal(namedArgs.$rs) | ME.getRegisterVal(namedArgs.$rt))
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'ORI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: function(namedArgs){
                ME.setRegisterVal(namedArgs.$rt,
                    (ME.getRegisterVal(namedArgs.$rs) | namedArgs.imm)
                );
                ME.incerementPC();
            } // TODO: make some tests
        },
        'SLL': {
            parseMethod: parse_$RD_$rt_shamt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rt) << namedArgs.shamt);
                ME.incerementPC();
            }
        },
        'SRL': {
            parseMethod: parse_$RD_$rt_shamt,
            runMethod: function(namedArgs) {
                ME.setRegisterVal(namedArgs.$rd, ME.getRegisterVal(namedArgs.$rt) >> namedArgs.shamt);
                ME.incerementPC();
            } // TODO: make some tests
        },
        /////////////////////////////////////////////
        // Mips Branch and Jump Instructions
        /////////////////////////////////////////////
        'BEQ': {
            parseMethod: parse_$rs_$rt_label,
            runMethod: function(namedArgs) {
                if (ME.getRegisterVal(namedArgs.$rs) === ME.getRegisterVal(namedArgs.$rt))
                    ME.goToLabel(namedArgs.label);
                else
                    ME.incerementPC();
            }
        },
        'BNE': {
            parseMethod: parse_$rs_$rt_label,
            runMethod: function(namedArgs) {
                if (ME.getRegisterVal(namedArgs.$rs) !== ME.getRegisterVal(namedArgs.$rt))
                    ME.goToLabel(namedArgs.label);
                else
                    ME.incerementPC();
            }
        },
        'J': {
            parseMethod: parse_label,
            runMethod: function(namedArgs) {
                ME.goToLabel(namedArgs.label);
            }
        },
        'JAL': {
            parseMethod: parse_label,
            runMethod: function(namedArgs) {
                ME.goToLabel(namedArgs.label);
                ME.setRegisterVal('$ra', ME.getLineNumber() + 1);

            } // TODO: make some tests
        },
        'JR': {
            parseMethod: parse_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        /////////////////////////////////////////////
        // Mips Memory Access Instructions
        /////////////////////////////////////////////
        'LW': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'SW': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LH': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LHU': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'SH': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: null // TODO: implement this and make some tests
        },
        'LB': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.stack.getByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm));
            	ME.incerementPC();
            }
        },
        'LBU': {
            parseMethod: parse_$RT_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.setRegisterVal(namedArgs.$rt, ME.stack.getUnsignedByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm));
            	ME.incerementPC();
            }
        },
        'SB': {
            parseMethod: parse_$rt_imm_$rs,
            runMethod: function(namedArgs) {
            	ME.stack.setByte(ME.getRegisterVal(namedArgs.$rs) + namedArgs.imm, ME.getRegisterVal(namedArgs.$rt));
            	ME.incerementPC();
            }
        },
        /////////////////////////////////////////////
        // Mips Comparison Instructions
        /////////////////////////////////////////////
        'SLT': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTI': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTU': {
            parseMethod: parse_$RD_$rs_$rt,
            runMethod: null // TODO: implement this and make some tests
        },
        'SLTIU': {
            parseMethod: parse_$RT_$rs_imm,
            runMethod: null // TODO: implement this and make some tests
        }
    };

    ////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////
    // these parse methods simply convert the 1-3 arguments of the instruction into an object that makes them more meaningful (i.e. $rd, $rs, $rt)
    // these methods are snake_case on purpose, for clarity
    // uppercase means the register is writable
    function parse_$RD_$rs_$rt(args) {
    	return {
    		'expectedArgCount': 3,
    		'$rd': parseWritableRegister(args[0]),
    		'$rs': parseRegister(args[1]),
    		'$rt': parseRegister(args[2])
    	};
    }
    function parse_$RT_$rs_imm(args) {
    	return {
    		'expectedArgCount': 3,
    		'$rt': parseWritableRegister(args[0]),
    		'$rs': parseRegister(args[1]),
    		'imm': parseImmediate(args[2])
    	};
    }
    function parse_$RT_imm(args){
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseWritableRegister(args[0]),
    		'imm': parseImmediate(args[1])
    	};
    };
    function parse_$RD_$rt_shamt(args){
    	return {
    		'expectedArgCount': 3,
    		'$rd': parseWritableRegister(args[0]),
    		'$rt': parseRegister(args[1]),
    		'shamt': parseImmediate(args[2], BITS_PER_SHAMT)
    	};
    };
    function parse_$rs_$rt_label(args){
    	return {
    		'expectedArgCount': 3,
    		'$rs': parseRegister(args[0]),
    		'$rt': parseRegister(args[1]),
    		'label': parseLabel(args[2])
    	};
    };
    function parse_label(args){
    	return {
    		'expectedArgCount': 1,
    		'label': parseLabel(args[0])
    	};
    };
    function parse_$rs(args){
    	return {
    		'expectedArgCount': 1,
    		'$rs': parseRegister(args[0])
    	};
    };
    function parse_$RT_imm_$rs(args){
    	var immediateAndRegister = parseImmAnd$rs(args[1]);
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseWritableRegister(args[0]),
    		'imm': parseImmediate(immediateAndRegister.imm),
    		'$rs': parseRegister(immediateAndRegister.$rs)
    	};
    };
    function parse_$rt_imm_$rs(args){
    	var immediateAndRegister = parseImmAnd$rs(args[1]);
    	return {
    		'expectedArgCount': 2,
    		'$rt': parseRegister(args[0]),
    		'imm': parseImmediate(immediateAndRegister.imm),
    		'$rs': parseRegister(immediateAndRegister.$rs)
    	};
    };

    function parseImmAnd$rs(string) {
    	var match = /^((?:[-+]\s*)?\d+)\(\s*(\$\w+)\s*\)$/.exec(string);
    	return {
    		'imm': match[1],
    		'$rs': match[2]
    	};
    }
    function parseRegister(reg) {
    	if (ME.isValidRegister(reg))
    		return reg;
    	else
    		return null;
    }
    function parseLabel(label) {
    	if (/^[a-zA-Z]\w*$/.test(label))
    		return label;
    	else
    		return null;
    }
    function parseWritableRegister(reg) {
    	if (ME.isValidWritableRegister(reg))
    		return reg;
    	else
    		return null;
    }
    /**
     * check if argument is an immediate, parse, and return the results.
     * @member mips_emulator
     * @private
     * @param  {String} arg
     * @return {Number}
     */
    function parseImmediate(arg, bits) {
        bits = bits || BITS_PER_IMMEDIATE;
        var isNumber = /^([-+]\s*)?\d+$/.test(arg);

        if (!isNumber) {
            return null;
        }

        var number = parseInt(arg, 10);

        var minValue = -Math.pow(2, bits-1);
        var maxValue = Math.pow(2, bits) - 1;
        if (number < minValue || maxValue < number) {
            return null; // TODO: return that it was out of range?
        }

        return number;
    };

    var result = {
    	parseInstruction: function(instructionName, args, outArgs, outError) {
    		var instruction = instructions[instructionName];
	        if (!instruction) {
	            if (outError) outError.message = "Unknown instruction: " + instructionName;
	            return false;
	        }

	        var parseMethod = instruction.parseMethod;
	        assert(parseMethod, "If this is a valid instruction, then it must have a parseMethod.");

	        // ensure that each of the keys (e.g. $rd, $rs, $rt) contain a non-null value in order to determine if we were successful or not
        	var validArgs = true;
        	var namedArgs = parseMethod(args); // e.g. namedArgs has $rd, $rs, imm, etc.
        	if (namedArgs['expectedArgCount'] !== args.length) {
	            if (outError) outError.message = "Incorrect number of arguments {0}, should be {1}.".format(args.length, namedArgs['expectedArgCount']);
	            return false;
        	}
			for (var key in namedArgs) {
				// important check that this is objects own property not from prototype prop inherited
				if (!namedArgs.hasOwnProperty(key)) continue;

				if (namedArgs[key] === null) {
					validArgs = false;
					break;
				}

				if (outArgs) outArgs[key] = namedArgs[key];
			}

			if (!validArgs) {
				if (outError) outError.message = "Invalid arguments: " + args;
				return false;
			}

	        return true;
	    },
	    runInstruction: function(instructionName, args) {
	    	var namedArgs = {};
	    	var parsed = this.parseInstruction(instructionName, args, namedArgs, null);
	    	assert(parsed, "Instruction did not parse correctly");

	    	var runMethod = instructions[instructionName].runMethod;
	    	assert(runMethod);
	    	runMethod(namedArgs);
    	}
    };

    return result;
}<|endoftext|>