<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import getField from '../utils/getField';
import getDisplayName from '../utils/StringUtils/getDisplayName';

import InkContainer from './InkContainer';
import inkContextTypes from './inkContextTypes';

/**
 * Takes any component and injects an ink container for having the Material Design Ink effect.
 *
 * The default triggers for an ink are:
 * - mouse down event
 * - touch start event
 * - keyboard focus
 * - form submit
 *
 * The form submit ink will only be triggered if the `ComposedComponent` has the attribute
 * `type="submit"`, the `ComposedComponent` is in a form, and the user hits the `enter` key
 * while not actively focusing the `ComposedComponent`.
 *
 * ```js
 * @param {function} ComposedComponent - The React Component to inject an `ink` prop into.
 * @return {function} a new React class rendering the `ComposedComponent` and adding an
 *    `ink` pop.
 * ```
 */
export default ComposedComponent => class InkedComponent extends PureComponent {
  static displayName = getDisplayName(ComposedComponent, 'Ink');
  static propTypes = {
    /**
     * An optional style to apply to each ink that gets generated.
     */
    inkStyle: PropTypes.object,

    /**
     * An optional className to apply to each ink that gets generated.
     */
    inkClassName: PropTypes.string,

    /**
     * An optional style to apply to the ink's container.
     */
    inkContainerStyle: PropTypes.object,

    /**
     * An optional className to apply to the ink's container.
     */
    inkContainerClassName: PropTypes.string,

    /**
     * Boolean if the composed component or the ink is disabled.
     */
    disabled: PropTypes.bool,

    /**
     * Boolean if only the ink is disabled for the composed component.
     */
    inkDisabled: PropTypes.bool,

    /**
     * The time (in ms) that the enter and leave transitions for the ink should overlap.
     * This really just allows for a more _fluid_ looking ink when something is quickly
     * touched or clicked by having it fade out while growing.
     */
    inkTransitionOverlap: PropTypes.number.isRequired,

    /**
     * The transition time for the ink to be considered fully entered. This should really
     * map up to whatever value you set for `$md-ink-enter-transition-time`.
     */
    inkTransitionEnterTimeout: PropTypes.number.isRequired,

    /**
     * The transition time for the ink to be considered fully leaved (left?). This should really
     * map up to whatever value you set for `$md-ink-leave-transition-time`.
     */
    inkTransitionLeaveTimeout: PropTypes.number.isRequired,

    /**
     * Boolean if the `ComposedComponent`'s click event only after the ink has finished transitioning
     * in and out. This is really only to get a more _fluid_ looking click event when clicking on
     * the `ComposedComponent` ends up taking it out of the view. (ex: Closing a Dialog).
     */
    waitForInkTransition: PropTypes.bool,

    /**
     * An optional array of interactions that can be disabled for the ink. This is a *very* limited
     * use case where `Switches` needed the ink disabled only when using a mouse.
     */
    disabledInteractions: PropTypes.arrayOf(PropTypes.oneOf(['keyboard', 'mouse', 'touch'])),

    /**
     * Boolean if the ink should do a pulse animation while focused. This was enabled by default in
     * previous versions.
     */
    pulse: PropTypes.bool,

    /**
     * When using inked components in a `TransitionGroup`, the ref callback is not actually invoked.
     * This is a little _hack_ to get it to work by not using `ref`, but this name.
     */
    __SUPER_SECRET_REF__: PropTypes.func,
  };

  static defaultProps = {
    inkTransitionOverlap: 150,
    inkTransitionEnterTimeout: 450,
    inkTransitionLeaveTimeout: 300,
  };

  static contextTypes = inkContextTypes;

  componentDidMount() {
    const { __SUPER_SECRET_REF__: ref } = this.props;
    // Emulate the ref callback...
    if (ref) {
      ref(this);
    }
  }

  componentWillUnmount() {
    const { __SUPER_SECRET_REF__: ref } = this.props;
    // Emulate the ref callback...
    if (ref) {
      ref(null);
    }
  }

  /**
   * A publicly accessible way to manually create an ink. This can be used with the `refs`.
   * The ink can either be created by using the `pageX` and `pageY` from a click/touch event
   * or it will be created in the center of the `ComposedComponent`.
   *
   * ```js
   * <SomeInkedComponent ref={inkHOC => inkHOC.createInk()} />
   * ```
   *
   * @param {number=} pageX - An optional pageX of the click or touch event.
   * @param {number=} pageY - An optional pageY of the click or touch event.
   */
  createInk = (pageX, pageY) => {
    if (this._inkContainer && !this.props.disabled && !this.props.inkDisabled) {
      this._inkContainer.createInk(pageX, pageY);
    }
  };

  /**
   * This will attempt to focus the composed component. If the component is disabled, nothing
   * will happen. If the `disabled` and `inkDisabled` props are not set to `true`, an ink will
   * also be created.
   *
   * ```js
   * <SomeInkedComponent ref={inkHOC => inkHOC.focus()} />
   * ```
   */
  focus = () => {
    if (this._inkContainer) {
      this._inkContainer.focus();
    }
  };

  /**
   * Gets the composed component as a ref. This is useful if you need to access the ref of the
   * composed component instead of the `injectInk` HOC to use some publicly accessible methods.
   *
   * ```js
   * <SomeInkedComponent
   *   ref={inkHOC => {
   *     inkHOC.getComposedComponent().focus();
   *   }}
   * />
   * ```
   */
  getComposedComponent = () => this._composed;

  _setInkRef= (inkContainer) => {
    if (inkContainer) {
      this._inkContainer = inkContainer;
    }
  };

  _setComposedComponent = (component) => {
    this._composed = component;
  };

  render() {
    const {
      inkTransitionOverlap: transitionOverlap,
      inkTransitionEnterTimeout: transitionEnterTimeout,
      inkTransitionLeaveTimeout: transitionLeaveTimeout,
      inkStyle,
      inkClassName,
      inkContainerStyle,
      inkContainerClassName,
      waitForInkTransition,
      disabledInteractions,
      pulse,
      inkDisabled: propInkDisabled, // eslint-disable-line no-unused-vars
      __SUPER_SECRET_REF__, // eslint-disable-line no-unused-vars
      ...props
    } = this.props;

    const inkDisabled = getField(this.props, this.context, 'inkDisabled');
    const inkDisabledInteractions = typeof disabledInteractions !== 'undefined'
      ? disabledInteractions
      : this.context.inkDisabledInteractions;

    if (!(props.disabled || inkDisabled)) {
      props.ink = (
        <InkContainer
          ref={this._setInkRef}
          key="ink-container"
          pulse={pulse}
          style={inkContainerStyle}
          className={inkContainerClassName}
          inkStyle={inkStyle}
          inkClassName={inkClassName}
          disabledInteractions={inkDisabledInteractions}
          transitionOverlap={transitionOverlap}
          transitionEnterTimeout={transitionEnterTimeout}
          transitionLeaveTimeout={transitionLeaveTimeout}
          waitForInkTransition={waitForInkTransition}
        />
      );
    }

    props.ref = this._setComposedComponent;

    return <ComposedComponent {...props} />;
  }
};
<commit_msg>Fixed focus for inked components<commit_after>import React, { PureComponent } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';

import getField from '../utils/getField';
import getDisplayName from '../utils/StringUtils/getDisplayName';

import InkContainer from './InkContainer';
import inkContextTypes from './inkContextTypes';

/**
 * Takes any component and injects an ink container for having the Material Design Ink effect.
 *
 * The default triggers for an ink are:
 * - mouse down event
 * - touch start event
 * - keyboard focus
 * - form submit
 *
 * The form submit ink will only be triggered if the `ComposedComponent` has the attribute
 * `type="submit"`, the `ComposedComponent` is in a form, and the user hits the `enter` key
 * while not actively focusing the `ComposedComponent`.
 *
 * ```js
 * @param {function} ComposedComponent - The React Component to inject an `ink` prop into.
 * @return {function} a new React class rendering the `ComposedComponent` and adding an
 *    `ink` pop.
 * ```
 */
export default ComposedComponent => class InkedComponent extends PureComponent {
  static displayName = getDisplayName(ComposedComponent, 'Ink');
  static propTypes = {
    /**
     * An optional style to apply to each ink that gets generated.
     */
    inkStyle: PropTypes.object,

    /**
     * An optional className to apply to each ink that gets generated.
     */
    inkClassName: PropTypes.string,

    /**
     * An optional style to apply to the ink's container.
     */
    inkContainerStyle: PropTypes.object,

    /**
     * An optional className to apply to the ink's container.
     */
    inkContainerClassName: PropTypes.string,

    /**
     * Boolean if the composed component or the ink is disabled.
     */
    disabled: PropTypes.bool,

    /**
     * Boolean if only the ink is disabled for the composed component.
     */
    inkDisabled: PropTypes.bool,

    /**
     * The time (in ms) that the enter and leave transitions for the ink should overlap.
     * This really just allows for a more _fluid_ looking ink when something is quickly
     * touched or clicked by having it fade out while growing.
     */
    inkTransitionOverlap: PropTypes.number.isRequired,

    /**
     * The transition time for the ink to be considered fully entered. This should really
     * map up to whatever value you set for `$md-ink-enter-transition-time`.
     */
    inkTransitionEnterTimeout: PropTypes.number.isRequired,

    /**
     * The transition time for the ink to be considered fully leaved (left?). This should really
     * map up to whatever value you set for `$md-ink-leave-transition-time`.
     */
    inkTransitionLeaveTimeout: PropTypes.number.isRequired,

    /**
     * Boolean if the `ComposedComponent`'s click event only after the ink has finished transitioning
     * in and out. This is really only to get a more _fluid_ looking click event when clicking on
     * the `ComposedComponent` ends up taking it out of the view. (ex: Closing a Dialog).
     */
    waitForInkTransition: PropTypes.bool,

    /**
     * An optional array of interactions that can be disabled for the ink. This is a *very* limited
     * use case where `Switches` needed the ink disabled only when using a mouse.
     */
    disabledInteractions: PropTypes.arrayOf(PropTypes.oneOf(['keyboard', 'mouse', 'touch'])),

    /**
     * Boolean if the ink should do a pulse animation while focused. This was enabled by default in
     * previous versions.
     */
    pulse: PropTypes.bool,

    /**
     * When using inked components in a `TransitionGroup`, the ref callback is not actually invoked.
     * This is a little _hack_ to get it to work by not using `ref`, but this name.
     */
    __SUPER_SECRET_REF__: PropTypes.func,
  };

  static defaultProps = {
    inkTransitionOverlap: 150,
    inkTransitionEnterTimeout: 450,
    inkTransitionLeaveTimeout: 300,
  };

  static contextTypes = inkContextTypes;

  componentDidMount() {
    const { __SUPER_SECRET_REF__: ref } = this.props;
    // Emulate the ref callback...
    if (ref) {
      ref(this);
    }
  }

  componentWillUnmount() {
    const { __SUPER_SECRET_REF__: ref } = this.props;
    // Emulate the ref callback...
    if (ref) {
      ref(null);
    }
  }

  /**
   * A publicly accessible way to manually create an ink. This can be used with the `refs`.
   * The ink can either be created by using the `pageX` and `pageY` from a click/touch event
   * or it will be created in the center of the `ComposedComponent`.
   *
   * ```js
   * <SomeInkedComponent ref={inkHOC => inkHOC.createInk()} />
   * ```
   *
   * @param {number=} pageX - An optional pageX of the click or touch event.
   * @param {number=} pageY - An optional pageY of the click or touch event.
   */
  createInk = (pageX, pageY) => {
    if (this._inkContainer && !this.props.disabled && !this.props.inkDisabled) {
      this._inkContainer.createInk(pageX, pageY);
    }
  };

  /**
   * This will attempt to focus the composed component. If the component is disabled, nothing
   * will happen. If the `disabled` and `inkDisabled` props are not set to `true`, an ink will
   * also be created.
   *
   * ```js
   * <SomeInkedComponent ref={inkHOC => inkHOC.focus()} />
   * ```
   */
  focus = () => {
    if (this.props.inkDisabled) {
      const composed = findDOMNode(this._composed);
      if (composed) {
        composed.focus();
      }
    } else if (this._inkContainer) {
      this._inkContainer.focus();
    }
  };

  /**
   * Gets the composed component as a ref. This is useful if you need to access the ref of the
   * composed component instead of the `injectInk` HOC to use some publicly accessible methods.
   *
   * ```js
   * <SomeInkedComponent
   *   ref={inkHOC => {
   *     inkHOC.getComposedComponent().focus();
   *   }}
   * />
   * ```
   */
  getComposedComponent = () => this._composed;

  _setInkRef= (inkContainer) => {
    if (inkContainer) {
      this._inkContainer = inkContainer;
    }
  };

  _setComposedComponent = (component) => {
    this._composed = component;
  };

  render() {
    const {
      inkTransitionOverlap: transitionOverlap,
      inkTransitionEnterTimeout: transitionEnterTimeout,
      inkTransitionLeaveTimeout: transitionLeaveTimeout,
      inkStyle,
      inkClassName,
      inkContainerStyle,
      inkContainerClassName,
      waitForInkTransition,
      disabledInteractions,
      pulse,
      inkDisabled: propInkDisabled, // eslint-disable-line no-unused-vars
      __SUPER_SECRET_REF__, // eslint-disable-line no-unused-vars
      ...props
    } = this.props;

    const inkDisabled = getField(this.props, this.context, 'inkDisabled');
    const inkDisabledInteractions = typeof disabledInteractions !== 'undefined'
      ? disabledInteractions
      : this.context.inkDisabledInteractions;

    if (!(props.disabled || inkDisabled)) {
      props.ink = (
        <InkContainer
          ref={this._setInkRef}
          key="ink-container"
          pulse={pulse}
          style={inkContainerStyle}
          className={inkContainerClassName}
          inkStyle={inkStyle}
          inkClassName={inkClassName}
          disabledInteractions={inkDisabledInteractions}
          transitionOverlap={transitionOverlap}
          transitionEnterTimeout={transitionEnterTimeout}
          transitionLeaveTimeout={transitionLeaveTimeout}
          waitForInkTransition={waitForInkTransition}
        />
      );
    }

    props.ref = this._setComposedComponent;

    return <ComposedComponent {...props} />;
  }
};
<|endoftext|>


