<commit_before>const { resolve } = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const OfflinePlugin = require('offline-plugin')

module.exports = {
  entry: {
    main: resolve(__dirname, '../src'),
    vendor: [
      'react',
      'react-dom',
      'react-redux',
      'react-router-dom',
      'redux',
      'redux-thunk',
      'styled-components',
      'semantic-ui-react',
    ],
  },
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },
  output: {
    filename: '[name].[chunkhash].js',
    path: resolve(__dirname, '../dist'),
    publicPath: '/dist',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        include: [resolve(__dirname, '../src')],
        loader: 'babel-loader',
        options: {
          presets: ["es2017", "es2015", "react", "stage-0"],
        },
      },
    ],
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      title: 'Sam Affiliates Portal',
      template: 'webpack/template.html',
    }),
    // new OfflinePlugin({
    //   ServiceWorker: {
    //     navigateFallbackURL: '/',
    //   },
    //   AppCache: false,
    // }),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production'),
        API_ROOT: JSON.stringify('http://www.sam-affiliate-portal.herokuapp.com'),
      },
    }),
  ],
}
<commit_msg>webpack configuration.....<commit_after>const { resolve } = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const OfflinePlugin = require('offline-plugin')

module.exports = {
  entry: {
    main: resolve(__dirname, '../src'),
    vendor: [
      'react',
      'react-dom',
      'react-redux',
      'react-router-dom',
      'redux',
      'redux-thunk',
      'styled-components',
    ],
  },
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },
  output: {
    filename: '[name].[chunkhash].js',
    path: resolve(__dirname, '../dist'),
    publicPath: '/dist',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        include: [resolve(__dirname, '../src')],
        loader: 'babel-loader',
        options: {
          presets: ["es2017", "es2015", "react", "stage-0"],
        },
      },
    ],
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      title: 'Sam Affiliates Portal',
      template: 'webpack/template.html',
    }),
    // new OfflinePlugin({
    //   ServiceWorker: {
    //     navigateFallbackURL: '/',
    //   },
    //   AppCache: false,
    // }),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production'),
        API_ROOT: JSON.stringify('http://www.sam-affiliate-portal.herokuapp.com'),
      },
    }),
  ],
}
<|endoftext|>


<commit_before>var path = require('path')
var webpack = require('webpack')

module.exports = {
  devtool: 'source-map',
  entry: './entry/client.js',
  resolve: {
    modules: ['node_modules', 'source'],
    extensions: ['.js', '.jsx']
  },
  output: {
    filename: '[name].js',
    publicPath: '/',
    path: path.join(__dirname, '../public')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
}
<commit_msg>sourcemap disabled from production build webpack<commit_after>var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: './entry/client.js',
  resolve: {
    modules: ['node_modules', 'source'],
    extensions: ['.js', '.jsx']
  },
  output: {
    filename: '[name].js',
    publicPath: '/',
    path: path.join(__dirname, '../public')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
}
<|endoftext|>


<commit_before>const React = require('react');
const classNames = require('classnames');

const Icon = require('./Icon');
const STYLES = require('./STYLES');
const SIZES = require('./SIZES');

const BUTTONS_STYLES = STYLES.concat([
    'link',
    'marketing-primary',
    'secondary',
    'count'
]);

const Button = React.createClass({
    propTypes: {
        className:      React.PropTypes.string,
        children:       React.PropTypes.node,
        type:           React.PropTypes.string,
        size:           React.PropTypes.oneOf(SIZES),
        style:          React.PropTypes.oneOf(BUTTONS_STYLES),
        // Makes a link button
        href:           React.PropTypes.string,
        // Form button
        value:          React.PropTypes.oneOfType([
            React.PropTypes.string,
            React.PropTypes.bool,
            React.PropTypes.number
        ]),
        name:           React.PropTypes.string,
        // For links
        target:         React.PropTypes.string,
        filled:         React.PropTypes.bool,
        noBorder:       React.PropTypes.bool,
        disabled:       React.PropTypes.bool,
        active:         React.PropTypes.bool,
        block:          React.PropTypes.bool,
        dropdownToggle: React.PropTypes.bool,
        onClick:        React.PropTypes.func,
        title:          React.PropTypes.string,
        icon:           React.PropTypes.string
    },

    getInitialState() {
        return {
            clicked: false
        };
    },

    getDefaultProps() {
        return {
            style:    'default',
            size:     'md',
            type:     'button',
            filled:   false,
            disabled: false,
            active:   false
        };
    },

    onClick(e) {
        const { clicked } = this.state;

        if (this.props.onClick && !clicked) {
            this.props.onClick(e);
        }

        if (!this.isFormButton()) {
            return;
        } else if (clicked) {
            return this.setState({
                clicked: false
            });
        }

        // Add this submitting button's value to the form as hidden
        // input. Only after that do the click.
        // This fixes issues with PJAX[1] in Firefox[2]
        // [1]: https://github.com/defunkt/jquery-pjax/pull/295
        // [2]: http://stackoverflow.com/questions/38277900/formdata-object-does-not-add-submit-type-inputs-from-form-while-on-firefox
        e.preventDefault();
        this.setState({
            clicked: true
        }, () => {
            this.refs.button.click();
        });
    },

    isFormButton() {
        const { type, name } = this.props;
        return type === 'submit' && name;
    },

    render() {
        const { clicked } = this.state;
        const {
            title, icon, filled, block, noBorder, active, dropdownToggle,
            style, size, className, children, name, value, disabled,
            ...props
        } = this.props;

        const inner = icon ? <Icon className={icon} /> : '';

        props.className = classNames(
            'btn', 'btn-' + style, 'btn-' + size,
            className, {
                'btn-fill':        filled,
                'btn-block':       block,
                'btn-noborder':    noBorder,
                active, disabled,
                'dropdown-toggle': dropdownToggle,
                'tooltipped':      Boolean(title)
            }
        );
        props['aria-label'] = title;
        props.role          = 'button';
        props.onClick       = this.onClick;

        let input;

        if (clicked && this.isFormButton()) {
            input = <input type="hidden" name={name} value={value} />;
        }

        if (props.href) {
            delete props.type;
            return {inner} {children};
        } else {
            return <button ref="button" {...props}>{inner} {children}{input}</button>;
        }
    }
});

const ButtonGroup = React.createClass({
    propTypes: {
        className: React.PropTypes.string,
        children:  React.PropTypes.node,
        pull:      React.PropTypes.string,
        block:     React.PropTypes.bool
    },

    render() {
        let { className, pull, children, block } = this.props;

        className = classNames(
            'btn-group',
            className,
            pull ? 'pull-' + pull : '',
            {
                'btn-group-block': block
            }
        );

        return
{children}
;
    }
});

const ButtonToolbar = React.createClass({
    propTypes: {
        className:  React.PropTypes.string,
        children: React.PropTypes.node
    },

    render() {
        return
{this.props.children}
;
    }
});

const ButtonCaret = React.createClass({
    render() {
        return ;
    }
});

const ButtonTextExpander = React.createClass({
    propTypes: {
        onClick: React.PropTypes.func
    },

    onClick(e) {
        if (this.props.onClick) {
            this.props.onClick();
        }
    },

    render() {
        return (

                <button type="button" className="ellipsis-expander" onClick={this.onClick}>…</button>

        );
    }
});

module.exports              = Button;
module.exports.Group        = ButtonGroup;
module.exports.Toolbar      = ButtonToolbar;
module.exports.Caret        = ButtonCaret;
module.exports.TextExpander = ButtonTextExpander;
module.exports.STYLES       = BUTTONS_STYLES;
<commit_msg>Prevent onClick action on Button component when disabled<commit_after>const React = require('react');
const classNames = require('classnames');

const Icon = require('./Icon');
const STYLES = require('./STYLES');
const SIZES = require('./SIZES');

const BUTTONS_STYLES = STYLES.concat([
    'link',
    'marketing-primary',
    'secondary',
    'count'
]);

const Button = React.createClass({
    propTypes: {
        className:      React.PropTypes.string,
        children:       React.PropTypes.node,
        type:           React.PropTypes.string,
        size:           React.PropTypes.oneOf(SIZES),
        style:          React.PropTypes.oneOf(BUTTONS_STYLES),
        // Makes a link button
        href:           React.PropTypes.string,
        // Form button
        value:          React.PropTypes.oneOfType([
            React.PropTypes.string,
            React.PropTypes.bool,
            React.PropTypes.number
        ]),
        name:           React.PropTypes.string,
        // For links
        target:         React.PropTypes.string,
        filled:         React.PropTypes.bool,
        noBorder:       React.PropTypes.bool,
        disabled:       React.PropTypes.bool,
        active:         React.PropTypes.bool,
        block:          React.PropTypes.bool,
        dropdownToggle: React.PropTypes.bool,
        onClick:        React.PropTypes.func,
        title:          React.PropTypes.string,
        icon:           React.PropTypes.string
    },

    getInitialState() {
        return {
            clicked: false
        };
    },

    getDefaultProps() {
        return {
            style:    'default',
            size:     'md',
            type:     'button',
            filled:   false,
            disabled: false,
            active:   false
        };
    },

    onClick(e) {
        const { clicked } = this.state;

        if (this.props.onClick && !clicked) {
            this.props.onClick(e);
        }

        if (!this.isFormButton()) {
            return;
        } else if (clicked) {
            return this.setState({
                clicked: false
            });
        }

        // Add this submitting button's value to the form as hidden
        // input. Only after that do the click.
        // This fixes issues with PJAX[1] in Firefox[2]
        // [1]: https://github.com/defunkt/jquery-pjax/pull/295
        // [2]: http://stackoverflow.com/questions/38277900/formdata-object-does-not-add-submit-type-inputs-from-form-while-on-firefox
        e.preventDefault();
        this.setState({
            clicked: true
        }, () => {
            this.refs.button.click();
        });
    },

    isFormButton() {
        const { type, name } = this.props;
        return type === 'submit' && name;
    },

    render() {
        const { clicked } = this.state;
        const {
            title, icon, filled, block, noBorder, active, dropdownToggle,
            style, size, className, children, name, value, disabled,
            ...props
        } = this.props;

        const inner = icon ? <Icon className={icon} /> : '';

        props.className = classNames(
            'btn', 'btn-' + style, 'btn-' + size,
            className, {
                'btn-fill':        filled,
                'btn-block':       block,
                'btn-noborder':    noBorder,
                active, disabled,
                'dropdown-toggle': dropdownToggle,
                'tooltipped':      Boolean(title)
            }
        );
        props['aria-label'] = title;
        props.role          = 'button';
        props.onClick       = disabled ? null : this.onClick;

        let input;

        if (clicked && this.isFormButton()) {
            input = <input type="hidden" name={name} value={value} />;
        }

        if (props.href) {
            delete props.type;
            return {inner} {children};
        } else {
            return <button ref="button" {...props}>{inner} {children}{input}</button>;
        }
    }
});

const ButtonGroup = React.createClass({
    propTypes: {
        className: React.PropTypes.string,
        children:  React.PropTypes.node,
        pull:      React.PropTypes.string,
        block:     React.PropTypes.bool
    },

    render() {
        let { className, pull, children, block } = this.props;

        className = classNames(
            'btn-group',
            className,
            pull ? 'pull-' + pull : '',
            {
                'btn-group-block': block
            }
        );

        return
{children}
;
    }
});

const ButtonToolbar = React.createClass({
    propTypes: {
        className:  React.PropTypes.string,
        children: React.PropTypes.node
    },

    render() {
        return
{this.props.children}
;
    }
});

const ButtonCaret = React.createClass({
    render() {
        return ;
    }
});

const ButtonTextExpander = React.createClass({
    propTypes: {
        onClick: React.PropTypes.func
    },

    onClick(e) {
        if (this.props.onClick) {
            this.props.onClick();
        }
    },

    render() {
        return (

                <button type="button" className="ellipsis-expander" onClick={this.onClick}>…</button>

        );
    }
});

module.exports              = Button;
module.exports.Group        = ButtonGroup;
module.exports.Toolbar      = ButtonToolbar;
module.exports.Caret        = ButtonCaret;
module.exports.TextExpander = ButtonTextExpander;
module.exports.STYLES       = BUTTONS_STYLES;
<|endoftext|>


<commit_before>// import React and Redux dependencies
var React = require('react');
var connect = require('react-redux').connect;
var _ = require('underscore');
var $ = require('jquery');
require('jquery-serializejson');
var History = require('react-router').History;
var bindActionCreators = require('redux').bindActionCreators;
var measureActions = require('../../actions/Measures.js');
var sampleActions = require('../../actions/Samples.js');
var actions = _.extend(measureActions, sampleActions);

var DepVar = require('./DepVar');

function mapStatetoProps (state, ownProps) {
  var depVars = state.Experiments.getIn([ownProps.params.expid, 'depVars']).toJS();
  var depVarId = depVars[0];
  var measureId = state.DepVars.getIn([depVarId, 'measures', 0]);
  return {
    depVars: depVars,
    measureId: measureId,
    indVars: state.Samples.getIn([ownProps.params.sampleid, 'indVarStates']).toJS()
  };
}

function mapDispatchToProps (dispatch) {
  return {
    actions : bindActionCreators(actions, dispatch)
  };
}

var DepVars = React.createClass({
  mixins: [ History ],
  handleSubmit: function (event) {
    event.preventDefault();
    var data = $(event.target).serializeJSON();
    if (data.invalid === 'on') {
      data.valid = false;
      delete data.invalid;
    } else {
      data.valid = true;
      delete data.invalid;
    }
    $.post('/samples', data, function(samples) {
      var sample = samples[0];
      this.props.actions.insertSample(sample);
      this.props.actions.addSample(sample._id, this.props.measureId);
      this.history.pushState(null, '/viewexp/' + this.props.params.expid);
    }.bind(this));
  },

  render: function () {
    var depVars = this.props.depVars.map(function(depVarId) {
      return <DepVar key={depVarId} depVarId={depVarId} />;
    });
    var indVars = _.map(this.props.indVars, function(value, indVarId) {
      return (<input
                name={'indVars[' + indVarId + '][value]'}
                type="hidden"
                value={value} />);
    });
    return (

        <form onSubmit={this.handleSubmit}>
          {indVars}
          {depVars}

<input type="checkbox" name="invalid" />Mark sample as invalid.

          <button type="submit">Submit Sample</button>
        </form>

    );
  }
});

module.exports = connect(mapStatetoProps, mapDispatchToProps)(DepVars);
<commit_msg>Send more info with samples<commit_after>// import React and Redux dependencies
var React = require('react');
var connect = require('react-redux').connect;
var _ = require('underscore');
var $ = require('jquery');
require('jquery-serializejson');
var History = require('react-router').History;
var bindActionCreators = require('redux').bindActionCreators;
var measureActions = require('../../actions/Measures.js');
var sampleActions = require('../../actions/Samples.js');
var actions = _.extend(measureActions, sampleActions);

var DepVar = require('./DepVar');

function mapStatetoProps (state, ownProps) {
  var depVars = state.Experiments.getIn([ownProps.params.expid, 'depVars']).toJS();
  var depVarId = depVars[0];
  var measureId = state.DepVars.getIn([depVarId, 'measures', 0]);
  var indVars = state.Samples.getIn([ownProps.params.sampleid, 'indVarStates']).toJS();
  return {
    depVarId: depVarId,
    indVarId: state.Experiments.getIn([ownProps.params.expid, 'indVars']).toJS()[0],
    depVars: depVars,
    measureId: measureId,
    indVars: indVars
  };
}

function mapDispatchToProps (dispatch) {
  return {
    actions : bindActionCreators(actions, dispatch)
  };
}

var DepVars = React.createClass({
  mixins: [ History ],
  handleSubmit: function (event) {
    event.preventDefault();
    var data = $(event.target).serializeJSON();
    if (data.invalid === 'on') {
      data.valid = false;
      delete data.invalid;
    } else {
      data.valid = true;
      delete data.invalid;
    }
    $.post('/samples', data, function(data) {
      var sample = data.samples[0];
      this.props.actions.insertSample(sample);
      this.props.actions.addSample(sample._id, this.props.measureId);
      this.history.pushState(null, '/viewexp/' + this.props.params.expid);
    }.bind(this));
  },

  render: function () {
    var depVars = this.props.depVars.map(function(depVarId) {
      return <DepVar key={depVarId} depVarId={depVarId} />;
    });
    var indVars = _.map(this.props.indVars, function(value, indVarId) {
      return (<input
                name={'indVars[' + indVarId + '][value]'}
                type="hidden"
                value={value} />);
    });
    return (

        <form onSubmit={this.handleSubmit}>
          <input
            name={'expId'}
            type="hidden"
            value={this.props.params.expid} />
          <input
            name={'indVarId'}
            type="hidden"
            value={this.props.indVarId} />
          <input
            name={'depVarId'}
            type="hidden"
            value={this.props.depVarId} />
          {indVars}
          {depVars}

<input type="checkbox" name="invalid" />Mark sample as invalid.

          <button type="submit">Submit Sample</button>
        </form>

    );
  }
});

module.exports = connect(mapStatetoProps, mapDispatchToProps)(DepVars);
<|endoftext|>


<commit_before>/**
 * @depends MatrixStack.js
 */
var Camera = new Class({
    initialize: function() {
        this.projection = new MatrixStack();
        this.modelview = new MatrixStack();
    },

    perspective: function(fovy, aspect, near, far) {
        mat4.perspective(fovy, aspect, near, far, this.projection);
    },

    ortho: function(left, right, top, bottom, near, far) {
        mat4.ortho(left, right, top, bottom, near, far, this.projection);
    },

    lookAt: function(eye, center, up) {
        mat4.lookAt(eye, center, up, this.modelview);
    }
});
<commit_msg>Make camera function actually affect the matrices<commit_after>/**
 * @depends MatrixStack.js
 */
var Camera = new Class({
    initialize: function() {
        this.projection = new MatrixStack();
        this.modelview = new MatrixStack();
    },

    perspective: function(fovy, aspect, near, far) {
        mat4.perspective(fovy, aspect, near, far, this.projection.matrix);
    },

    ortho: function(left, right, top, bottom, near, far) {
        mat4.ortho(left, right, top, bottom, near, far, this.projection.matrix);
    },

    lookAt: function(eye, center, up) {
        mat4.lookAt(eye, center, up, this.modelview.matrix);
    }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * @class Camera
 * @constructor
 * @component
 * @param {RenderNode} RenderNode to which the instance of Camera will be a component of
 */
function Camera(dispatch) {
    this._dispatch = dispatch;
    this._projectionType = Camera.ORTHOGRAPHIC_PROJECTION;
    this._focalDepth = 0;
    this._near = 0;
    this._far = 0;
    this._id = dispatch.addComponent(this);
    this._viewTransform = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);

    dispatch.onTransformChange(buildViewTransform.bind(this));

    this.setFlat();
}

Camera.FRUSTUM_PROJECTION = 0;
Camera.PINHOLE_PROJECTION = 1;
Camera.ORTHOGRAPHIC_PROJECTION = 2;

// Return the name of the Element Class: 'Camera'
Camera.toString = function toString() {
    return 'Camera';
};

Camera.prototype.getState = function getState() {
    return {
        component: this.constructor.toString(),
        projectionType: this._projectionType,
        focalDepth: this._focalDepth,
        near: this._near,
        far: this._far
    };
};

Camera.prototype.setState = function setState(state) {
    this._dispatch.dirtyComponent(this._id);
    if (state.component === this.constructor.toString()) {
        this.set(state.projectionType, state.focalDepth, state.near, state.far);
        return true;
    }
    return false;
};

Camera.prototype.set = function set(type, depth, near, far) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = type;
    this._focalDepth = depth;
    this._near = near;
    this._far = far;
};

Camera.prototype.setDepth = function setDepth(depth) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.PINHOLE_PROJECTION;
    this._focalDepth = depth;
    this._near = 0;
    this._far = 0;

    return this;
};

