<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

// An Algorithm for Automatically Fitting Digitized Curves
// by Philip J. Schneider
// from "Graphics Gems", Academic Press, 1990
var PathFitter = Base.extend({
	initialize: function(path, error) {
		//console.log(path.segments + '');
		this.maxIterations = 4;
		this.points = [];
		var segments = path._segments,
			prev;
		for (var i = 0, l = segments.length; i < l; i++) {
			var point = segments[i].point.clone();
			if (!prev || !prev.equals(point)) {
				this.points[i] = point;
				prev = point;
			}
		}
		this.error = error;
		this.iterationError = error * error;
	},

	fit: function() {
		this.segments = [new Segment(this.points[0])];
		this.fitCubic(0, this.points.length - 1,
				// Left Tangent
				this.points[1].subtract(this.points[0]).normalize(),
				// Right Tangent
				this.points[this.points.length - 2].subtract(
					this.points[this.points.length - 1]).normalize());
		return this.segments;
	},

	// Fit a Bezier curve to a (sub)set of digitized points
	fitCubic: function(first, last, tHat1, tHat2) {
		//	Use heuristic if region only has two points in it
		if (last - first == 1) {
			var pt1 = this.points[first],
				pt2 = this.points[last],
				dist = pt1.getDistance(pt2) / 3;
			this.addCurve([pt1, pt1.add(tHat1.normalize(dist)),
					pt2.add(tHat2.normalize(dist)), pt2]);
			return;
		}
		// Parameterize points, and attempt to fit curve
		var uPrime = this.chordLengthParameterize(first, last),
			prevMaxError = this.iterationError,
			error,
			split;
		for (var i = 0; i < this.maxIterations; i++) {
			var bezCurve = this.generateBezier(first, last, uPrime, tHat1, tHat2);
			//	Find max deviation of points to fitted curve
			var max = this.findMaxError(first, last, bezCurve, uPrime);
			if (max.error < this.error) {
				this.addCurve(bezCurve);
				return;
			}
			split = max.index;
			// If error not too large, try some reparameterization and iteration
			if (max.error >= this.iterationError || max.error >= prevMaxError)
				break;
			uPrime = this.reparameterize(first, last, uPrime, bezCurve);
			prevMaxError = max.error;
		}
		// Fitting failed -- split at max error point and fit recursively
		var V1 = this.points[split - 1].subtract(this.points[split]),
			V2 = this.points[split].subtract(this.points[split + 1]),
			tHatCenter = V1.add(V2).divide(2).normalize();
		this.fitCubic(first, split, tHat1, tHatCenter);
		this.fitCubic(split, last, tHatCenter.negate(), tHat2);
	},

	addCurve: function(bezCurve) {
		var prev = this.segments[this.segments.length - 1];
		prev.setHandleOut(bezCurve[1].subtract(bezCurve[0]));
		this.segments.push(
				new Segment(bezCurve[3], bezCurve[2].subtract(bezCurve[3])));
	},

	// Use least-squares method to find Bezier control points for region.
	generateBezier: function(first, last, uPrime, tHat1, tHat2) {
		var nPts = last - first + 1,
			pt1 = this.points[first],
			pt2 = this.points[last];

		var A = [];
		// Compute the A's
		for (var i = 0; i < nPts; i++) {
			var u = uPrime[i],
				t = 1 - u,
				b = 3 * u * t;
			A[i] = [
				tHat1.normalize(b * t), // b1
				tHat2.normalize(b * u)  // b2
			];
		}

		// Create the C and X matrices
		var C = [[0, 0], [0, 0]],
			X = [0, 0];

		for (var i = 0; i < nPts; i++) {
			C[0][0] += A[i][0].dot(A[i][0]);
			C[0][1] += A[i][0].dot(A[i][1]);
			// C[1][0] += A[i][0].dot(A[i][1]);
			C[1][0] = C[0][1];
			C[1][1] += A[i][1].dot(A[i][1]);

			var u = uPrime[i],
				t = 1 - u,
				b = 3 * u * t,
				tmp = this.points[first + i]
					.subtract(pt1.multiply(t * t * t) // b0
						.add(pt1.multiply(b * t)) // b1
						.add(pt2.multiply(b * u)) // b2
						.add(pt2.multiply(u * u * u))); // b3
			X[0] += A[i][0].dot(tmp);
			X[1] += A[i][1].dot(tmp);
		}

		// Compute the determinants of C and X
		var det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
			det_C0_X  = C[0][0] * X[1]    - C[1][0] * X[0],
			det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1];

		// Finally, derive alpha values
		var alpha_l = (det_C0_C1 == 0) ? 0.0 : det_X_C1 / det_C0_C1,
			alpha_r = (det_C0_C1 == 0) ? 0.0 : det_C0_X / det_C0_C1;

		// If alpha negative, use the Wu/Barsky heuristic (see text)
		// (if alpha is 0, you get coincident control points that lead to
		// divide by zero in any subsequent NewtonRaphsonRootFind() call.
		var segLength = pt2.getDistance(pt1),
			epsilon = Numerical.TOLERANCE * segLength;
		if (alpha_l < epsilon || alpha_r < epsilon) {
			// fall back on standard (probably inaccurate) formula,
			// and subdivide further if needed.
			alpha_l = alpha_r = segLength / 3;
		}

		// First and last control points of the Bezier curve are
		// positioned exactly at the first and last data points
		// Control points 1 and 2 are positioned an alpha distance out
		// on the tangent vectors, left and right, respectively
		return [pt1, pt1.add(tHat1.normalize(alpha_l)),
				pt2.add(tHat2.normalize(alpha_r)), pt2];
	},

	// Given set of points and their parameterization, try to find
	// a better parameterization.
	reparameterize: function(first, last, u, bezCurve) {
		var uPrime = [];
		for (var i = first; i <= last; i++) {
			uPrime[i - first] = this.findRoot(bezCurve, this.points[i],
					u[i - first]);
		}
		return uPrime;
	},

	// Use Newton-Raphson iteration to find better root.
	findRoot: function(Q, P, u) {
		var Q1 = [],
			Q2 = [];
		// Generate control vertices for Q'
		for (var i = 0; i <= 2; i++) {
			Q1[i] = Q[i + 1].subtract(Q[i]).multiply(3);
		}
		// Generate control vertices for Q''
		for (var i = 0; i <= 1; i++) {
			Q2[i] = Q1[i + 1].subtract(Q1[i]).multiply(2);
		}
		// Compute Q(u), Q'(u) and Q''(u)
		Q_u = this.evaluate(3, Q, u);
		Q1_u = this.evaluate(2, Q1, u);
		Q2_u = this.evaluate(1, Q2, u);
		// Compute f(u)/f'(u)
		var V = Q_u.subtract(P),
			df = Q1_u.dot(Q1_u) + V.dot(Q2_u);
		if (df == 0)
			return u;
		// u = u - f(u) / f'(u)
		return u - V.dot(Q1_u) / df;
	},

	// Evaluate a Bezier curve at a particular parameter value
	evaluate: function(degree, V, t) {
		// Copy array
		var Vtemp = V.slice();
		// Triangle computation
		for (var i = 1; i <= degree; i++) {
			for (var j = 0; j <= degree - i; j++) {
				Vtemp[j] = Vtemp[j].multiply(1 - t).add(Vtemp[j + 1].multiply(t));
			}
		}
		return Vtemp[0];
	},

	// Assign parameter values to digitized points
	// using relative distances between points.
	chordLengthParameterize: function(first, last) {
		var u = [0];
		for (var i = first + 1; i <= last; i++) {
			u[i - first] = u[i - first - 1]
					+ this.points[i].getDistance(this.points[i - 1]);
		}
		for (var i = first + 1; i <= last; i++) {
			u[i - first] = u[i - first] / u[last - first];
		}
		return u;
	},

	// Find the maximum squared distance of digitized points
	// to fitted curve.
	findMaxError: function(first, last, bezCurve, u) {
		var index = Math.floor((last - first + 1) / 2),
			maxDist = 0;
		for (var i = first + 1; i < last; i++) {
			var P = this.evaluate(3, bezCurve, u[i - first]);
			var v = P.subtract(this.points[i]);
			var dist = v.x * v.x + v.y * v.y; // squared
			if (dist >= maxDist) {
				maxDist = dist;
				index = i;
			}
		}
		return {
			error: maxDist,
			index: index
		};
	}
});
<commit_msg>Check for determinant singularity and handle separately.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