<commit_before>
var sprintf = require('sprintf');
var gutil = require('gulp-util');
var extend = require('object-extend');

var gulpWebpack = require('webpack-stream');
var WebpackDevServer = require("webpack-dev-server");
var HtmlWebpackPlugin = require('html-webpack-plugin')

var gulp = require('gulp');
var src  = gulp.src;
var dest = gulp.dest;

var webpack = require('webpack'),
path = require('path');

/*
 *  Bundle a component
 *
 *  entryPoint        - the entry point for the bundle
 *  outputFileName    - file name for produced bundle
 *  destPath          - destination path for produced bundle,
 *                      relative to the root directory of the project
 *                      being built
 *  pageDefs          - options to pass on into the template
 *                      for each index.html entry point to be
 *                      generated. must be either an array (or null,
 *                      if no index.html:s need to be generated)
 *  watch             - start webpack-dev-server with hot reloading
 *                      setting this true will cause this to block
 */
function bundle(entryPoint, outputFileName, destPath, pageDefs, watch, assetContext, callback) {

  var config = {
     resolve: {
       modulesDirectories: ['node_modules', 'components'],
       root: [path.resolve(__dirname,'../node_modules')]
     },
     module: {
       loaders: getLoaders()
     },
     resolveLoader: {
      root: [path.resolve(__dirname, '../node_modules')],
      modulesDirectories: ['node_modules'],
     },
     output: {
      filename: outputFileName,
      path: process.cwd() + "/" + destPath,
      publicPath: '/' + assetContext
     },
     entry: entryPoint,
     plugins: htmlWebpackPluginsFromPageDefs(pageDefs, watch),
  };

  if (watch) {
    devServerBundle(config, destPath);
    return;
  }
  plainBundle(config, callback);
}

//
// Private functions
// -----------------
//

/*
 * Create HtmlWebpack plugin entries
 * based on given page defs
 */
function htmlWebpackPluginsFromPageDefs(pageDefs, watch) {
    var arr;
    if (Array.isArray(pageDefs)) {
      arr = pageDefs;
    } else {
      return [];
    }

    return arr.map(item => {
        if (!item.path) {
          item.path = '';
        }
        var config = {
            template: 'node_modules/lucify-component-builder/src/www/embed.hbs',
            inject: false,
            filename: path.join(item.path, 'index.html'),
            devServer: watch
        };
        var fullConfig = extend(config, item);
        return new HtmlWebpackPlugin(fullConfig);
    });
}

/*
 * Start webpack dev server for given webpack configuration
 */
function devServerBundle(config, destPath) {
  config.output.publicPath = '/';

  // setup for automatic refresh
  // https://webpack.github.io/docs/webpack-dev-server.html#automatic-refresh
  config.entry = ['webpack-dev-server/client?http://localhost:3000', config.entry];
  var compiler = webpack(config);
  new WebpackDevServer(compiler, {
      contentBase: destPath,
      noInfo: true
  }).listen(3000, "localhost", function(err) {
        if(err) throw new gutil.PluginError("webpack-dev-server", err);
        // keep the server alive or continue?
        // callback();
  });
}

/*
 * Create distribution for given webpack configuration
 */
function plainBundle(config, callback) {
  webpack(config, function(err, stats) {
      if (err) {
        gutil.log("[webpack]", err);
        process.exit(1);
      }
      gutil.log("[webpack]", stats.toString({chunks: false}));
      callback();
  });
}

/*
 * Get the webpack loaders object for the webpack configuration
 */
function getLoaders() {
  return [
      {
        test: /\.(js|jsx)$/,
        loader: 'babel',
        //loaders: ['react-hot', 'babel-loader'],
        include: [
          process.cwd() + '/src',
          process.cwd() + '/temp',
          process.cwd() + '/node_modules/lucify-commons/src' // TODO
        ],
        query: {
          presets: [
            // https://github.com/babel/babel-loader/issues/166
            require.resolve('babel-preset-es2015'),
            require.resolve('babel-preset-stage-0'),
            require.resolve('babel-preset-react')
          ]
        }
      },
      {
        test: /\.css$/,
        loaders: [
          'style-loader',
          'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
          'postcss-loader'
        ]
      },
      {
        test: /\.svg$/,
        loader: 'url-loader?limit=10000&mimetype=image/svg+xml'
      },
      {
        test: /\.scss$/,
        loaders: ["style", "css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]", "sass"]
      },
      {
        test: /\.(jpeg|jpg|gif|png|json)$/,
        loaders: ["file-loader?name=[name]-[hash:12].[ext]"]
      },
      {
        test: /\.hbs$/,
        loader: "handlebars"
      }
  ];
}

module.exports = bundle;
<commit_msg>Add experimental support for hot module replacement with --hot option<commit_after>
var sprintf = require('sprintf');
var gutil = require('gulp-util');
var extend = require('object-extend');

var gulpWebpack = require('webpack-stream');
var WebpackDevServer = require("webpack-dev-server");
var HtmlWebpackPlugin = require('html-webpack-plugin')

var gulp = require('gulp');
var src  = gulp.src;
var dest = gulp.dest;

var webpack = require('webpack'),
path = require('path');

var parseArgs = require('minimist');

var options = parseArgs(process.argv, {default: {
  hot: false
}});

/*
 *  Bundle a component
 *
 *  entryPoint        - the entry point for the bundle
 *  outputFileName    - file name for produced bundle
 *  destPath          - destination path for produced bundle,
 *                      relative to the root directory of the project
 *                      being built
 *  pageDefs          - options to pass on into the template
 *                      for each index.html entry point to be
 *                      generated. must be either an array (or null,
 *                      if no index.html:s need to be generated)
 *  watch             - start webpack-dev-server with hot reloading
 *                      setting this true will cause this to block
 */
function bundle(entryPoint, outputFileName, destPath, pageDefs, watch, assetContext, callback) {

  var config = {
     resolve: {
       modulesDirectories: ['node_modules', 'components'],
       root: [path.resolve(__dirname,'../node_modules')]
     },
     module: {
       loaders: getLoaders()
     },
     resolveLoader: {
      root: [path.resolve(__dirname, '../node_modules')],
      modulesDirectories: ['node_modules'],
     },
     output: {
      filename: outputFileName,
      path: process.cwd() + "/" + destPath,
      publicPath: '/' + assetContext
     },
     entry: entryPoint,
     plugins: htmlWebpackPluginsFromPageDefs(pageDefs, watch),
  };

  if (watch) {
    devServerBundle(config, destPath);
    return;
  }
  plainBundle(config, callback);
}

//
// Private functions
// -----------------
//

/*
 * Create HtmlWebpack plugin entries
 * based on given page defs
 */
function htmlWebpackPluginsFromPageDefs(pageDefs, watch) {
    var arr;
    if (Array.isArray(pageDefs)) {
      arr = pageDefs;
    } else {
      return [];
    }

    return arr.map(item => {
        if (!item.path) {
          item.path = '';
        }
        var config = {
            template: 'node_modules/lucify-component-builder/src/www/embed.hbs',
            inject: false,
            filename: path.join(item.path, 'index.html'),
            devServer: watch // note: this is also needed for hot module replacement
        };
        var fullConfig = extend(config, item);
        return new HtmlWebpackPlugin(fullConfig);
    });
}

/*
 * Start webpack dev server for given webpack configuration
 */
function devServerBundle(config, destPath) {
  config.output.publicPath = '/';

  if (!options.hot) {
    // setup for automatic refresh
    // https://webpack.github.io/docs/webpack-dev-server.html#automatic-refresh
    config.entry = ['webpack-dev-server/client?http://localhost:3000', config.entry];

  } else {
    // Experimental setup for hot module replacement
    // enable via --hot option
    //
    // This seems to work correctly, but we get a message that
    // some modules could not be updated, as they would need a
    // full reload. I suspect this is related to the use of higher-order
    // components.
    //
    // https://medium.com/@dan_abramov/the-death-of-react-hot-loader-765fa791d7c4#.wrqoafdic
    // https://gaearon.github.io/react-hot-loader/getstarted/
    // https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
    config.entry = [config.entry, 'webpack/hot/only-dev-server'];
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
  }

  var compiler = webpack(config);
  new WebpackDevServer(compiler, {
      contentBase: destPath,
      noInfo: true
  }).listen(3000, "localhost", function(err) {
        if(err) throw new gutil.PluginError("webpack-dev-server", err);
        // keep the server alive or continue?
        // callback();
  });
}