Camera.prototype.setFrustum = function setFrustum(near, far) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.FRUSTUM_PROJECTION;
    this._focalDepth = 0;
    this._near = near;
    this._far = far;

    return this;
};

Camera.prototype.setFlat = function setFlat() {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.ORTHOGRAPHIC_PROJECTION;
    this._focalDepth = 0;
    this._near = 0;
    this._far = 0;

    return this;
};

Camera.prototype.clean = function clean() {
    switch (this._projectionType) {
        case Camera.FRUSTUM_PROJECTION:
            this._dispatch.sendDrawCommand('FRUSTUM_PROJECTION');
            this._dispatch.sendDrawCommand(this._near);
            this._dispatch.sendDrawCommand(this._far);
            break;
        case Camera.PINHOLE_PROJECTION:
            this._dispatch.sendDrawCommand('PINHOLE_PROJECTION');
            this._dispatch.sendDrawCommand(this._focalDepth);
            break;
        case Camera.ORTHOGRAPHIC_PROJECTION:
            this._dispatch.sendDrawCommand('ORTHOGRAPHIC_PROJECTION');
            break;
    }

    if (this._viewDirty) {
        this._viewDirty = false;

        this._dispatch.sendDrawCommand('CHANGE_VIEW_TRANSFORM');
        this._dispatch.sendDrawCommand(this._viewTransform[0]);
        this._dispatch.sendDrawCommand(this._viewTransform[1]);
        this._dispatch.sendDrawCommand(this._viewTransform[2]);
        this._dispatch.sendDrawCommand(this._viewTransform[3]);

        this._dispatch.sendDrawCommand(this._viewTransform[4]);
        this._dispatch.sendDrawCommand(this._viewTransform[5]);
        this._dispatch.sendDrawCommand(this._viewTransform[6]);
        this._dispatch.sendDrawCommand(this._viewTransform[7]);

        this._dispatch.sendDrawCommand(this._viewTransform[8]);
        this._dispatch.sendDrawCommand(this._viewTransform[9]);
        this._dispatch.sendDrawCommand(this._viewTransform[10]);
        this._dispatch.sendDrawCommand(this._viewTransform[11]);

        this._dispatch.sendDrawCommand(this._viewTransform[12]);
        this._dispatch.sendDrawCommand(this._viewTransform[13]);
        this._dispatch.sendDrawCommand(this._viewTransform[14]);
        this._dispatch.sendDrawCommand(this._viewTransform[15]);
    }
    return false;
};