// An Algorithm for Automatically Fitting Digitized Curves
// by Philip J. Schneider
// from "Graphics Gems", Academic Press, 1990
var PathFitter = Base.extend({
	initialize: function(path, error) {
		//console.log(path.segments + '');
		this.maxIterations = 4;
		this.points = [];
		var segments = path._segments,
			prev;
		for (var i = 0, l = segments.length; i < l; i++) {
			var point = segments[i].point.clone();
			if (!prev || !prev.equals(point)) {
				this.points[i] = point;
				prev = point;
			}
		}
		this.error = error;
		this.iterationError = error * error;
	},

	fit: function() {
		this.segments = [new Segment(this.points[0])];
		this.fitCubic(0, this.points.length - 1,
				// Left Tangent
				this.points[1].subtract(this.points[0]).normalize(),
				// Right Tangent
				this.points[this.points.length - 2].subtract(
					this.points[this.points.length - 1]).normalize());
		return this.segments;
	},

	// Fit a Bezier curve to a (sub)set of digitized points
	fitCubic: function(first, last, tHat1, tHat2) {
		//	Use heuristic if region only has two points in it
		if (last - first == 1) {
			var pt1 = this.points[first],
				pt2 = this.points[last],
				dist = pt1.getDistance(pt2) / 3;
			this.addCurve([pt1, pt1.add(tHat1.normalize(dist)),
					pt2.add(tHat2.normalize(dist)), pt2]);
			return;
		}
		// Parameterize points, and attempt to fit curve
		var uPrime = this.chordLengthParameterize(first, last),
			prevMaxError = this.iterationError,
			error,
			split;
		for (var i = 0; i < this.maxIterations; i++) {
			var bezCurve = this.generateBezier(first, last, uPrime, tHat1, tHat2);
			//	Find max deviation of points to fitted curve
			var max = this.findMaxError(first, last, bezCurve, uPrime);
			if (max.error < this.error) {
				this.addCurve(bezCurve);
				return;
			}
			split = max.index;
			// If error not too large, try some reparameterization and iteration
			if (max.error >= this.iterationError || max.error >= prevMaxError)
				break;
			uPrime = this.reparameterize(first, last, uPrime, bezCurve);
			prevMaxError = max.error;
		}
		// Fitting failed -- split at max error point and fit recursively
		var V1 = this.points[split - 1].subtract(this.points[split]),
			V2 = this.points[split].subtract(this.points[split + 1]),
			tHatCenter = V1.add(V2).divide(2).normalize();
		this.fitCubic(first, split, tHat1, tHatCenter);
		this.fitCubic(split, last, tHatCenter.negate(), tHat2);
	},

	addCurve: function(bezCurve) {
		var prev = this.segments[this.segments.length - 1];
		prev.setHandleOut(bezCurve[1].subtract(bezCurve[0]));
		this.segments.push(
				new Segment(bezCurve[3], bezCurve[2].subtract(bezCurve[3])));
	},

	// Use least-squares method to find Bezier control points for region.
	generateBezier: function(first, last, uPrime, tHat1, tHat2) {
		var nPts = last - first + 1,
			pt1 = this.points[first],
			pt2 = this.points[last];

		var A = [];
		// Compute the A's
		for (var i = 0; i < nPts; i++) {
			var u = uPrime[i],
				t = 1 - u,
				b = 3 * u * t;
			A[i] = [
				tHat1.normalize(b * t), // b1
				tHat2.normalize(b * u)  // b2
			];
		}

		// Create the C and X matrices
		var C = [[0, 0], [0, 0]],
			X = [0, 0];

		for (var i = 0; i < nPts; i++) {
			C[0][0] += A[i][0].dot(A[i][0]);
			C[0][1] += A[i][0].dot(A[i][1]);
			// C[1][0] += A[i][0].dot(A[i][1]);
			C[1][0] = C[0][1];
			C[1][1] += A[i][1].dot(A[i][1]);

			var u = uPrime[i],
				t = 1 - u,
				b = 3 * u * t,
				tmp = this.points[first + i]
					.subtract(pt1.multiply(t * t * t) // b0
						.add(pt1.multiply(b * t)) // b1
						.add(pt2.multiply(b * u)) // b2
						.add(pt2.multiply(u * u * u))); // b3
			X[0] += A[i][0].dot(tmp);
			X[1] += A[i][1].dot(tmp);
		}

		// Compute the determinants of C and X
		var det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
			det_C0_X  = C[0][0] * X[1]    - C[1][0] * X[0],
			det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1],
			singularity = det_C0_C1 < Numerical.TOLERANCE;

		// Finally, derive alpha values
		var alpha_l = singularity ? 0 : det_X_C1 / det_C0_C1,
			alpha_r = singularity ? 0 : det_C0_X / det_C0_C1;

		// If alpha negative, use the Wu/Barsky heuristic (see text)
		// (if alpha is 0, you get coincident control points that lead to
		// divide by zero in any subsequent NewtonRaphsonRootFind() call.
		var segLength = pt2.getDistance(pt1),
			epsilon = Numerical.TOLERANCE * segLength;
		if (alpha_l < epsilon || alpha_r < epsilon) {
			// fall back on standard (probably inaccurate) formula,
			// and subdivide further if needed.
			alpha_l = alpha_r = segLength / 3;
		}

		// First and last control points of the Bezier curve are
		// positioned exactly at the first and last data points
		// Control points 1 and 2 are positioned an alpha distance out
		// on the tangent vectors, left and right, respectively
		return [pt1, pt1.add(tHat1.normalize(alpha_l)),
				pt2.add(tHat2.normalize(alpha_r)), pt2];
	},

	// Given set of points and their parameterization, try to find
	// a better parameterization.
	reparameterize: function(first, last, u, bezCurve) {
		var uPrime = [];
		for (var i = first; i <= last; i++) {
			uPrime[i - first] = this.findRoot(bezCurve, this.points[i],
					u[i - first]);
		}
		return uPrime;
	},

	// Use Newton-Raphson iteration to find better root.
	findRoot: function(Q, P, u) {
		var Q1 = [],
			Q2 = [];
		// Generate control vertices for Q'
		for (var i = 0; i <= 2; i++) {
			Q1[i] = Q[i + 1].subtract(Q[i]).multiply(3);
		}
		// Generate control vertices for Q''
		for (var i = 0; i <= 1; i++) {
			Q2[i] = Q1[i + 1].subtract(Q1[i]).multiply(2);
		}
		// Compute Q(u), Q'(u) and Q''(u)
		Q_u = this.evaluate(3, Q, u);
		Q1_u = this.evaluate(2, Q1, u);
		Q2_u = this.evaluate(1, Q2, u);
		// Compute f(u)/f'(u)
		var V = Q_u.subtract(P),
			df = Q1_u.dot(Q1_u) + V.dot(Q2_u);
		if (df == 0)
			return u;
		// u = u - f(u) / f'(u)
		return u - V.dot(Q1_u) / df;
	},

	// Evaluate a Bezier curve at a particular parameter value
	evaluate: function(degree, V, t) {
		// Copy array
		var Vtemp = V.slice();
		// Triangle computation
		for (var i = 1; i <= degree; i++) {
			for (var j = 0; j <= degree - i; j++) {
				Vtemp[j] = Vtemp[j].multiply(1 - t).add(Vtemp[j + 1].multiply(t));
			}
		}
		return Vtemp[0];
	},

	// Assign parameter values to digitized points
	// using relative distances between points.
	chordLengthParameterize: function(first, last) {
		var u = [0];
		for (var i = first + 1; i <= last; i++) {
			u[i - first] = u[i - first - 1]
					+ this.points[i].getDistance(this.points[i - 1]);
		}
		for (var i = first + 1; i <= last; i++) {
			u[i - first] = u[i - first] / u[last - first];
		}
		return u;
	},

	// Find the maximum squared distance of digitized points
	// to fitted curve.
	findMaxError: function(first, last, bezCurve, u) {
		var index = Math.floor((last - first + 1) / 2),
			maxDist = 0;
		for (var i = first + 1; i < last; i++) {
			var P = this.evaluate(3, bezCurve, u[i - first]);
			var v = P.subtract(this.points[i]);
			var dist = v.x * v.x + v.y * v.y; // squared
			if (dist >= maxDist) {
				maxDist = dist;
				index = i;
			}
		}
		return {
			error: maxDist,
			index: index
		};
	}
});
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import { bool, object } from 'prop-types'
import R from 'ramda'

import Modal from '../../../components/Modal/Modal'
import Button from '../../../components/Button/Button'
import JSONeditor from '../../../components/JSONeditor/JSONeditor'

import adjustToJSONeditor from '../../../helpers/adjustToJSONeditor'

const propTypes = {
  message: object.isRequired,
  show: bool.isRequired
}

const defaultProps = {
  message: {},
  show: false
}

class JSONmodal extends PureComponent {
  constructor (props) {
    super(props)

    this.state = {
      show: false
    }
  }

  componentWillReceiveProps (nextProps) {
    console.warn('compoennt did mount', nextProps)
    this.setState({
      show: nextProps.show
    })
  }

  componentWillUnmount () {
    console.error('componentWillUnmount()')
  }

  handleClose = () => this.setState({
    show: false
  })

  handleCopyToClipBoard = () => {
    const textarea = R.compose(
      setValue(this.props.message),
      createElement
    )('textarea')

    executeCopy(textarea)
  }

  render () {
    return (
      <Modal
        show={this.state.show}
        message={
          <JSONeditor
            value={adjustToJSONeditor(this.props.message)}
            readOnly
          />
        }
        closeModal={this.handleClose}
        title={'Preview'}
        buttons={[
          <Button
            key='cancel'
            mod='default'
            onClick={this.handleClose}
          >
            Close
          </Button>,
          <Button
            key='ok'
            mod='primary'
            onClick={this.handleCopyToClipBoard}
          >
            Copy to clipboard
          </Button>,
          <Button
            key='ok'
            mod='github'
            onClick={this.handleClose}
          >
            Download
          </Button>
        ]}
      />
    )
  }
}

JSONmodal.propTypes = propTypes
JSONmodal.defaultProps = defaultProps

export default JSONmodal

function executeCopy (textarea) {
  document.body.appendChild(textarea)
  textarea.select()
  document.execCommand('Copy')
  document.body.removeChild(textarea)
}

function setValue (value) {
  return (element) => {
    element.value = JSON.stringify(value)

    return element
  }
}

function createElement (name) {
  return document.createElement(name)
}
<commit_msg>Close after copy<commit_after>import React, { PureComponent } from 'react'
import { bool, object } from 'prop-types'
import R from 'ramda'

import Modal from '../../../components/Modal/Modal'
import Button from '../../../components/Button/Button'
import JSONeditor from '../../../components/JSONeditor/JSONeditor'

import adjustToJSONeditor from '../../../helpers/adjustToJSONeditor'

const propTypes = {
  message: object.isRequired,
  show: bool.isRequired
}

const defaultProps = {
  message: {},
  show: false
}

class JSONmodal extends PureComponent {
  constructor (props) {
    super(props)

    this.state = {
      show: false
    }
  }

  componentWillReceiveProps (nextProps) {
    console.warn('compoennt did mount', nextProps)
    this.setState({
      show: nextProps.show
    })
  }

  componentWillUnmount () {
    console.error('componentWillUnmount()')
  }

  handleClose = () => this.setState({
    show: false
  })

  handleCopyToClipBoard = () => {
    const textarea = R.compose(
      setValue(this.props.message),
      createElement
    )('textarea')

    executeCopy(textarea)
  }

  render () {
    return (
      <Modal
        show={this.state.show}
        message={
          <JSONeditor
            value={adjustToJSONeditor(this.props.message)}
            readOnly
          />
        }
        closeModal={this.handleClose}
        title={'Preview'}
        buttons={[
          <Button
            key='cancel'
            mod='default'
            onClick={this.handleClose}
          >
            Close
          </Button>,
          <Button
            key='ok'
            mod='primary'
            onClick={() => {
              this.handleCopyToClipBoard()
              this.handleClose()
            }}
          >
            Copy to clipboard
          </Button>,
          <Button
            key='ok'
            mod='github'
            onClick={this.handleClose}
          >
            Download
          </Button>
        ]}
      />
    )
  }
}