/*
 * Create distribution for given webpack configuration
 */
function plainBundle(config, callback) {
  webpack(config, function(err, stats) {
      if (err) {
        gutil.log("[webpack]", err);
        process.exit(1);
      }
      gutil.log("[webpack]", stats.toString({chunks: false}));
      callback();
  });
}

/*
 * Get the webpack loaders object for the webpack configuration
 */
function getLoaders() {
  return [
      {
        test: /\.(js|jsx)$/,
        loader: 'babel',
        //loaders: ['react-hot', 'babel-loader'],
        include: [
          process.cwd() + '/src',
          process.cwd() + '/temp',
          process.cwd() + '/node_modules/lucify-commons/src' // TODO
        ],
        query: {
          presets: [
            // https://github.com/babel/babel-loader/issues/166
            require.resolve('babel-preset-es2015'),
            require.resolve('babel-preset-stage-0'),
            require.resolve('babel-preset-react')
          ]
        }
      },
      {
        test: /\.css$/,
        loaders: [
          'style-loader',
          'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
          'postcss-loader'
        ]
      },
      {
        test: /\.svg$/,
        loader: 'url-loader?limit=10000&mimetype=image/svg+xml'
      },
      {
        test: /\.scss$/,
        loaders: ["style", "css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]", "sass"]
      },
      {
        test: /\.(jpeg|jpg|gif|png|json)$/,
        loaders: ["file-loader?name=[name]-[hash:12].[ext]"]
      },
      {
        test: /\.hbs$/,
        loader: "handlebars"
      }
  ];
}

module.exports = bundle;
<|endoftext|>


<commit_before>const gutil         = require('gulp-util'),
  extend            = require('object-extend'),
  WebpackDevServer  = require('webpack-dev-server'),
  HtmlWebpackPlugin = require('html-webpack-plugin'),
  webpack           = require('webpack'),
  path              = require('path'),
  parseArgs         = require('minimist'),
  envs              = require('./envs.js'),
  autoprefixer      = require('autoprefixer'),
  postcssReporter   = require('postcss-reporter');
  getport           = require('getport');

var options = parseArgs(process.argv, {
  default: {
    hot: false
  }
});

/*
 *  Bundle a component
 *
 *  entryPoint        - the entry point for the bundle
 *  outputFileName    - file name for produced bundle
 *  destPath          - destination path for produced bundle,
 *                      relative to the root directory of the project
 *                      being built
 *  pageDefs          - options to pass on into the template
 *                      for each index.html entry point to be
 *                      generated. must be either an array (or null,
 *                      if no index.html:s need to be generated)
 *  watch             - start webpack-dev-server with hot reloading
 *                      setting this true will cause this to block
 *  babelPaths        - paths for which .js and .jsx files should be
 *                      run through babel-loader. this options is at
 *                      least needed for backwards compatiblity for some time
 */
function bundle(
  entryPoint,
  outputFileName,
  destPath,
  pageDefs,
  watch,
  assetContext,
  babelPaths,
  callback) {

  var config = {
    resolve: {
      modulesDirectories: ['node_modules']
    },
    module: {
      loaders: getLoaders(babelPaths)
    },
    resolveLoader: {
      root: [path.resolve(__dirname, '../node_modules')]
    },
    output: {
      filename: outputFileName,
      path: process.cwd() + '/' + destPath,
      publicPath: '/' + assetContext
    },
    postcss: function () {
      return [
        autoprefixer,
        postcssReporter
      ];
    },
    entry: entryPoint,
    plugins: htmlWebpackPluginsFromPageDefs(pageDefs, watch),
    // enabling watch here seems to fix a weird problem where
    // the bundle would seemingly randomly be built incorrectly
    // when using the webpack-dev-server, resulting in TypeError
    // for embed-decorator in the browser console
    watch: watch
  };

  if (!config.output.filename) {
    config.output.filename = 'index-[hash].js';
  }

  if(process.env.NODE_ENV === envs.STAGING || process.env.NODE_ENV === envs.PRODUCTION) {
    config.plugins = config.plugins.concat([
      new webpack.optimize.UglifyJsPlugin(),
      new webpack.optimize.DedupePlugin(),
      new webpack.optimize.OccurenceOrderPlugin()
    ]);
    //console.log(config.plugins);
  }

  if (watch) {
    devServerBundle(config, destPath);
    return;
  }
  plainBundle(config, callback);
}

//
// Private functions
// -----------------
//

/*
 * Create HtmlWebpack plugin entries
 * based on given page defs
 */
function htmlWebpackPluginsFromPageDefs(pageDefs, watch) {
  var arr;
  if (Array.isArray(pageDefs)) {
    arr = pageDefs;
  } else {
    return [];
  }

  return arr.map(item => {
    var itemPath = item.path;

    if (!itemPath) {
      itemPath = '';
    }

    if (itemPath.charAt(0) == '/') {
      itemPath = itemPath.substr(1);
    }

    var config = {
      template: require.resolve('../www/embed.hbs'),
      inject: false,
      filename: path.join(itemPath, 'index.html'),

      // this enables a script tag for automatic refresh
      // https://webpack.github.io/docs/webpack-dev-server.html#automatic-refresh
      devServer: watch
    };
    var fullConfig = extend(config, item);
    return new HtmlWebpackPlugin(fullConfig);
  });
}

/*
 * Start webpack dev server for given webpack configuration
 */
function devServerBundle(config, destPath) {
  const host = '0.0.0.0';
  config.output.publicPath = '/';
  config.devtool = 'source-map';

  if (options.hot) {
    // Experimental setup for hot module replacement
    // enable via --hot option
    //
    // This seems to work correctly, but we get a message that
    // some modules could not be updated, as they would need a
    // full reload. I suspect this is related to the use of higher-order
    // components.
    //
    // https://medium.com/@dan_abramov/the-death-of-react-hot-loader-765fa791d7c4#.wrqoafdic
    // https://gaearon.github.io/react-hot-loader/getstarted/
    // https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
    config.entry = [config.entry, 'webpack/hot/only-dev-server'];
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
  }

  const compiler = webpack(config);
  const server = new WebpackDevServer(compiler, {
    contentBase: destPath,
    noInfo: false,
    hot: options.hot,
    colors: true,
    quiet: false,
    stats: {
      colors: true,
      chunks: false,
      chunkOrigins: false,
      chunkModules: false,
      errorDetails: false,
      source: false,
      reasons: false,
      children: false,
      modules: false,
      version: false,
      hash: false,
      timings: false,
      assets: false
    }
  });

  getport(3000, 3999, function(err, port) {

    if (err) {
      throw new gutil.PluginError('webpack-dev-server', err);
    }

    server.listen(port, host, function(err) {
      // this is only run once after the
      // server has started
      //
      // it is unclear when err == true, at least
      // jsx syntax errors will not cause it
      // probably it may be caused by some configuration
      // problems
      //
      // ( webpack-dev-server is writing jsx syntax errors etc
      // to the console directly on its own )
      //
      if (err) {
        throw new gutil.PluginError('webpack-dev-server', err);
      }
      gutil.log(`[WebpackDevServer] listening on ${host}:${port}`);
      // keep the server alive or continue?
      // callback();
    });
  });
}

/*
 * Create distribution for given webpack configuration
 */
function plainBundle(config, callback) {
  webpack(config, function(err, stats) {
    if (err || stats.hasErrors()) {
      gutil.log('[webpack] ERROR');
      if (err) {
        gutil.log('[webpack]', err);
      } else {
        gutil.log('[webpack]', stats.toString({
          chunks: false,
          colors: true
        }));
      }
      // for some strange reason process.exit(1) will not
      // work, so using SIGTERM. also
      process.kill(process.pid, 'SIGTERM');
      callback(true);
    }
    gutil.log('[webpack]', stats.toString({
      chunks: false,
      colors: true
    }));
    callback();
  });
}

/*
 * Get the webpack loaders object for the webpack configuration
 */
function getLoaders(babelPaths) {

  if (!babelPaths) {
    babelPaths = [];
  }

  return [{
    test: /\.(js|jsx)$/,
    loader: require.resolve('babel-loader'),
    //loaders: ['react-hot', 'babel-loader'],
    include: [
      process.cwd() + '/src',
      process.cwd() + '/temp',
      __dirname
    ].concat(babelPaths),
    query: {
      presets: [
        // https://github.com/babel/babel-loader/issues/166
        require.resolve('babel-preset-es2015'),
        require.resolve('babel-preset-stage-0'),
        require.resolve('babel-preset-react')
      ]
    }
  }, {
    test: /\.svg$/,
    loader: require.resolve('url-loader') + '?limit=10000&mimetype=image/svg+xml'
  }, {
    test: /\.scss$/,
    loaders: [
      require.resolve('style-loader'),
      require.resolve('css-loader') + '?modules&importLoaders=2&localIdentName=[name]__[local]___[hash:base64:5]',
      require.resolve('postcss-loader'),
      require.resolve('sass-loader')
    ]
  }, {
    test: /\.less$/,
    loaders: [
      require.resolve('style-loader'),
      require.resolve('css-loader') + '?modules&importLoaders=2&localIdentName=[name]__[local]___[hash:base64:5]',
      require.resolve('postcss-loader'),
      require.resolve('less-loader')
    ]
  },
  {
    test: /\.(jpeg|jpg|gif|png)$/,
    loaders: [require.resolve('file-loader') + '?name=[name]-[hash:12].[ext]']
  }, {
    test: /\.hbs$/,
    loader: require.resolve('handlebars-loader')
  }];
}

module.exports = bundle;
<commit_msg>Fix linter error<commit_after>const gutil         = require('gulp-util'),
  extend            = require('object-extend'),
  WebpackDevServer  = require('webpack-dev-server'),
  HtmlWebpackPlugin = require('html-webpack-plugin'),
  webpack           = require('webpack'),
  path              = require('path'),
  parseArgs         = require('minimist'),
  envs              = require('./envs.js'),
  autoprefixer      = require('autoprefixer'),
  postcssReporter   = require('postcss-reporter'),
  getport           = require('getport');

var options = parseArgs(process.argv, {
  default: {
    hot: false
  }
});

/*
 *  Bundle a component
 *
 *  entryPoint        - the entry point for the bundle
 *  outputFileName    - file name for produced bundle
 *  destPath          - destination path for produced bundle,
 *                      relative to the root directory of the project
 *                      being built
 *  pageDefs          - options to pass on into the template
 *                      for each index.html entry point to be
 *                      generated. must be either an array (or null,
 *                      if no index.html:s need to be generated)
 *  watch             - start webpack-dev-server with hot reloading
 *                      setting this true will cause this to block
 *  babelPaths        - paths for which .js and .jsx files should be
 *                      run through babel-loader. this options is at
 *                      least needed for backwards compatiblity for some time
 */