function buildViewTransform(transform) {
    var a = transform._matrix;
    this._viewDirty = true;
    this._dispatch.dirtyComponent(this._id);

    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
    a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
    a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
    a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

    b00 = a00 * a11 - a01 * a10,
    b01 = a00 * a12 - a02 * a10,
    b02 = a00 * a13 - a03 * a10,
    b03 = a01 * a12 - a02 * a11,
    b04 = a01 * a13 - a03 * a11,
    b05 = a02 * a13 - a03 * a12,
    b06 = a20 * a31 - a21 * a30,
    b07 = a20 * a32 - a22 * a30,
    b08 = a20 * a33 - a23 * a30,
    b09 = a21 * a32 - a22 * a31,
    b10 = a21 * a33 - a23 * a31,
    b11 = a22 * a33 - a23 * a32,

    det = 1/(b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);

    this._viewTransform[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    this._viewTransform[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    this._viewTransform[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    this._viewTransform[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
    this._viewTransform[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    this._viewTransform[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    this._viewTransform[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    this._viewTransform[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
    this._viewTransform[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    this._viewTransform[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    this._viewTransform[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    this._viewTransform[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
    this._viewTransform[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
    this._viewTransform[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
    this._viewTransform[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
    this._viewTransform[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
}

module.exports = Camera;
<commit_msg>feat: sending renderTag to compositor<commit_after>'use strict';

/**
 * @class Camera
 * @constructor
 * @component
 * @param {RenderNode} RenderNode to which the instance of Camera will be a component of
 */
function Camera(dispatch) {
    this._dispatch = dispatch;
    this._projectionType = Camera.ORTHOGRAPHIC_PROJECTION;
    this._focalDepth = 0;
    this._near = 0;
    this._far = 0;
    this._id = dispatch.addComponent(this);
    this._viewTransform = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);

    dispatch.onTransformChange(buildViewTransform.bind(this));

    this.setFlat();
}

Camera.FRUSTUM_PROJECTION = 0;
Camera.PINHOLE_PROJECTION = 1;
Camera.ORTHOGRAPHIC_PROJECTION = 2;

// Return the name of the Element Class: 'Camera'
Camera.toString = function toString() {
    return 'Camera';
};

Camera.prototype.getState = function getState() {
    return {
        component: this.constructor.toString(),
        projectionType: this._projectionType,
        focalDepth: this._focalDepth,
        near: this._near,
        far: this._far
    };
};

Camera.prototype.setState = function setState(state) {
    this._dispatch.dirtyComponent(this._id);
    if (state.component === this.constructor.toString()) {
        this.set(state.projectionType, state.focalDepth, state.near, state.far);
        return true;
    }
    return false;
};

Camera.prototype.set = function set(type, depth, near, far) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = type;
    this._focalDepth = depth;
    this._near = near;
    this._far = far;
};

Camera.prototype.setDepth = function setDepth(depth) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.PINHOLE_PROJECTION;
    this._focalDepth = depth;
    this._near = 0;
    this._far = 0;

    return this;
};

Camera.prototype.setFrustum = function setFrustum(near, far) {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.FRUSTUM_PROJECTION;
    this._focalDepth = 0;
    this._near = near;
    this._far = far;

    return this;
};

Camera.prototype.setFlat = function setFlat() {
    this._dispatch.dirtyComponent(this._id);
    this._projectionType = Camera.ORTHOGRAPHIC_PROJECTION;
    this._focalDepth = 0;
    this._near = 0;
    this._far = 0;

    return this;
};

Camera.prototype.clean = function clean() {
    var path = this._dispatch.getRenderPath();

    this._dispatch
        .sendDrawCommand('WITH')
        .sendDrawCommand(path)
        .sendDrawCommand('*');

    switch (this._projectionType) {
        case Camera.FRUSTUM_PROJECTION:
            this._dispatch.sendDrawCommand('FRUSTUM_PROJECTION');
            this._dispatch.sendDrawCommand(this._near);
            this._dispatch.sendDrawCommand(this._far);
            break;
        case Camera.PINHOLE_PROJECTION:
            this._dispatch.sendDrawCommand('PINHOLE_PROJECTION');
            this._dispatch.sendDrawCommand(this._focalDepth);
            break;
        case Camera.ORTHOGRAPHIC_PROJECTION:
            this._dispatch.sendDrawCommand('ORTHOGRAPHIC_PROJECTION');
            break;
    }

    if (this._viewDirty) {
        this._viewDirty = false;

        this._dispatch.sendDrawCommand('CHANGE_VIEW_TRANSFORM');
        this._dispatch.sendDrawCommand(this._viewTransform[0]);
        this._dispatch.sendDrawCommand(this._viewTransform[1]);
        this._dispatch.sendDrawCommand(this._viewTransform[2]);
        this._dispatch.sendDrawCommand(this._viewTransform[3]);

        this._dispatch.sendDrawCommand(this._viewTransform[4]);
        this._dispatch.sendDrawCommand(this._viewTransform[5]);
        this._dispatch.sendDrawCommand(this._viewTransform[6]);
        this._dispatch.sendDrawCommand(this._viewTransform[7]);

        this._dispatch.sendDrawCommand(this._viewTransform[8]);
        this._dispatch.sendDrawCommand(this._viewTransform[9]);
        this._dispatch.sendDrawCommand(this._viewTransform[10]);
        this._dispatch.sendDrawCommand(this._viewTransform[11]);

        this._dispatch.sendDrawCommand(this._viewTransform[12]);
        this._dispatch.sendDrawCommand(this._viewTransform[13]);
        this._dispatch.sendDrawCommand(this._viewTransform[14]);
        this._dispatch.sendDrawCommand(this._viewTransform[15]);
    }
    return false;
};

function buildViewTransform(transform) {
    var a = transform._matrix;
    this._viewDirty = true;
    this._dispatch.dirtyComponent(this._id);

    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
    a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
    a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
    a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

    b00 = a00 * a11 - a01 * a10,
    b01 = a00 * a12 - a02 * a10,
    b02 = a00 * a13 - a03 * a10,
    b03 = a01 * a12 - a02 * a11,
    b04 = a01 * a13 - a03 * a11,
    b05 = a02 * a13 - a03 * a12,
    b06 = a20 * a31 - a21 * a30,
    b07 = a20 * a32 - a22 * a30,
    b08 = a20 * a33 - a23 * a30,
    b09 = a21 * a32 - a22 * a31,
    b10 = a21 * a33 - a23 * a31,
    b11 = a22 * a33 - a23 * a32,

    det = 1/(b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);

    this._viewTransform[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    this._viewTransform[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    this._viewTransform[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    this._viewTransform[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
    this._viewTransform[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    this._viewTransform[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    this._viewTransform[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    this._viewTransform[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
    this._viewTransform[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    this._viewTransform[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    this._viewTransform[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    this._viewTransform[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
    this._viewTransform[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
    this._viewTransform[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
    this._viewTransform[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
    this._viewTransform[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
}

module.exports = Camera;
<|endoftext|>


<commit_before>/* Emoticons Plugin
 * This is a chat-plugin for Emoticons
 * You will need a line in parser to actually
 * parse this so that it works. (See command-parser.js)
 * Credits: panpawn, jd
 */

var fs = require('fs');
var path = require('path');
var serialize = require('node-serialize');

if (typeof Gold === 'undefined') global.Gold = {};

Gold.emoticons = {
	chatEmotes: {}, //much object such wow :^)
	processEmoticons: function(text) {
		var patterns = [],
			metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g,
			self = this;
		for (var i in this.chatEmotes) {
			if (this.chatEmotes.hasOwnProperty(i)) {
				patterns.push('(' + i.replace(metachars, "\\$&") + ')');
			}
		}
		return text.replace(new RegExp(patterns.join('|'), 'g'), function(match) {
			return typeof self.chatEmotes[match] != 'undefined' ?
				'' :
				match;
		});
	},
	processChatData: function(user, room, connection, message) {
		var match = false;
		for (var i in this.chatEmotes) {
			if (message.indexOf(i) >= 0) {
				match = true;
			}
		}
		if (!room.emoteStatus) {
			kitty = message = this.processEmoticons(message);
			var message = Tools.escapeHTML(kitty);
			return (message);
			return;
		} else if (room.emoteStatus) {
			if (!match || message.charAt(0) === '!') return true;
			message = Tools.escapeHTML(message);
			message = this.processEmoticons(message);
			if (user.hiding) {
				room.addRaw(' <button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
				room.update();
			}
			room.addRaw(user.getIdentity(room).substr(0,1) + '<button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
			room.update();
			return false;
		}
	}
};

/* EZ-Emote Commands plugin
 * These are commands that make it easier for people to actually
 * add / remove emotes without having to manaully do so every time
 * By: panpawn (inspired by jd's ez-tc plugin)
 */

var emotes = {};

function loadEmotes() {
	try {
		emotes = serialize.unserialize(fs.readFileSync('config/emotes.json', 'utf8'));
		Object.merge(Gold.emoticons.chatEmotes, emotes);
	} catch (e) {}
}
setTimeout(function(){loadEmotes();},1000);

function saveEmotes() {
	try {
		fs.writeFileSync('config/emotes.json',serialize.serialize(emotes));
		Object.merge(Gold.emoticons.chatEmotes, emotes);
	} catch (e) {}
}

exports.commands = {
	emotes: 'ezemote',
	temotes: 'ezemote',
	temote: 'ezemote',
	emote: 'ezemote',
	ec: 'ezemote',
	ezemote: function (target, room, user) {
		if (!target) target = "help";
		var parts = target.split(',');
		for (var u in parts) parts[u] = parts[u].trim();

		try {
			switch (toId(parts[0])) {
				case 'add':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!(parts[2] || parts[3])) return this.sendReply("Usage: /ezemote add, [emote], [link]");
					var emoteName = parts[1];
					if (Gold.emoticons.chatEmotes[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " already exists.");
					var link = parts.splice(2, parts.length).join(',');
					var fileTypes = [".gif",".png",".jpg"];
					if (fileTypes.indexOf(link.substr(-4)) < 0) return this.sendReply("ERROR: the emote you are trying to add must be a gif, png, or jpg.");
					emotes[emoteName] = Gold.emoticons.chatEmotes[emoteName] = link;
					saveEmotes();
					this.sendReply("The emote " + emoteName + " has been added.");
					this.logModCommand(user.name + " added the emote " + emoteName);
					Rooms.rooms.staff.add(Tools.escapeHTML(user.name) + " added the emote " + emoteName);
					room.update();
					break;
				case 'rem':
				case 'remove':
				case 'del':
				case 'delete':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!parts[1]) return this.sendReplyBox("/ezemote remove, [emote]");
					var emoteName = parts[1];
					if (!Gold.emoticons.chatEmotes[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " does not exist.");
					delete Gold.emoticons.chatEmotes[emoteName];
					delete emotes[emoteName];
					saveEmotes();
					this.sendReply("The emote " + emoteName + " was removed.");
					this.logModCommand("The emote " + emoteName + " was removed by " + user.name);
					Rooms.rooms.staff.add("The emote " + emoteName + " was removed by " + Tools.escapeHTML(user.name));
					room.update();
					break;
				case 'list':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var output = "There's a total of " + Object.size(emotes) + " emotes added with this command:
";
					for (var e in emotes) {
						output += e + "
";
					}
					this.sendReplyBox(output);
					break;
				case 'view':
					if (!this.canBroadcast()) return;
					//if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var name = Object.keys(Gold.emoticons.emoticons);
					emoticons = [];
					var len = name.length;
					while (len--) {
						emoticons.push((Gold.emoticons.processEmoticons(name[(name.length - 1) - len]) + ' ' + name[(name.length - 1) - len]));
					}
					this.sendReplyBox("List of emoticons (" + Object.size(emotes) + "):

" + emoticons.join(' ').toString());
					break;
				case 'object':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					this.sendReplyBox("Gold.emoticons.chatEmotes = " + fs.readFileSync('config/emotes.json','utf8'));
					break;
				case 'status':
					if (!this.canBroadcast()) return;
					if (!parts[1]) {
						switch (room.emoteStatus) {
							case true:
								this.sendReply("Chat emotes are currently enabled in this room.");
								break;
							case false:
								this.sendReply("Chat emotes are currently disabled in this room.");
								break;
						}
					} else {
						switch (toId(parts[1])) {
							case 'on':
							case 'enable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (room.emoteStatus) return this.sendReply("Um, emotes are already enabled in this room.");
								room.emoteStatus = true;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								this.logModCommand(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								break;
							case 'off':
							case 'disable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (!room.emoteStatus) return this.sendReply("Um, emotes are already disabled in this room.");
								room.emoteStatus = false;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								this.logModCommand(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								break;
							default:
								this.sendReply("Usage: /ezemote status, [on / off] - Enables or disables the current emote status.  Requires #, &, ~.");
						}
					}
					break;
				case 'help':
				default:
					if (!this.canBroadcast()) return;
					this.sendReplyBox(
						"
" +
							"EZ-Emote Commands:
" +
							"(By: panpawn)
" +
							"/ezemote add, [emote], [link] - Adds an emote. Requires @, &, ~.
" +
							"/ezemote remove, [emote] - Removes an emote. Requires @, &, ~.
" +
							"/ezemote status, [on / off] - Enables or disables the status of emotes. Requires #, &, ~.
" +
							"/ezemote status - Views the current status of emotes.
" +
							"/ezemote list - Shows the emotes that were added with this command in a list form.
" +
							"/ezemote view - Shows all of the current emotes with their respected image.
" +
							"/ezemote object - Shows the object of Gold.emoticons.chatEmotes. (Mostly for development usage)
" +
							"/ezemote help - Shows this help command.
" +
						"
"
					);
			}
		} catch (e) {
			console.log("ERROR!  The EZ-Emote script has crashed!\n" + e.stack);
		}
	}
};
<commit_msg>Log crashes for emotes to a room<commit_after>/* Emoticons Plugin
 * This is a chat-plugin for Emoticons
 * You will need a line in parser to actually
 * parse this so that it works. (See command-parser.js)
 * Credits: panpawn, jd
 */

var fs = require('fs');
var path = require('path');
var serialize = require('node-serialize');

if (typeof Gold === 'undefined') global.Gold = {};

Gold.emoticons = {
	chatEmotes: {}, //much object such wow :^)
	processEmoticons: function(text) {
		var patterns = [],
			metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g,
			self = this;
		for (var i in this.chatEmotes) {
			if (this.chatEmotes.hasOwnProperty(i)) {
				patterns.push('(' + i.replace(metachars, "\\$&") + ')');
			}
		}
		return text.replace(new RegExp(patterns.join('|'), 'g'), function(match) {
			return typeof self.chatEmotes[match] != 'undefined' ?
				'' :
				match;
		});
	},
	processChatData: function(user, room, connection, message) {
		var match = false;
		for (var i in this.chatEmotes) {
			if (message.indexOf(i) >= 0) {
				match = true;
			}
		}
		if (!room.emoteStatus) {
			kitty = message = this.processEmoticons(message);
			var message = Tools.escapeHTML(kitty);
			return (message);
			return;
		} else if (room.emoteStatus) {
			if (!match || message.charAt(0) === '!') return true;
			message = Tools.escapeHTML(message);
			message = this.processEmoticons(message);
			if (user.hiding) {
				room.addRaw(' <button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
				room.update();
			}
			room.addRaw(user.getIdentity(room).substr(0,1) + '<button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
			room.update();
			return false;
		}
	}
};

/* EZ-Emote Commands plugin
 * These are commands that make it easier for people to actually
 * add / remove emotes without having to manaully do so every time
 * By: panpawn (inspired by jd's ez-tc plugin)
 */

var emotes = {};

function loadEmotes() {
	try {
		emotes = serialize.unserialize(fs.readFileSync('config/emotes.json', 'utf8'));
		Object.merge(Gold.emoticons.chatEmotes, emotes);
	} catch (e) {}
}
setTimeout(function(){loadEmotes();},1000);

function saveEmotes() {
	try {
		fs.writeFileSync('config/emotes.json',serialize.serialize(emotes));
		Object.merge(Gold.emoticons.chatEmotes, emotes);
	} catch (e) {}
}

exports.commands = {
	emotes: 'ezemote',
	temotes: 'ezemote',
	temote: 'ezemote',
	emote: 'ezemote',
	ec: 'ezemote',
	ezemote: function (target, room, user) {
		if (!target) target = "help";
		var parts = target.split(',');
		for (var u in parts) parts[u] = parts[u].trim();

		try {
			switch (toId(parts[0])) {
				case 'add':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!(parts[2] || parts[3])) return this.sendReply("Usage: /ezemote add, [emote], [link]");
					var emoteName = parts[1];
					if (Gold.emoticons.chatEmotes[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " already exists.");
					var link = parts.splice(2, parts.length).join(',');
					var fileTypes = [".gif",".png",".jpg"];
					if (fileTypes.indexOf(link.substr(-4)) < 0) return this.sendReply("ERROR: the emote you are trying to add must be a gif, png, or jpg.");
					emotes[emoteName] = Gold.emoticons.chatEmotes[emoteName] = link;
					saveEmotes();
					this.sendReply("The emote " + emoteName + " has been added.");
					this.logModCommand(user.name + " added the emote " + emoteName);
					Rooms.rooms.staff.add(Tools.escapeHTML(user.name) + " added the emote " + emoteName);
					room.update();
					break;
				case 'rem':
				case 'remove':
				case 'del':
				case 'delete':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!parts[1]) return this.sendReplyBox("/ezemote remove, [emote]");
					var emoteName = parts[1];
					if (!Gold.emoticons.chatEmotes[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " does not exist.");
					delete Gold.emoticons.chatEmotes[emoteName];
					delete emotes[emoteName];
					saveEmotes();
					this.sendReply("The emote " + emoteName + " was removed.");
					this.logModCommand("The emote " + emoteName + " was removed by " + user.name);
					Rooms.rooms.staff.add("The emote " + emoteName + " was removed by " + Tools.escapeHTML(user.name));
					room.update();
					break;
				case 'list':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var output = "There's a total of " + Object.size(emotes) + " emotes added with this command:
";
					for (var e in emotes) {
						output += e + "
";
					}
					this.sendReplyBox(output);
					break;
				case 'view':
					if (!this.canBroadcast()) return;
					//if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var name = Object.keys(Gold.emoticons.emoticons);
					emoticons = [];
					var len = name.length;
					while (len--) {
						emoticons.push((Gold.emoticons.processEmoticons(name[(name.length - 1) - len]) + ' ' + name[(name.length - 1) - len]));
					}
					this.sendReplyBox("List of emoticons (" + Object.size(emotes) + "):

" + emoticons.join(' ').toString());
					break;
				case 'object':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					this.sendReplyBox("Gold.emoticons.chatEmotes = " + fs.readFileSync('config/emotes.json','utf8'));
					break;
				case 'status':
					if (!this.canBroadcast()) return;
					if (!parts[1]) {
						switch (room.emoteStatus) {
							case true:
								this.sendReply("Chat emotes are currently enabled in this room.");
								break;
							case false:
								this.sendReply("Chat emotes are currently disabled in this room.");
								break;
						}
					} else {
						switch (toId(parts[1])) {
							case 'on':
							case 'enable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (room.emoteStatus) return this.sendReply("Um, emotes are already enabled in this room.");
								room.emoteStatus = true;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								this.logModCommand(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								break;
							case 'off':
							case 'disable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (!room.emoteStatus) return this.sendReply("Um, emotes are already disabled in this room.");
								room.emoteStatus = false;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								this.logModCommand(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								break;
							default:
								this.sendReply("Usage: /ezemote status, [on / off] - Enables or disables the current emote status.  Requires #, &, ~.");
						}
					}
					break;
				case 'help':
				default:
					if (!this.canBroadcast()) return;
					this.sendReplyBox(
						"
" +
							"EZ-Emote Commands:
" +
							"(By: panpawn)
" +
							"/ezemote add, [emote], [link] - Adds an emote. Requires @, &, ~.
" +
							"/ezemote remove, [emote] - Removes an emote. Requires @, &, ~.
" +
							"/ezemote status, [on / off] - Enables or disables the status of emotes. Requires #, &, ~.
" +
							"/ezemote status - Views the current status of emotes.
" +
							"/ezemote list - Shows the emotes that were added with this command in a list form.
" +
							"/ezemote view - Shows all of the current emotes with their respected image.
" +
							"/ezemote object - Shows the object of Gold.emoticons.chatEmotes. (Mostly for development usage)
" +
							"/ezemote help - Shows this help command.
" +
						"
"
					);
			}
		} catch (e) {
			try {
				Rooms.rooms.development.add(e.stack);
			} catch (e) {
				console.log("ERROR!  The EZ-Emote script has crashed!\n" + e.stack);
			}
		}
	}
};
<|endoftext|>


<commit_before>include('common.js')

lid_height = 5;
protusion = 1;

function cable_guide() {
  shell = linear_extrude({ height: conversor['z']},
    hull(square([cable['x_ini'], 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2, cable['y'], 0], square(cable['x_fim'])))
  )

  hollow = linear_extrude({ height: conversor['z']},
    hull(square([cable['x_ini'] - parede * 2, 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2 - parede/2, cable['y'], 0], square(cable['x_fim'] - parede)))
  )

  hollow = hollow.union(
    cube([cable['x_fim'] - parede , 10, 50]).translate([
      (cable['x_ini'] - cable['x_fim'])/2 - parede/2,
      cable['y'],
      0])
  )

  hollow = hollow.translate([parede, 0, base_z])

  shell = shell.subtract(hollow)
  shell = shell.setColor([0.1, 0.4, 0.6])

  middle_cylinder = cylinder({r: 3, h: conversor['z']}).subtract(cylinder({r: 2, h:conversor['z']}))

  middle_cylinder = middle_cylinder.translate([cable['x_ini']/2, 7, 0]).setColor([0.3, 0.7, 0.8])
  shell = shell.union(middle_cylinder)

  return shell.translate([
    (conversor['x'] + parede + folga) - cable['x_ini'],
    conversor['y'] + folga,
    0])
}

function cable_guide_lid() {
  outer = linear_extrude({ height: lid_height},
    hull(square([cable['x_ini'], 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2, cable['y'], 0], square(cable['x_fim'])))
  )

  prot = linear_extrude({ height: protusion + lid_height},
    hull(square([cable['x_ini'] - parede * 2 - folga * 2, 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2 - parede/2 + folga, cable['y'], 0], square(cable['x_fim'] - parede - folga * 2)))
  )

  prot = prot.translate([parede + folga, 0, 0]).setColor([1,0,0])

  outer = outer.union(prot)
  // outer = outer.setColor([0.1, 0.4, 0.6])

  middle_cylinder_hollow = cylinder({r: 4, h: conversor['z']})//.subtract(cylinder({r: 2, h:conversor['z']}))
  middle_cylinder = cylinder({r: 2 - folga, h: protusion * 2})

  middle_cylinder_hollow = middle_cylinder_hollow.translate([cable['x_ini']/2, 7, lid_height]).setColor([0.3, 0.7, 0.8])
  middle_cylinder = middle_cylinder.translate([cable['x_ini']/2, 7, lid_height]).setColor([0.3, 0.7, 0.8])
  outer = outer.subtract(middle_cylinder_hollow).union(middle_cylinder)

  return outer
}

function extra_cover() {
  size = 7
  extra_cover = cube([cable['x_ini'], size, lid_height])
  hollow = cube([cable['x_ini'] - 2 * parede, size - parede, lid_height - parede])
  hollow = hollow.translate([parede, parede, parede])

  extra_cover = extra_cover.subtract(hollow)
  return extra_cover.translate([0,-7,0])
}

function main() {
  return cable_guide_lid().union(extra_cover())

}
<commit_msg>Mais cores<commit_after>include('common.js')

lid_height = 5;
protusion = 1;

function cable_guide() {
  shell = linear_extrude({ height: conversor['z']},
    hull(square([cable['x_ini'], 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2, cable['y'], 0], square(cable['x_fim'])))
  )

  hollow = linear_extrude({ height: conversor['z']},
    hull(square([cable['x_ini'] - parede * 2, 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2 - parede/2, cable['y'], 0], square(cable['x_fim'] - parede)))
  )

  hollow = hollow.union(
    cube([cable['x_fim'] - parede , 10, 50]).translate([
      (cable['x_ini'] - cable['x_fim'])/2 - parede/2,
      cable['y'],
      0])
  )

  hollow = hollow.translate([parede, 0, base_z])

  shell = shell.subtract(hollow)
  shell = shell.setColor([0.1, 0.4, 0.6])

  middle_cylinder = cylinder({r: 3, h: conversor['z']}).subtract(cylinder({r: 2, h:conversor['z']}))

  middle_cylinder = middle_cylinder.translate([cable['x_ini']/2, 7, 0]).setColor([0.3, 0.7, 0.8])
  shell = shell.union(middle_cylinder)

  return shell.translate([
    (conversor['x'] + parede + folga) - cable['x_ini'],
    conversor['y'] + folga,
    0])
}

function cable_guide_lid() {
  outer = linear_extrude({ height: lid_height},
    hull(square([cable['x_ini'], 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2, cable['y'], 0], square(cable['x_fim'])))
  )

  prot = linear_extrude({ height: protusion + lid_height},
    hull(square([cable['x_ini'] - parede * 2 - folga * 2, 5]),
    translate([(cable['x_ini'] - cable['x_fim'])/2 - parede/2 + folga, cable['y'], 0], square(cable['x_fim'] - parede - folga * 2)))
  )

  prot = prot.translate([parede + folga, 0, 0])

  outer = outer.union(prot)
  // outer = outer.setColor([0.1, 0.4, 0.6])

  middle_cylinder_hollow = cylinder({r: 4, h: conversor['z']})//.subtract(cylinder({r: 2, h:conversor['z']}))
  middle_cylinder = cylinder({r: 2 - folga, h: protusion * 2})

  middle_cylinder_hollow = middle_cylinder_hollow.translate([cable['x_ini']/2, 7, lid_height])
  middle_cylinder = middle_cylinder.translate([cable['x_ini']/2, 7, lid_height])
  outer = outer.subtract(middle_cylinder_hollow).union(middle_cylinder)

  return outer
}

function extra_cover() {
  size = 7
  extra_cover = cube([cable['x_ini'], size, lid_height])
  hollow = cube([cable['x_ini'] - 2 * parede, size - parede, lid_height - parede])
  hollow = hollow.translate([parede, parede, parede])

  extra_cover = extra_cover.subtract(hollow)
  return extra_cover.translate([0,-7,0])
}

function main() {
  return cable_guide_lid().union(extra_cover())

}
<|endoftext|>


<commit_before>/* Emoticons Plugin
 * This is a chat-plugin for Emoticons
 * You will need a line in parser to actually
 * parse this so that it works.
 * Credits: panpawn, jd
 */

var fs = require('fs');
var path = require('path');
var serialize = require('node-serialize');

if (typeof Gold === 'undefined') global.Gold = {};

Gold.emoticons = {
	emoticons: {}, //much object such wow :^)
	processEmoticons: function(text) {
		var patterns = [],
			metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g,
			self = this;
		for (var i in this.emoticons) {
			if (this.emoticons.hasOwnProperty(i)) {
				patterns.push('(' + i.replace(metachars, "\\$&") + ')');
			}
		}
		return text.replace(new RegExp(patterns.join('|'), 'g'), function(match) {
			return typeof self.emoticons[match] != 'undefined' ?
				'' :
				match;
		});
	},
	processChatData: function(user, room, connection, message) {
		var match = false;
		for (var i in this.emoticons) {
			if (message.indexOf(i) >= 0) {
				match = true;
			}
		}
		if (!room.emoteStatus) {
			kitty = message = this.processEmoticons(message);
			var message = Tools.escapeHTML(kitty);
			return (message);
			return;
		} else if (room.emoteStatus) {
			if (!match || message.charAt(0) === '!') return true;
			message = Tools.escapeHTML(message);
			message = this.processEmoticons(message);
			if (user.hiding) {
						room.addRaw(' <button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
				room.update();
			}
			room.addRaw(user.getIdentity(room).substr(0,1) + '<button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
			room.update();
			return false;
		}
	}
};

/* EZ-Emote Commands plugin
 * These are commands that make it easier for people to actually
 * add / remove emotes without having to manaully do so every time
 * By: panpawn (inspired by jd's ez-tc plugin)
 */

var emotes = {};

function loadEmotes() {
	try {
		emotes = serialize.unserialize(fs.readFileSync('config/emotes.json', 'utf8'));
		Object.merge(Gold.emoticons.emoticons, emotes);
	} catch (e) {}
}
setTimeout(function(){loadEmotes();},1000);

function saveEmotes() {
	try {
		fs.writeFileSync('config/emotes.json',serialize.serialize(emotes));
		Object.merge(Gold.emoticons.emoticons, emotes);
	} catch (e) {}
}

exports.commands = {
	emotes: 'ezemote',
	temotes: 'ezemote',
	temote: 'ezemote',
	emote: 'ezemote',
	ec: 'ezemote',
	ezemote: function (target, room, user) {
		if (!target) target = "help";
		var parts = target.split(',');
		for (var u in parts) parts[u] = parts[u].trim();

		try {
			switch (toId(parts[0])) {
				case 'add':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!(parts[2] || parts[3])) return this.sendReply("Usage: /ezemote add, [emote], [link]");
					var emoteName = parts[1];
					if (Gold.emoticons.emoticons[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " already exists.");
					var link = parts.splice(2, parts.length).join(',');
					var fileTypes = [".gif",".png",".jpg"];
					if (fileTypes.indexOf(link.substr(-4)) < 0) return this.sendReply("ERROR: the emote you are trying to add must be a gif, png, or jpg.");
					emotes[emoteName] = Gold.emoticons.emoticons[emoteName] = link;
					saveEmotes();
					this.sendReply("The emote " + emoteName + " has been added.");
					this.logModCommand(user.name + " added the emote " + emoteName);
					Rooms.rooms.staff.add(Tools.escapeHTML(user.name) + " added the emote " + emoteName);
					room.update();
					break;
				case 'rem':
				case 'remove':
				case 'del':
				case 'delete':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!parts[1]) return this.sendReplyBox("/ezemote remove, [emote]");
					var emoteName = parts[1];
					if (!Gold.emoticons.emoticons[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " does not exist.");
					delete Gold.emoticons.emoticons[emoteName];
					delete emotes[emoteName];
					saveEmotes();
					this.sendReply("The emote " + emoteName + " was removed.");
					this.logModCommand("The emote " + emoteName + " was removed by " + user.name);
					Rooms.rooms.staff.add("The emote " + emoteName + " was removed by " + Tools.escapeHTML(user.name));
					room.update();
					break;
				case 'list':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var output = "There's a total of " + Object.size(emotes) + " emotes added with this command:
";
					for (var e in emotes) {
						output += e + "
";
					}
					this.sendReplyBox(output);
					break;
				case 'view':
					if (!this.canBroadcast()) return;
					//if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					if (!room.emoteStatus) {
						return this.sendReplyBox("Sorry, chat emotes have been disabled. :(");
					} else {
						var name = Object.keys(Gold.emoticons.emoticons);
						emoticons = [];
						var len = name.length;
						while (len--) {
							emoticons.push((Gold.emoticons.processEmoticons(name[(name.length - 1) - len]) + ' ' + name[(name.length - 1) - len]));
						}
						this.sendReplyBox("List of emoticons (" + Object.size(emotes) + "):

" + emoticons.join(' ').toString());
					}
					break;
				case 'object':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					this.sendReplyBox(" ok = " + fs.readFileSync('config/emotes.json','utf8'));
					break;
				case 'status':
					if (!this.canBroadcast()) return;
					if (!parts[1]) {
						switch (room.emoteStatus) {
							case true:
								this.sendReply("Chat emotes are currently enabled in this room.");
								break;
							case false:
								this.sendReply("Chat emotes are currently disabled in this room.");
								break;
						}
					} else {
						switch (toId(parts[1])) {
							case 'on':
							case 'enable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (room.emoteStatus) return this.sendReply("Um, emotes are already enabled in this room.");
								room.emoteStatus = true;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								this.logModCommand(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								break;
							case 'off':
							case 'disable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (!room.emoteStatus) return this.sendReply("Um, emotes are already disabled in this room.");
								room.emoteStatus = false;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								this.logModCommand(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								break;
							default:
								this.sendReply("Usage: /ezemote status, [on / off] - Enables or disables the current emote status.  Requires #, &, ~.");
						}
					}
					break;
				case 'help':
				default:
					if (!this.canBroadcast()) return;
					this.sendReplyBox(
						"
" +
							"EZ-Emote Commands:
" +
							"(By: panpawn)
" +
							"/ezemote add, [emote], [link] - Adds an emote. Requires @, &, ~.
" +
							"/ezemote remove, [emote] - Removes an emote. Requires @, &, ~.
" +
							"/ezemote status, [on / off] - Enables or disables the status of emotes. Requires #, &, ~.
" +
							"/ezemote status - Views the current status of emotes.
" +
							"/ezemote list - Shows the emotes that were added with this command in a list form.
" +
							"/ezemote view - Shows all of the current emotes with their respected image.
" +
							"/ezemote object - Shows the object of Emoticons.emoticons. (Mostly for development usage)
" +
							"/ezemote help - Shows this help command.
" +
						"
"
					);
			}
		} catch (e) {
			console.log("ERROR!  The EZ-Emote script has crashed!\n" + e.stack);
		}
	}
};
<commit_msg>fix spacing<commit_after>/* Emoticons Plugin
 * This is a chat-plugin for Emoticons
 * You will need a line in parser to actually
 * parse this so that it works.
 * Credits: panpawn, jd
 */

var fs = require('fs');
var path = require('path');
var serialize = require('node-serialize');

if (typeof Gold === 'undefined') global.Gold = {};

Gold.emoticons = {
	emoticons: {}, //much object such wow :^)
	processEmoticons: function(text) {
		var patterns = [],
			metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g,
			self = this;
		for (var i in this.emoticons) {
			if (this.emoticons.hasOwnProperty(i)) {
				patterns.push('(' + i.replace(metachars, "\\$&") + ')');
			}
		}
		return text.replace(new RegExp(patterns.join('|'), 'g'), function(match) {
			return typeof self.emoticons[match] != 'undefined' ?
				'' :
				match;
		});
	},
	processChatData: function(user, room, connection, message) {
		var match = false;
		for (var i in this.emoticons) {
			if (message.indexOf(i) >= 0) {
				match = true;
			}
		}
		if (!room.emoteStatus) {
			kitty = message = this.processEmoticons(message);
			var message = Tools.escapeHTML(kitty);
			return (message);
			return;
		} else if (room.emoteStatus) {
			if (!match || message.charAt(0) === '!') return true;
			message = Tools.escapeHTML(message);
			message = this.processEmoticons(message);
			if (user.hiding) {
				room.addRaw(' <button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
				room.update();
			}
			room.addRaw(user.getIdentity(room).substr(0,1) + '<button class="astext" name="parseCommand" value="/user ' +
				user.name + '">' + '' + Tools.escapeHTML(user.name) + ':</button> ' + message + '
');
			room.update();
			return false;
		}
	}
};

/* EZ-Emote Commands plugin
 * These are commands that make it easier for people to actually
 * add / remove emotes without having to manaully do so every time
 * By: panpawn (inspired by jd's ez-tc plugin)
 */

var emotes = {};

function loadEmotes() {
	try {
		emotes = serialize.unserialize(fs.readFileSync('config/emotes.json', 'utf8'));
		Object.merge(Gold.emoticons.emoticons, emotes);
	} catch (e) {}
}
setTimeout(function(){loadEmotes();},1000);

function saveEmotes() {
	try {
		fs.writeFileSync('config/emotes.json',serialize.serialize(emotes));
		Object.merge(Gold.emoticons.emoticons, emotes);
	} catch (e) {}
}

exports.commands = {
	emotes: 'ezemote',
	temotes: 'ezemote',
	temote: 'ezemote',
	emote: 'ezemote',
	ec: 'ezemote',
	ezemote: function (target, room, user) {
		if (!target) target = "help";
		var parts = target.split(',');
		for (var u in parts) parts[u] = parts[u].trim();

		try {
			switch (toId(parts[0])) {
				case 'add':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!(parts[2] || parts[3])) return this.sendReply("Usage: /ezemote add, [emote], [link]");
					var emoteName = parts[1];
					if (Gold.emoticons.emoticons[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " already exists.");
					var link = parts.splice(2, parts.length).join(',');
					var fileTypes = [".gif",".png",".jpg"];
					if (fileTypes.indexOf(link.substr(-4)) < 0) return this.sendReply("ERROR: the emote you are trying to add must be a gif, png, or jpg.");
					emotes[emoteName] = Gold.emoticons.emoticons[emoteName] = link;
					saveEmotes();
					this.sendReply("The emote " + emoteName + " has been added.");
					this.logModCommand(user.name + " added the emote " + emoteName);
					Rooms.rooms.staff.add(Tools.escapeHTML(user.name) + " added the emote " + emoteName);
					room.update();
					break;
				case 'rem':
				case 'remove':
				case 'del':
				case 'delete':
					if (!this.can('ban')) return this.sendReply("Access denied.");
					if (!parts[1]) return this.sendReplyBox("/ezemote remove, [emote]");
					var emoteName = parts[1];
					if (!Gold.emoticons.emoticons[emoteName]) return this.sendReply("ERROR - the emote: " + emoteName + " does not exist.");
					delete Gold.emoticons.emoticons[emoteName];
					delete emotes[emoteName];
					saveEmotes();
					this.sendReply("The emote " + emoteName + " was removed.");
					this.logModCommand("The emote " + emoteName + " was removed by " + user.name);
					Rooms.rooms.staff.add("The emote " + emoteName + " was removed by " + Tools.escapeHTML(user.name));
					room.update();
					break;
				case 'list':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					var output = "There's a total of " + Object.size(emotes) + " emotes added with this command:
";
					for (var e in emotes) {
						output += e + "
";
					}
					this.sendReplyBox(output);
					break;
				case 'view':
					if (!this.canBroadcast()) return;
					//if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					if (!room.emoteStatus) {
						return this.sendReplyBox("Sorry, chat emotes have been disabled. :(");
					} else {
						var name = Object.keys(Gold.emoticons.emoticons);
						emoticons = [];
						var len = name.length;
						while (len--) {
							emoticons.push((Gold.emoticons.processEmoticons(name[(name.length - 1) - len]) + ' ' + name[(name.length - 1) - len]));
						}
						this.sendReplyBox("List of emoticons (" + Object.size(emotes) + "):

" + emoticons.join(' ').toString());
					}
					break;
				case 'object':
					if (!this.canBroadcast()) return;
					if (this.broadcasting) return this.sendReply("ERROR: this command is too spammy to broadcast.  Use / instead of ! to see it for yourself.");
					this.sendReplyBox(" ok = " + fs.readFileSync('config/emotes.json','utf8'));
					break;
				case 'status':
					if (!this.canBroadcast()) return;
					if (!parts[1]) {
						switch (room.emoteStatus) {
							case true:
								this.sendReply("Chat emotes are currently enabled in this room.");
								break;
							case false:
								this.sendReply("Chat emotes are currently disabled in this room.");
								break;
						}
					} else {
						switch (toId(parts[1])) {
							case 'on':
							case 'enable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (room.emoteStatus) return this.sendReply("Um, emotes are already enabled in this room.");
								room.emoteStatus = true;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								this.logModCommand(Tools.escapeHTML(user.name) + ' has enabled chat emotes in this room.');
								break;
							case 'off':
							case 'disable':
								if (!this.can('declare', null, room)) return this.sendReply("Access denied.");
								if (!room.emoteStatus) return this.sendReply("Um, emotes are already disabled in this room.");
								room.emoteStatus = false;
								room.chatRoomData.emoteStatus = room.emoteStatus;
								Rooms.global.writeChatRoomData();
								room.add(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								this.logModCommand(Tools.escapeHTML(user.name) + " has disabled chat emotes in this room.");
								break;
							default:
								this.sendReply("Usage: /ezemote status, [on / off] - Enables or disables the current emote status.  Requires #, &, ~.");
						}
					}
					break;
				case 'help':
				default:
					if (!this.canBroadcast()) return;
					this.sendReplyBox(
						"
" +
							"EZ-Emote Commands:
" +
							"(By: panpawn)
" +
							"/ezemote add, [emote], [link] - Adds an emote. Requires @, &, ~.
" +
							"/ezemote remove, [emote] - Removes an emote. Requires @, &, ~.
" +
							"/ezemote status, [on / off] - Enables or disables the status of emotes. Requires #, &, ~.
" +
							"/ezemote status - Views the current status of emotes.
" +
							"/ezemote list - Shows the emotes that were added with this command in a list form.
" +
							"/ezemote view - Shows all of the current emotes with their respected image.
" +
							"/ezemote object - Shows the object of Emoticons.emoticons. (Mostly for development usage)
" +
							"/ezemote help - Shows this help command.
" +
						"
"
					);
			}
		} catch (e) {
			console.log("ERROR!  The EZ-Emote script has crashed!\n" + e.stack);
		}
	}
};
<|endoftext|>


<commit_before>'use strict'
/* global WebSocket */

const EventEmitter = require('eventemitter3')
const { assign, attempt, fromCallback, Promise } = require('./utils')

const blacklist = [ 'close', 'open', 'error', 'pong', 'retry' ]

// utils

const concat = [].concat.bind([])

const emit = EventEmitter.prototype.emit

function isBlacklistedEvent (name) {
  return blacklist.indexOf(name) >= 0
}

function getOwnProp (obj, name) {
  return obj.hasOwnProperty(name) ? obj[name] : undefined
}

// errors

function ConnectionError (id) {
  this.name = 'ConnectionError'
  this.id = id
}

ConnectionError.prototype = Object.create(Error.prototype)

function NoProcedureError (procedure) {
  this.name = 'NoProcedureError'
  this.procedure = procedure
}

NoProcedureError.prototype = Object.create(Error.prototype)

function ParsingError (errorData) {
  this.name = 'ParsingError'
  this.errorData = errorData
}

ParsingError.prototype = Object.create(Error.prototype)

function RPCError (errorData) {
  this.name = 'RPCError'
  this.errorData = errorData
}

RPCError.prototype = Object.create(Error.prototype)

function TimeoutError (id) {
  this.name = 'TimeoutError'
  this.id = id
}

TimeoutError.prototype = Object.create(Error.prototype)

// validation

function validateId (id) {
  return typeof id === 'number' && (id % 1) === 0 && id > 0
}

function validate (message) {
  let passed = false
  if (message instanceof Object) {
    if (message.name) { // message
      if (typeof message.name === 'string' && message.args instanceof Array) {
        let nprops = Object.keys(message).length
        if (nprops === 2 || (nprops === 3 && validateId(message.id))) {
          passed = true
        }
      }
    } else { // ack message
      let nprops = Object.keys(message).length
      if (nprops === 2 && validateId(message.id)) {
        if (message.hasOwnProperty('error') || message.hasOwnProperty('result')) {
          passed = true
        }
      }
    }
  }
  if (!passed) {
    throw new Error('Validation error')
  }
}

// ack

class Ack {
  constructor (id, timeout, cb) {
    this.id = id
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve
      this.reject = reject
    })
    this.cb = cb
    this.timeout = setTimeout(() => this.forceNack(new TimeoutError(id)), timeout)
  }

  forceNack (error) {
    clearTimeout(this.timeout)
    this.cb()
    this.reject(error)
  }

  settle (message) {
    clearTimeout(this.timeout)
    this.cb()
    if (message.hasOwnProperty('error')) {
      let error = new RPCError(message.error)
      this.reject(error)
    } else {
      this.resolve(message.result)
    }
  }
}

// client

/**
 * General format for all data that is sent or received over a
 * websocket.
 *
 * @typedef {Object} Client.Message
 *
 * @property {number} [id]
 * @property {string} [name]
 * @property {Array} [args]
 * @property {Object} [result]
 * @property {Object} [error]
 */

/**
 * Messages decoder. May also return promises for an asynchronous
 * execution.
 *
 * @callback Client.Encoder
 * @param {Client.Message} message Message.
 * @return {Promise<Object>|Object} Data to send.
 */

/**
 * Messages encoder. May also return promises for an asynchronous
 * execution.
 *
 * @callback Client.Decoder
 * @param {Object} data Received data.
 * @return {Promise<Client.Message>|Client.Message} Message.
 */

/**
 * Receive hook is run when a client receives a valid message via a
 * websocket. May also return promises for an asynchronous execution.
 *
 * @callback Client.ReceiveHook
 * @param {Client.Message} message Message.
 * @return {Promise<undefined>|undefined} Promise, if it is rejected no
 * handlers will be called.
 */

/**
 * @typedef {Object} Client.SocketOptions
 *
 * @property {number} [ackTimeout=20000] Result wait timeout for
 * {@link Client#invoke} in ms.
 * @property {Object} [auth={}] Auth data.
 * @property {string} [binaryType='arraybuffer'] W3C WebSocket
 * binary data type.
 * @property {Client.Decoder} [decoder=JSON.parse] Messages decoder.
 * @property {Client.Encoder} [encoder=JSON.stringify] Messages
 * encoder.
 * @property {function} [errorFormatter=String] Converter for JS
 * errors to some network format.
 * @property {number} [pingInterval=10000] Ping interval in ms.
 * @property {number} [pingTimeout=5000] Ping timeout in ms.
 * @property {string|Array<string>} [protocols='ws-messaging']
 * WebSocket protocols.
 * @property {Client.ReceiveHook} [receiveHook] Receive hook.
 * @property {boolean} [skipValidation=false] Skips build-in
 * messages validation.
 */

const defaults = {
  ackTimeout: 20000,
  auth: {},
  binaryType: 'arraybuffer',
  decoder: JSON.parse,
  encoder: JSON.stringify,
  errorFormatter: String,
  pingInterval: 10000,
  pingTimeout: 5000,
  protocols: 'ws-messaging',
  receiveHook: null,
  skipValidation: false
}

/**
 * @extends EventEmitter
 *
 * @emits Client#close
 * @emits Client#open
 * @emits Client#error
 * @emits Client#connect
 */
class Client extends EventEmitter {
  /**
   * Creates a client.
   *
   * @param {string} url WebSocket connection url.
   * @param {Client.SocketOptions} options Socket options.
   */
  constructor (url, options = {}) {
    super()
    this.url = url
    /**
     * Client id. Server-side only.
     * @member {number}
     * @readonly
     */
    this.id
    assign(this, defaults, options)
    if (!this.WebSocket) {
      this.WebSocket = WebSocket
      this.w3c = true
    }
    /**
     * If true, then a client is connected.
     * @member {boolean}
     * @readonly
     */
    this.connected = false
    this.counter = 1
    this.data = {}
    this.handlers = {}
    this.pendingAcks = {}
    /**
     * If true, then a client was closed via a close method or an auth
     * error occurred.
     * @member {boolean}
     * @readonly
     */
    this.terminated = false
    this.register('ping', () => Promise.resolve())
    this.reconnect()
  }

  _onMessage (data) {
    let message
    attempt(() => this.decoder(data.data))
      .then(msg => { message = msg })
      .then(() => { if (!this.skipValidation) { validate(message) } })
      .then(() => { if (this.receiveHook) { this.receiveHook(message) } })
      .then(() => this._dispatch(message))
      .catch(error => {
        let data = new ParsingError(this.errorFormatter(error))
        this.send('ParsingError', data)
      })
  }

  _setEvents () {
    /**
     * Emits w3c onopen WebSocket events.
     * @event Client#open
     */
    this.socket.onopen = emit.bind(this, 'open')
    /**
     * Emits w3c onerror WebSocket events. Does not throw if there are
     * no listeners.
     * @event Client#error
     * @param {Error} error Error.
     */
    this.socket.onerror = emit.bind(this, 'error')
    this.socket.onclose = this._onClose.bind(this)
    this.socket.onmessage = this._onMessage.bind(this)
  }

  _ping () {
    this.pingTimeoutId = setTimeout(() => {
      emit.call(this, 'ping')
      let { message, promise } =
            this._makeMessage('ping', [], true, this.pingTimeout)
      this._send(message)
        .then(() => promise)
        .then(() => {
          emit.call(this, 'pong')
          this._ping()
        })
        .catch(() => this.close(4008, 'Ping timeout', false))
    }, this.pingInterval)
  }

  _isOpen () {
    return this.socket &&
      (this.socket.readyState === 0 || this.socket.readyState === 1)
  }

  _onClose (ev) {
    this.connected = false
    clearTimeout(this.pingTimeoutId)
    if (ev.code === 4003) { this.terminated = true }
    for (let id in this.pendingAcks) {
      /* istanbul ignore else */
      if (this.pendingAcks.hasOwnProperty(id)) {
        let ack = this.pendingAcks[id]
        ack.forceNack(new ConnectionError(id))
      }
    }
    this.pendingAcks = {}
    /**
     * Emits w3c onclose WebSocket events.
     * @event Client#close
     * @param {CloseEvent} data Close event data.
     */
    emit.call(this, 'close', ev)
  }

  _makeMessage (name, args, needsAck, ackTimeout = this.ackTimeout) {
    let promise, message
    message = {name, args}
    if (needsAck) {
      let id = this.counter++
      let ack = new Ack(id, ackTimeout, () => delete this.pendingAcks[id])
      this.pendingAcks[id] = ack
      promise = ack.promise
      message.id = id
    }
    return { message, promise }
  }

  /**
   * Socket connection is open and client has passed an auth
   * check. Client-side only.
   * @event Client#connect
   * @param {Object|undefined} data Auth reply data.
   */
  _dispatch (message) {
    if (message.name) {
      if (isBlacklistedEvent(message.name)) { return }
      if (message.name === 'connect' && this.connected) { return }
      if (message.id) {
        let id = message.id
        let fn = getOwnProp(this.handlers, message.name)
        if (fn) {
          attempt(() => fn.apply(null, message.args))
            .then((result = null) => this._send({id, result}))
            .catch(error => this._send({id, error: this.errorFormatter(error)}))
        } else {
          this._send({id, error: new NoProcedureError(message.name)})
        }
      } else {
        emit.apply(this, concat(message.name, message.args))
      }
    } else {
      let ack = this.pendingAcks[message.id]
      if (ack) { ack.settle(message) }
    }
  }

  _send (message) {
    return attempt(() => this.encoder(message)).then(data => {
      if (this.w3c) {
        return this.socket.send(data)
      } else {
        let binary = typeof data !== 'string'
        return fromCallback(cb => this.socket.send(data, {binary}, cb))
      }
    })
  }

  /**
   * Send an event, no reply. Use {@link on} or {@link once} methods
   * to listen events on a recipient side. Reserved event names
   * (__MUST NOT__ be used): `connect`, `close`, `open`, `error`,
   * `ping`, `pong`, `retry`.
   * @param {string} event Event name.
   * @param {*} [args] Arguments.
   * @returns {Promise<undefined>} Resolves when a data has been sent.
   */
  send (event, ...args) {
    let { message } = this._makeMessage(event, args, false)
    return this._send(message)
  }

  /**
   * Invoke an RPC procedure. Use {@link Client#register} method to
   * assign an RPC method handler. Reserved procedure names (__MUST
   * NOT__ be used): `connect`, `close`, `open`, `error`, `ping`,
   * `pong`, `retry`.
   * @param {string} name Procedure name.
   * @param {*} [args] Arguments.
   * @returns {Promise<Object>} Resolves or rejects when a reply is
   * received.
   */
  invoke (name, ...args) {
    let { message, promise } = this._makeMessage(name, args, true)
    return this._send(message).then(() => promise)
  }

  /**
   * Register an RPC handler. Each name must have no more than a one
   * handler, so it throws an error on a duplicate handler
   * registration attempt. Use {@link Client#invoke} to call a method.
   * @param {string} name Procedure name.
   * @param {function} handler A function that returns a Promise.
   */
  register (name, handler) {
    if (getOwnProp(this.handlers, name)) {
      throw new Error(`Can't register a duplicate RPC handler for ${name}`)
    }
    this.handlers[name] = handler
  }

  /**
   * Reconnect. Client-side only.
   */
  reconnect () {
    if (this._isOpen() || this.terminated) {
      return
    } else if (this.WebSocket && this.url) {
      /**
       * Underlying websocket.
       * @member {WebSocket}
       * @readonly
       */
      this.socket = new this.WebSocket(this.url, this.protocols)
      if (this.w3c) { this.socket.binaryType = this.binaryType }
      this.once('open', () => {
        this._send(this.auth)
        this.once('connect', () => {
          this.connected = true
          this._ping()
        })
      })
      this._setEvents()
    } else {
      throw new Error('Malformed configuration options')
    }
  }

  /**
   * Closes a client connection.
   * @param {number} [code=1000] Code as per WebSocket spec.
   * @param {string} [str] Optional string.
   * @param {boolean} [terminate=true] Disable reconnect.
   */
  close (code = 1000, str, terminate = true) {
    clearTimeout(this.pingTimeoutId)
    if (!this.terminated) {
      this.terminated = terminate
      if (this._isOpen()) { this.socket.close(code, str) }
    }
  }
}

/**
 * Alias for {@link Client#send}.
 * @method
 * @name Client#emit
 */
Client.prototype.emit = Client.prototype.send

Client.ConnectionError = ConnectionError
Client.RPCError = RPCError
Client.TimeoutError = TimeoutError

module.exports = Client
<commit_msg>refactor: allow reconnect on terminated clients<commit_after>'use strict'
/* global WebSocket */

const EventEmitter = require('eventemitter3')
const { assign, attempt, fromCallback, Promise } = require('./utils')

const blacklist = [ 'close', 'open', 'error', 'pong', 'retry' ]

// utils

const concat = [].concat.bind([])

const emit = EventEmitter.prototype.emit

function isBlacklistedEvent (name) {
  return blacklist.indexOf(name) >= 0
}

function getOwnProp (obj, name) {
  return obj.hasOwnProperty(name) ? obj[name] : undefined
}

// errors

function ConnectionError (id) {
  this.name = 'ConnectionError'
  this.id = id
}

ConnectionError.prototype = Object.create(Error.prototype)

function NoProcedureError (procedure) {
  this.name = 'NoProcedureError'
  this.procedure = procedure
}

NoProcedureError.prototype = Object.create(Error.prototype)

function ParsingError (errorData) {
  this.name = 'ParsingError'
  this.errorData = errorData
}

ParsingError.prototype = Object.create(Error.prototype)

function RPCError (errorData) {
  this.name = 'RPCError'
  this.errorData = errorData
}

RPCError.prototype = Object.create(Error.prototype)

function TimeoutError (id) {
  this.name = 'TimeoutError'
  this.id = id
}

TimeoutError.prototype = Object.create(Error.prototype)

// validation

function validateId (id) {
  return typeof id === 'number' && (id % 1) === 0 && id > 0
}

function validate (message) {
  let passed = false
  if (message instanceof Object) {
    if (message.name) { // message
      if (typeof message.name === 'string' && message.args instanceof Array) {
        let nprops = Object.keys(message).length
        if (nprops === 2 || (nprops === 3 && validateId(message.id))) {
          passed = true
        }
      }
    } else { // ack message
      let nprops = Object.keys(message).length
      if (nprops === 2 && validateId(message.id)) {
        if (message.hasOwnProperty('error') || message.hasOwnProperty('result')) {
          passed = true
        }
      }
    }
  }
  if (!passed) {
    throw new Error('Validation error')
  }
}

// ack

class Ack {
  constructor (id, timeout, cb) {
    this.id = id
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve
      this.reject = reject
    })
    this.cb = cb
    this.timeout = setTimeout(() => this.forceNack(new TimeoutError(id)), timeout)
  }

  forceNack (error) {
    clearTimeout(this.timeout)
    this.cb()
    this.reject(error)
  }

  settle (message) {
    clearTimeout(this.timeout)
    this.cb()
    if (message.hasOwnProperty('error')) {
      let error = new RPCError(message.error)
      this.reject(error)
    } else {
      this.resolve(message.result)
    }
  }
}

// client

/**
 * General format for all data that is sent or received over a
 * websocket.
 *
 * @typedef {Object} Client.Message
 *
 * @property {number} [id]
 * @property {string} [name]
 * @property {Array} [args]
 * @property {Object} [result]
 * @property {Object} [error]
 */

/**
 * Messages decoder. May also return promises for an asynchronous
 * execution.
 *
 * @callback Client.Encoder
 * @param {Client.Message} message Message.
 * @return {Promise<Object>|Object} Data to send.
 */

/**
 * Messages encoder. May also return promises for an asynchronous
 * execution.
 *
 * @callback Client.Decoder
 * @param {Object} data Received data.
 * @return {Promise<Client.Message>|Client.Message} Message.
 */

/**
 * Receive hook is run when a client receives a valid message via a
 * websocket. May also return promises for an asynchronous execution.
 *
 * @callback Client.ReceiveHook
 * @param {Client.Message} message Message.
 * @return {Promise<undefined>|undefined} Promise, if it is rejected no
 * handlers will be called.
 */

/**
 * @typedef {Object} Client.SocketOptions
 *
 * @property {number} [ackTimeout=20000] Result wait timeout for
 * {@link Client#invoke} in ms.
 * @property {Object} [auth={}] Auth data.
 * @property {string} [binaryType='arraybuffer'] W3C WebSocket
 * binary data type.
 * @property {Client.Decoder} [decoder=JSON.parse] Messages decoder.
 * @property {Client.Encoder} [encoder=JSON.stringify] Messages
 * encoder.
 * @property {function} [errorFormatter=String] Converter for JS
 * errors to some network format.
 * @property {number} [pingInterval=10000] Ping interval in ms.
 * @property {number} [pingTimeout=5000] Ping timeout in ms.
 * @property {string|Array<string>} [protocols='ws-messaging']
 * WebSocket protocols.
 * @property {Client.ReceiveHook} [receiveHook] Receive hook.
 * @property {boolean} [skipValidation=false] Skips build-in
 * messages validation.
 */

const defaults = {
  ackTimeout: 20000,
  auth: {},
  binaryType: 'arraybuffer',
  decoder: JSON.parse,
  encoder: JSON.stringify,
  errorFormatter: String,
  pingInterval: 10000,
  pingTimeout: 5000,
  protocols: 'ws-messaging',
  receiveHook: null,
  skipValidation: false
}

/**
 * @extends EventEmitter
 *
 * @emits Client#close
 * @emits Client#open
 * @emits Client#error
 * @emits Client#connect
 */
class Client extends EventEmitter {
  /**
   * Creates a client.
   *
   * @param {string} url WebSocket connection url.
   * @param {Client.SocketOptions} options Socket options.
   */
  constructor (url, options = {}) {
    super()
    this.url = url
    /**
     * Client id. Server-side only.
     * @member {number}
     * @readonly
     */
    this.id
    assign(this, defaults, options)
    if (!this.WebSocket) {
      this.WebSocket = WebSocket
      this.w3c = true
    }
    /**
     * If true, then a client is connected.
     * @member {boolean}
     * @readonly
     */
    this.connected = false
    this.counter = 1
    this.data = {}
    this.handlers = {}
    this.pendingAcks = {}
    /**
     * If true, then a client was closed via a close method or an auth
     * error occurred.
     * @member {boolean}
     * @readonly
     */
    this.terminated = false
    this.register('ping', () => Promise.resolve())
    this.reconnect()
  }

  _onMessage (data) {
    let message
    attempt(() => this.decoder(data.data))
      .then(msg => { message = msg })
      .then(() => { if (!this.skipValidation) { validate(message) } })
      .then(() => { if (this.receiveHook) { this.receiveHook(message) } })
      .then(() => this._dispatch(message))
      .catch(error => {
        let data = new ParsingError(this.errorFormatter(error))
        this.send('ParsingError', data)
      })
  }

  _setEvents () {
    /**
     * Emits w3c onopen WebSocket events.
     * @event Client#open
     */
    this.socket.onopen = emit.bind(this, 'open')
    /**
     * Emits w3c onerror WebSocket events. Does not throw if there are
     * no listeners.
     * @event Client#error
     * @param {Error} error Error.
     */
    this.socket.onerror = emit.bind(this, 'error')
    this.socket.onclose = this._onClose.bind(this)
    this.socket.onmessage = this._onMessage.bind(this)
  }

  _ping () {
    this.pingTimeoutId = setTimeout(() => {
      emit.call(this, 'ping')
      let { message, promise } =
            this._makeMessage('ping', [], true, this.pingTimeout)
      this._send(message)
        .then(() => promise)
        .then(() => {
          emit.call(this, 'pong')
          this._ping()
        })
        .catch(() => this.close(4008, 'Ping timeout', false))
    }, this.pingInterval)
  }

  _isOpen () {
    return this.socket &&
      (this.socket.readyState === 0 || this.socket.readyState === 1)
  }

  _onClose (ev) {
    this.connected = false
    clearTimeout(this.pingTimeoutId)
    if (ev.code === 4003) { this.terminated = true }
    for (let id in this.pendingAcks) {
      /* istanbul ignore else */
      if (this.pendingAcks.hasOwnProperty(id)) {
        let ack = this.pendingAcks[id]
        ack.forceNack(new ConnectionError(id))
      }
    }
    this.pendingAcks = {}
    /**
     * Emits w3c onclose WebSocket events.
     * @event Client#close
     * @param {CloseEvent} data Close event data.
     */
    emit.call(this, 'close', ev)
  }

  _makeMessage (name, args, needsAck, ackTimeout = this.ackTimeout) {
    let promise, message
    message = {name, args}
    if (needsAck) {
      let id = this.counter++
      let ack = new Ack(id, ackTimeout, () => delete this.pendingAcks[id])
      this.pendingAcks[id] = ack
      promise = ack.promise
      message.id = id
    }
    return { message, promise }
  }

  /**
   * Socket connection is open and client has passed an auth
   * check. Client-side only.
   * @event Client#connect
   * @param {Object|undefined} data Auth reply data.
   */
  _dispatch (message) {
    if (message.name) {
      if (isBlacklistedEvent(message.name)) { return }
      if (message.name === 'connect' && this.connected) { return }
      if (message.id) {
        let id = message.id
        let fn = getOwnProp(this.handlers, message.name)
        if (fn) {
          attempt(() => fn.apply(null, message.args))
            .then((result = null) => this._send({id, result}))
            .catch(error => this._send({id, error: this.errorFormatter(error)}))
        } else {
          this._send({id, error: new NoProcedureError(message.name)})
        }
      } else {
        emit.apply(this, concat(message.name, message.args))
      }
    } else {
      let ack = this.pendingAcks[message.id]
      if (ack) { ack.settle(message) }
    }
  }

  _send (message) {
    return attempt(() => this.encoder(message)).then(data => {
      if (this.w3c) {
        return this.socket.send(data)
      } else {
        let binary = typeof data !== 'string'
        return fromCallback(cb => this.socket.send(data, {binary}, cb))
      }
    })
  }

  /**
   * Send an event, no reply. Use {@link on} or {@link once} methods
   * to listen events on a recipient side. Reserved event names
   * (__MUST NOT__ be used): `connect`, `close`, `open`, `error`,
   * `ping`, `pong`, `retry`.
   * @param {string} event Event name.
   * @param {*} [args] Arguments.
   * @returns {Promise<undefined>} Resolves when a data has been sent.
   */
  send (event, ...args) {
    let { message } = this._makeMessage(event, args, false)
    return this._send(message)
  }

  /**
   * Invoke an RPC procedure. Use {@link Client#register} method to
   * assign an RPC method handler. Reserved procedure names (__MUST
   * NOT__ be used): `connect`, `close`, `open`, `error`, `ping`,
   * `pong`, `retry`.
   * @param {string} name Procedure name.
   * @param {*} [args] Arguments.
   * @returns {Promise<Object>} Resolves or rejects when a reply is
   * received.
   */
  invoke (name, ...args) {
    let { message, promise } = this._makeMessage(name, args, true)
    return this._send(message).then(() => promise)
  }

  /**
   * Register an RPC handler. Each name must have no more than a one
   * handler, so it throws an error on a duplicate handler
   * registration attempt. Use {@link Client#invoke} to call a method.
   * @param {string} name Procedure name.
   * @param {function} handler A function that returns a Promise.
   */
  register (name, handler) {
    if (getOwnProp(this.handlers, name)) {
      throw new Error(`Can't register a duplicate RPC handler for ${name}`)
    }
    this.handlers[name] = handler
  }

  /**
   * Reconnect. Client-side only.
   */
  reconnect () {
    if (this._isOpen()) {
      return
    } else if (this.WebSocket && this.url) {
      /**
       * Underlying websocket.
       * @member {WebSocket}
       * @readonly
       */
      this.socket = new this.WebSocket(this.url, this.protocols)
      if (this.w3c) { this.socket.binaryType = this.binaryType }
      this.once('open', () => {
        this._send(this.auth)
        this.once('connect', () => {
          this.connected = true
          this._ping()
        })
      })
      this._setEvents()
    } else {
      throw new Error('Malformed configuration options')
    }
  }

  /**
   * Closes a client connection.
   * @param {number} [code=1000] Code as per WebSocket spec.
   * @param {string} [str] Optional string.
   * @param {boolean} [terminate=true] Disable reconnect.
   */
  close (code = 1000, str, terminate = true) {
    clearTimeout(this.pingTimeoutId)
    if (!this.terminated) {
      this.terminated = terminate
      if (this._isOpen()) { this.socket.close(code, str) }
    }
  }
}

/**
 * Alias for {@link Client#send}.
 * @method
 * @name Client#emit
 */
Client.prototype.emit = Client.prototype.send

Client.ConnectionError = ConnectionError
Client.RPCError = RPCError
Client.TimeoutError = TimeoutError

module.exports = Client
<|endoftext|>