JSONmodal.propTypes = propTypes
JSONmodal.defaultProps = defaultProps

export default JSONmodal

function executeCopy (textarea) {
  document.body.appendChild(textarea)
  textarea.select()
  document.execCommand('Copy')
  document.body.removeChild(textarea)
}

function setValue (value) {
  return (element) => {
    element.value = JSON.stringify(value)

    return element
  }
}

function createElement (name) {
  return document.createElement(name)
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Field, formValueSelector, reduxForm } from 'redux-form';

import block from '../../../helpers/bem-cn';

import Section from '../../Layout/Section/Section';
import Title from '../../Layout/Title/Title';
import Subtitle from '../../Layout/Title/Subtitle';
import Row from '../../Layout/Row/Row';
import Input from '../../inputs/Input';
import Radio from '../../inputs/Radio/Radio';
import Label from '../../labels/Label';
import Hint from '../../labels/Hint/Hint';
import MultiSelect from '../../selects/MultiSelect/MultiSelect';
import Button from '../../buttons/Button';
import Icon from '../../Icon/Icon';

import FormRow from '../../forms/FormRow';
import FormInput from '../../forms/FormInput/FormInput';
import FormLabel from '../../forms/FormLabel';

import RenderPlugins from '../../forms/plugins/RenderPlugins';

const b = block('j-api-form');

const propTypes = {
    api: PropTypes.object.isRequired,
    handleDelete: PropTypes.func.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    initialValues: PropTypes.object,
};

const ApiForm = (props) => {
    console.error('THIS.PROPS: ', props);
    const { initialValues, handleSubmit } = props;
    const parse = value => (value === undefined ? undefined : parseInt(value));

    const optionsTransformer = config => {
        return config.map(item => ({
            label: item,
            value: item,
        }));
    };

    return (
        <form onSubmit={handleSubmit}>

                <Row>
                    <Title>Edit API</Title>

                        <Link
                            to={'/'}
                            onClick={() => {
                                props.handleDelete(props.api.name);
                            }}
                        >
                            <Button
                                type="button"
                                mod="danger"
                            >
                                <Icon type="delete-white" />
                                Delete
                            </Button>
                        </Link>
                        <Button
                            type="submit"
                            mod="primary"
                        >
                            Save
                        </Button>

                </Row>

1. General

                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            API Name
                            <Field
                                name="name"
                                type="text"
                                component={Input}
                                disabled
                            />
                        </Row>
                        <Row col>
                            Is Active?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="active"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="is-active"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="active"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="is-not-active"
                                    />
                                    No
                                </Row>
                            </Row>
                        </Row>
                    </Row>

2. Proxy

                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Listen Path
                            <Field
                                name="proxy.listen_path"
                                type="text"
                                placeholder="eg. http://gw.hellofresh.com/"
                                component={Input}
                            />
                            <Hint>The public url that is exposed by the Gateway</Hint>
                        </Row>
                        <Row col>
                            Upstream URL
                            <Field
                                name="proxy.upstream_url"
                                type="text"
                                component={Input}
                            />
                            <Hint>The url to which the Gateway forwards requests made to the public url.</Hint>
                        </Row>
                    </Row>
                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Methods
                            <Field
                                name="proxy.methods"
                                type="text"
                                placeholder="Choose one or more methods"
                                options={optionsTransformer(initialValues.proxy.methods)}
                                component={MultiSelect}
                            />
                            <Hint>HTTP methods that are supported for the endpoint.</Hint>
                        </Row>
                        <Row col>
                            Preserve Host?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.preserve_host"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="preserve-host-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.preserve_host"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="preserve-host-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint>Preserve the host header the client used for the incoming request.</Hint>
                        </Row>
                    </Row>
                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Append Path?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.append_path"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="append-path-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.append_path"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="append-path-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint>Appends the path from the listen_path when forwarding the request to the upstream_url.</Hint>
                        </Row>
                        <Row col>
                            Strips Path?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.strip_path"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="strip-path-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.strip_path"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="strip-path-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint> Strip the path out of the listen_path when forwarding the request to the upstream_url.</Hint>
                        </Row>
                    </Row>

                <Subtitle>{props.api.name}</Subtitle>
                <Link
                    to={'/'}
                    onClick={() => {
                        props.handleDelete(props.api.name);
                    }}
                >
                    <Button
                        type="button"
                        mod="danger"
                    >
                        <Icon type="delete-white" />
                        Delete
                    </Button>
                </Link>
                <Button
                    type="submit"
                    mod="primary"
                >
                    Save
                </Button>

                <FormRow>
                    <FormInput component="input" label="Listen Path" attachTo="proxy.listen_path" type="text" tooltip="some another tooltip about something usefull" />
                    <FormInput component="input" label="Upstream URL" attachTo="proxy.upstream_url" type="text" />
                    <FormInput component="input" label="Preserve HOST" attachTo="proxy.preserve_host" type="checkbox" tooltip="some another tooltip about something usefull" />
                    <FormInput component="input" label="Strip Path" attachTo="proxy.strip_path" type="checkbox" />
                    <FormInput component="input" label="Append Path" attachTo="proxy.append_path" type="checkbox" />
                </FormRow>

                    <FormRow>
                        <FormLabel text="Health check" />
                    </FormRow>
                    <FormRow>
                        <FormInput component="input" label="url" attachTo="health_check.url" type="text" />
                        <FormInput component="input" label="timeout" attachTo="health_check.timeout" type="text" parse={parse} />
                    </FormRow>

            {/*{
                !!props.initialValues.plugins &&
                    <RenderPlugins plugins={props.initialValues.plugins} />
            }*/}

            <FormRow centered>
                <Button
                    type="submit"
                    mod="primary"
                >
                    Save
                </Button>
            </FormRow>
        </form>
    );
};

ApiForm.propTypes = propTypes;

const form = reduxForm({
    form: 'apiForm',
    enableReinitialize: true, // this is needed!!
})(ApiForm);

export default connect(
    state => ({
        initialValues: state.apiReducer.api,
    }),
    null,
)(form);
<commit_msg>fix radios rendering<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Field, formValueSelector, reduxForm } from 'redux-form';

import block from '../../../helpers/bem-cn';
import transformFormValues from '../../../helpers/transformFormValues';

import Section from '../../Layout/Section/Section';
import Title from '../../Layout/Title/Title';
import Subtitle from '../../Layout/Title/Subtitle';
import Row from '../../Layout/Row/Row';
import Input from '../../inputs/Input';
import Radio from '../../inputs/Radio/Radio';
import Label from '../../labels/Label';
import Hint from '../../labels/Hint/Hint';
import MultiSelect from '../../selects/MultiSelect/MultiSelect';
import Button from '../../buttons/Button';
import Icon from '../../Icon/Icon';

import FormRow from '../../forms/FormRow';
import FormInput from '../../forms/FormInput/FormInput';
import FormLabel from '../../forms/FormLabel';

import RenderPlugins from '../../forms/plugins/RenderPlugins';

const b = block('j-api-form');

const propTypes = {
    api: PropTypes.object.isRequired,
    handleDelete: PropTypes.func.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    initialValues: PropTypes.object,
};

const ApiForm = (props) => {
    console.error('THIS.PROPS: ', props);
    const { initialValues, handleSubmit } = props;
    const parse = value => (value === undefined ? undefined : parseInt(value));

    const optionsTransformer = config => {
        return config.map(item => ({
            label: item,
            value: item,
        }));
    };

    return (
        <form onSubmit={handleSubmit}>

                <Row>
                    <Title>Edit API</Title>

                        <Link
                            to={'/'}
                            onClick={() => {
                                props.handleDelete(props.api.name);
                            }}
                        >
                            <Button
                                type="button"
                                mod="danger"
                            >
                                <Icon type="delete-white" />
                                Delete
                            </Button>
                        </Link>
                        <Button
                            type="submit"
                            mod="primary"
                        >
                            Save
                        </Button>

                </Row>

1. General

                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            API Name
                            <Field
                                name="name"
                                type="text"
                                component={Input}
                                disabled
                            />
                        </Row>
                        <Row col>
                            Is Active?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="active"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="is-active"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="active"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="is-not-active"
                                    />
                                    No
                                </Row>
                            </Row>
                        </Row>
                    </Row>

2. Proxy

                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Listen Path
                            <Field
                                name="proxy.listen_path"
                                type="text"
                                placeholder="eg. http://gw.hellofresh.com/"
                                component={Input}
                            />
                            <Hint>The public url that is exposed by the Gateway</Hint>
                        </Row>
                        <Row col>
                            Upstream URL
                            <Field
                                name="proxy.upstream_url"
                                type="text"
                                component={Input}
                            />
                            <Hint>The url to which the Gateway forwards requests made to the public url.</Hint>
                        </Row>
                    </Row>
                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Methods
                            <Field
                                name="proxy.methods"
                                type="text"
                                placeholder="Choose one or more methods"
                                options={optionsTransformer(initialValues.proxy.methods)}
                                component={MultiSelect}
                            />
                            <Hint>HTTP methods that are supported for the endpoint.</Hint>
                        </Row>
                        <Row col>
                            Preserve Host?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.preserve_host"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="preserve-host-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.preserve_host"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="preserve-host-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint>Preserve the host header the client used for the incoming request.</Hint>
                        </Row>
                    </Row>
                    <Row className={b('row')()} fullwidth>
                        <Row col>
                            Append Path?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.append_path"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="append-path-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.append_path"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="append-path-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint>Appends the path from the listen_path when forwarding the request to the upstream_url.</Hint>
                        </Row>
                        <Row col>
                            Strips Path?
                            <Row className={b('radio-wrap')()}>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.strip_path"
                                        component={Radio}
                                        value={'true'}
                                        type="radio"
                                        id="strip-path-true"
                                    />
                                    Yes
                                </Row>
                                <Row className={b('radio')()}>
                                    <Field
                                        name="proxy.strip_path"
                                        component={Radio}
                                        value={'false'}
                                        type="radio"
                                        id="strip-path-false"
                                    />
                                    No
                                </Row>
                            </Row>
                            <Hint> Strip the path out of the listen_path when forwarding the request to the upstream_url.</Hint>
                        </Row>
                    </Row>

                <Subtitle>{props.api.name}</Subtitle>
                <Link
                    to={'/'}
                    onClick={() => {
                        props.handleDelete(props.api.name);
                    }}
                >
                    <Button
                        type="button"
                        mod="danger"
                    >
                        <Icon type="delete-white" />
                        Delete
                    </Button>
                </Link>
                <Button
                    type="submit"
                    mod="primary"
                >
                    Save
                </Button>

                <FormRow>
                    <FormInput component="input" label="Listen Path" attachTo="proxy.listen_path" type="text" tooltip="some another tooltip about something usefull" />
                    <FormInput component="input" label="Upstream URL" attachTo="proxy.upstream_url" type="text" />
                    <FormInput component="input" label="Preserve HOST" attachTo="proxy.preserve_host" type="checkbox" tooltip="some another tooltip about something usefull" />
                    <FormInput component="input" label="Strip Path" attachTo="proxy.strip_path" type="checkbox" />
                    <FormInput component="input" label="Append Path" attachTo="proxy.append_path" type="checkbox" />
                </FormRow>

                    <FormRow>
                        <FormLabel text="Health check" />
                    </FormRow>
                    <FormRow>
                        <FormInput component="input" label="url" attachTo="health_check.url" type="text" />
                        <FormInput component="input" label="timeout" attachTo="health_check.timeout" type="text" parse={parse} />
                    </FormRow>

            {/*{
                !!props.initialValues.plugins &&
                    <RenderPlugins plugins={props.initialValues.plugins} />
            }*/}

            <FormRow centered>
                <Button
                    type="submit"
                    mod="primary"
                >
                    Save
                </Button>
            </FormRow>
        </form>
    );
};