function bundle(
  entryPoint,
  outputFileName,
  destPath,
  pageDefs,
  watch,
  assetContext,
  babelPaths,
  callback) {

  var config = {
    resolve: {
      modulesDirectories: ['node_modules']
    },
    module: {
      loaders: getLoaders(babelPaths)
    },
    resolveLoader: {
      root: [path.resolve(__dirname, '../node_modules')]
    },
    output: {
      filename: outputFileName,
      path: process.cwd() + '/' + destPath,
      publicPath: '/' + assetContext
    },
    postcss: function () {
      return [
        autoprefixer,
        postcssReporter
      ];
    },
    entry: entryPoint,
    plugins: htmlWebpackPluginsFromPageDefs(pageDefs, watch),
    // enabling watch here seems to fix a weird problem where
    // the bundle would seemingly randomly be built incorrectly
    // when using the webpack-dev-server, resulting in TypeError
    // for embed-decorator in the browser console
    watch: watch
  };

  if (!config.output.filename) {
    config.output.filename = 'index-[hash].js';
  }

  if(process.env.NODE_ENV === envs.STAGING || process.env.NODE_ENV === envs.PRODUCTION) {
    config.plugins = config.plugins.concat([
      new webpack.optimize.UglifyJsPlugin(),
      new webpack.optimize.DedupePlugin(),
      new webpack.optimize.OccurenceOrderPlugin()
    ]);
    //console.log(config.plugins);
  }

  if (watch) {
    devServerBundle(config, destPath);
    return;
  }
  plainBundle(config, callback);
}

//
// Private functions
// -----------------
//

/*
 * Create HtmlWebpack plugin entries
 * based on given page defs
 */
function htmlWebpackPluginsFromPageDefs(pageDefs, watch) {
  var arr;
  if (Array.isArray(pageDefs)) {
    arr = pageDefs;
  } else {
    return [];
  }

  return arr.map(item => {
    var itemPath = item.path;

    if (!itemPath) {
      itemPath = '';
    }

    if (itemPath.charAt(0) == '/') {
      itemPath = itemPath.substr(1);
    }

    var config = {
      template: require.resolve('../www/embed.hbs'),
      inject: false,
      filename: path.join(itemPath, 'index.html'),

      // this enables a script tag for automatic refresh
      // https://webpack.github.io/docs/webpack-dev-server.html#automatic-refresh
      devServer: watch
    };
    var fullConfig = extend(config, item);
    return new HtmlWebpackPlugin(fullConfig);
  });
}

/*
 * Start webpack dev server for given webpack configuration
 */
function devServerBundle(config, destPath) {
  const host = '0.0.0.0';
  config.output.publicPath = '/';
  config.devtool = 'source-map';

  if (options.hot) {
    // Experimental setup for hot module replacement
    // enable via --hot option
    //
    // This seems to work correctly, but we get a message that
    // some modules could not be updated, as they would need a
    // full reload. I suspect this is related to the use of higher-order
    // components.
    //
    // https://medium.com/@dan_abramov/the-death-of-react-hot-loader-765fa791d7c4#.wrqoafdic
    // https://gaearon.github.io/react-hot-loader/getstarted/
    // https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
    config.entry = [config.entry, 'webpack/hot/only-dev-server'];
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
  }

  const compiler = webpack(config);
  const server = new WebpackDevServer(compiler, {
    contentBase: destPath,
    noInfo: false,
    hot: options.hot,
    colors: true,
    quiet: false,
    stats: {
      colors: true,
      chunks: false,
      chunkOrigins: false,
      chunkModules: false,
      errorDetails: false,
      source: false,
      reasons: false,
      children: false,
      modules: false,
      version: false,
      hash: false,
      timings: false,
      assets: false
    }
  });

  getport(3000, 3999, function(err, port) {

    if (err) {
      throw new gutil.PluginError('webpack-dev-server', err);
    }

    server.listen(port, host, function(err) {
      // this is only run once after the
      // server has started
      //
      // it is unclear when err == true, at least
      // jsx syntax errors will not cause it
      // probably it may be caused by some configuration
      // problems
      //
      // ( webpack-dev-server is writing jsx syntax errors etc
      // to the console directly on its own )
      //
      if (err) {
        throw new gutil.PluginError('webpack-dev-server', err);
      }
      gutil.log(`[WebpackDevServer] listening on ${host}:${port}`);
      // keep the server alive or continue?
      // callback();
    });
  });
}

/*
 * Create distribution for given webpack configuration
 */
function plainBundle(config, callback) {
  webpack(config, function(err, stats) {
    if (err || stats.hasErrors()) {
      gutil.log('[webpack] ERROR');
      if (err) {
        gutil.log('[webpack]', err);
      } else {
        gutil.log('[webpack]', stats.toString({
          chunks: false,
          colors: true
        }));
      }
      // for some strange reason process.exit(1) will not
      // work, so using SIGTERM. also
      process.kill(process.pid, 'SIGTERM');
      callback(true);
    }
    gutil.log('[webpack]', stats.toString({
      chunks: false,
      colors: true
    }));
    callback();
  });
}

/*
 * Get the webpack loaders object for the webpack configuration
 */
function getLoaders(babelPaths) {

  if (!babelPaths) {
    babelPaths = [];
  }

  return [{
    test: /\.(js|jsx)$/,
    loader: require.resolve('babel-loader'),
    //loaders: ['react-hot', 'babel-loader'],
    include: [
      process.cwd() + '/src',
      process.cwd() + '/temp',
      __dirname
    ].concat(babelPaths),
    query: {
      presets: [
        // https://github.com/babel/babel-loader/issues/166
        require.resolve('babel-preset-es2015'),
        require.resolve('babel-preset-stage-0'),
        require.resolve('babel-preset-react')
      ]
    }
  }, {
    test: /\.svg$/,
    loader: require.resolve('url-loader') + '?limit=10000&mimetype=image/svg+xml'
  }, {
    test: /\.scss$/,
    loaders: [
      require.resolve('style-loader'),
      require.resolve('css-loader') + '?modules&importLoaders=2&localIdentName=[name]__[local]___[hash:base64:5]',
      require.resolve('postcss-loader'),
      require.resolve('sass-loader')
    ]
  }, {
    test: /\.less$/,
    loaders: [
      require.resolve('style-loader'),
      require.resolve('css-loader') + '?modules&importLoaders=2&localIdentName=[name]__[local]___[hash:base64:5]',
      require.resolve('postcss-loader'),
      require.resolve('less-loader')
    ]
  },
  {
    test: /\.(jpeg|jpg|gif|png)$/,
    loaders: [require.resolve('file-loader') + '?name=[name]-[hash:12].[ext]']
  }, {
    test: /\.hbs$/,
    loader: require.resolve('handlebars-loader')
  }];
}

module.exports = bundle;
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Oskar Homburg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/*global require, module, exports */
var
// Module
    parser = require('./parser'),
    executor = require('./executor'),
// Local
    registry = {};

executor.commandEngine = exports;

exports.env = null;

exports.executeString = function (cmdstring, env, ctx) {
    exports.env = env;
    var retval = executor.execute(parser.parse(cmdstring), ctx);
    exports.env = null;
    return retval;
};

exports.executeFunction = function (fn, args, env) {
    var ctx = {};
    if (Array.isArray(args)) {
        ctx['arguments'] = args;
        if (args.length > 0) {
            ctx['$'] = args[0];
            for (var i = 1; i < args.length; i++) {
                ctx[i] = args[i];
            }
        }
    } else {
        ctx = args;
    }
    var envRestore = exports.env;
    exports.env = env;
    var retval = executor.execute(fn.body, ctx);
    exports.env = envRestore;
    return retval;
};

exports.executeCommand = function (name, args) {
    var cmd = registry[name];
    if (!cmd) {
        throw "Command '" + name + "' could not be found.";
    }
    if (cmd.type === 'function') {
        return cmd.handler.call(null, args);
    } else if (cmd.type === 'cvar') {
        if (args.length > 0) {
            return cmd.setter(args[0]);
        } else {
            return cmd.getter();
        }
    } else {
        // How did you get here??
        debugger;
        throw "Unknown command type in command registry";
    }
};

exports.registerCommand = function (name, handler) {
    if (registry[name]) {
        // warn("Command '" + name + "' already exists. Overriding.");
    }
    registry[name] = {type: 'function', handler: handler};
};

exports.registerCvar = function (name, object, key) {
    exports.registerGetSet(name, function () {
        return object[key];
    }, function (value) {
        object[key] = value;
    })
};

exports.registerGetSet = function (name, getter, setter) {
    if (registry[name]) {
        // warn("Command '" + name + "' already exists. Overriding.");
    }
    if (setter === undefined) {
        setter = getter;
    }
    registry[name] = {type: 'cvar', getter: getter, setter: setter};
};

exports.registerAlias = function (name, fn) {
    exports.registerCommand(name, function (args) {
        exports.executeFunction(fn, args);
    });
};

exports.getCvar = function (name) {
    return registry[name].getter();
};

exports.setCvar = function (name, value) {
    registry[name].setter(value);
};

exports.isRegistered = function (name) {
    return registry.hasOwnProperty(name);
};

function toValueString(obj) {
    if (typeof obj === 'string') {
        return '"' + obj + '"';
    }
    if (typeof obj === 'number') {
        // toString might give us exponential form, which we dont support
        // toPrecision might too, but it's less likely.
        return obj.toPrecision(21);
    }
    if (typeof obj === 'undefined') {
        return "[undefined]";
    }
    if (obj === null) {
        return "[null]";
    }
    if (obj === true) {
        return '[true]';
    }
    if (obj === false) {
        return '[false]';
    }
    if (typeof obj === 'object') {
        if (Array.isArray(obj)) {
            return '[array ' + obj.map(toValueString).join(" ") + ']';
        }
        // Resort to json command
        return '[json "' + JSON.stringify(obj) + '"]';
    }
    // This should not happen, so debug if it does
    debugger;
    throw 'Unknown type';
}

exports.getCfgString = function () {
    var str = "";
    for (var k in registry) {
        if (registry.hasOwnProperty(k) && registry[k].type == 'cvar') {
            str += k + " " + toValueString(registry[k].getter()) + ";";
        }
    }
    return str;
};

// Low level access
exports.unregister = function (name) {
    delete registry[name];
};

exports.getBinding = function (name) {
    return registry[name];
};

exports.getRegistry = function () {
    return registry;
};
<commit_msg>Register cvars with corresponding types<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Oskar Homburg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/*global require, module, exports */
var
// Module
    parser = require('./parser'),
    executor = require('./executor'),
// Local
    registry = {};

executor.commandEngine = exports;

exports.env = null;

exports.executeString = function (cmdstring, env, ctx) {
    exports.env = env;
    var retval = executor.execute(parser.parse(cmdstring), ctx);
    exports.env = null;
    return retval;
};

exports.executeFunction = function (fn, args, env) {
    var ctx = {};
    if (Array.isArray(args)) {
        ctx['arguments'] = args;
        if (args.length > 0) {
            ctx['$'] = args[0];
            for (var i = 1; i < args.length; i++) {
                ctx[i] = args[i];
            }
        }
    } else {
        ctx = args;
    }
    var envRestore = exports.env;
    exports.env = env;
    var retval = executor.execute(fn.body, ctx);
    exports.env = envRestore;
    return retval;
};

exports.executeCommand = function (name, args) {
    var cmd = registry[name];
    if (!cmd) {
        throw "Command '" + name + "' could not be found.";
    }
    if (cmd.type === 'function') {
        return cmd.handler.call(null, args);
    } else if (cmd.type === 'cvar') {
        if (args.length > 0) {
            return cmd.setter(args[0]);
        } else {
            return cmd.getter();
        }
    } else {
        // How did you get here??
        debugger;
        throw "Unknown command type in command registry";
    }
};

exports.registerCommand = function (name, handler) {
    if (registry[name]) {
        // warn("Command '" + name + "' already exists. Overriding.");
    }
    registry[name] = {type: 'function', handler: handler};
};

var typesRegistry = {};

exports.registerCvar = function (name, object, key, types) {
    exports.registerGetSet(name, function () {
        return object[key];
    }, function (value) {
        object[key] = value;
    }, types);
};

exports.registerGetSet = function (name, getter, setter, types) {
    if (registry[name]) {
        // warn("Command '" + name + "' already exists. Overriding.");
    }
    if (setter === undefined) {
        setter = getter;
    }
    var setterWrap = types ? function (value) {
        for (var i = 0; i < types.length; i++) {
            if (!typesRegistry[types[i]].call(null, name, value)) {
                return;
            }
        }
        setter(value);
    } : setter;

    registry[name] = {type: 'cvar', getter: getter, setter: setterWrap, cvartypes: types};
};

exports.registerCvarType = function (name, handler) {
    typesRegistry[name] = handler;
};

exports.registerAlias = function (name, fn) {
    exports.registerCommand(name, function (args) {
        exports.executeFunction(fn, args);
    });
};

exports.getCvar = function (name) {
    return registry[name].getter();
};

exports.setCvar = function (name, value) {
    registry[name].setter(value);
};

exports.isRegistered = function (name) {
    return registry.hasOwnProperty(name);
};

function toValueString(obj) {
    if (typeof obj === 'string') {
        return '"' + obj + '"';
    }
    if (typeof obj === 'number') {
        // toString might give us exponential form, which we dont support
        // toPrecision might too, but it's less likely.
        return obj.toPrecision(21);
    }
    if (typeof obj === 'undefined') {
        return "[undefined]";
    }
    if (obj === null) {
        return "[null]";
    }
    if (obj === true) {
        return '[true]';
    }
    if (obj === false) {
        return '[false]';
    }
    if (typeof obj === 'object') {
        if (Array.isArray(obj)) {
            return '[array ' + obj.map(toValueString).join(" ") + ']';
        }
        // Resort to json command
        return '[json "' + JSON.stringify(obj) + '"]';
    }
    // This should not happen, so debug if it does
    debugger;
    throw 'Unknown type';
}

exports.getCfgString = function () {
    var str = "";
    for (var k in registry) {
        if (registry.hasOwnProperty(k) && registry[k].type == 'cvar') {
            str += k + " " + toValueString(registry[k].getter()) + ";";
        }
    }
    return str;
};

// Low level access
exports.unregister = function (name) {
    delete registry[name];
};

exports.getBinding = function (name) {
    return registry[name];
};

exports.getRegistry = function () {
    return registry;
};
<|endoftext|>


<commit_before>'use strict';

Darwinator.Enemy = function(game, target, x, y, health, strength, agility, intellect) {
  if (strength > intellect && strength > agility) {
    this.category = 'enemy_strength';
  } else if (agility > intellect && agility > strength) {
    this.category = 'enemy_agility';
  } else if (intellect > strength && intellect > agility) {
    this.category = 'enemy_intellect';
  } else {
    this.category = 'enemy';
  }
  Darwinator.Entity.call(this, game, x, y, this.category,/* [],*/ health, strength, agility, intellect);

  this.scale.setTo(0.25,0.25);
  this.target = target;
  this.path   = [];
  this.debug  = true;

  //Allow enemy to overlap objects, i.e. reduce the hitbox
  //this.body.setRectangle(20*4, 16*4, 0, 16*4);
  this.lastPathUpdate = 0;

  // score properties to measure success
  this.damageDone = 0;

  // melee cooldown
  this.lastMeleeTimestamp = 0;
  this.cooldownMs         = 250;
};

Darwinator.Enemy.prototype = Object.create(Darwinator.Entity.prototype);

Darwinator.Enemy.prototype.arm = function(weapon) {
  this.weapon = weapon;
};

Darwinator.Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  this.body.velocity.setTo(0,0);
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  var pathLength = this.path.length;
  if(!(pathLength && this.path[pathLength - 1].x === targetTile.x &&
                     this.path[pathLength - 1].y === targetTile.y &&
                     this.path[0].x === currTile.x &&
                     this.path[0].y === currTile.y)) {
    if (Darwinator.Helpers.calculateDistance(targetTile, currTile) * 5 < this.lastPathUpdate) {
      this.updatePath();
    } else {
      this.lastPathUpdate++;
    }
  }

  switch(this.category) {
  case 'enemy_intellect':
    if (this.path.length && Darwinator.Helpers.calculateDistance(targetTile, currTile) > 10) {
      this.followPath();
    } else {
      this.weapon.fire(this.target.body.x, this.target.body.y);
    }
    break;
  default:
    /* If a path exists - follow it. Else, try to move in the general direction of the player, ignoring
       obsticles*/
    if (this.path.length) {
      this.followPath();
      if (Math.random() > 0.99) {
        this.weapon.fire(this.target.body.x, this.target.body.y);
      }
    } else {
      this.game.physics.arcade.moveToXY(this, this.target.body.x, this.target.body.y, this.speed);
    }
    break;
  }
  // Target (ie. player) takes damage while the target and enemy overlap.
  // If they continuously overlap the target will take damage every 0.25 seconds
  this.game.physics.arcade.overlap(this, this.target, this.meleeAttack, null, this);

  if (this.health <= 0 && this.alive){
    console.log('%c Enemy killed by player! ', 'background: black; color: orange');
    this.kill();
  }

};

Darwinator.Enemy.prototype.meleeAttack = function(){ //callback for overlapping with target
  var onCooldown = (Date.now() - this.lastMeleeTimestamp) < this.cooldownMs;
  if (!onCooldown){
    var crit  = Math.random() - this.criticalStrike;
    var dmg   = this.damage;

    if (crit < 0){
      dmg *= 2;
      console.log('%c Enemy made a critical hit! ', 'background: red; color: white');
    }
    this.target.takeDamage(dmg);
    this.damageDone += dmg;
    this.lastMeleeTimestamp = Date.now();
  }
};

Darwinator.Enemy.prototype.updatePath = function() {
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  Darwinator.Pathfinder.findPath(currTile.x, currTile.y, targetTile.x, targetTile.y,
                                    function(path){ this.path = !!path ? path : [];}.bind(this) );
  Darwinator.Pathfinder.calculate();
  this.lastPathUpdate = 0;
};

Darwinator.Enemy.prototype.followPath = function() {
  var targetPos = Darwinator.Helpers.tileToPixels(this.path[1].x, this.path[1].y);
  targetPos.x   = Math.round(targetPos.x - this.body.width / 2);
  targetPos.y   = Math.round(targetPos.y - this.body.height / 2);

  var distance = Darwinator.Helpers.calculateDistance(targetPos, [this.body.x, this.body.y]);
  if (distance < 4 && this.path.length > 2) {      // Trial and error - modify if need be.
    // Remember, include (x,y,health) in reset, otherwise health will = 1.
    this.reset(targetPos.x, targetPos.y, this.health);
    this.path.splice(0,1); // Remove first tile in path.
    this.updatePath();
  } else {
    this.game.physics.arcade.moveToXY(this, targetPos.x, targetPos.y, this.speed);
  }
  if (this.path.length < 5 && this.currBreath > 1) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }
};
<commit_msg>added enemy category wrapper object<commit_after>'use strict';

Darwinator.Enemy = function(game, target, x, y, health, strength, agility, intellect) {
  if (strength > intellect && strength > agility) {
    this.category = this.categories.STRONG;
  } else if (agility > intellect && agility > strength) {
    this.category = this.categories.AGILE;
  } else if (intellect > strength && intellect > agility) {
    this.category = this.categories.INTELLIGENT;
  } else {
    this.category = this.categories.DEFAULT;
  }
  Darwinator.Entity.call(this, game, x, y, this.category,/* [],*/ health, strength, agility, intellect);

  this.scale.setTo(0.25,0.25);
  this.target = target;
  this.path   = [];
  this.debug  = true;

  //Allow enemy to overlap objects, i.e. reduce the hitbox
  //this.body.setRectangle(20*4, 16*4, 0, 16*4);
  this.lastPathUpdate = 0;

  // score properties to measure success
  this.damageDone = 0;

  // melee cooldown
  this.lastMeleeTimestamp = 0;
  this.cooldownMs         = 250;
};

Darwinator.Enemy.prototype = Object.create(Darwinator.Entity.prototype);

// for convenience, categories are represented by image names
Darwinator.Enemy.prototype.categories = {
  INTELLIGENT:  'enemy_intellect',
  AGILE:        'enemy_agility',
  STRONG:       'enemy_strength',
  DEFAULT:      'enemy'
};

Darwinator.Enemy.prototype.arm = function(weapon) {
  this.weapon = weapon;
};

Darwinator.Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  this.body.velocity.setTo(0,0);
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  var pathLength = this.path.length;
  if(!(pathLength && this.path[pathLength - 1].x === targetTile.x &&
                     this.path[pathLength - 1].y === targetTile.y &&
                     this.path[0].x === currTile.x &&
                     this.path[0].y === currTile.y)) {
    if (Darwinator.Helpers.calculateDistance(targetTile, currTile) * 5 < this.lastPathUpdate) {
      this.updatePath();
    } else {
      this.lastPathUpdate++;
    }
  }

  switch(this.category) {
  case this.categories.INTELLIGENT:
    if (this.path.length && Darwinator.Helpers.calculateDistance(targetTile, currTile) > 10) {
      this.followPath();
    } else {
      this.weapon.fire(this.target.body.x, this.target.body.y);
    }
    break;
  default:
    /* If a path exists - follow it. Else, try to move in the general direction of the player, ignoring
       obsticles*/
    if (this.path.length) {
      this.followPath();
      if (Math.random() > 0.99) {
        this.weapon.fire(this.target.body.x, this.target.body.y);
      }
    } else {
      this.game.physics.arcade.moveToXY(this, this.target.body.x, this.target.body.y, this.speed);
    }
    break;
  }
  // Target (ie. player) takes damage while the target and enemy overlap.
  // If they continuously overlap the target will take damage every 0.25 seconds
  this.game.physics.arcade.overlap(this, this.target, this.meleeAttack, null, this);

  if (this.health <= 0 && this.alive){
    console.log('%c Enemy killed by player! ', 'background: black; color: orange');
    this.kill();
  }

};

Darwinator.Enemy.prototype.meleeAttack = function(){ //callback for overlapping with target
  var onCooldown = (Date.now() - this.lastMeleeTimestamp) < this.cooldownMs;
  if (!onCooldown){
    var crit  = Math.random() - this.criticalStrike;
    var dmg   = this.damage;

    if (crit < 0){
      dmg *= 2;
      console.log('%c Enemy made a critical hit! ', 'background: red; color: white');
    }
    this.target.takeDamage(dmg);
    this.damageDone += dmg;
    this.lastMeleeTimestamp = Date.now();
  }
};

Darwinator.Enemy.prototype.updatePath = function() {
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  Darwinator.Pathfinder.findPath(currTile.x, currTile.y, targetTile.x, targetTile.y,
                                    function(path){ this.path = !!path ? path : [];}.bind(this) );
  Darwinator.Pathfinder.calculate();
  this.lastPathUpdate = 0;
};