ApiForm.propTypes = propTypes;

const form = reduxForm({
    form: 'apiForm',
    enableReinitialize: true, // this is needed!!
})(ApiForm);

export default connect(
    state => ({
        initialValues: transformFormValues(state.apiReducer.api),
    }),
    null,
)(form);
<|endoftext|>


<commit_before>(function (window, document, undefined, Butter) {

  var urlRegex, videoURL,
    iframe, iframeBody,
    popcornString, butterId,
    userSetMedia, videoString,
    popcornURL, originalHead,
    popcorns;

  Butter.registerModule( "previewer", {

    // setup function used to set default values, as well as setting up the iframe
    setup: function( options ) {

      originalHead = {};
      popcornURL = options.popcornURL || "http://popcornjs.org/code/dist/popcorn-complete.js";
      urlRegex = /(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu|vimeo|soundcloud|baseplayer)/;
      layout = options.layout;
      butterIds = {};
      userSetMedia = options.media;
      popcorns = {};
      videoString = {};

      var that = this,
          targetSrc = document.getElementById( options.target );

        // check if target is a div or iframe
      if ( targetSrc.tagName === "DIV" ) {

        target = document.getElementById( options.target );

        // force iframe to fill parent and set source
        iframe = document.createElement( "IFRAME" );
        iframe.src = layout;
        iframe.width = target.style.width;
        iframe.height = target.style.height;
        target.appendChild( iframe );

        // begin scraping once iframe has loaded, remove listener when complete
        iframe.addEventListener( "load", function (e) {
          that.scraper( iframe, options.callback );
          this.removeEventListener( "load", arguments.callee, false );
        }, false);

      } else if ( targetSrc.tagName === "IFRAME" ) {

        iframe = targetSrc;
        iframe.src = options.layout;

        targetSrc.addEventListener( "load", function (e) {
          that.scraper( iframe, options.callback );
          this.removeEventListener( "load", arguments.callee, false );
        }, false);
      } // else
    }, // setup

    extend: {

      // scraper function that scrapes all DOM elements of the given layout,
      // only scrapes elements with the butter-data attribute
      scraper: function( iframe, callback ) {

        // obtain a reference to the iframes body
        var body = iframe.contentWindow.document.getElementsByTagName( "BODY" ),
            that = this;

        Butter.extend( originalHead, iframe.contentWindow.document.head );

        //originalHead = iframe.contentWindow.document.head;

        // store original iframeBody incase we rebuild
        iframeBody = "" + iframe.contentWindow.document.body.innerHTML + "\n";

        // function to ensure body is actually there
        var ensureLoaded = function() {

          if ( body.length < 1 ) {
            setTimeout( function() {
              ensureLoaded();
            }, 5 );
          } else {

            // begin scraping once body is actually there, call callback once done
            bodyReady( body[ 0 ].children );
            callback();
          } // else
        } // ensureLoaded

        ensureLoaded();

        // scraping is done here
        function bodyReady( children ) {

          // loop for every child of the body
          for( var i = 0; i < children.length; i++ ) {

            // if DOM element has an data-butter tag that is equal to target or media,
            // add it to butters target list with a respective type
            if( children[ i ].getAttribute( "data-butter" ) === "target" ) {
              that.addTarget( {
                name: children[ i ].id,
                type: "target"
              } );
            } else if( children[ i ].getAttribute( "data-butter" ) === "media" ) {
              that.addMedia( {
                name: children[ i ].id,
                media: userSetMedia
              } );
            } // else

            // ensure we get every child, search recursively
            if ( children[ i ].children.length > 0 ) {

              bodyReady( children[ i ].children );
            } // if
          } // for
        } // ok

      }, // scraper

      // buildPopcorn function, builds an instance of popcorn in the iframe and also
      // a local version of popcorn
      buildPopcorn: function( videoTarget, callback ) {

        videoURL = this.getCurrentMedia().getMedia();

        // default to first butter-media tagged object if none is specified
        videoTarget = videoTarget || this.getAllMedia()[ 0 ].getName();

        iframe.contentWindow.document.body.innerHTML = iframeBody;

        // create a string that will create an instance of popcorn with the proper video source
        popcornString = "document.addEventListener('DOMContentLoaded', function () {\n";

        var regexResult = urlRegex.exec( videoURL ) || "",
            players = [], that = this;

        players[ "youtu" ] = function() {
          iframe.contentWindow.document.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.youtube( '" + videoTarget + "', '" +
            videoURL + "', {\n" +
            "width: 430, height: 300\n" +
          "} ) );\n";
        };

        players[ "vimeo " ] = function() {
          iframe.contentWindow.document.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.vimeo( '" + videoTarget + "', '" +
          videoURL + "', {\n" +
            "css: {\n" +
              "width: '430px',\n" +
              "height: '300px'\n" +
            "}\n" +
          "} ) );\n";
        };

        players[ "soundcloud" ] = function() {
          iframe.contentWindow.document.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.soundcloud( '" + videoTarget + "'," +
          " '" + videoURL + "' ) );\n";
        };

        players[ "baseplayer" ] = function() {
          iframe.contentWindow.document.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.baseplayer( '" + videoTarget + "' ) );\n";
        };

        players[ undefined ] = function() {
          var src = document.createElement( "source" ),
          video = document.createElement( "video" );
          src.src = videoURL;

          video.style.width = videoTarget.width;
          video.style.height = videoTarget.height;
          video.appendChild( src );
          video.controls = true;
          video.id = videoTarget + "-butter";

          iframe.contentWindow.document.getElementById( videoTarget ).appendChild( video );

          var vidId = "#" + video.id;

          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( '" + vidId + "');\n";
        };

        // call certain player function depending on the regexResult
        players[ regexResult[ 1 ] ]();

        for( video in videoString ) {
          popcornString += videoString[ video ];
        }

        // if for some reason the iframe is refreshed, we want the most up to date popcorn code
        // to be represented in the head of the iframe, incase someone views source
        for( popcorn in popcorns ) {

          var trackEvents = popcorns[ popcorn ].getTrackEvents();

          if ( trackEvents ) {

            // loop through each track event
            for ( var k = 0; k < trackEvents.length; k++ ) {

              // obtain all of the options in the manifest
              var options = trackEvents[ k ]._natives.manifest.options;
              popcornString += " popcorn" + popcorn + "." + trackEvents[ k ]._natives.type + "({\n";

              // for each option
              for ( item in options ) {

                if ( options.hasOwnProperty( item ) ) {

                  // add the data to the string so it looks like normal popcorn code
                  // that someone would write
                  popcornString += item + ": '" + trackEvents[ k ][ item ] + "',\n";
                } // if
              } // for

              popcornString += "});";

            } // for
          } // if
        }

        popcornString += "}, false);";

        this.fillIframe( callback );
      },

      getPopcorn: function( callback ) {
        var popcornz = "var " + videoString;

        // if for some reason the iframe is refreshed, we want the most up to date popcorn code
        // to be represented in the head of the iframe, incase someone views source
        for( popcorn in popcorns ) {

          var trackEvents = popcorns[ popcorn ].getTrackEvents();

          if ( trackEvents ) {

            // loop through each track event
            for ( var k = 0; k < trackEvents.length; k++ ) {

              // obtain all of the options in the manifest
              var options = trackEvents[ k ]._natives.manifest.options;
              popcornz += "popcorn" + popcorn + "." + trackEvents[ k ]._natives.type + "({\n";

              // for each option
              for ( item in options ) {

                if ( options.hasOwnProperty( item ) ) {

                  // add the data to the string so it looks like normal popcorn code
                  // that someone would write
                  popcornz += item + ": '" + trackEvents[ k ][ item ] + "',\n";
                } // if
              } // for

              popcornz += "});\n";

            } // for
          } // if
        }

        return popcornz;

      },

      getRegistry: function() {
        return iframe.contentWindow.Popcorn.registry;
      },

      // fillIframe function used to populate the iframe with changes made by the user,
      // which is mostly managing track events added by the user
      fillIframe: function( callback ) {

        var popcornScript, iframeHead, body,
            that = this, doc = iframe.contentWindow.document;

        // create a script within the iframe and populate it with our popcornString
        popcornScript = doc.createElement( "script" );
        popcornScript.innerHTML = popcornString;

        doc.head.appendChild( popcornScript );

        // create a new head element with our new data
        iframeHead = "" + originalHead.innerHTML + "\n\n";

        iframeHead += "\n\n";

        // create a new body element with our new data
        body = doc.body.innerHTML;

        // open, write our changes to the iframe, and close it
        doc.open();
        doc.write( "\n" + iframeHead + body + "\n" );
        doc.close();

        var popcornReady = function( e, callback2 ) {

          var framePopcorn = iframe.contentWindow[ "popcorn" + that.getCurrentMedia().getId() ];

          if ( !framePopcorn ) {
            setTimeout( function() {
              popcornReady( e, callback2 );
            }, 10 );
          } else {
            callback2 && callback2( framePopcorn );
          } // else
        }

        popcornReady( null, function( framePopcorn ) {

          var videoReady = function() {

            if( framePopcorn.media.readyState >= 2 || framePopcorn.media.duration > 0 ) {
              that.duration( framePopcorn.media.duration );

              that.trigger( "mediaready", that.getCurrentMedia() );
              framePopcorn.media.addEventListener( "timeupdate", function() {

                that.currentTime( framePopcorn.media.currentTime );
                that.trigger( "timeupdate", that.getCurrentMedia() );
              },false);
              callback && callback();
            } else {
              setTimeout( function() {
                videoReady( framePopcorn );
              }, 10);
            }
          }
          videoReady( framePopcorn );
        } );

        this.teAdded = function( event ) {
          var that = this, e = event.data;

          popcornReady( e, function( framePopcorn ) {

            if( !popcorns[ that.getCurrentMedia().getId() ] ) {
                popcorns[ that.getCurrentMedia().getId() ] = framePopcorn;
            } else {
              framePopcorn = popcorns[ that.getCurrentMedia().getId() ];
            }

            framePopcorn.removeTrackEvent( butterIds[ e.getId() ] );

            // add track events to the iframe verison of popcorn
            framePopcorn[ e.type ]( iframe.contentWindow.Popcorn.extend( {},
              e.popcornOptions ) );

            butterIds[ e.getId() ] = framePopcorn.getLastTrackEventId();

            e.manifest = framePopcorn.getTrackEvent( butterIds[ e.getId() ] )._natives.manifest;

          } );
        }

        // listen for a trackeventadded
        this.listen( "trackeventupdated", function ( e ) {
          this.teAdded( e );
        }); // listener

        this.listen( "trackeventadded", function ( e ) {
          e = e.data;
          popcornReady( e, function( framePopcorn ) {

            if( !popcorns[ that.getCurrentMedia().getId() ] ) {
              popcorns[ that.getCurrentMedia().getId() ] = framePopcorn;
            } else {
              framePopcorn = popcorns[ that.getCurrentMedia().getId() ];
            }

            // add track events to the iframe verison of popcorn
            framePopcorn[ e.type ]( iframe.contentWindow.Popcorn.extend( {},
              e.popcornOptions ) );

            butterIds[ e.getId() ] = framePopcorn.getLastTrackEventId();

            e.manifest = framePopcorn.getTrackEvent( butterIds[ e.getId() ] )._natives.manifest;
          } );
        }); // listener

        this.listen( "trackeventremoved", function( e ) {
          iframe.contentWindow[ "popcorn" + that.getCurrentMedia().getId() ].removeTrackEvent( butterIds[ e.data.getId() ] );
        } );

        this.listen( "mediachanged", function( e ) {
          that.buildPopcorn( e.data.getName() );
        } );

      } // fillIframe
    } // exnteds

  });
})(window, document, undefined, Butter);
<commit_msg>Some small fixes, mostly trial and error<commit_after>(function (window, document, undefined, Butter) {

  var urlRegex, videoURL,
    iframe, iframeBody,
    popcornString, butterId,
    userSetMedia, videoString,
    popcornURL, originalHead,
    popcorns;

  Butter.registerModule( "previewer", {

    // setup function used to set default values, as well as setting up the iframe
    setup: function( options ) {

      originalHead = {};
      popcornURL = options.popcornURL || "http://popcornjs.org/code/dist/popcorn-complete.js";
      urlRegex = /(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu|vimeo|soundcloud|baseplayer)/;
      layout = options.layout;
      butterIds = {};
      userSetMedia = options.media;
      popcorns = {};
      videoString = {};

      var that = this,
          targetSrc = document.getElementById( options.target );

        // check if target is a div or iframe
      if ( targetSrc.tagName === "DIV" ) {

        target = document.getElementById( options.target );

        // force iframe to fill parent and set source
        iframe = document.createElement( "IFRAME" );
        iframe.src = layout;
        iframe.width = target.style.width;
        iframe.height = target.style.height;
        target.appendChild( iframe );

        // begin scraping once iframe has loaded, remove listener when complete
        iframe.addEventListener( "load", function (e) {
          that.scraper( iframe, options.callback );
          this.removeEventListener( "load", arguments.callee, false );
        }, false);

      } else if ( targetSrc.tagName === "IFRAME" ) {

        iframe = targetSrc;
        iframe.src = options.layout;

        targetSrc.addEventListener( "load", function (e) {
          that.scraper( iframe, options.callback );
          this.removeEventListener( "load", arguments.callee, false );
        }, false);
      } // else
    }, // setup

    extend: {

      // scraper function that scrapes all DOM elements of the given layout,
      // only scrapes elements with the butter-data attribute
      scraper: function( iframe, callback ) {

        // obtain a reference to the iframes body
        var body = iframe.contentWindow.document.getElementsByTagName( "BODY" ) || iframe.contentDocument.parentWindow.document.getElementsByTagName( "BODY" ),
            that = this;

        Butter.extend( originalHead, iframe.contentWindow.document.head || iframe.contentDocument.parentWindow.document.head );

        //originalHead = iframe.contentWindow.document.head;

        // store original iframeBody incase we rebuild
        var ifrmBody = iframe.contentWindow.document || iframe.contentDocument.parentWindow.document;
        iframeBody = "" + ifrmBody.body.innerHTML + "\n";

        // function to ensure body is actually there
        var ensureLoaded = function() {

          if ( body.length < 1 ) {
            setTimeout( function() {
              ensureLoaded();
            }, 5 );
          } else {

            // begin scraping once body is actually there, call callback once done
            bodyReady( body[ 0 ].children );
            callback();
          } // else
        } // ensureLoaded

        ensureLoaded();

        // scraping is done here
        function bodyReady( children ) {

          // loop for every child of the body
          for( var i = 0; i < children.length; i++ ) {

            // if DOM element has an data-butter tag that is equal to target or media,
            // add it to butters target list with a respective type
            if( children[ i ].getAttribute( "data-butter" ) === "target" ) {
              that.addTarget( {
                name: children[ i ].id,
                type: "target"
              } );
            } else if( children[ i ].getAttribute( "data-butter" ) === "media" ) {
              that.addMedia( {
                name: children[ i ].id,
                media: userSetMedia
              } );
            } // else

            // ensure we get every child, search recursively
            if ( children[ i ].children.length > 0 ) {

              bodyReady( children[ i ].children );
            } // if
          } // for
        } // ok

      }, // scraper

      // buildPopcorn function, builds an instance of popcorn in the iframe and also
      // a local version of popcorn
      buildPopcorn: function( videoTarget, callback ) {

        videoURL = this.getCurrentMedia().getMedia();

        // default to first butter-media tagged object if none is specified
        videoTarget = videoTarget || this.getAllMedia()[ 0 ].getName();

        var bpIframe = iframe.contentWindow.document ||
            iframe.contentDocument.parentWindow.document;
            bpIframe.body.innerHTML = iframeBody;

        // create a string that will create an instance of popcorn with the proper video source
        popcornString = "document.addEventListener('DOMContentLoaded', function () {\n";

        var regexResult = urlRegex.exec( videoURL ) || "",
            players = [], that = this;

        players[ "youtu" ] = function() {
          bpIframe.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.youtube( '" + videoTarget + "', '" +
            videoURL + "', {\n" +
            "width: 430, height: 300\n" +
          "} ) );\n";
        };

        players[ "vimeo " ] = function() {
          bpIframe.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.vimeo( '" + videoTarget + "', '" +
          videoURL + "', {\n" +
            "css: {\n" +
              "width: '430px',\n" +
              "height: '300px'\n" +
            "}\n" +
          "} ) );\n";
        };

        players[ "soundcloud" ] = function() {
          bpIframe.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.soundcloud( '" + videoTarget + "'," +
          " '" + videoURL + "' ) );\n";
        };

        players[ "baseplayer" ] = function() {
          bpIframe.getElementById( videoTarget ).innerHTML = "";
          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( Popcorn.baseplayer( '" + videoTarget + "' ) );\n";
        };

        players[ undefined ] = function() {
          var src = document.createElement( "source" ),
          video = document.createElement( "video" );
          src.src = videoURL;

          video.style.width = videoTarget.width;
          video.style.height = videoTarget.height;
          video.appendChild( src );
          video.controls = true;
          video.id = videoTarget + "-butter";

          bpIframe.getElementById( videoTarget ).appendChild( video );

          var vidId = "#" + video.id;

          videoString[ that.getCurrentMedia().getId() ] = "popcorn" + that.getCurrentMedia().getId() + " = Popcorn( '" + vidId + "');\n";
        };

        // call certain player function depending on the regexResult
        players[ regexResult[ 1 ] ]();

        for( video in videoString ) {
          popcornString += videoString[ video ];
        }

        // if for some reason the iframe is refreshed, we want the most up to date popcorn code
        // to be represented in the head of the iframe, incase someone views source
        for( popcorn in popcorns ) {

          var trackEvents = popcorns[ popcorn ].getTrackEvents();

          if ( trackEvents ) {

            // loop through each track event
            for ( var k = 0; k < trackEvents.length; k++ ) {

              // obtain all of the options in the manifest
              var options = trackEvents[ k ]._natives.manifest.options;
              popcornString += " popcorn" + popcorn + "." + trackEvents[ k ]._natives.type + "({\n";

              // for each option
              for ( item in options ) {

                if ( options.hasOwnProperty( item ) ) {

                  // add the data to the string so it looks like normal popcorn code
                  // that someone would write
                  popcornString += item + ": '" + trackEvents[ k ][ item ] + "',\n";
                } // if
              } // for

              popcornString += "});";

            } // for
          } // if
        }

        popcornString += "}, false);";

        this.fillIframe( callback );
      },

      getPopcorn: function( callback ) {
        var popcornz = "var " + videoString;

        // if for some reason the iframe is refreshed, we want the most up to date popcorn code
        // to be represented in the head of the iframe, incase someone views source
        for( popcorn in popcorns ) {

          var trackEvents = popcorns[ popcorn ].getTrackEvents();

          if ( trackEvents ) {

            // loop through each track event
            for ( var k = 0; k < trackEvents.length; k++ ) {

              // obtain all of the options in the manifest
              var options = trackEvents[ k ]._natives.manifest.options;
              popcornz += "popcorn" + popcorn + "." + trackEvents[ k ]._natives.type + "({\n";

              // for each option
              for ( item in options ) {

                if ( options.hasOwnProperty( item ) ) {

                  // add the data to the string so it looks like normal popcorn code
                  // that someone would write
                  popcornz += item + ": '" + trackEvents[ k ][ item ] + "',\n";
                } // if
              } // for

              popcornz += "});\n";

            } // for
          } // if
        }

        return popcornz;

      },

      getRegistry: function() {
        var ifrme = iframe.contentWindow ||
            iframe.contentDocument.parentWindow;
        return ifrme.Popcorn.registry;
      },

      // fillIframe function used to populate the iframe with changes made by the user,
      // which is mostly managing track events added by the user
      fillIframe: function( callback ) {

        var popcornScript, iframeHead, body,
            that = this, doc = iframe.contentWindow.document || iframe.contentDocument.parentWindow.document;

        // create a script within the iframe and populate it with our popcornString
        popcornScript = doc.createElement( "script" );
        popcornScript.innerHTML = popcornString;

        doc.head.appendChild( popcornScript );

        // create a new head element with our new data
        iframeHead = "" + originalHead.innerHTML + "\n\n";

        iframeHead += "\n\n";

        // create a new body element with our new data
        body = doc.body.innerHTML;

        // open, write our changes to the iframe, and close it
        doc.open();
        doc.write( "\n" + iframeHead + body + "\n" );
        doc.close();

        var popcornReady = function( e, callback2 ) {

          var popcornIframe = iframe.contentWindow || iframe.contentDocument.parentWindow;
          var framePopcorn = popcornIframe[ "popcorn" + that.getCurrentMedia().getId() ];

          if ( !framePopcorn ) {
            setTimeout( function() {
              popcornReady( e, callback2 );
            }, 10 );
          } else {
            callback2 && callback2( framePopcorn );
          } // else
        }

        popcornReady( null, function( framePopcorn ) {

          var videoReady = function() {

            if( framePopcorn.media.readyState >= 2 || framePopcorn.media.duration > 0 ) {
              that.duration( framePopcorn.media.duration );

              that.trigger( "mediaready", that.getCurrentMedia() );
              framePopcorn.media.addEventListener( "timeupdate", function() {

                that.currentTime( framePopcorn.media.currentTime );
                that.trigger( "timeupdate", that.getCurrentMedia() );
              },false);
              callback && callback();
            } else {
              setTimeout( function() {
                videoReady( framePopcorn );
              }, 10);
            }
          }
          videoReady( framePopcorn );
        } );

        this.teAdded = function( event ) {
          var that = this, e = event.data;

          popcornReady( e, function( framePopcorn ) {

            var ifrme = iframe.contentWindow || iframe.contentDocument.parentWindow

            if( !popcorns[ that.getCurrentMedia().getId() ] ) {
                popcorns[ that.getCurrentMedia().getId() ] = framePopcorn;
            } else {
              framePopcorn = popcorns[ that.getCurrentMedia().getId() ];
            }

            framePopcorn.removeTrackEvent( butterIds[ e.getId() ] );

            // add track events to the iframe verison of popcorn
            framePopcorn[ e.type ]( ifrme.Popcorn.extend( {},
              e.popcornOptions ) );

            butterIds[ e.getId() ] = framePopcorn.getLastTrackEventId();

            e.manifest = framePopcorn.getTrackEvent( butterIds[ e.getId() ] )._natives.manifest;

          } );
        }

        // listen for a trackeventadded
        this.listen( "trackeventupdated", function ( e ) {
          this.teAdded( e );
        }); // listener

        this.listen( "trackeventadded", function ( e ) {
          e = e.data;
          popcornReady( e, function( framePopcorn ) {

            var ifrme = iframe.contentWindow || iframe.contentDocument.parentWindow

            if( !popcorns[ that.getCurrentMedia().getId() ] ) {
              popcorns[ that.getCurrentMedia().getId() ] = framePopcorn;
            } else {
              framePopcorn = popcorns[ that.getCurrentMedia().getId() ];
            }

            // add track events to the iframe verison of popcorn
            framePopcorn[ e.type ]( ifrme.Popcorn.extend( {},
              e.popcornOptions ) );

            butterIds[ e.getId() ] = framePopcorn.getLastTrackEventId();

            e.manifest = framePopcorn.getTrackEvent( butterIds[ e.getId() ] )._natives.manifest;
          } );
        }); // listener

        this.listen( "trackeventremoved", function( e ) {
          var ifrme = iframe.contentWindow || iframe.contentDocument.parentWindow
          ifrme[ "popcorn" + that.getCurrentMedia().getId() ].removeTrackEvent( butterIds[ e.data.getId() ] );
        } );

        this.listen( "mediachanged", function( e ) {
          that.buildPopcorn( e.data.getName() );
        } );

        this.listen( "timeupdate", function( e ) {

        } );

      } // fillIframe
    } // exnteds

  });
})(window, document, undefined, Butter);
<|endoftext|>


<commit_before>// info about each config option.

var debug = process.env.DEBUG_NOPT
  ? function () { console.error.apply(console, arguments) }
  : function () {}

var url = require("url")
  , path = require("path")
  , Stream = require("stream").Stream
  , abbrev = require("abbrev")

module.exports = exports = nopt
exports.clean = clean

exports.typeDefs =
  { String  : { type: String,  validate: validateString  }
  , Boolean : { type: Boolean, validate: validateBoolean }
  , url     : { type: url,     validate: validateUrl     }
  , Number  : { type: Number,  validate: validateNumber  }
  , path    : { type: path,    validate: validatePath    }
  , Stream  : { type: Stream,  validate: validateStream  }
  }

function nopt (types, shorthands, args, slice) {
  args = args || process.argv
  types = types || {}
  shorthands = shorthands || {}
  if (typeof slice !== "number") slice = 2

  debug(types, shorthands, args, slice)

  args = args.slice(slice)
  var data = {}
    , key
    , remain = []
    , cooked = args
    , original = args.slice(0)

  parse(args, data, remain, types, shorthands)
  // now data is full
  clean(data, types, exports.typeDefs)
  data.argv = {remain:remain,cooked:cooked,original:original}
  data.argv.toString = function () {
    return this.original.map(JSON.stringify).join(" ")
  }
  return data
}

function clean (data, types, typeDefs) {
  typeDefs = typeDefs || exports.typeDefs
  Object.keys(data).forEach(function (k) {
    var val = data[k]
    // if it's an unknown value, then parse false/true/null/numbers
    if (typeof val === "string") {
      val = val.trim()
      if (val === "null" || val === "true" || val === "false") {
        val = JSON.parse(val)
      } else if (!isNaN(val)) {
        val = +val
      }
    }
    if (!types.hasOwnProperty(k)) {
      data[k] = val
      return
    }
    if (!validate(data, k, val, types[k], typeDefs)) {
      if (exports.invalidHandler) {
        exports.invalidHandler(k, val, types[k], data)
      } else if (exports.invalidHandler !== false) {
        debug("invalid: "+k+"="+val, types[k])
      }
    }
  })
}

function validateString (data, k, val) {
  data[k] = String(val)
}

function validatePath (data, k, val) {
  data[k] = path.resolve(String(val))
}

function validateNumber (data, k, val) {
  debug("validate Number %j %j %j", k, val, isNaN(val))
  if (isNaN(val)) return false
  data[k] = +val
}

function validateBoolean (data, k, val) {
  if (val instanceof Boolean) val = val.valueOf()
  else if (typeof val === "string") {
    if (!isNaN(val)) val = !!(+val)
    else if (val === "null" || val === "false") val = false
    else val = true
  } else val = !!val
  data[k] = val
}

function validateUrl (data, k, val) {
  val = url.parse(String(val))
  if (!val.host) return false
  data[k] = val.href
}

function validateStream (data, k, val) {
  if (!(val instanceof Stream)) return false
  data[k] = val
}

function validate (data, k, val, type, typeDefs) {
  // arrays are lists of types.
  if (Array.isArray(type)) {
    for (var i = 0, l = type.length; i < l; i ++) {
      if (validate(data, k, val, type[i], typeDefs)) return true
    }
    delete data[k]
    return false
  }

  // NaN is poisonous.  Means that something is not allowed.
  if (type !== type) {
    debug("Poison NaN", k, val, type)
    delete data[k]
    return false
  }

  // explicit list of values
  if (val === type) {
    debug("Explicitly allowed %j", val)
    data[k] = val
    return true
  }

  // now go through the list of typeDefs, validate against each one.
  var ok = false
    , types = Object.keys(typeDefs)
  for (var i = 0, l = types.length; i < l; i ++) {
    debug("test type %j %j %j", k, val, types[i])
    var t = typeDefs[types[i]]
    if (t && type === t.type) {
      ok = false !== t.validate(data, k, val)
      if (ok) break
    }
  }
  debug("OK? %j (%j %j %j)", ok, k, val, types[i])

  if (!ok) delete data[k]
  return ok
}

function parse (args, data, remain, types, shorthands) {
  debug("parse", args, data, remain)

  var key = null
    , abbrevs = abbrev(Object.keys(types))
    , shortAbbr = abbrev(Object.keys(shorthands))

  for (var i = 0; i < args.length; i ++) {
    var arg = args[i]
    debug("arg", arg)

    if (arg.match(/^-{2,}$/)) {
      // done with keys.
      // the rest are args.
      remain.push.apply(remain, args.slice(i + 1))
      args[i] = "--"
      break
    }
    if (arg.charAt(0) === "-") {
      if (arg.indexOf("=") !== -1) {
        var v = arg.split("=")
        arg = v.shift()
        v = v.join("=")
        args.splice.apply(args, [i, 1].concat([arg, v]))
      }
      // see if it's a shorthand
      // if so, splice and back up to re-parse it.
      var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
      debug("arg=%j shRes=%j", arg, shRes)
      if (shRes) {
        debug(arg, shRes)
        args.splice.apply(args, [i, 1].concat(shRes))
        if (arg !== shRes[0]) {
          i --
          continue
        }
      }
      arg = arg.replace(/^-+/, "")
      var no = false
      while (arg.toLowerCase().indexOf("no-") === 0) {
        no = !no
        arg = arg.substr(3)
      }
      var isBool = no || types[arg] === Boolean ||
        Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1
      if (abbrevs[arg]) arg = abbrevs[arg]
      if (isBool) {
        // just set and move along
        data[arg] = !no
        // however, also support --bool true or --bool false
        var la = args[i + 1]
        if (la === "true" || la === "false") {
          data[arg] = JSON.parse(la)
          if (no) data[arg] = !data[arg]
          i ++
        }
        continue
      }
      var la = args[i + 1]
      if (la && la.match(/^-{2,}$/)) {
        la = undefined
        i --
      }
      data[arg] = la === undefined ? true : la
      i ++
      continue
    }
    remain.push(arg)
  }
}

function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
  // handle single-char shorthands glommed together, like
  // npm ls -glp, but only if there is one dash, and only if
  // all of the chars are single-char shorthands, and it's
  // not a match to some other abbrev.
  arg = arg.replace(/^-+/, '')
  if (abbrevs[arg] && !shorthands[arg]) {
    return null
  }
  if (shortAbbr[arg]) {
    arg = shortAbbr[arg]
  } else {
    var singles = shorthands.___singles
    if (!singles) {
      singles = Object.keys(shorthands).filter(function (s) {
        return s.length === 1
      }).reduce(function (l,r) { l[r] = true ; return l }, {})
      shorthands.___singles = singles
    }
    var chrs = arg.split("").filter(function (c) {
      return singles[c]
    })
    if (chrs.join("") === arg) return chrs.map(function (c) {
      return shorthands[c]
    }).reduce(function (l, r) {
      return l.concat(r)
    }, [])
  }

  if (shorthands[arg] && !Array.isArray(shorthands[arg])) {
    shorthands[arg] = shorthands[arg].split(/\s+/)
  }
  return shorthands[arg]
}

if (module === require.main) {
var assert = require("assert")
  , sys = require("sys")

  , shorthands =
    { s : ["--loglevel", "silent"]
    , d : ["--loglevel", "info"]
    , dd : ["--loglevel", "verbose"]
    , ddd : ["--loglevel", "silly"]
    , noreg : ["--no-registry"]
    , reg : ["--registry"]
    , "no-reg" : ["--no-registry"]
    , silent : ["--loglevel", "silent"]
    , verbose : ["--loglevel", "verbose"]
    , h : ["--usage"]
    , H : ["--usage"]
    , "?" : ["--usage"]
    , help : ["--usage"]
    , v : ["--version"]
    , f : ["--force"]
    , desc : ["--description"]
    , "no-desc" : ["--no-description"]
    , "local" : ["--no-global"]
    , l : ["--long"]
    , p : ["--parseable"]
    , porcelain : ["--parseable"]
    , g : ["--global"]
    }

  , types =
    { argv : NaN
    , browser : String
    , cache : path
    , color : ["always", Boolean]
    , depth : Number
    , description : Boolean
    , dev : Boolean
    , editor : path
    , force : Boolean
    , global : Boolean
    , globalconfig : path
    , group : [String, Number]
    , gzipbin : String
    , logfd : [Number, Stream]
    , loglevel : ["silent","win","error","warn","info","verbose","silly"]
    , long : Boolean
    , "node-version" : [false, String]
    , npaturl : url
    , npat : Boolean
    , "onload-script" : [false, String]
    , outfd : [Number, Stream]
    , parseable : Boolean
    , pre: Boolean
    , prefix: path
    , proxy : url
    , "rebuild-bundle" : Boolean
    , registry : url
    , searchopts : String
    , searchexclude: [null, String]
    , shell : path
    , tag : String
    , tar : String
    , tmp : path
    , "unsafe-perm" : Boolean
    , usage : Boolean
    , user : String
    , username : String
    , userconfig : path
    , version : Boolean
    , viewer: path
    , _exit : Boolean
    }

; [["-v", {version:true}, []]
  ,["---v", {version:true}, []]
  ,["ls -s --no-reg connect -d",
    {loglevel:"info",registry:null},["ls","connect"]]
  ,["ls ---s foo",{loglevel:"silent"},["ls","foo"]]
  ,["ls --registry blargle", {}, ["ls"]]
  ,["--no-registry", {registry:null}, []]
  ,["--no-color true", {color:false}, []]
  ,["--no-color false", {color:true}, []]
  ,["--no-color", {color:false}, []]
  ,["--color false", {color:false}, []]
  ,["--color --logfd 7", {logfd:7,color:true}, []]
  ,["--color=true", {color:true}, []]
  ,["--logfd=10", {logfd:10}, []]
  ,["--tmp=/tmp -tar=gtar",{tmp:"/tmp",tar:"gtar"},[]]
  ,["--tmp=tmp -tar=gtar",
    {tmp:path.join(process.cwd(), "tmp"),tar:"gtar"},[]]
  ,["--logfd x", {}, []]
  ,["a -true -- -no-false", {true:true},["a","-no-false"]]
  ,["a -no-false", {false:false},["a"]]
  ,["a -no-no-true", {true:true}, ["a"]]
  ,["a -no-no-no-false", {false:false}, ["a"]]
  ,["---NO-no-No-no-no-no-nO-no-no"+
    "-No-no-no-no-no-no-no-no-no"+
    "-no-no-no-no-NO-NO-no-no-no-no-no-no"+
    "-no-body-can-do-the-boogaloo-like-I-do"
   ,{"body-can-do-the-boogaloo-like-I-do":false}, []]
  ,["we are -no-strangers-to-love "+
    "--you-know the-rules --and so-do-i "+
    "---im-thinking-of=a-full-commitment "+
    "--no-you-would-get-this-from-any-other-guy "+
    "--no-gonna-give-you-up "+
    "-no-gonna-let-you-down=true "+
    "--no-no-gonna-run-around false "+
    "--desert-you=false "+
    "--make-you-cry false "+
    "--no-tell-a-lie "+
    "--no-no-and-hurt-you false"
   ,{"strangers-to-love":false
    ,"you-know":"the-rules"
    ,"and":"so-do-i"
    ,"you-would-get-this-from-any-other-guy":false
    ,"gonna-give-you-up":false
    ,"gonna-let-you-down":false
    ,"gonna-run-around":false
    ,"desert-you":false
    ,"make-you-cry":false
    ,"tell-a-lie":false
    ,"and-hurt-you":false
    },["we", "are"]]
  ].forEach(function (test) {
    var argv = test[0].split(/\s+/)
      , opts = test[1]
      , rem = test[2]
      , actual = nopt(types, shorthands, argv, 0)
      , parsed = actual.argv
    delete actual.argv
    console.log(parsed.remain)
    console.log(sys.inspect(actual, false, 2, true))
    for (var i in opts) {
      assert.equal(opts[i], actual[i])
    }
    assert.deepEqual(rem, parsed.remain)
  })
}
<commit_msg>Support 'Array' types to get a list of items<commit_after>// info about each config option.

var debug = process.env.DEBUG_NOPT
  ? function () { console.error.apply(console, arguments) }
  : function () {}

var url = require("url")
  , path = require("path")
  , Stream = require("stream").Stream
  , abbrev = require("abbrev")

module.exports = exports = nopt
exports.clean = clean

exports.typeDefs =
  { String  : { type: String,  validate: validateString  }
  , Boolean : { type: Boolean, validate: validateBoolean }
  , url     : { type: url,     validate: validateUrl     }
  , Number  : { type: Number,  validate: validateNumber  }
  , path    : { type: path,    validate: validatePath    }
  , Stream  : { type: Stream,  validate: validateStream  }
  }

function nopt (types, shorthands, args, slice) {
  args = args || process.argv
  types = types || {}
  shorthands = shorthands || {}
  if (typeof slice !== "number") slice = 2

  debug(types, shorthands, args, slice)

  args = args.slice(slice)
  var data = {}
    , key
    , remain = []
    , cooked = args
    , original = args.slice(0)

  parse(args, data, remain, types, shorthands)
  // now data is full
  clean(data, types, exports.typeDefs)
  data.argv = {remain:remain,cooked:cooked,original:original}
  data.argv.toString = function () {
    return this.original.map(JSON.stringify).join(" ")
  }
  return data
}

function clean (data, types, typeDefs) {
  typeDefs = typeDefs || exports.typeDefs
  var remove = {}
  Object.keys(data).forEach(function (k) {
    var val = data[k]
      , isArray = Array.isArray(val)
    if (!isArray) val = [val]

    debug("val=%j", val)
    val = val.map(function (val) {
      // if it's an unknown value, then parse false/true/null/numbers
      if (typeof val === "string") {
        val = val.trim()
        if (val === "null" || val === "true" || val === "false") {
          val = JSON.parse(val)
        } else if (!isNaN(val)) {
          val = +val
        }
      }
      if (!types.hasOwnProperty(k)) {
        return val
      }
      var d = {}
      d[k] = val
      debug("prevalidated val", d, val, types[k])
      if (!validate(d, k, val, types[k], typeDefs)) {
        if (exports.invalidHandler) {
          exports.invalidHandler(k, val, types[k], data)
        } else if (exports.invalidHandler !== false) {
          debug("invalid: "+k+"="+val, types[k])
        }
        return remove
      }
      debug("validated val", d, val, types[k])
      return d[k]
    }).filter(function (val) { return val !== remove })

    if (!val.length) delete data[k]
    else if (isArray) {
      debug(isArray, data[k], val)
      data[k] = val
    } else data[k] = val[0]

    debug("k=%s val=%j", k, val, data[k])
  })
}

function validateString (data, k, val) {
  data[k] = String(val)
}

function validatePath (data, k, val) {
  data[k] = path.resolve(String(val))
  return true
}

function validateNumber (data, k, val) {
  debug("validate Number %j %j %j", k, val, isNaN(val))
  if (isNaN(val)) return false
  data[k] = +val
}

function validateBoolean (data, k, val) {
  if (val instanceof Boolean) val = val.valueOf()
  else if (typeof val === "string") {
    if (!isNaN(val)) val = !!(+val)
    else if (val === "null" || val === "false") val = false
    else val = true
  } else val = !!val
  data[k] = val
}

function validateUrl (data, k, val) {
  val = url.parse(String(val))
  if (!val.host) return false
  data[k] = val.href
}

function validateStream (data, k, val) {
  if (!(val instanceof Stream)) return false
  data[k] = val
}

function validate (data, k, val, type, typeDefs) {
  // arrays are lists of types.
  if (Array.isArray(type)) {
    for (var i = 0, l = type.length; i < l; i ++) {
      if (type[i] === Array) continue
      if (validate(data, k, val, type[i], typeDefs)) return true
    }
    delete data[k]
    return false
  }

  // an array of anything?
  if (type === Array) return true

  // NaN is poisonous.  Means that something is not allowed.
  if (type !== type) {
    debug("Poison NaN", k, val, type)
    delete data[k]
    return false
  }

  // explicit list of values
  if (val === type) {
    debug("Explicitly allowed %j", val)
    // if (isArray) (data[k] = data[k] || []).push(val)
    // else data[k] = val
    data[k] = val
    return true
  }

  // now go through the list of typeDefs, validate against each one.
  var ok = false
    , types = Object.keys(typeDefs)
  for (var i = 0, l = types.length; i < l; i ++) {
    debug("test type %j %j %j", k, val, types[i])
    var t = typeDefs[types[i]]
    if (t && type === t.type) {
      var d = {}
      ok = false !== t.validate(d, k, val)
      val = d[k]
      if (ok) {
        // if (isArray) (data[k] = data[k] || []).push(val)
        // else data[k] = val
        data[k] = val
        break
      }
    }
  }
  debug("OK? %j (%j %j %j)", ok, k, val, types[i])

  if (!ok) delete data[k]
  return ok
}

function parse (args, data, remain, types, shorthands) {
  debug("parse", args, data, remain)

  var key = null
    , abbrevs = abbrev(Object.keys(types))
    , shortAbbr = abbrev(Object.keys(shorthands))

  for (var i = 0; i < args.length; i ++) {
    var arg = args[i]
    debug("arg", arg)

    if (arg.match(/^-{2,}$/)) {
      // done with keys.
      // the rest are args.
      remain.push.apply(remain, args.slice(i + 1))
      args[i] = "--"
      break
    }
    if (arg.charAt(0) === "-") {
      if (arg.indexOf("=") !== -1) {
        var v = arg.split("=")
        arg = v.shift()
        v = v.join("=")
        args.splice.apply(args, [i, 1].concat([arg, v]))
      }
      // see if it's a shorthand
      // if so, splice and back up to re-parse it.
      var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
      debug("arg=%j shRes=%j", arg, shRes)
      if (shRes) {
        debug(arg, shRes)
        args.splice.apply(args, [i, 1].concat(shRes))
        if (arg !== shRes[0]) {
          i --
          continue
        }
      }
      arg = arg.replace(/^-+/, "")
      var no = false
      while (arg.toLowerCase().indexOf("no-") === 0) {
        no = !no
        arg = arg.substr(3)
      }

      if (abbrevs[arg]) arg = abbrevs[arg]

      var isBool = no || types[arg] === Boolean ||
        Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1

      var isArray = types[arg] === Array ||
        Array.isArray(types[arg]) && types[arg].indexOf(Array) !== -1

      var val

      if (isBool) {
        // just set and move along
        val = !no
        // however, also support --bool true or --bool false
        var la = args[i + 1]
        if (la === "true" || la === "false") {
          val = JSON.parse(la)
          if (no) val = !val
          i ++
        }

        if (isArray) (data[arg] = data[arg] || []).push(val)
        else data[arg] = val

        continue
      }

      var la = args[i + 1]
      if (la && la.match(/^-{2,}$/)) {
        la = undefined
        i --
      }
      val = la === undefined ? true : la
      if (isArray) (data[arg] = data[arg] || []).push(val)
      else data[arg] = val

      i ++
      continue
    }
    remain.push(arg)
  }
}

function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
  // handle single-char shorthands glommed together, like
  // npm ls -glp, but only if there is one dash, and only if
  // all of the chars are single-char shorthands, and it's
  // not a match to some other abbrev.
  arg = arg.replace(/^-+/, '')
  if (abbrevs[arg] && !shorthands[arg]) {
    return null
  }
  if (shortAbbr[arg]) {
    arg = shortAbbr[arg]
  } else {
    var singles = shorthands.___singles
    if (!singles) {
      singles = Object.keys(shorthands).filter(function (s) {
        return s.length === 1
      }).reduce(function (l,r) { l[r] = true ; return l }, {})
      shorthands.___singles = singles
    }
    var chrs = arg.split("").filter(function (c) {
      return singles[c]
    })
    if (chrs.join("") === arg) return chrs.map(function (c) {
      return shorthands[c]
    }).reduce(function (l, r) {
      return l.concat(r)
    }, [])
  }

  if (shorthands[arg] && !Array.isArray(shorthands[arg])) {
    shorthands[arg] = shorthands[arg].split(/\s+/)
  }
  return shorthands[arg]
}

if (module === require.main) {
var assert = require("assert")
  , sys = require("sys")

  , shorthands =
    { s : ["--loglevel", "silent"]
    , d : ["--loglevel", "info"]
    , dd : ["--loglevel", "verbose"]
    , ddd : ["--loglevel", "silly"]
    , noreg : ["--no-registry"]
    , reg : ["--registry"]
    , "no-reg" : ["--no-registry"]
    , silent : ["--loglevel", "silent"]
    , verbose : ["--loglevel", "verbose"]
    , h : ["--usage"]
    , H : ["--usage"]
    , "?" : ["--usage"]
    , help : ["--usage"]
    , v : ["--version"]
    , f : ["--force"]
    , desc : ["--description"]
    , "no-desc" : ["--no-description"]
    , "local" : ["--no-global"]
    , l : ["--long"]
    , p : ["--parseable"]
    , porcelain : ["--parseable"]
    , g : ["--global"]
    }

  , types =
    { argv : NaN
    , aoa: Array
    , browser : String
    , cache : path
    , color : ["always", Boolean]
    , depth : Number
    , description : Boolean
    , dev : Boolean
    , editor : path
    , force : Boolean
    , global : Boolean
    , globalconfig : path
    , group : [String, Number]
    , gzipbin : String
    , logfd : [Number, Stream]
    , loglevel : ["silent","win","error","warn","info","verbose","silly"]
    , long : Boolean
    , "node-version" : [false, String]
    , npaturl : url
    , npat : Boolean
    , "onload-script" : [false, String]
    , outfd : [Number, Stream]
    , parseable : Boolean
    , pre: Boolean
    , prefix: path
    , proxy : url
    , "rebuild-bundle" : Boolean
    , registry : url
    , searchopts : String
    , searchexclude: [null, String]
    , shell : path
    , t: [Array, String]
    , tag : String
    , tar : String
    , tmp : path
    , "unsafe-perm" : Boolean
    , usage : Boolean
    , user : String
    , username : String
    , userconfig : path
    , version : Boolean
    , viewer: path
    , _exit : Boolean
    }

; [["-v", {version:true}, []]
  ,["---v", {version:true}, []]
  ,["ls -s --no-reg connect -d",
    {loglevel:"info",registry:null},["ls","connect"]]
  ,["ls ---s foo",{loglevel:"silent"},["ls","foo"]]
  ,["ls --registry blargle", {}, ["ls"]]
  ,["--no-registry", {registry:null}, []]
  ,["--no-color true", {color:false}, []]
  ,["--no-color false", {color:true}, []]
  ,["--no-color", {color:false}, []]
  ,["--color false", {color:false}, []]
  ,["--color --logfd 7", {logfd:7,color:true}, []]
  ,["--color=true", {color:true}, []]
  ,["--logfd=10", {logfd:10}, []]
  ,["--tmp=/tmp -tar=gtar",{tmp:"/tmp",tar:"gtar"},[]]
  ,["--tmp=tmp -tar=gtar",
    {tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]]
  ,["--logfd x", {}, []]
  ,["a -true -- -no-false", {true:true},["a","-no-false"]]
  ,["a -no-false", {false:false},["a"]]
  ,["a -no-no-true", {true:true}, ["a"]]
  ,["a -no-no-no-false", {false:false}, ["a"]]
  ,["---NO-no-No-no-no-no-nO-no-no"+
    "-No-no-no-no-no-no-no-no-no"+
    "-no-no-no-no-NO-NO-no-no-no-no-no-no"+
    "-no-body-can-do-the-boogaloo-like-I-do"
   ,{"body-can-do-the-boogaloo-like-I-do":false}, []]
  ,["we are -no-strangers-to-love "+
    "--you-know the-rules --and so-do-i "+
    "---im-thinking-of=a-full-commitment "+
    "--no-you-would-get-this-from-any-other-guy "+
    "--no-gonna-give-you-up "+
    "-no-gonna-let-you-down=true "+
    "--no-no-gonna-run-around false "+
    "--desert-you=false "+
    "--make-you-cry false "+
    "--no-tell-a-lie "+
    "--no-no-and-hurt-you false"
   ,{"strangers-to-love":false
    ,"you-know":"the-rules"
    ,"and":"so-do-i"
    ,"you-would-get-this-from-any-other-guy":false
    ,"gonna-give-you-up":false
    ,"gonna-let-you-down":false
    ,"gonna-run-around":false
    ,"desert-you":false
    ,"make-you-cry":false
    ,"tell-a-lie":false
    ,"and-hurt-you":false
    },["we", "are"]]
  ,["-t one -t two -t three"
   ,{t: ["one", "two", "three"]}
   ,[]]
  ,["-t one -t null -t three four five null"
   ,{t: ["one", "null", "three"]}
   ,["four", "five", "null"]]
  ,["-t foo"
   ,{t:["foo"]}
   ,[]]
  ,["--no-t"
   ,{t:["false"]}
   ,[]]
  ,["-no-no-t"
   ,{t:["true"]}
   ,[]]
  ,["-aoa one -aoa null -aoa 100"
   ,{aoa:["one", null, 100]}
   ,[]]
  ].forEach(function (test) {
    var argv = test[0].split(/\s+/)
      , opts = test[1]
      , rem = test[2]
      , actual = nopt(types, shorthands, argv, 0)
      , parsed = actual.argv
    delete actual.argv
    console.log(sys.inspect(actual, false, 2, true), parsed.remain)
    for (var i in opts) {
      var e = JSON.stringify(opts[i])
        , a = JSON.stringify(actual[i] === undefined ? null : actual[i])
      assert.equal(e, a)
    }
    assert.deepEqual(rem, parsed.remain)
  })
}
<|endoftext|>


<commit_before><commit_msg>Remove empty actions/tabs file (#5350)<commit_after><|endoftext|>