Darwinator.Enemy.prototype.followPath = function() {
  var targetPos = Darwinator.Helpers.tileToPixels(this.path[1].x, this.path[1].y);
  targetPos.x   = Math.round(targetPos.x - this.body.width / 2);
  targetPos.y   = Math.round(targetPos.y - this.body.height / 2);

  var distance = Darwinator.Helpers.calculateDistance(targetPos, [this.body.x, this.body.y]);
  if (distance < 4 && this.path.length > 2) {      // Trial and error - modify if need be.
    // Remember, include (x,y,health) in reset, otherwise health will = 1.
    this.reset(targetPos.x, targetPos.y, this.health);
    this.path.splice(0,1); // Remove first tile in path.
    this.updatePath();
  } else {
    this.game.physics.arcade.moveToXY(this, targetPos.x, targetPos.y, this.speed);
  }
  if (this.path.length < 5 && this.currBreath > 1) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }
};
<|endoftext|>


<commit_before>
/* Inverted index class */
class InvertedIndex {
  /**
   * @constructor
   */
  constructor() {
    this.indexMap = {};
  }

  /**
   * createIndex
   * create an index map of an array of object
   * @param {Object} jsonContent an array of objects
   * @return {Object}
   */
  createIndex(jsonContent) {
    if (!(this.isValidJson(jsonContent))) {
      return "invaid json";
    }
    jsonContent.forEach((currentDocument, docId) => {
      let wordsInDocument = `${currentDocument.title} ${currentDocument.text}`;
      let currentDocumentTokens = this.getCleanTokens(wordsInDocument);
      this.indexBot(currentDocumentTokens, docId)

    });
  }

  /**
   * getIndex
   * @return {Object} indexMap
   */
  getIndex() {
    return this.indexMap;
  }

  /**
   * searchIndex
   * @param {String} terms string of terms to search for
   * @return {Object} An array of the search terms index
   */
  searchIndex(terms) {
    let searchTermTokens = this.getCleanTokens(terms);
    let foundInDocuments = [];

    searchTermTokens.forEach((word) => {
      if (this.search(word)) {
        foundInDocuments.push(this.search(word));
      }
    })
    return foundInDocuments;
  }

  /* Helper methods */

  /**
   * isValidJson
   * Check if all items in an array is a JSON valid.
   * @param {Object} jsonArray Array of JSON objects
   * @return {Boolean}
   */
  isValidJson(jsonArray) {
    if (typeof jsonArray !== "object" || jsonArray.length === 0) {
      return false
    }

    jsonArray.forEach((currentBook) => {
      if (!(currentBook.hasOwnProperty("title") && currentBook.hasOwnProperty("text"))) {
        return false;
      }
    });
    return true;
  }

  /**
   * getCleanTokens
   * sanitizes and splits a string
   * @param {String} string string to sanitize and tokenize
   * @return {Object} An array of clean splitted string
   */
  getCleanTokens(string) {
    let invalidCharaters = /[^a-z0-9\s]/gi;
    return string.replace(invalidCharaters, " ")
      .toLowerCase()
      .split(" ")
      .filter((word) => {
        return Boolean(word);
      });
  }

  /**
   * indexBot
   * maps an arrays their it docs index
   * @param {Object}  tokens An array of words
   */
  indexBot(tokens, docId) {
    tokens.forEach((token) => {
      if (token in this.indexMap) {
        if (this.indexMap[token].indexOf(docId) === -1) {
          this.indexMap[token].push(docId);
        }
      } else {
        this.indexMap[token] = [docId];
      }
    });
  }

  /**
   * search
   * returns an array containing document id in which a search term exist
   * @param {String} searchTerm A single search term string
   * @return {Object} An array containing index of a search term
   */
  search(searchTerm) {
    let indexDatabase = this.indexMap;

    if (indexDatabase.hasOwnProperty(searchTerm)) {
      return indexDatabase[searchTerm];
    } else {
      return false;
    }
  }
}
<commit_msg>update InvertedIndex class to handle bad json<commit_after>
/* Inverted index class */
class InvertedIndex {
  /**
   * @constructor
   */
  constructor() {
    this.indexMap = {};
  }

  /**
   * createIndex
   * create an index map of an array of object
   * @param {Object} jsonContent an array of objects
   * @return {Object}
   */
  createIndex(jsonContent) {
    if (!(this.isValidJson(jsonContent))) {
      return "invaid json";
    }
    jsonContent.forEach((currentDocument, docId) => {
      let wordsInDocument = `${currentDocument.title} ${currentDocument.text}`;
      let currentDocumentTokens = this.getCleanTokens(wordsInDocument);
      this.indexBot(currentDocumentTokens, docId)

    });
  }

  /**
   * getIndex
   * @return {Object} indexMap
   */
  getIndex() {
    return this.indexMap;
  }

  /**
   * searchIndex
   * @param {String} terms string of terms to search for
   * @return {Object} An array of the search terms index
   */
  searchIndex(terms) {
    let searchTermTokens = this.getCleanTokens(terms);
    let foundInDocuments = [];

    searchTermTokens.forEach((word) => {
      if (this.search(word)) {
        foundInDocuments.push(this.search(word));
      }
    })
    return foundInDocuments;
  }

  /* Helper methods */

  /**
   * isValidJson
   * Check if all items in an array is a JSON valid.
   * @param {Object} jsonArray Array of JSON objects
   * @return {Boolean}
   */
  isValidJson(jsonArray) {
    if (typeof jsonArray !== "object" || jsonArray.length === 0) {
      return false
    }

    try {

      jsonArray.forEach((currentBook) => {
        if (!(currentBook.hasOwnProperty("title") && currentBook.hasOwnProperty("text"))) {
          return false;
        }
      });
      return true;

    }
    catch (err) {
      return false
    }

  }

  /**
   * getCleanTokens
   * sanitizes and splits a string
   * @param {String} string string to sanitize and tokenize
   * @return {Object} An array of clean splitted string
   */
  getCleanTokens(string) {
    let invalidCharaters = /[^a-z0-9\s]/gi;
    return string.replace(invalidCharaters, " ")
      .toLowerCase()
      .split(" ")
      .filter((word) => {
        return Boolean(word);
      });
  }

  /**
   * indexBot
   * maps an arrays their it docs index
   * @param {Object}  tokens An array of words
   */
  indexBot(tokens, docId) {
    tokens.forEach((token) => {
      if (token in this.indexMap) {
        if (this.indexMap[token].indexOf(docId) === -1) {
          this.indexMap[token].push(docId);
        }
      } else {
        this.indexMap[token] = [docId];
      }
    });
  }

  /**
   * search
   * returns an array containing document id in which a search term exist
   * @param {String} searchTerm A single search term string
   * @return {Object} An array containing index of a search term
   */
  search(searchTerm) {
    let indexDatabase = this.indexMap;

    if (indexDatabase.hasOwnProperty(searchTerm)) {
      return indexDatabase[searchTerm];
    } else {
      return false;
    }
  }
}
<|endoftext|>


<commit_before>var ajaxPromise = (function(){
  'use strict';
  var req =  new XMLHttpRequest();

  return function(method, url, data, contentType) {
    var req =  new XMLHttpRequest();
    var data = JSON.stringify(data) || null

    contentType = contentType || "application/json";

    if(!url) throw "url is required!"
    else req.open(method.toUpperCase(),url,true)
    req.setRequestHeader("Content-Type", contentType);
    req.send(data);

    return new Promise(function(resolve, reject)  {
      req.addEventListener("load",success,false);
      function success() {
        if(req.status < 300 && req.readyState === 4){
          return resolve(JSON.parse(req.response))
        }
        else if(req.status >= 300){
          return reject({ response : req.statusText, status : req.status})
        }
      }
    });
  }
})();
<commit_msg>TypeError added and same duplicate code remove<commit_after>var ajaxPromise = (function(){
  'use strict';

   var req =  new XMLHttpRequest();

  return function ajaxparams(method, url, data, contentType) {

    var data = JSON.stringify(data) || null;

    contentType = contentType || "application/json";

    if(!url) return function () {throw new TypeError("url is required!");};
    else req.open(method.toUpperCase(),url,true);
    req.setRequestHeader("Content-Type", contentType);
    req.send(data);

    return new Promise(function(resolve, reject)  {
      req.addEventListener("load",success,false);
      function success() {
        if(req.status < 300 && req.readyState === 4){
          return resolve(JSON.parse(req.response));
        }
        else if(req.status >= 300){
          return reject({ response : req.statusText, status : req.status});
        }
      }
    });
  };
})();
<|endoftext|>


<commit_before>/* Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) 2010 Mr.doob, rhyolight, bebraw
 */
function brushes() {
    this.init();
}
brushes.prototype = {
    brushOptions: {'size': {'min': 1, 'max': 30, 'value': 1},
        'opacity': {'min': 0, 'max': 100, 'value': 50},
        'location': {}},
    init: function () {
        this.selected = BRUSHES[0];
    },
    initUI: function () {
        setUpPod("Brushes");

        $('#brushesPod').click(function() {
            $(this).css('visibility', 'hidden'); //hide();
            $('#brushesCheckbox').attr('checked', false);
            $('#brushesPanel').dialog('open');
        });

        // set up brushes panel
        panelWidth = 150;
        $("body").append(
            '
 \

 \

 \

'
        );

        $("#brushesPanel").dialog({
            closeOnEscape: false, resizable: true,
            width: panelWidth, minWidth: panelWidth, maxWidth: 360,
            height: 370, minHeight: 410,
            autoOpen: false
        });

        $("#brushesPanel").dialog("option", "position", "left");
        $("#brushesPanel").bind("dialogclose", function(event, ui) {
            $("#brushesPod").css("visibility", "visible");}
        );

        for (var brushOptionName in this.brushOptions) {
            brushOptionValue = this.brushOptions[brushOptionName];

            $("#brushOptions").append('
');

            optionTitle = capitalizeFirstLetter(brushOptionName);
            $("#brushOptions").append('
' + optionTitle + ':
');

            optionId = 'brush' + brushOptionName;
            if(brushOptionValue) {
                $("#brushOptions").append('

');

                $("#" + optionId).slider({
                    range: "max",
                    min: brushOptionValue.min,
                    max: brushOptionValue.max,
                    value: brushOptionValue.value,
                    slide: function(event, ui) {
                        // XXX: hack! figure out a nicer way to pass option name!
                        option = $(this).attr('id').replace('brush', '');
                        panels['brushes'].brushOptions[option].value = ui.value;
                        panels['brushes'].renderBrushPreviews();
                    }
                });
            }

            $("#brushOptions").append('

');

            jitterToggleId = optionId + 'JitterToggle';
            jitterAmountId = optionId + 'JitterAmount';
            $("#brushOptions").append(
                '
 \
                    <input type="checkbox" style="float:left;" id="' +
                        jitterToggleId + '" />Jitter \

 \

 \

 \
                '
            );

            this.brushOptions[brushOptionName]['jitter'] = {'enabled': false,
                'value': 0};

            $("#brushOptions").append('
');

            this.setUpJitter(jitterToggleId, jitterAmountId);
        }

        this.createBrushes(panelWidth - 45);
    },
    destroy: function () {},
    setUpJitter: function ( jitterToggleId, jitterAmountId ) {
        $("#" + jitterToggleId).button().toggle(
            function () {
                $(this).parent('.jitter').children('div').css('visibility',
                    'visible');
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterToggle', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.enabled = true;
                panels['brushes'].renderBrushPreviews();
            },
            function () {
                $(this).parent('.jitter').children('div').css('visibility',
                    'hidden');
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterToggle', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.enabled = false;
                panels['brushes'].renderBrushPreviews();
            }
        );

        $("#" + jitterAmountId).slider({
            range: "max",
            min: 0,
            max: 100,
            value: 0,
            slide: function(event, ui) {
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterAmount', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.value = ui.value;
                panels['brushes'].renderBrushPreviews();
            }
        }).css('visibility', 'hidden');
    },
    createBrushes: function ( brushWidth ) {
        for (i = 0; i < BRUSHES.length; i++) {
            brushName = BRUSHES[i];
            brushId = brushName; //XXX: use + 'Brush'; to avoid id clashes!

            $("#brushes").append('<canvas class="brush" id="' + brushId + '"' +
                ' style="height:4em;width:' + brushWidth + 'px"' +  '></canvas>');
            //$('.brush:first').css('border', '2px solid red');

            $('#' + brushId).click(function() {
                panels['brushes'].selected = $(this).attr('id');

                // XXX: render bg as gray instead?
                //$('.brush').css('border', '');
                //$(this).css('border', '2px solid red');
                // XXX: render border in the canvas itself!
            });
        }

        this.renderBrushPreviews();
    },
    renderBrushPreviews: function () {
        for (i = 0; i < BRUSHES.length; i++) {
            brushName = BRUSHES[i];
            brushId = brushName; //XXX: use + 'Brush'; to avoid id clashes!

            this.renderBrushPreview(brushId);
        }
    },
    renderBrushPreview: function (brushId) {
        brushCanvas = new ProxyCanvas(brushId);
        brushCanvas.fill('white');

        brush = eval("new " + brushName + "()");

        brushPainter = new Painter(brushCanvas, brush,
            panels['palette'].getColor());

        canvasWidth = brushCanvas.width;
        pad = 10;

        for (x = pad; x < canvasWidth - pad; x+=10) {
            y = Math.sin((x - pad) / (canvasWidth - pad) * 2 * Math.PI) *
                (brushCanvas.height / 2 - pad * 2) + (brushCanvas.height / 2);

            point = new Point(x, y);
            point = this.applyJitter(point);
            brushPainter.paint(point, this.getSize(), this.getOpacity(),
                "source-over");
        }

        brushCanvas.text(brushId, 'black', '48px Segoe UI, Arial, sans-serif', 10,
        brushCanvas.height / 2);
    },
    applyJitter: function ( point ) {
        if(this.brushOptions.location.jitter.enabled) {
            direction = 2 * Math.PI * Math.random();
            distance = this.brushOptions.location.jitter.value * Math.random();

            xOffset = Math.sin(direction) * distance;
            yOffset = Math.cos(direction) * distance;

            return new Point(point.x + xOffset, point.y + yOffset);
        }

        return point;
    },
    getSelected: function () {
        return eval("new " + this.selected + '()');
    },
    getSize: function () {
        return this.getValueTemplate('size');
    },
    getOpacity: function () {
        return this.getValueTemplate('opacity');
    },
    getValueTemplate: function (valueName) {
        value = this.brushOptions[valueName].value;

        if(this.brushOptions[valueName].jitter.enabled) {
            jitterValue = this.brushOptions[valueName].jitter.value / 100;
            valueMax = this.brushOptions[valueName].max;
            valueMin = this.brushOptions[valueName].min;
            randomValue = Math.random() * ((value - valueMin) / (valueMax - valueMin));
            jitterNeg = value * jitterValue * randomValue;

            return value - jitterNeg;
        }

        return value;
    },
    getMode: function () {
        return "source-over"; // TODO: implement mode selector!
    }
}
<commit_msg>Made brush selector indicate selected brush in a nicer way.<commit_after>/* Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) 2010 Mr.doob, rhyolight, bebraw
 */
function brushes() {
    this.init();
}
brushes.prototype = {
    brushOptions: {'size': {'min': 1, 'max': 30, 'value': 1},
        'opacity': {'min': 0, 'max': 100, 'value': 50},
        'location': {}},
    init: function () {
        this.selected = BRUSHES[0];
    },
    initUI: function () {
        setUpPod("Brushes");

        $('#brushesPod').click(function() {
            $(this).css('visibility', 'hidden'); //hide();
            $('#brushesCheckbox').attr('checked', false);
            $('#brushesPanel').dialog('open');
        });

        // set up brushes panel
        panelWidth = 150;
        $("body").append(
            '
 \

 \

 \

'
        );

        $("#brushesPanel").dialog({
            closeOnEscape: false, resizable: true,
            width: panelWidth, minWidth: panelWidth, maxWidth: 360,
            height: 370, minHeight: 410,
            autoOpen: false
        });

        $("#brushesPanel").dialog("option", "position", "left");
        $("#brushesPanel").bind("dialogclose", function(event, ui) {
            $("#brushesPod").css("visibility", "visible");}
        );

        for (var brushOptionName in this.brushOptions) {
            brushOptionValue = this.brushOptions[brushOptionName];

            $("#brushOptions").append('
');

            optionTitle = capitalizeFirstLetter(brushOptionName);
            $("#brushOptions").append('
' + optionTitle + ':
');

            optionId = 'brush' + brushOptionName;
            if(brushOptionValue) {
                $("#brushOptions").append('

');

                $("#" + optionId).slider({
                    range: "max",
                    min: brushOptionValue.min,
                    max: brushOptionValue.max,
                    value: brushOptionValue.value,
                    slide: function(event, ui) {
                        // XXX: hack! figure out a nicer way to pass option name!
                        option = $(this).attr('id').replace('brush', '');
                        panels['brushes'].brushOptions[option].value = ui.value;
                        panels['brushes'].renderBrushPreviews();
                    }
                });
            }

            $("#brushOptions").append('

');

            jitterToggleId = optionId + 'JitterToggle';
            jitterAmountId = optionId + 'JitterAmount';
            $("#brushOptions").append(
                '
 \
                    <input type="checkbox" style="float:left;" id="' +
                        jitterToggleId + '" />Jitter \

 \

 \

 \
                '
            );

            this.brushOptions[brushOptionName]['jitter'] = {'enabled': false,
                'value': 0};

            $("#brushOptions").append('
');

            this.setUpJitter(jitterToggleId, jitterAmountId);
        }

        this.createBrushes(panelWidth - 45);
    },
    destroy: function () {},
    setUpJitter: function ( jitterToggleId, jitterAmountId ) {
        $("#" + jitterToggleId).button().toggle(
            function () {
                $(this).parent('.jitter').children('div').css('visibility',
                    'visible');
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterToggle', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.enabled = true;
                panels['brushes'].renderBrushPreviews();
            },
            function () {
                $(this).parent('.jitter').children('div').css('visibility',
                    'hidden');
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterToggle', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.enabled = false;
                panels['brushes'].renderBrushPreviews();
            }
        );

        $("#" + jitterAmountId).slider({
            range: "max",
            min: 0,
            max: 100,
            value: 0,
            slide: function(event, ui) {
                // XXX: hack! figure out a nicer way to pass option name!
                option = $(this).attr('id').replace('JitterAmount', '').replace('brush', '');
                panels['brushes'].brushOptions[option].jitter.value = ui.value;
                panels['brushes'].renderBrushPreviews();
            }
        }).css('visibility', 'hidden');
    },
    createBrushes: function ( brushWidth ) {
        for (i = 0; i < BRUSHES.length; i++) {
            brushName = BRUSHES[i];
            brushId = brushName; //XXX: use + 'Brush'; to avoid id clashes!
            brushHeight = 40;

            $("#brushes").append('<canvas class="brush" id="' + brushId + '"' +
                ' style="height:' + brushHeight + 'px;width:' + brushWidth +
                'px"' +  '></canvas>');
            $('.brush:first').css('border', '1px dashed');

            $('#' + brushId).click(function() {
                panels['brushes'].selected = $(this).attr('id');

                // XXX: render bg as gray instead?
                $('.brush').css('border', '');
                $(this).css('border', '1px dashed');
                // XXX: render border in the canvas itself!
            });
        }

        this.renderBrushPreviews();
    },
    renderBrushPreviews: function () {
        for (i = 0; i < BRUSHES.length; i++) {
            brushName = BRUSHES[i];
            brushId = brushName; //XXX: use + 'Brush'; to avoid id clashes!

            this.renderBrushPreview(brushId);
        }
    },
    renderBrushPreview: function (brushId) {
        brushCanvas = new ProxyCanvas(brushId);
        brushCanvas.fill('white');

        brush = eval("new " + brushName + "()");

        brushPainter = new Painter(brushCanvas, brush,
            panels['palette'].getColor());

        canvasWidth = brushCanvas.width;
        pad = 10;

        for (x = pad; x < canvasWidth - pad; x+=10) {
            y = Math.sin((x - pad) / (canvasWidth - pad) * 2 * Math.PI) *
                (brushCanvas.height / 2 - pad * 2) + (brushCanvas.height / 2);

            point = new Point(x, y);
            point = this.applyJitter(point);
            brushPainter.paint(point, this.getSize(), this.getOpacity(),
                "source-over");
        }

        brushCanvas.text(brushId, 'black', '48px Segoe UI, Arial, sans-serif', 10,
        brushCanvas.height / 2);
    },
    applyJitter: function ( point ) {
        if(this.brushOptions.location.jitter.enabled) {
            direction = 2 * Math.PI * Math.random();
            distance = this.brushOptions.location.jitter.value * Math.random();

            xOffset = Math.sin(direction) * distance;
            yOffset = Math.cos(direction) * distance;

            return new Point(point.x + xOffset, point.y + yOffset);
        }

        return point;
    },
    getSelected: function () {
        return eval("new " + this.selected + '()');
    },
    getSize: function () {
        return this.getValueTemplate('size');
    },
    getOpacity: function () {
        return this.getValueTemplate('opacity');
    },
    getValueTemplate: function (valueName) {
        value = this.brushOptions[valueName].value;

        if(this.brushOptions[valueName].jitter.enabled) {
            jitterValue = this.brushOptions[valueName].jitter.value / 100;
            valueMax = this.brushOptions[valueName].max;
            valueMin = this.brushOptions[valueName].min;
            randomValue = Math.random() * ((value - valueMin) / (valueMax - valueMin));
            jitterNeg = value * jitterValue * randomValue;

            return value - jitterNeg;
        }

        return value;
    },
    getMode: function () {
        return "source-over"; // TODO: implement mode selector!
    }
}
<|endoftext|>


<commit_before>function get(url, parameters, options) {

    options = typeof options !== "undefined" ? options : {withCredentials: false};
    return new Promise(function(resolve, reject) {

        let xhr = new XMLHttpRequest();
        xhr.withCredentials = options.withCredentials;

        xhr.addEventListener("readystatechange", function() {
            if (xhr.status == 200) {
                // Resolve the promise with the response text
                resolve(JSON.parse(xhr.responseText));
            }
        });

        xhr.addEventListener("error", function() {
            reject(Error(xhr));
        });

        xhr.open("GET", `${url}?${serialize(parameters)}`);
        xhr.send();
    });

}

function post(url, data, options) {

    options = typeof options !== "undefined" ? options : {withCredentials: false};

    return new Promise(function(resolve, reject) {

        let xhr = new XMLHttpRequest();
        xhr.withCredentials = options.withCredentials;

        xhr.addEventListener("readystatechange", function() {
            if (xhr.status == 200) {
                // Resolve the promise with the response text
                resolve(JSON.parse(xhr.responseText));
            }
        });

        xhr.addEventListener("error", function() {
            reject(Error(xhr));
        });

        xhr.open("POST", url);
        xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
        xhr.send(serialize(data));
    });

}

function serialize(obj, prefix) {
    let str = [];
    for (let p in obj) {
        if (obj.hasOwnProperty(p)) {
            let k = prefix ? prefix + "[" + p + "]" : p;
            let v = obj[p];
            str.push(typeof v == "object" ?
                serialize(v, k) :
                encodeURIComponent(k) + "=" + encodeURIComponent(v));
        }
    }
    return str.join("&");
}

export let Request = {
    get: get,
    post: post
};

export default Request;
<commit_msg>fixed browser console errors JSON.parse() was being called on an incomplete response. Waiting for the readyState to indicate DONE fixes this.<commit_after>function get(url, parameters, options) {

    options = typeof options !== "undefined" ? options : {withCredentials: false};
    return new Promise(function(resolve, reject) {

        let xhr = new XMLHttpRequest();
        xhr.withCredentials = options.withCredentials;

        xhr.addEventListener("readystatechange", function() {
            if (xhr.readyState === 4 && xhr.status == 200) {
                // Resolve the promise with the response text.
                resolve(JSON.parse(xhr.responseText));
            }
        });

        xhr.addEventListener("error", function() {
            reject(Error(xhr));
        });

        xhr.open("GET", `${url}?${serialize(parameters)}`);
        xhr.send();
    });

}

function post(url, data, options) {

    options = typeof options !== "undefined" ? options : {withCredentials: false};

    return new Promise(function(resolve, reject) {

        let xhr = new XMLHttpRequest();
        // xhr.responseType = "json";
        xhr.withCredentials = options.withCredentials;

        xhr.addEventListener("readystatechange", function() {
            if (xhr.readyState === 4 && xhr.status == 200) {
                // Resolve the promise with the response text.
                resolve(JSON.parse(xhr.responseText));
            }
        });

        xhr.addEventListener("error", function() {
            reject(Error(xhr));
        });

        xhr.open("POST", url);
        xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
        xhr.send(serialize(data));
    });

}

function serialize(obj, prefix) {
    let str = [];
    for (let p in obj) {
        if (obj.hasOwnProperty(p)) {
            let k = prefix ? prefix + "[" + p + "]" : p;
            let v = obj[p];
            str.push(typeof v == "object" ?
                serialize(v, k) :
                encodeURIComponent(k) + "=" + encodeURIComponent(v));
        }
    }
    return str.join("&");
}

export let Request = {
    get: get,
    post: post
};

export default Request;
<|endoftext|>


<commit_before>// @flow

import R from 'ramda';
import { createTypes as reduxSauceCreateTypes } from 'reduxsauce';

import warn from './warn';

import type {
  dict, thunk,
  CreateOfflineActionsConfigs,
  CreateOfflineActionsOptions,
} from './types';

// matches on capital letters (except at the start & end of the string)
const RX_CAPS = /(?!^)([A-Z])/g;

// converts a camelCaseWord into a SCREAMING_SNAKE_CASE word
const camelToScreamingSnake: string => string = R.pipe(
  R.replace(RX_CAPS, '_$1'),
  R.toUpper,
);

// noop
const noop = () => ({});
const omitActionMeta = R.omit(['action', 'meta']);

function createTypes(config, options): { [string]: string } {
  /* eslint no-underscore-dangle: ["off"] */

  const typesCreator = R.curry(reduxSauceCreateTypes)(R.__, options);

  return R.pipe(
    R.keys,                       // just the keys
    R.map(camelToScreamingSnake), // CONVERT_THEM
    R.join(' '),                  // space separated
    typesCreator,                // make them into Redux Types
  )(config);
}

function transformAction(action: thunk | dict): { action: thunk, meta?: Array<string|dict> } {
  if (typeof action === 'object') {
    return {
      action: noop,
      ...action,
    };
  } else if (typeof action === 'function') {
    return { action };
  }
  return { action: noop };
}

function getMeta(metaArray: ?Array<string|dict> = [], context: dict) : dict {
  return R.pipe(
    R.converge(R.concat, [
      R.pipe(
        R.filter(v => typeof v === 'string'),
        R.map(v => ({ [v]: context[v] })),
      ),
      R.filter(v => typeof v === 'object'),
    ]),
    R.mergeAll,
  )(metaArray);
}

function addPayloadToEffectBody(effect, payload) : ?dict {
  if (typeof effect === 'object') {
    const { body, ...rest } = effect;

    return {
      effect: {
        ...rest,
        body: {
          ...body,
          ...payload,
        },
      },
    };
  }
  return undefined;
}

function createActionCreator({
  offline,
  commit,
  rollback,
  effect,
  meta,
 }, options) {
  /* eslint no-unused-vars: ["error",
    { "varsIgnorePattern": "type", "argsIgnorePattern": "options" }]*/

  if (typeof offline !== 'function' && typeof offline !== 'object') {
    throw new Error('[redux-offline-sauce] config.offline must be either a function or object');
  }

  const offlineObj = transformAction(offline);
  const commitObj = transformAction(commit);
  const rollbackObj = transformAction(rollback);

  const offlineRest = omitActionMeta(offlineObj);
  const commitRest = omitActionMeta(commitObj);
  const rollbackRest = omitActionMeta(rollbackObj);

  return (...args) => {
    const offlineAction = offlineObj.action(...args);
    const commitAction = commitObj.action(...args);
    const rollbackAction = rollbackObj.action(...args);
    const { type: offlineType, ...payload } = offlineAction;

    return {
      type: offlineType,
      ...payload,
      payload,
      ...offlineRest,
      meta: {
        offline: {
          ...addPayloadToEffectBody(effect, payload),
          commit: {
            ...commitAction,
            ...commitRest,
            meta: getMeta(commitObj.meta, payload),
          },
          rollback: {
            ...rollbackAction,
            ...rollbackRest,
            meta: getMeta(rollbackObj.meta, payload),
          },
        },
        ...meta,
      },
    };
  };
}

function createCreators(config, options) {
  return R.pipe(
    R.map(value => createActionCreator(value, options)),
  )(config);
}

export default (config: ?CreateOfflineActionsConfigs, options: CreateOfflineActionsOptions) => {
  if (R.isNil(config)) {
    throw new Error('an object is required to setup types and creators');
  }

  if (R.isEmpty(config)) {
    warn('empty object passed in for createOfflineActions', options);
    return {
      Types: {},
      Creators: {},
      merge: (Types: dict, Creators: dict): { Types: dict, Creators: dict } =>
        ({ Types, Creators }),
    };
  }

  const Types = createTypes(config, options);
  const Creators = createCreators(config, options);

  return {
    Types,
    Creators,
    merge: (mTypes: dict, mCreators: dict): { Types: dict, Creators: dict } => ({
      Types: R.merge(mTypes, Types),
      Creators: R.merge(mCreators, Creators),
    }),
  };
};
<commit_msg>fix lint<commit_after>// @flow

import R from 'ramda';
import { createTypes as reduxSauceCreateTypes } from 'reduxsauce';

import warn from './warn';

import type {
  dict, thunk,
  CreateOfflineActionsConfigs,
  CreateOfflineActionsOptions,
} from './types';

// matches on capital letters (except at the start & end of the string)
const RX_CAPS = /(?!^)([A-Z])/g;

// converts a camelCaseWord into a SCREAMING_SNAKE_CASE word
const camelToScreamingSnake: string => string = R.pipe(
  R.replace(RX_CAPS, '_$1'),
  R.toUpper,
);

// noop
const noop = () => ({});
const omitActionMeta = R.omit(['action', 'meta']);

function createTypes(config, options): { [string]: string } {
  /* eslint no-underscore-dangle: ["off"] */

  const typesCreator = R.curry(reduxSauceCreateTypes)(R.__, options);

  return R.pipe(
    R.keys,                       // just the keys
    R.map(camelToScreamingSnake), // CONVERT_THEM
    R.join(' '),                  // space separated
    typesCreator,                // make them into Redux Types
  )(config);
}

function transformAction(action: thunk | dict): { action: thunk, meta?: Array<string | dict> } {
  if (typeof action === 'object') {
    return {
      action: noop,
      ...action,
    };
  } else if (typeof action === 'function') {
    return { action };
  }
  return { action: noop };
}

function getMeta(metaArray: ?Array<string | dict> = [], context: dict): dict {
  return R.pipe(
    R.converge(R.concat, [
      R.pipe(
        R.filter(v => typeof v === 'string'),
        R.map(v => ({ [v]: context[v] })),
      ),
      R.filter(v => typeof v === 'object'),
    ]),
    R.mergeAll,
  )(metaArray);
}

function addPayloadToEffectBody(effect, payload): ?dict {
  if (typeof effect === 'object') {
    const { body, ...rest } = effect;

    return {
      effect: {
        ...rest,
        body: {
          ...body,
          ...payload,
        },
      },
    };
  }
  return undefined;
}

function createActionCreator({
  offline,
  commit,
  rollback,
  effect,
  meta,
 }, options) {
  /* eslint no-unused-vars: ["error",
    { "varsIgnorePattern": "type", "argsIgnorePattern": "options" }]*/

  if (typeof offline !== 'function' && typeof offline !== 'object') {
    throw new Error('[redux-offline-sauce] config.offline must be either a function or object');
  }

  const offlineObj = transformAction(offline);
  const commitObj = transformAction(commit);
  const rollbackObj = transformAction(rollback);

  const offlineRest = omitActionMeta(offlineObj);
  const commitRest = omitActionMeta(commitObj);
  const rollbackRest = omitActionMeta(rollbackObj);

  return (...args) => {
    const offlineAction = offlineObj.action(...args);
    const commitAction = commitObj.action(...args);
    const rollbackAction = rollbackObj.action(...args);
    const { type: offlineType, ...payload } = offlineAction;

    return {
      type: offlineType,
      ...payload,
      payload,
      ...offlineRest,
      meta: {
        offline: {
          ...addPayloadToEffectBody(effect, payload),
          commit: {
            ...commitAction,
            ...commitRest,
            meta: getMeta(commitObj.meta, payload),
          },
          rollback: {
            ...rollbackAction,
            ...rollbackRest,
            meta: getMeta(rollbackObj.meta, payload),
          },
        },
        ...meta,
      },
    };
  };
}

function createCreators(config, options) {
  return R.pipe(
    R.map(value => createActionCreator(value, options)),
  )(config);
}

export default (config: ?CreateOfflineActionsConfigs, options: CreateOfflineActionsOptions) => {
  if (R.isNil(config)) {
    throw new Error('an object is required to setup types and creators');
  }

  if (R.isEmpty(config)) {
    warn('empty object passed in for createOfflineActions', options);
    return {
      Types: {},
      Creators: {},
      merge: (Types: dict, Creators: dict): { Types: dict, Creators: dict } =>
        ({ Types, Creators }),
    };
  }

  const Types = createTypes(config, options);
  const Creators = createCreators(config, options);

  return {
    Types,
    Creators,
    merge: (mTypes: dict, mCreators: dict): { Types: dict, Creators: dict } => ({
      Types: R.merge(mTypes, Types),
      Creators: R.merge(mCreators, Creators),
    }),
  };
};
<|endoftext|>