<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Shape
 *
 * @class
 *
 * @extends Item
 */
var Shape = Item.extend(/** @lends Shape# */{
	_class: 'Shape',
	_transformContent: false,
	_boundsSelected: true,

	// TODO: SVG, serialization

	initialize: function Shape(type, point, size, radius, props) {
		this._type = type;
		this._size = size;
		this._radius = radius;
		this._initialize(props, point);
	},

	/**
	 * The size of the shape.
	 *
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		var size = this._size;
		return new LinkedSize(size.width, size.height, this, 'setSize');
	},

	setSize: function(/* size */) {
		var type = this._type,
			size = Size.read(arguments);
		if (!this._size.equals(size)) {
			var width = size.width,
				height = size.height;
			if (type === 'circle') {
				// Use average of width and height as new size, then calculate
				// radius as a number from that:
				width = height = (width + height) / 2;
				this._radius = width / 2;
			} else if (type === 'ellipse') {
				// The radius is a size.
				this._radius.set(width / 2, height / 2);
			}
			this._size.set(width, height);
			this._changed(/*#=*/ Change.GEOMETRY);
		}
	},

	/**
	 * The radius of the shape, as a number if it is a circle, or a size object
	 * for ellipses and rounded rectangles.
	 *
	 * @type Number|Size
	 * @bean
	 */
	getRadius: function() {
		var rad = this._radius;
		return this._type === 'circle'
				? rad
				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
	},

	setRadius: function(radius) {
		var type = this._type;
		if (type === 'circle') {
			if (radius === this._radius)
				return;
			var size = radius * 2;
			this._size.set(size, size);
		} else {
			radius = Size.read(arguments);
			if (this._radius.equals(radius))
				return;
			this._radius.set(radius.width, radius.height);
			if (type === 'ellipse')
				this._size.set(radius.width * 2, radius.height * 2);
		}
		this._changed(/*#=*/ Change.GEOMETRY);
	},

	isEmpty: function() {
		// A shape can never be "empty" in the sense that it does not hold a
		// definition. This is required for Group#bounds to work correctly when
		// containing a Shape.
		return false;
	},

	_draw: function(ctx, param) {
		var style = this._style,
			fillColor = style.getFillColor(),
			strokeColor = style.getStrokeColor();
		if (fillColor || strokeColor || param.clip) {
			var radius = this._radius,
				type = this._type;
			ctx.beginPath();
			if (type === 'circle') {
				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
			} else {
				var rx = radius.width,
					ry = radius.height,
					kappa = Numerical.KAPPA;
				if (type === 'ellipse') {
					// Use four bezier curves and KAPPA value to aproximate ellipse
					var	cx = rx * kappa,
						cy = ry * kappa;
					ctx.moveTo(-rx, 0);
					ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
					ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
					ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
					ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
				} else { // rect
					var size = this._size,
						width = size.width,
						height = size.height;
					if (rx === 0 && ry === 0) {
						// straight rect
						ctx.rect(-width / 2, -height / 2, width, height);
					} else {
						// rounded rect. Use inverse kappa to calculate position
						// of control points from the corners inwards.
						kappa = 1 - kappa;
						var x = width / 2,
							y = height / 2,
							cx = rx * kappa,
							cy = ry * kappa;
						ctx.moveTo(-x, -y + ry);
						ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
						ctx.lineTo(x - rx, -y);
						ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
						ctx.lineTo(x, y - ry);
						ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
						ctx.lineTo(-x + rx, y);
						ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
					}
				}
			}
		}
		if (!param.clip && (fillColor || strokeColor)) {
			this._setStyles(ctx);
			if (fillColor)
				ctx.fill();
			if (strokeColor)
				ctx.stroke();
		}
	},

	_canComposite: function() {
		// A path with only a fill  or a stroke can be directly blended, but if
		// it has both, it needs to be drawn into a separate canvas first.
		return !(this.hasFill() && this.hasStroke());
	},

	_getBounds: function(getter, matrix) {
		var rect = new Rectangle(this._size).setCenter(0, 0);
		if (getter !== 'getBounds' && this.hasStroke())
			rect = rect.expand(this.getStrokeWidth());
		return matrix ? matrix._transformBounds(rect) : rect;
	},

	_contains: function _contains(point) {
		switch (this._type) {
		case 'rect':
			return _contains.base.call(this, point);
		case 'circle':
		case 'ellipse':
			return point.divide(this._size).getLength() <= 0.5;
		}
	},

	_hitTest: function _hitTest(point) {
		if (this.hasStroke()) {
			var type = this._type,
				strokeWidth = this.getStrokeWidth();
			switch (type) {
			case 'rect':
				var rect = new Rectangle(this._size).setCenter(0, 0),
					outer = rect.expand(strokeWidth),
					inner = rect.expand(-strokeWidth);
				if (outer._containsPoint(point) && !inner._containsPoint(point))
					return new HitResult('stroke', this);
				break;
			case 'circle':
			case 'ellipse':
				var radius;
				if (type === 'ellipse') {
					// Calculate ellipse radius at angle
					var angle = point.getAngleInRadians(),
						size = this._size,
						width = size.width,
						height = size.height,
						x = width * Math.sin(angle),
						y = height * Math.cos(angle);
					radius = width * height / (2 * Math.sqrt(x * x + y * y));
				} else {
					radius = this._radius;
				}
				if (2 * Math.abs(point.getLength() - radius) <= strokeWidth)
					return new HitResult('stroke', this);
				break;
			}
		}
		return _hitTest.base.apply(this, arguments);
	},

	statics: new function() {
		function createShape(type, point, size, radius, args) {
			return new Shape(type, point, size, radius, Base.getNamed(args));
		}

		return /** @lends Shape */{
			/**
			 * Creates a circular Shape item.
			 *
			 * @param {Point} center the center point of the circle
			 * @param {Number} radius the radius of the circle
			 * @return {Shape} the newly created shape
			 *
			 * @example {@paperscript}
			 * var shape = new Shape.Circle(new Point(80, 50), 30);
			 * shape.strokeColor = 'black';
			 *
			 * @example {@paperscript} // Using object notation
			 * var shape = new Shape.Circle({
			 * 	center: [80, 50],
			 * 	radius: 30,
			 * 	strokeColor: 'black'
			 * });
			 */
			Circle: function(/* center, radius */) {
				var center = Point.readNamed(arguments, 'center'),
					radius = Base.readNamed(arguments, 'radius');
				return createShape('circle', center, new Size(radius * 2),
						radius, arguments);
			},

			/**
			 * Creates a rectangular Shape item from the passed point and size.
			 *
			 * @name Shape.Rectangle
			 * @param {Point} point
			 * @param {Size} size
			 * @return {Shape} the newly created shape
			 *
			 * @example {@paperscript}
			 * var point = new Point(20, 20);
			 * var size = new Size(60, 60);
			 * var shape = new Shape.Rectangle(point, size);
			 * shape.strokeColor = 'black';
			 *
			 * @example {@paperscript} // Using object notation
			 * var shape = new Shape.Rectangle({
			 * 	point: [20, 20],
			 * 	size: [60, 60],
			 * 	strokeColor: 'black'
			 * });
			 */
			/**
			 * Creates a rectanglular Shape item from the passed points. These
			 * do not necessarily need to be the top left and bottom right
			 * corners, the constructor figures out how to fit a rectangle
			 * between them.
			 *
			 * @name Shape.Rectangle
			 * @param {Point} from The first point defining the rectangle
			 * @param {Point} to The second point defining the rectangle
			 * @return {Shape} the newly created shape
			 *
			 * @example {@paperscript}
			 * var from = new Point(20, 20);
			 * var to = new Point(80, 80);
			 * var shape = new Shape.Rectangle(from, to);
			 * shape.strokeColor = 'black';
			 *
			 * @example {@paperscript} // Using object notation
			 * var shape = new Shape.Rectangle({
			 * 	from: [20, 20],
			 * 	to: [80, 80],
			 * 	strokeColor: 'black'
			 * });
			 */
			/**
			 * Creates a rectangular Shape item from the passed abstract
			 * {@link Rectangle}.
			 *
			 * @name Shape.Rectangle
			 * @param {Rectangle} rectangle
			 * @return {Shape} the newly created shape
			 *
			 * @example {@paperscript}
			 * var rectangle = new Rectangle({
			 * 	point: new Point(20, 20),
			 * 	size: new Size(60, 60)
			 * });
			 * var shape = new Shape.Rectangle(rectangle);
			 * shape.strokeColor = 'black';
			 *
			 * @example {@paperscript}
			 * var rectangle = new Rectangle({
			 * 	point: [20, 20],
			 * 	size: [60, 60]
			 * });
			 *
			 * var shape = new Shape.Rectangle({
			 * 	rectangle: rectangle,
			 * 	strokeColor: 'black'
			 * });
			 */
			Rectangle: function(/* rectangle */) {
				var rect = Rectangle.readNamed(arguments, 'rectangle');
				return createShape('rect', rect.getCenter(true),
						rect.getSize(true), Size.readNamed(arguments, 'radius'),
						arguments);
			},

			/**
			 * Creates an elliptic Shape item.
			 *
			 * @param {Rectangle} rectangle
			 * @return {Shape} the newly created shape
			 *
			 * @example {@paperscript}
			 * var rectangle = new Rectangle({
			 * 	point: [20, 20],
			 * 	size: [180, 60]
			 * });
			 * var shape = new Shape.Ellipse(rectangle);
			 * shape.fillColor = 'black';
			 *
			 * @example {@paperscript} // Using object notation
			 * var shape = new Shape.Ellipse({
			 * 	point: [20, 20],
			 * 	size: [180, 60],
			 * 	fillColor: 'black'
			 * });
			 */
			Ellipse: function(/* rectangle */) {
				var rect = Rectangle.readNamed(arguments, 'rectangle'),
					size = rect.getSize(true);
				return createShape('ellipse', rect.getCenter(true), size,
						new Size(size.width / 2, size.height / 2), arguments);
			}
		};
	}
});
<commit_msg>Improve Shape.Ellipse to support center & radius parameters, and update documentation.<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Shape
 *
 * @class
 *
 * @extends Item
 */
var Shape = Item.extend(/** @lends Shape# */{
	_class: 'Shape',
	_transformContent: false,
	_boundsSelected: true,

	// TODO: SVG, serialization

	initialize: function Shape(type, point, size, radius, props) {
		this._type = type;
		this._size = size;
		this._radius = radius;
		this._initialize(props, point);
	},

	/**
	 * The size of the shape.
	 *
	 * @type Size
	 * @bean
	 */
	getSize: function() {
		var size = this._size;
		return new LinkedSize(size.width, size.height, this, 'setSize');
	},

	setSize: function(/* size */) {
		var type = this._type,
			size = Size.read(arguments);
		if (!this._size.equals(size)) {
			var width = size.width,
				height = size.height;
			if (type === 'circle') {
				// Use average of width and height as new size, then calculate
				// radius as a number from that:
				width = height = (width + height) / 2;
				this._radius = width / 2;
			} else if (type === 'ellipse') {
				// The radius is a size.
				this._radius.set(width / 2, height / 2);
			}
			this._size.set(width, height);
			this._changed(/*#=*/ Change.GEOMETRY);
		}
	},

	/**
	 * The radius of the shape, as a number if it is a circle, or a size object
	 * for ellipses and rounded rectangles.
	 *
	 * @type Number|Size
	 * @bean
	 */
	getRadius: function() {
		var rad = this._radius;
		return this._type === 'circle'
				? rad
				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
	},

	setRadius: function(radius) {
		var type = this._type;
		if (type === 'circle') {
			if (radius === this._radius)
				return;
			var size = radius * 2;
			this._size.set(size, size);
		} else {
			radius = Size.read(arguments);
			if (this._radius.equals(radius))
				return;
			this._radius.set(radius.width, radius.height);
			if (type === 'ellipse')
				this._size.set(radius.width * 2, radius.height * 2);
		}
		this._changed(/*#=*/ Change.GEOMETRY);
	},

	isEmpty: function() {
		// A shape can never be "empty" in the sense that it does not hold a
		// definition. This is required for Group#bounds to work correctly when
		// containing a Shape.
		return false;
	},

	_draw: function(ctx, param) {
		var style = this._style,
			fillColor = style.getFillColor(),
			strokeColor = style.getStrokeColor(),
			clip = param.clip;
		if (fillColor || strokeColor || clip) {
			var radius = this._radius,
				type = this._type;
			ctx.beginPath();
			if (type === 'circle') {
				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
			} else {
				var rx = radius.width,
					ry = radius.height,
					kappa = Numerical.KAPPA;
				if (type === 'ellipse') {
					// Use four bezier curves and KAPPA value to aproximate ellipse
					var	cx = rx * kappa,
						cy = ry * kappa;
					ctx.moveTo(-rx, 0);
					ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
					ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
					ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
					ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
				} else { // rect
					var size = this._size,
						width = size.width,
						height = size.height;
					if (rx === 0 && ry === 0) {
						// straight rect
						ctx.rect(-width / 2, -height / 2, width, height);
					} else {
						// rounded rect. Use inverse kappa to calculate position
						// of control points from the corners inwards.
						kappa = 1 - kappa;
						var x = width / 2,
							y = height / 2,
							cx = rx * kappa,
							cy = ry * kappa;
						ctx.moveTo(-x, -y + ry);
						ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
						ctx.lineTo(x - rx, -y);
						ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
						ctx.lineTo(x, y - ry);
						ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
						ctx.lineTo(-x + rx, y);
						ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
					}
				}
			}
			ctx.closePath();
		}
		if (!clip && (fillColor || strokeColor)) {
			this._setStyles(ctx);
			if (fillColor)
				ctx.fill();
			if (strokeColor)
				ctx.stroke();
		}
	},

	_canComposite: function() {
		// A path with only a fill  or a stroke can be directly blended, but if
		// it has both, it needs to be drawn into a separate canvas first.
		return !(this.hasFill() && this.hasStroke());
	},

	_getBounds: function(getter, matrix) {
		var rect = new Rectangle(this._size).setCenter(0, 0);
		if (getter !== 'getBounds' && this.hasStroke())
			rect = rect.expand(this.getStrokeWidth());
		return matrix ? matrix._transformBounds(rect) : rect;
	},

	_contains: function _contains(point) {
		switch (this._type) {
		case 'rect':
			return _contains.base.call(this, point);
		case 'circle':
		case 'ellipse':
			return point.divide(this._size).getLength() <= 0.5;
		}
	},

	_hitTest: function _hitTest(point) {
		if (this.hasStroke()) {
			var type = this._type,
				strokeWidth = this.getStrokeWidth();
			switch (type) {
			case 'rect':
				var rect = new Rectangle(this._size).setCenter(0, 0),
					outer = rect.expand(strokeWidth),
					inner = rect.expand(-strokeWidth);
				if (outer._containsPoint(point) && !inner._containsPoint(point))
					return new HitResult('stroke', this);
				break;
			case 'circle':
			case 'ellipse':
				var radius;
				if (type === 'ellipse') {
					// Calculate ellipse radius at angle
					var angle = point.getAngleInRadians(),
						size = this._size,
						width = size.width,
						height = size.height,
						x = width * Math.sin(angle),
						y = height * Math.cos(angle);
					radius = width * height / (2 * Math.sqrt(x * x + y * y));
				} else {
					radius = this._radius;
				}
				if (2 * Math.abs(point.getLength() - radius) <= strokeWidth)
					return new HitResult('stroke', this);
				break;
			}
		}
		return _hitTest.base.apply(this, arguments);
	},

// Mess with indentation in order to get more line-space below:
statics: new function() {
	function createShape(type, point, size, radius, args) {
		return new Shape(type, point, size, radius, Base.getNamed(args));
	}

	return /** @lends Shape */{
		/**
		 * Creates a circular shape item.
		 *
		 * @name Shape.Circle
		 * @param {Point} center the center point of the circle
		 * @param {Number} radius the radius of the circle
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Circle(new Point(80, 50), 30);
		 * shape.strokeColor = 'black';
		 */
		/**
		 * Creates a circular shape item from the properties described by an
		 * object literal.
		 *
		 * @name Shape.Circle
		 * @param {Object} object an object literal containing properties
		 * describing the shape's attributes
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Circle({
		 * 	center: [80, 50],
		 * 	radius: 30,
		 * 	strokeColor: 'black'
		 * });
		 */
		Circle: function(/* center, radius */) {
			var center = Point.readNamed(arguments, 'center'),
				radius = Base.readNamed(arguments, 'radius');
			return createShape('circle', center, new Size(radius * 2), radius,
					arguments);
		},

		/**
		 * Creates a rectangular shape item, with optionally rounded corners.
		 *
		 * @name Shape.Rectangle
		 * @param {Rectangle} rectangle the rectangle object describing the
		 * geometry of the rectangular shape to be created.
		 * @param {Size} [radius=null] the size of the rounded corners
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var rectangle = new Rectangle(new Point(20, 20), new Size(60, 60));
		 * var shape = new Shape.Rectangle(rectangle);
		 * shape.strokeColor = 'black';
		 *
		 * @example {@paperscript} // The same, with rounder corners
		 * var rectangle = new Rectangle(new Point(20, 20), new Size(60, 60));
		 * var cornerSize = new Size(10, 10);
		 * var shape = new Shape.Rectangle(rectangle, cornerSize);
		 * shape.strokeColor = 'black';
		 */
		/**
		 * Creates a rectangular shape item from a point and a size object.
		 *
		 * @name Shape.Rectangle
		 * @param {Point} point the rectangle's top-left corner.
		 * @param {Size} size the rectangle's size.
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var point = new Point(20, 20);
		 * var size = new Size(60, 60);
		 * var shape = new Shape.Rectangle(point, size);
		 * shape.strokeColor = 'black';
		 */
		/**
		 * Creates a rectangular shape item from the passed points. These do not
		 * necessarily need to be the top left and bottom right corners, the
		 * constructor figures out how to fit a rectangle between them.
		 *
		 * @name Shape.Rectangle
		 * @param {Point} from the first point defining the rectangle
		 * @param {Point} to the second point defining the rectangle
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var from = new Point(20, 20);
		 * var to = new Point(80, 80);
		 * var shape = new Shape.Rectangle(from, to);
		 * shape.strokeColor = 'black';
		 */
		/**
		 * Creates a rectangular shape item from the properties described by an
		 * object literal.
		 *
		 * @name Shape.Rectangle
		 * @param {Object} object an object literal containing properties
		 * describing the shape's attributes
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Rectangle({
		 * 	point: [20, 20],
		 * 	size: [60, 60],
		 * 	strokeColor: 'black'
		 * });
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Rectangle({
		 * 	from: [20, 20],
		 * 	to: [80, 80],
		 * 	strokeColor: 'black'
		 * });
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Rectangle({
		 * 	rectangle: {
		 * 		topLeft: [20, 20],
		 * 		bottomRight: [80, 80]
		 * 	},
		 * 	strokeColor: 'black'
		 * });
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Rectangle({
	 	 *	topLeft: [20, 20],
	 	 * 	bottomRight: [80, 80],
		 * 	radius: 10,
		 * 	strokeColor: 'black'
		 * });
		 */
		Rectangle: function(/* rectangle */) {
			var rect = Rectangle.readNamed(arguments, 'rectangle');
			return createShape('rect', rect.getCenter(true), rect.getSize(true),
					Size.readNamed(arguments, 'radius'), arguments);
		},

		/**
		 * Creates an elliptical shape item.
		 *
		 * @name Shape.Ellipse
		 * @param {Rectangle} rectangle the rectangle circumscribing the ellipse
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var rectangle = new Rectangle(new Point(20, 20), new Size(180, 60));
		 * var shape = new Shape.Ellipse(rectangle);
		 * shape.fillColor = 'black';
		 */
		/**
		 * Creates an elliptical shape item from the properties described by an
		 * object literal.
		 *
		 * @name Shape.Ellipse
		 * @param {Object} object an object literal containing properties
		 * describing the shape's attributes
		 * @return {Shape} the newly created shape
		 *
		 * @example {@paperscript}
		 * var shape = new Shape.Ellipse({
		 * 	point: [20, 20],
		 * 	size: [180, 60],
		 * 	fillColor: 'black'
		 * });
		 *
		 * @example {@paperscript} // Placing by center and radius
		 * var shape = new Shape.Ellipse({
		 * 	center: [110, 50],
		 * 	radius: [90, 30],
		 * 	fillColor: 'black'
		 * });
		 */
		Ellipse: function(/* rectangle */) {
			var center,
				size,
				radius;
			if (Base.hasNamed(arguments, 'center')) {
				center = Point.readNamed(arguments, 'center');
				radius = Size.readNamed(arguments, 'radius');
				size = radius.multiply(2);
			} else {
				var rect = Rectangle.readNamed(arguments, 'rectangle');
				center = rect.getCenter(true);
				size = rect.getSize(true);
				radius = size.divide(2);
			}
			return createShape('ellipse', center, size, radius, arguments);
		}
	};
}});
<|endoftext|>


<commit_before><commit_msg>Only remove a single route when it is removed instead of rerendering the entire list.<commit_after><|endoftext|>


<commit_before><commit_msg>use navigator.clipboard.writeText<commit_after><|endoftext|>


<commit_before><commit_msg>properly push state to history using pushState<commit_after><|endoftext|>


<commit_before><commit_msg>remove redundant code<commit_after><|endoftext|>


<commit_before><commit_msg>Vast refactor<commit_after><|endoftext|>


<commit_before><commit_msg>Missed line<commit_after><|endoftext|>


<commit_before><commit_msg>for some reason there's color overlap - fixing<commit_after><|endoftext|>


<commit_before><commit_msg>Add refresh actions<commit_after><|endoftext|>


<commit_before><commit_msg>Mark where non-public methods start.<commit_after><|endoftext|>


<commit_before>const core  = require("./core");
const fs    = require("fs");
const utl   = require("./utl");
const _     = require("lodash");

const STARTING_STRING_DELIMITER = "# DO NOT DELETE THIS LINE -- js-makedepend depends on it.";
const DEFAULT_MODULE_SYSTEMS    = ["cjs", "amd", "es6"];
const MODULE_SYSTEM_LIST_RE     = /^((cjs|amd|es6)(,|$))+$/gi;

function appendToOrReplaceInFile(pOutputTo, pArray, pDelimiter, pAppend) {
    if (!pAppend) {
        try {
            const lOutputFile = fs.readFileSync(pOutputTo, {encoding: "utf8", flag: "r"});
            const lLines      = lOutputFile.split("\n");
            const lDelimiterPosition = lLines.indexOf(pDelimiter);

            if (lDelimiterPosition > -1) {
                fs.writeFileSync(
                    pOutputTo,
                    lLines.splice(0, lDelimiterPosition).join("\n"),
                    {encoding: "utf8", flag: "w"}
                );
            }
        } catch (e) {
            // process.stdout.write("'" + pOutputTo + "' didn't exist. We'll create the file instead.\n");
        }
    }

    fs.appendFileSync(
        pOutputTo,
        pArray.join(""),
        {encoding: "utf8", flag: "a"}
    );
}

function normalizeModuleSystems(pSystemList) {
    if (_.isString(pSystemList)) {
        return _(pSystemList.split(",")).sort().uniq();
    }
    /* istanbul ignore else  */
    if (_.isArray(pSystemList)) {
        return _(pSystemList).sort().uniq();
    }
    /* istanbul ignore next  */
    return DEFAULT_MODULE_SYSTEMS;
}

function validateParameters(pDirOrFile, pOptions) {
    if (!utl.fileExists(pDirOrFile)) {
        throw Error("Can't open '" + pDirOrFile + "' for reading. Does it exist?\n");
    }

    if (!!pOptions.system && _.isString(pOptions.system)) {
        const lParamArray = pOptions.system.match(MODULE_SYSTEM_LIST_RE);
        if (!lParamArray || lParamArray.length !== 1) {
            throw Error("Invalid module system list: '" + pOptions.system + "'\n");
        }
    }
}

exports.main = function(pDirOrFile, pOptions) {
    _.defaults(pOptions, {
        exclude: "",
        outputTo: "Makefile",
        delimiter: STARTING_STRING_DELIMITER,
        system: DEFAULT_MODULE_SYSTEMS,
    });

    try {
        validateParameters(pDirOrFile, pOptions);
        pOptions.system = normalizeModuleSystems(pOptions.system);
        if ("-" === pOptions.outputTo) {
            core.getDependencyStrings(pDirOrFile, pOptions).forEach(
                function(pLine) {
                    process.stdout.write(pLine);
                }
            );
        } else {
            appendToOrReplaceInFile(
                pOptions.outputTo,
                core.getDependencyStrings(pDirOrFile, pOptions),
                pOptions.delimiter,
                pOptions.append
            );
        }
    } catch (e) {
        process.stderr.write("ERROR: " + e.message);
    }
};
<commit_msg>bugfix: handle the case where no options are passed<commit_after>const core  = require("./core");
const fs    = require("fs");
const utl   = require("./utl");
const _     = require("lodash");

const STARTING_STRING_DELIMITER = "# DO NOT DELETE THIS LINE -- js-makedepend depends on it.";
const DEFAULT_MODULE_SYSTEMS    = ["cjs", "amd", "es6"];
const MODULE_SYSTEM_LIST_RE     = /^((cjs|amd|es6)(,|$))+$/gi;

function appendToOrReplaceInFile(pOutputTo, pArray, pDelimiter, pAppend) {
    if (!pAppend) {
        try {
            const lOutputFile = fs.readFileSync(pOutputTo, {encoding: "utf8", flag: "r"});
            const lLines      = lOutputFile.split("\n");
            const lDelimiterPosition = lLines.indexOf(pDelimiter);

            if (lDelimiterPosition > -1) {
                fs.writeFileSync(
                    pOutputTo,
                    lLines.splice(0, lDelimiterPosition).join("\n"),
                    {encoding: "utf8", flag: "w"}
                );
            }
        } catch (e) {
            // process.stdout.write("'" + pOutputTo + "' didn't exist. We'll create the file instead.\n");
        }
    }

    fs.appendFileSync(
        pOutputTo,
        pArray.join(""),
        {encoding: "utf8", flag: "a"}
    );
}

function normalizeModuleSystems(pSystemList) {
    if (_.isString(pSystemList)) {
        return _(pSystemList.split(",")).sort().uniq();
    }
    /* istanbul ignore else  */
    if (_.isArray(pSystemList)) {
        return _(pSystemList).sort().uniq();
    }
    /* istanbul ignore next  */
    return DEFAULT_MODULE_SYSTEMS;
}

function validateParameters(pDirOrFile, pOptions) {
    if (!utl.fileExists(pDirOrFile)) {
        throw Error("Can't open '" + pDirOrFile + "' for reading. Does it exist?\n");
    }

    if (!!pOptions.system && _.isString(pOptions.system)) {
        const lParamArray = pOptions.system.match(MODULE_SYSTEM_LIST_RE);
        if (!lParamArray || lParamArray.length !== 1) {
            throw Error("Invalid module system list: '" + pOptions.system + "'\n");
        }
    }
}

exports.main = function(pDirOrFile, pOptions) {
    pOptions = _.defaults(pOptions, {
        exclude: "",
        outputTo: "Makefile",
        delimiter: STARTING_STRING_DELIMITER,
        system: DEFAULT_MODULE_SYSTEMS,
    });

    try {
        validateParameters(pDirOrFile, pOptions);
        pOptions.system = normalizeModuleSystems(pOptions.system);
        if ("-" === pOptions.outputTo) {
            core.getDependencyStrings(pDirOrFile, pOptions).forEach(
                function(pLine) {
                    process.stdout.write(pLine);
                }
            );
        } else {
            appendToOrReplaceInFile(
                pOptions.outputTo,
                core.getDependencyStrings(pDirOrFile, pOptions),
                pOptions.delimiter,
                pOptions.append
            );
        }
    } catch (e) {
        process.stderr.write("ERROR: " + e.message);
    }
};
<|endoftext|>


<commit_before><commit_msg>Fix bug with resolve() handling of undefined values<commit_after><|endoftext|>


<commit_before><commit_msg>Delete nl_NL.js<commit_after><|endoftext|>


<commit_before>LazyLoad=function(k){function p(b,a){var g=k.createElement(b),c;for(c in a)a.hasOwnProperty(c)&&g.setAttribute(c,a[c]);return g}function l(b){var a=m[b],c,f;if(a)c=a.callback,f=a.urls,f.shift(),h=0,f.length||(c&&c.call(a.context,a.obj),m[b]=null,n[b].length&&j(b))}function w(){var b=navigator.userAgent;c={async:k.createElement("script").async===!0};(c.webkit=/AppleWebKit\//.test(b))||(c.ie=/MSIE/.test(b))||(c.opera=/Opera/.test(b))||(c.gecko=/Gecko\//.test(b))||(c.unknown=!0)}function j(b,a,g,f,h){var j=
function(){l(b)},o=b==="css",q=[],d,i,e,r;c||w();if(a)if(a=typeof a==="string"?[a]:a.concat(),o||c.async||c.gecko||c.opera)n[b].push({urls:a,callback:g,obj:f,context:h});else{d=0;for(i=a.length;d<i;++d)n[b].push({urls:[a[d]],callback:d===i-1?g:null,obj:f,context:h})}if(!m[b]&&(r=m[b]=n[b].shift())){s||(s=k.head||k.getElementsByTagName("head")[0]);a=r.urls;d=0;for(i=a.length;d<i;++d)g=a[d],o?e=c.gecko?p("style"):p("link",{href:g,rel:"stylesheet"}):(e=p("script",{src:g}),e.async=!1),e.className="lazyload",
e.setAttribute("charset","utf-8"),c.ie&&!o?e.onreadystatechange=function(){if(/loaded|complete/.test(e.readyState))e.onreadystatechange=null,j()}:o&&(c.gecko||c.webkit)?c.webkit?(r.urls[d]=e.href,t()):(e.innerHTML='@import "'+g+'";',u(e)):e.onload=e.onerror=j,q.push(e);d=0;for(i=q.length;d<i;++d)s.appendChild(q[d])}}function u(b){var a;try{a=!!b.sheet.cssRules}catch(c){h+=1;h<200?setTimeout(function(){u(b)},50):a&&l("css");return}l("css")}function t(){var b=m.css,a;if(b){for(a=v.length;--a>=0;)if(v[a].href===
b.urls[0]){l("css");break}h+=1;b&&(h<200?setTimeout(t,50):l("css"))}}var c,s,m={},h=0,n={css:[],js:[]},v=k.styleSheets;return{css:function(b,a,c,f){j("css",b,a,c,f)},js:function(b,a,c,f){j("js",b,a,c,f)}}}(this.document); // Just...Lazy Load! :D
(function(){
	var allScripts = document.getElementsByTagName("script");
	var thePath = null;
	var regex = /(.*)PHPWebThread\.js$/;
	var server_supported = true;
	for(var c=0, l=allScripts.length; c<l; ++c){
		if(regex.test(allScripts[c].src)){
			thePath = regex.exec(allScripts[c].src)[1];
			server_supported = allScripts[c].getAttribute("data-server-supported") == "1";
			break; // Just breaks! :D
		}
	}
	if(!server_supported){
		thePath = thePath+"PHPWebThreadDispatch.php/"; // It's don't supported, use PATH INFO instead
	}
	var instances = [];
	window.PHPWebThread = function(name, cached, is_server_supported){
		this.id = instances.length;
		this.name = name;
		this.start = function(){
			var request_uri = window.location.href;
			request_uri = request_uri.split("/");
			request_uri = request_uri.slice(3);
			var last_part = request_uri.pop();
			if(!!last_part){
				request_uri.push(last_part);
			}
			request_uri = request_uri.join("/");
			var asyncScript = document.createElement("script");
			asyncScript.async = true;
			asyncScript.src = thePath+"phpwebthread/"+this.id+"/"+this.name+"."+(cached?"js":"php"); // Set the URL
			asyncScript.charset = "UTF-8"; //It uses UTF-8
			(document.getElementsByTagName("head")[0]||document.head||document.body).appendChild(asyncScript); // And load it!
		}
		this.put = function(elements, css_files, js_files){
			/* Put the elements in the DOM */
			var self = this;
			var callback = function(){
					var allScripts = document.getElementsByTagName("script");
					var theScript = null;
					for(var c=0, l=allScripts.length; c<l; ++c){
						if(allScripts[c].getAttribute("data-phpwebthread-id") == self.id.toString()){
							theScript = allScripts[c];
							break; // Just breaks! :D
						}
					}
					if(theScript == null){
						throw("Not encountered :/");
					}
					else{
						var parentElement = theScript.parentNode;
						for(var c=0, l=elements.length;c<l; ++c){
							parentElement.insertBefore(elements[c], theScript);
						}
					}
					if(js_files.length > 0){
						LazyLoad.js(js_files); // Load the JS files after the loading of the DOM:D
					}
					self.put = null; // One time function :D
			};
			if(css_files.length > 0){
				LazyLoad.css(css_files, callback); // Just load the CSS files and call callback
			}
			else{
				callback(); // Just call the callback
			}
		}
		instances.push(this); // Append the instance
	};
	PHPWebThread.getInstance = function(id){
		return instances[id];
	}
	var tasks = window.php_web_threads||[];
	for(var c=0, l=tasks.length; c<l; ++c){
		var task = tasks[c]; //Just save a pointer
		task(); // Call! :D
	}
})();
<commit_msg>Added timeout<commit_after>LazyLoad=function(k){function p(b,a){var g=k.createElement(b),c;for(c in a)a.hasOwnProperty(c)&&g.setAttribute(c,a[c]);return g}function l(b){var a=m[b],c,f;if(a)c=a.callback,f=a.urls,f.shift(),h=0,f.length||(c&&c.call(a.context,a.obj),m[b]=null,n[b].length&&j(b))}function w(){var b=navigator.userAgent;c={async:k.createElement("script").async===!0};(c.webkit=/AppleWebKit\//.test(b))||(c.ie=/MSIE/.test(b))||(c.opera=/Opera/.test(b))||(c.gecko=/Gecko\//.test(b))||(c.unknown=!0)}function j(b,a,g,f,h){var j=
function(){l(b)},o=b==="css",q=[],d,i,e,r;c||w();if(a)if(a=typeof a==="string"?[a]:a.concat(),o||c.async||c.gecko||c.opera)n[b].push({urls:a,callback:g,obj:f,context:h});else{d=0;for(i=a.length;d<i;++d)n[b].push({urls:[a[d]],callback:d===i-1?g:null,obj:f,context:h})}if(!m[b]&&(r=m[b]=n[b].shift())){s||(s=k.head||k.getElementsByTagName("head")[0]);a=r.urls;d=0;for(i=a.length;d<i;++d)g=a[d],o?e=c.gecko?p("style"):p("link",{href:g,rel:"stylesheet"}):(e=p("script",{src:g}),e.async=!1),e.className="lazyload",
e.setAttribute("charset","utf-8"),c.ie&&!o?e.onreadystatechange=function(){if(/loaded|complete/.test(e.readyState))e.onreadystatechange=null,j()}:o&&(c.gecko||c.webkit)?c.webkit?(r.urls[d]=e.href,t()):(e.innerHTML='@import "'+g+'";',u(e)):e.onload=e.onerror=j,q.push(e);d=0;for(i=q.length;d<i;++d)s.appendChild(q[d])}}function u(b){var a;try{a=!!b.sheet.cssRules}catch(c){h+=1;h<200?setTimeout(function(){u(b)},50):a&&l("css");return}l("css")}function t(){var b=m.css,a;if(b){for(a=v.length;--a>=0;)if(v[a].href===
b.urls[0]){l("css");break}h+=1;b&&(h<200?setTimeout(t,50):l("css"))}}var c,s,m={},h=0,n={css:[],js:[]},v=k.styleSheets;return{css:function(b,a,c,f){j("css",b,a,c,f)},js:function(b,a,c,f){j("js",b,a,c,f)}}}(this.document); // Just...Lazy Load! :D
(function(){
	var allScripts = document.getElementsByTagName("script");
	var thePath = null;
	var regex = /(.*)PHPWebThread\.js$/;
	var server_supported = true;
	for(var c=0, l=allScripts.length; c<l; ++c){
		if(regex.test(allScripts[c].src)){
			thePath = regex.exec(allScripts[c].src)[1];
			server_supported = allScripts[c].getAttribute("data-server-supported") == "1";
			break; // Just breaks! :D
		}
	}
	if(!server_supported){
		thePath = thePath+"PHPWebThreadDispatch.php/"; // It's don't supported, use PATH INFO instead
	}
	var instances = [];
	window.PHPWebThread = function(name, cached, is_server_supported){
		this.id = instances.length;
		this.name = name;
		this.timeoutId = null;
		this.start = function(){
			var request_uri = window.location.href;
			request_uri = request_uri.split("/");
			request_uri = request_uri.slice(3);
			var last_part = request_uri.pop();
			if(!!last_part){
				request_uri.push(last_part);
			}
			request_uri = request_uri.join("/");
			var asyncScript = document.createElement("script");
			asyncScript.async = true;
			asyncScript.src = thePath+"phpwebthread/"+this.id+"/"+this.name+"."+(cached?"js":"php"); // Set the URL
			asyncScript.charset = "UTF-8"; //It uses UTF-8
			(document.getElementsByTagName("head")[0]||document.head||document.body).appendChild(asyncScript); // And load it!
			if(!!PHPWebThread.timeout){
				this.timeoutId = setTimeout(function(){
					if(window.location.href.indexOf("?") > -1){
						window.location.href = window.location.href+"&phpwebthread_deactivate=1";
					}
					else{
						window.location.href = window.location.href+"?phpwebthread_deactivate=1";
					}
				}, PHPWebThread.timeout*1000); //Issue a timeout! :D
			}
		}
		this.put = function(elements, css_files, js_files){
			/* Put the elements in the DOM */
			var self = this;
			var callback = function(){
					var allScripts = document.getElementsByTagName("script");
					var theScript = null;
					for(var c=0, l=allScripts.length; c<l; ++c){
						if(allScripts[c].getAttribute("data-phpwebthread-id") == self.id.toString()){
							theScript = allScripts[c];
							break; // Just breaks! :D
						}
					}
					if(theScript == null){
						throw("Not encountered :/");
					}
					else{
						var parentElement = theScript.parentNode;
						if(self.timeoutId != null){
							clearTimeout(self.timeoutId); // Stop the timeout! :D
						}

						var fallbackMsg = document.getElementById("phpwebthread_fallbackmsg");
						if(!!fallbackMsg){
							fallbackMsg.style.display = "none";
						}
						for(var c=0, l=elements.length;c<l; ++c){
							parentElement.insertBefore(elements[c], theScript);
						}
					}
					if(js_files.length > 0){
						LazyLoad.js(js_files); // Load the JS files after the loading of the DOM:D
					}
			};
			if(css_files.length > 0){
				LazyLoad.css(css_files, callback); // Just load the CSS files and call callback
			}
			else{
				callback(); // Just call the callback
			}
		}
		instances.push(this); // Append the instance
	};
	PHPWebThread.timeout = 10;
	PHPWebThread.setTimeout = function(timeout){
		PHPWebThread.timeout = timeout;
	}
	PHPWebThread.getInstance = function(id){
		return instances[id];
	}
	var tasks = window.php_web_threads||[];
	for(var c=0, l=tasks.length; c<l; ++c){
		var task = tasks[c]; //Just save a pointer
		task(); // Call! :D
	}
})();
<|endoftext|>


<commit_before>import _ from 'underscore';
import Events from './events';

/**
 * Wraps the passed method so that `this._super` will point to the superMethod
 * when the method is invoked.
 *
 * @private
 * @method wrap
 * @param {Function} method - The method to call.
 * @param {Function} superMeqthod - The super method.
 * @return {Function} - wrapped function.
 */
function _wrap(method, superMethod) {
  return function() {
    let prevSuper = this._super;
    this._super = superMethod;
    let ret = method.apply(this, arguments);
    this._super = prevSuper;
    return ret;
  };
}

/**
 * A reference to safe regex for checking if a function calls `_super`.
 *
 * @private
 * @const {RegExp}
 */
const CONTAINS_SUPER = (/xyz/.test(function() { xyz; })) ? /\b_super\b/ : /.*/; // eslint-disable-line

/**
 * Assigns properties of source object to destination object, wrapping methods
 * that call their super method.
 *
 * @private
 * @method wrapAll
 * @param {Object} dest - The destination object.
 * @param {Object} source - The source object.
 */
function _wrapAll(dest, source) {
  let keys = _.keys(source),
      length = keys.length,
      i, name, method, superMethod, hasSuper;

  for (i = 0; i < length; i++) {
    name = keys[i];
    method = source[name];
    superMethod = dest[name];

    // Test if new method calls `_super`
    hasSuper = CONTAINS_SUPER.test(method);

    // Only wrap the new method if the original method was a function and the
    // new method calls `_super`.
    if (hasSuper && _.isFunction(method) && _.isFunction(superMethod)) {
      dest[name] = _wrap(method, superMethod);

    // Otherwise just add the new method or property to the object.
    } else {
      dest[name] = method;
    }
  }
}

/**
 * Creates a new Class.
 *
 * ```js
 * const MyClass = Class.extend({
 *   initialize() {
 *     console.log('Created!');
 *   }
 * });
 *
 * new MyClass();
 * // >> Created!
 * ```
 *
 * @public
 * @class Class
 * @memberOf Metal
 * @memberOf Backbone
 */
const Class = function Class() {
  this.cid = _.uniqueId(this.cidPrefix);
  this.initialize(...arguments);
};

_.extend(Class.prototype, {

  /**
   * Identifier prefix of the cid. It can be override.
   *
   * @public
   * @attributeOf Class {String}
   */
  cidPrefix: 'metal',

  /**
   * An overridable method called when objects are instantiated. Does not do
   * anything by default.
   *
   * @public
   * @abstract
   * @method initialize
   */
  initialize: function() {},

  /**
   * Destroy a Class by removing all listeners.
   *
   * @public
   * @method destroy
   */
  destroy() {
    this.stopListening();
  }
});

_.extend(Class, {

  /**
   * Creates a new subclass.
   *
   * ```js
   * const MyClass = Class.extend({
   *   // ...
   * });
   *
   * const myClass = new MyClass();
   * myClass instanceof MyClass
   * // true
   * myClass instanceof Class
   * // true
   * ```
   *
   * @public
   * @static
   * @method extend
   * @param {Object} [protoProps] - The properties to be added to the prototype.
   * @param {Object} [staticProps] - The properties to be added to the constructor.
   */
  extend(protoProps, staticProps) {
    let Parent = this;
    let Child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent's constructor.
    if (!protoProps || !_.has(protoProps, 'constructor')) {
      Child = function() { Parent.apply(this, arguments); };
    } else if (CONTAINS_SUPER.test(protoProps.constructor)) {
      Child = _wrap(protoProps.constructor, Parent.prototype.constructor);
    } else {
      Child = protoProps.constructor;
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(Child, Parent);
    if (staticProps) {
      _wrapAll(Child, staticProps);
    }

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent`'s constructor function.
    let Surrogate = function() {
      this.constructor = Child;
    };

    Surrogate.prototype = Parent.prototype;
    Child.prototype = new Surrogate();

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) {
      _wrapAll(Child.prototype, protoProps);
    }

    // Set a convenience property in case the parent class is needed later.
    Child.superclass = Parent;

    // Set a convenience property in case the parent's prototype is needed
    // later.
    Child.__super__ = Parent.prototype;

    return Child;
  },

  /**
   * Mixes properties onto the class's prototype.
   *
   * ```js
   * const MyMixin = new Mixin({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * const MyClass = Class.extend({
   *   initialize() {
   *     this.alert();
   *   }
   * });
   *
   * MyClass.mixin(MyMixin);
   *
   * new MyClass();
   * // >> Alert!
   * ```
   *
   * @public
   * @static
   * @method mixin
   * @param {Object} protoProps - The properties to be added to the prototype.
   * @return {Class} - The class.
   */
  mixin(protoProps) {
    // Add prototype properties (instance properties) to the class, if supplied.
    _wrapAll(this.prototype, protoProps);
    return this;
  },

  /**
   * Mixes properties onto the class's constructor.
   *
   * ```js
   * const MyMixin = new Mixin({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * const MyClass = Class.extend(...);
   *
   * MyClass.include(MyMixin);
   *
   * MyClass.alert();
   * // >> Alert!
   * ```
   *
   * You can also simply pass a plain javascript object.
   *
   * ```js
   * const MyClass = Class.extend(...);
   *
   * MyClass.include({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * MyClass.alert();
   * // >> Alert!
   * ```
   *
   * @public
   * @static
   * @method mixin
   * @param {Object} staticProps - The properties to be added to the constructor.
   * @return {Class} - The class.
   */
  include(staticProps) {
    // Add static properties to the constructor function, if supplied.
    _wrapAll(this, staticProps);
    return this;
  },

  /**
   * Checks if `value` is a Metal Class.
   *
   * ```js
   * Class.isClass(Class.extend(...));
   * // >> true
   * Class.isClass(new Class());
   * // >> true
   * Class.isClass(function() {...});
   * // >> false
   * Class.isClass({...});
   * // >> false
   * ```
   * @public
   * @method isClass
   * @memberOf _
   * @param {*} value - The value to check.
   */
  isClass(value) {
    return !!value && (
      value instanceof Class ||
      value.prototype instanceof Class ||
      value === Class
    );
  }
});

/**
 * @class Class
 * @mixes Events
 */
Class.mixin(Events);

export default Class;
<commit_msg>Fix typo in class.js comments<commit_after>import _ from 'underscore';
import Events from './events';

/**
 * Wraps the passed method so that `this._super` will point to the superMethod
 * when the method is invoked.
 *
 * @private
 * @method wrap
 * @param {Function} method - The method to call.
 * @param {Function} superMethod - The super method.
 * @return {Function} - wrapped function.
 */
function _wrap(method, superMethod) {
  return function() {
    let prevSuper = this._super;
    this._super = superMethod;
    let ret = method.apply(this, arguments);
    this._super = prevSuper;
    return ret;
  };
}

/**
 * A reference to safe regex for checking if a function calls `_super`.
 *
 * @private
 * @const {RegExp}
 */
const CONTAINS_SUPER = (/xyz/.test(function() { xyz; })) ? /\b_super\b/ : /.*/; // eslint-disable-line

/**
 * Assigns properties of source object to destination object, wrapping methods
 * that call their super method.
 *
 * @private
 * @method wrapAll
 * @param {Object} dest - The destination object.
 * @param {Object} source - The source object.
 */
function _wrapAll(dest, source) {
  let keys = _.keys(source),
      length = keys.length,
      i, name, method, superMethod, hasSuper;

  for (i = 0; i < length; i++) {
    name = keys[i];
    method = source[name];
    superMethod = dest[name];

    // Test if new method calls `_super`
    hasSuper = CONTAINS_SUPER.test(method);

    // Only wrap the new method if the original method was a function and the
    // new method calls `_super`.
    if (hasSuper && _.isFunction(method) && _.isFunction(superMethod)) {
      dest[name] = _wrap(method, superMethod);

    // Otherwise just add the new method or property to the object.
    } else {
      dest[name] = method;
    }
  }
}

/**
 * Creates a new Class.
 *
 * ```js
 * const MyClass = Class.extend({
 *   initialize() {
 *     console.log('Created!');
 *   }
 * });
 *
 * new MyClass();
 * // >> Created!
 * ```
 *
 * @public
 * @class Class
 * @memberOf Metal
 * @memberOf Backbone
 */
const Class = function Class() {
  this.cid = _.uniqueId(this.cidPrefix);
  this.initialize(...arguments);
};

_.extend(Class.prototype, {

  /**
   * Identifier prefix of the cid. It can be override.
   *
   * @public
   * @attributeOf Class {String}
   */
  cidPrefix: 'metal',

  /**
   * An overridable method called when objects are instantiated. Does not do
   * anything by default.
   *
   * @public
   * @abstract
   * @method initialize
   */
  initialize: function() {},

  /**
   * Destroy a Class by removing all listeners.
   *
   * @public
   * @method destroy
   */
  destroy() {
    this.stopListening();
  }
});

_.extend(Class, {

  /**
   * Creates a new subclass.
   *
   * ```js
   * const MyClass = Class.extend({
   *   // ...
   * });
   *
   * const myClass = new MyClass();
   * myClass instanceof MyClass
   * // true
   * myClass instanceof Class
   * // true
   * ```
   *
   * @public
   * @static
   * @method extend
   * @param {Object} [protoProps] - The properties to be added to the prototype.
   * @param {Object} [staticProps] - The properties to be added to the constructor.
   */
  extend(protoProps, staticProps) {
    let Parent = this;
    let Child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent's constructor.
    if (!protoProps || !_.has(protoProps, 'constructor')) {
      Child = function() { Parent.apply(this, arguments); };
    } else if (CONTAINS_SUPER.test(protoProps.constructor)) {
      Child = _wrap(protoProps.constructor, Parent.prototype.constructor);
    } else {
      Child = protoProps.constructor;
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(Child, Parent);
    if (staticProps) {
      _wrapAll(Child, staticProps);
    }

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent`'s constructor function.
    let Surrogate = function() {
      this.constructor = Child;
    };

    Surrogate.prototype = Parent.prototype;
    Child.prototype = new Surrogate();

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) {
      _wrapAll(Child.prototype, protoProps);
    }

    // Set a convenience property in case the parent class is needed later.
    Child.superclass = Parent;

    // Set a convenience property in case the parent's prototype is needed
    // later.
    Child.__super__ = Parent.prototype;

    return Child;
  },

  /**
   * Mixes properties onto the class's prototype.
   *
   * ```js
   * const MyMixin = new Mixin({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * const MyClass = Class.extend({
   *   initialize() {
   *     this.alert();
   *   }
   * });
   *
   * MyClass.mixin(MyMixin);
   *
   * new MyClass();
   * // >> Alert!
   * ```
   *
   * @public
   * @static
   * @method mixin
   * @param {Object} protoProps - The properties to be added to the prototype.
   * @return {Class} - The class.
   */
  mixin(protoProps) {
    // Add prototype properties (instance properties) to the class, if supplied.
    _wrapAll(this.prototype, protoProps);
    return this;
  },

  /**
   * Mixes properties onto the class's constructor.
   *
   * ```js
   * const MyMixin = new Mixin({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * const MyClass = Class.extend(...);
   *
   * MyClass.include(MyMixin);
   *
   * MyClass.alert();
   * // >> Alert!
   * ```
   *
   * You can also simply pass a plain javascript object.
   *
   * ```js
   * const MyClass = Class.extend(...);
   *
   * MyClass.include({
   *   alert() {
   *     console.log('Alert!');
   *   }
   * });
   *
   * MyClass.alert();
   * // >> Alert!
   * ```
   *
   * @public
   * @static
   * @method mixin
   * @param {Object} staticProps - The properties to be added to the constructor.
   * @return {Class} - The class.
   */
  include(staticProps) {
    // Add static properties to the constructor function, if supplied.
    _wrapAll(this, staticProps);
    return this;
  },

  /**
   * Checks if `value` is a Metal Class.
   *
   * ```js
   * Class.isClass(Class.extend(...));
   * // >> true
   * Class.isClass(new Class());
   * // >> true
   * Class.isClass(function() {...});
   * // >> false
   * Class.isClass({...});
   * // >> false
   * ```
   * @public
   * @method isClass
   * @memberOf _
   * @param {*} value - The value to check.
   */
  isClass(value) {
    return !!value && (
      value instanceof Class ||
      value.prototype instanceof Class ||
      value === Class
    );
  }
});

/**
 * @class Class
 * @mixes Events
 */
Class.mixin(Events);

export default Class;
<|endoftext|>


<commit_before><commit_msg>Working<commit_after><|endoftext|>


<commit_before><commit_msg>syntax fix<commit_after><|endoftext|>


<commit_before><commit_msg>ensures file is resolved relative to base path<commit_after><|endoftext|>


<commit_before><commit_msg>Use frameless windows<commit_after><|endoftext|>


<commit_before>export default class Color {
  constructor(space, components) {
    this.space      = space;
    this.components = new Map(components);
    this.components.forEach((value, key) => {
      let name = Symbol.keyFor(key);
      Object.defineProperty(this, name, {
        get: () => {
          return function(value = null) {
            if (value === null) {
              return this.get(key);
            }
            this.set(key, value);
            return this;
          }.bind(this);
        }
      });
    });
  }

  space() {
    return Symbol.keyFor(this.space);
  }

  has(key) {
    return this.components.has(key);
  }

  get(key) {
    if (!this.has(key)) {
      throw new TypeError();
    }
    return this.components.get(key);
  }

  set(key, value) {
    if (!this.has(key)) {
      throw new TypeError();
    }
    this.components.set(key, value);
    return this;
  }

  keys() {
    return Array.from(this.components.keys());
  }

  values() {
    return Array.from(this.components.values());
  }

  cmy() {
  }

  cmyk() {
  }

  hsl() {
  }

  hsv() {
  }

  rgb() {
  }

  xyz() {
  }
}
<commit_msg>Removed unused method<commit_after>export default class Color {
  constructor(space, components) {
    this.space      = space;
    this.components = new Map(components);
    this.components.forEach((value, key) => {
      let name = Symbol.keyFor(key);
      Object.defineProperty(this, name, {
        get: () => {
          return function(value = null) {
            if (value === null) {
              return this.get(key);
            }
            this.set(key, value);
            return this;
          }.bind(this);
        }
      });
    });
  }

  has(key) {
    return this.components.has(key);
  }

  get(key) {
    if (!this.has(key)) {
      throw new TypeError();
    }
    return this.components.get(key);
  }

  set(key, value) {
    if (!this.has(key)) {
      throw new TypeError();
    }
    this.components.set(key, value);
    return this;
  }

  keys() {
    return Array.from(this.components.keys());
  }

  values() {
    return Array.from(this.components.values());
  }

  cmy() {
  }

  cmyk() {
  }

  hsl() {
  }

  hsv() {
  }

  rgb() {
  }

  xyz() {
  }
}
<|endoftext|>


<commit_before>var fs = require('fs'),
    ncp = require('ncp').ncp,
    path = require('path'),
    argv = require('optimist')
      .usage('Usage: ionic appname')
      .argv;

Ionic = function() {
};

Ionic.prototype = {
  _checkArgs: function() {
    if(argv._.length == 0) {
      return false;
    }
    return true;
  },

  _printUsage: function() {
    process.stderr.write('Usage: ionic appname');
    process.exit(1);
  },

  _fail: function(msg) {
    process.stderr.write(msg + '\n');
    process.exit(1);
  },

  _writeTemplateFolder: function() {
    console.log('Copying template to', this.targetPath);
    ncp('template', this.appName, function(err) {
      if(err) {
        this._fail('Unable to build starter folder', err);
      }
    });
  },

  // Prompt the user for a response
  _ask: function(question) {
    var response;

    process.stdout.write(question + ' ');
    process.stdin.resume();
    response = fs.readSync(process.stdin.fd, 100, 0, "utf8");
    process.stdin.pause();
    return response[0].trim();
  },

  _checkTargetPath: function() {
    if(fs.existsSync(this.targetPath)) {
      var resp = this._ask('The ' + this.targetPath + ' directory already exists. Overwrite files? (y/n)')
      if(resp === 'y') {
        return true;
      }
      return false;
    }
    return true;
  },

  run: function() {
    if(this._checkArgs() === false) {
      return this._printUsage();
    }

    this.appName = argv._[0];
    this.targetPath = path.resolve(this.appName);

    // Make sure to create this, or ask them if they want to override it
    if(this._checkTargetPath() === false) {
      process.stderr.write('Not continuing.');
      process.exit(1);
    }

    console.log('Creating Ionic app in folder', this.targetPath);

    this._writeTemplateFolder();
  }
};

var ionic = new Ionic();
ionic.run();<commit_msg>Having fun with ASCII Art<commit_after>/*
   __          __
| /  \ |\ | | /  `
| \__/ | \| | \__,

Licensed under the Apache 2.0 license. See LICENSE For mroe.

Copyright 2013 Drifty (http://drifty.com/)
*/

var fs = require('fs'),
    ncp = require('ncp').ncp,
    path = require('path'),
    argv = require('optimist')
      .usage('Usage: ionic appname')
      .argv;

Ionic = function() {
};

Ionic.prototype = {
  _checkArgs: function() {
    if(argv._.length == 0) {
      return false;
    }
    return true;
  },

  _printUsage: function() {
    this._printIonic();
    process.stderr.write('Usage: ionic appname\n');
    process.exit(1);
  },

  _printIonic: function() {
    process.stdout.write('\n   __          __  \n');
    process.stdout.write('| /  \\ |\\ | | /  `\n' + '| \\__/ | \\| | \\__,\n\n');
  },

  _fail: function(msg) {
    process.stderr.write(msg + '\n');
    process.exit(1);
  },

  _writeTemplateFolder: function() {
    console.log('Copying template to', this.targetPath);
    ncp('template', this.appName, function(err) {
      if(err) {
        this._fail('Unable to build starter folder', err);
      }
    });
  },

  // Prompt the user for a response
  _ask: function(question) {
    var response;

    process.stdout.write(question + ' ');
    process.stdin.resume();
    response = fs.readSync(process.stdin.fd, 100, 0, "utf8");
    process.stdin.pause();
    return response[0].trim();
  },

  _checkTargetPath: function() {
    if(fs.existsSync(this.targetPath)) {
      var resp = this._ask('The ' + this.targetPath + ' directory already exists. Overwrite files? (y/n)')
      if(resp === 'y') {
        return true;
      }
      return false;
    }
    return true;
  },

  run: function() {
    if(this._checkArgs() === false) {
      return this._printUsage();
    }

    this._printIonic();

    this.appName = argv._[0];
    this.targetPath = path.resolve(this.appName);

    // Make sure to create this, or ask them if they want to override it
    if(this._checkTargetPath() === false) {
      process.stderr.write('Not continuing.');
      process.exit(1);
    }

    console.log('Creating Ionic app in folder', this.targetPath);

    this._writeTemplateFolder();
  }
};

var ionic = new Ionic();
ionic.run();<|endoftext|>


<commit_before>const db = require("sqlite");
const fs = require("fs-extra-promise");

const baseLocation = "./bwd/db/sqlite";

const dataSchema = {
  str: {
    create: "TEXT",
    insert: value => value,
    select: value => value,
  },
  int: {
    create: "INTEGER",
    insert: value => parseInt(value),
    select: value => value,
  },
  autoid: {
    create: "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE",
    insert: () => { throw new Error("Cannot insert Auto ID value!"); },
    select: value => value,
  },
  timestamp: {
    create: "DATETIME",
    insert: value => parseInt(value),
    select: value => value,
  },
  autots: {
    create: "DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL",
    insert: () => { throw new Error("Cannot insert Auto Timestamp value!"); },
    select: value => value,
  },
  bool: {
    create: "INTEGER",
    insert: value => !value ? 0 : -1, // eslint-disable-line no-confusing-arrow
    select: value => !!value,
  },
  json: {
    create: "TEXT",
    insert: value => JSON.stringify(value),
    select: value => JSON.parse(value),
  },
};

const schemaCache = new Map();

exports.init = async () => new Promise(async (resolve, reject) => {
  try {
    await fs.ensureDir(baseLocation);
    await db.open(`${baseLocation}/db.sqlite`);
    await db.run("CREATE TABLE IF NOT EXISTS dataProviderSchemas (id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, name, schema)");
    const rows = await db.all("SELECT * FROM dataProviderSchemas");
    rows.map(r => schemaCache.set(r.name, JSON.parse(r.schema)));
    resolve();
  } catch (e) {
    reject(e);
  }
});

exports.insertSchema = (client, tableName, schema) => {
  schemaCache.set(tableName, schema);
  return db.run("INSERT INTO dataProviderSchemas (name, schema) VALUES (?, ?)", [tableName, JSON.stringify(schema)]);
};

exports.get = (client, table, key, value) => db.get(`SELECT * FROM ${table} WHERE ${key} = '${value}'`);

exports.getRandom = (client, table) => db.get(`SELECT * FROM ${table} ORDER BY RANDOM() LIMIT 1;`);

exports.getAll = (client, table, key = null, value = null) => {
  if (key) {
    return db.all(`SELECT * FROM ${table} WHERE ${key} = '${value}'`);
  }
  return db.all(`SELECT * FROM ${table}`);
};

exports.insert = (client, table, keys, values) => new Promise((resolve, reject) => {
  if (!schemaCache.has(table)) reject("Table not found in schema cache");
  let schema = schemaCache.get(table);
  schema = schema.filter(f => keys.includes(f.name));
  client.funcs.validateData(schema, keys, values); // automatically throws error
  const insertValues = schema.map((field, index) => dataSchema[field.type].insert(values[index]));
  const questionMarks = schema.map(() => "?").join(", ");
  client.funcs.log(`Inserting Values: ${insertValues.join(";")}`);
  db.run(`INSERT INTO ${table}(${keys.join(", ")}) VALUES(${questionMarks});`, insertValues)
      .then(resolve(true))
      .catch(e => reject(`Error inserting data: ${e}`));
});

exports.has = (client, table, key, value) => new Promise((resolve) => {
  db.get(`SELECT id FROM ${table} WHERE ${key} = '${value}'`)
      .then(() => resolve(true))
      .catch(() => resolve(false));
});

exports.update = (client, table, keys, values, whereKey, whereValue) => new Promise((resolve, reject) => {
  if (!schemaCache.has(table)) reject("Table not found in schema cache");
  const schema = schemaCache.get(table);
  const filtered = schema.filter(f => keys.includes(f.name));
  client.funcs.validateData(schema, keys, values);
  const inserts = filtered.map((field, index) => `${field.name} = '${dataSchema[field.type].insert(values[index])}'`);
  db.run(`UPDATE '${table}' SET ${inserts} WHERE ${whereKey} = '${whereValue}';`)
      .then(resolve(true))
      .catch(e => reject(`Error inserting data: ${e}`));
});

exports.delete = (client, table, key) => db.run(`DELETE FROM ${table} WHERE id = '${key}'`);

exports.hasTable = (client, table) => new Promise((resolve, reject) => {
  db.get(`SELECT name FROM sqlite_master WHERE type='table' AND name='${table}';`)
      .then((row) => {
        if (!row) resolve(false);
        resolve(true);
      })
      .catch(e => reject(e));
});

exports.createTable = (client, tableName, keys) => new Promise(async (resolve, reject) => {
  const tags = client.funcs.parseTags(keys);
  const schema = client.funcs.createDBSchema(tags);
  if (!schema.find(s => s.name === "id")) {
    schema.push({ name: "id", type: "autoid" });
  }
  const inserts = schema.map(field => `${field.name} ${dataSchema[field.type].create}`).join(", ");
  await db.run(`CREATE TABLE '${tableName}' (${inserts});`);
  this.insertSchema(client, tableName, schema).catch(reject);
  resolve(true);
});

exports.deleteTable = (client, tableName) => db.run(`DROP TABLE '${tableName}'`);

exports.run = sql => db.get(sql); // Returns a result row Best to be used with limit

exports.runAll = sql => db.all(sql); // Returns **ALL** result rows

exports.exec = sql => db.run(sql); // Does not return result rows.

exports.help = {};
exports.help.name = "sqlite";
exports.help.type = "providers";
exports.help.description = "Allows you use SQLite functionality throughout Komada.";
exports.conf = {};
exports.conf.moduleName = "sqlite";
exports.conf.enabled = true;
exports.conf.requiredFuncs = ["createDBSchema", "validateData", "parseTags"];
exports.conf.requiredModules = ["sqlite", "fs-extra-promise"];
<commit_msg>Update sqlite.js<commit_after>const { sep, resolve } = require("path");
const db = require("sqlite");
const fs = require("fs-nextra");

exports.init = async (client) => {
  const baseDir = resolve(client.clientBaseDir, "bwd", "provider", "sqlite");
  await fs.ensureDir(baseDir);
  await fs.ensureFile(`${baseDir + sep}db.sqlite`);
  return db.open(`${baseDir + sep}db.sqlite`);
};

/* Table methods */

/**
 * Checks if a table exists.
 * @param {string} table The name of the table you want to check.
 * @returns {Promise<boolean>}
 */
exports.hasTable = table => db.get(`SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`)
  .then(row => !!row);

/**
 * Creates a new table.
 * @param {string} table The name for the new table.
 * @param {string} rows The rows for the table.
 * @returns {Promise<Object>}
 */
exports.createTable = (table, rows) => db.run(`CREATE TABLE '${table}' (${rows});`);

/**
 * Drops a table.
 * @param {string} table The name of the table to drop.
 * @returns {Promise<Object>}
 */
exports.deleteTable = table => db.run(`DROP TABLE '${table}'`);

/* Document methods */

/**
 * Get all documents from a table.
 * @param {string} table The name of the table to fetch from.
 * @returns {Promise<Object[]>}
 */
exports.getAll = (table, { key = null, value = null }) => {
  if (key) return db.all(`SELECT * FROM ${table} WHERE ${key} = '${value}'`);
  return db.all(`SELECT * FROM ${table}`);
};

/**
 * Get a row from a table.
 * @param {string} table The name of the table.
 * @param {string} key The row id or the key to find by. If value is undefined, it'll search by 'id'.
 * @param {string} [value=null] The desired value to find.
 * @returns {Promise<?Object>}
 */
exports.get = (table, key, value = null) => {
  if (key && !value) return db.get(`SELECT * FROM ${table} WHERE id = '${key}'`).catch(() => null);
  return db.get(`SELECT * FROM ${table} WHERE ${key} = '${value}'`).catch(() => null);
};

/**
 * Check if a row exists.
 * @param {string} table The name of the table
 * @param {string} value The value to search by 'id'.
 * @returns {Promise<boolean>}
 */
exports.has = (table, value) => db.get(`SELECT id FROM ${table} WHERE id = '${value}'`)
  .then(() => true)
  .catch(() => false);

/**
 * Get a random row from a table.
 * @param {string} table The name of the table.
 * @returns {Promise<Object>}
 */
exports.getRandom = table => db.get(`SELECT * FROM ${table} ORDER BY RANDOM() LIMIT 1`);

/**
 * Insert a new document into a table.
 * @param {string} table The name of the table.
 * @param {string} row The row id.
 * @param {Object} data The object with all properties you want to insert into the document.
 * @returns {Promise<Object>}
 */
exports.create = (table, row, data) => {
  const { keys, values } = this.serialize(Object.assign(data, { id: row }));
  return db.run(`INSERT INTO ${table} (${keys.join(", ")}) VALUES(${values.join(", ")})`);
};
exports.set = (...args) => this.create(...args);
exports.insert = (...args) => this.create(...args);

/**
 * Update a row from a table.
 * @param {string} table The name of the table.
 * @param {string} row The row id.
 * @param {Object} data The object with all the properties you want to update.
 * @returns {Promise<Object>}
 */
exports.update = (table, row, data) => {
  const inserts = Object.entries(data).map(value => `'${value[0]}' = '${value[1]}'`).join(", ");
  return db.run(`UPDATE '${table}' SET ${inserts} WHERE id = '${row}'`);
};
exports.replace = (...args) => this.update(...args);

/**
 * Delete a document from the table.
 * @param {string} table The name of the directory.
 * @param {string} row The row id.
 * @returns {Promise<Object>}
 */
exports.delete = (table, row) => db.run(`DELETE FROM ${table} WHERE id = '${row}'`);

/**
 * Get a row from an arbitrary SQL query.
 * @param {string} sql The query to execute.
 * @returns {Promise<Object>}
 */
exports.run = sql => db.get(sql); // Returns a result row Best to be used with limit

/**
 * Get all rows from an arbitrary SQL query.
 * @param {string} sql The query to execute.
 * @returns {Promise<Object>}
 */
exports.runAll = sql => db.all(sql); // Returns **ALL** result rows

/**
 * Run arbitrary SQL query.
 * @param {string} sql The query to execute.
 * @returns {Promise<Object>}
 */
exports.exec = sql => db.run(sql);

/**
 * Transform NoSQL queries into SQL.
 * @param {Object} data The object.
 * @returns {Object}
 */
exports.serialize = (data) => {
  const keys = [];
  const values = [];
  const entries = Object.entries(data);
  for (let i = 0; i < entries.length; i++) {
    keys[i] = entries[i][0];
    values[i] = entries[i][1];
  }

  return { keys, values };
};

exports.conf = {
  moduleName: "sqlite",
  enabled: true,
  requiredModules: ["sqlite", "fs-nextra"],
};

exports.help = {
  name: "sqlite",
  type: "providers",
  description: "Allows you use SQLite functionality throughout Komada.",
};
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import ConfigOption from './ConfigOption'
import Container from '../common/Container'
import './audio-visual.scss'

class AudioVisual extends Component {
  constructor(props) {
    super(props)
    this.state = {}
  }

  componentWillUnmount() {
    if (this.state.audio) {
      this.state.audio.pause()
    }
  }

  render() {
    const toggleBrokenBuilds = (newValue) => this.props.setBrokenBuildTimers(newValue)
    const toggleBrokenSounds = (newValue) => this.props.setBrokenBuildSounds(newValue)
    const setSoundFx = () => this.props.setBrokenBuildSoundFx(this.refs.soundFx.value)
    const testSoundFx = () => {
      let audio = new Audio(this.refs.soundFx.value)
      this.setState({audio})
      audio.addEventListener('ended', () => this.setState({audio: null}))
      audio.play()
    }

    return (

        <Container title='Display Settings'>
          <fieldset className='settings-list'>
            <ConfigOption name='Show broken build timers'
                          enabled={this.props.showBrokenBuildTimers}
                          onToggle={toggleBrokenBuilds}/>
          </fieldset>
        </Container>
        <Container title='Audio Settings'>
          <fieldset className='settings-list'>
            <ConfigOption name='Enable sound for broken builds'
                          enabled={this.props.showBrokenBuildSounds}
                          onToggle={toggleBrokenSounds}/>

              Broken build sound
              <input id='sound-fx'
                     ref='soundFx'
                     type='text'
                     placeholder='URL to an audio file'
                     defaultValue={this.props.brokenBuildSoundFx}
                     onBlur={setSoundFx}/>
              <button className='test' onClick={testSoundFx}>test</button>

          </fieldset>
        </Container>

    )
  }
}

AudioVisual.propTypes = {
  showBrokenBuildTimers: PropTypes.bool.isRequired,
  showBrokenBuildSounds: PropTypes.bool.isRequired,
  brokenBuildSoundFx: PropTypes.string.isRequired,
  setBrokenBuildTimers: PropTypes.func.isRequired,
  setBrokenBuildSounds: PropTypes.func.isRequired,
  setBrokenBuildSoundFx: PropTypes.func.isRequired
}

export default AudioVisual
<commit_msg>adds an error message if the broken build sound can not be tested<commit_after>import React, {Component, PropTypes} from 'react'
import ConfigOption from './ConfigOption'
import Container from '../common/Container'
import Messages from '../common/messages/Messages'
import './audio-visual.scss'

class AudioVisual extends Component {
  constructor(props) {
    super(props)
    this.state = {
      errors: []
    }
  }

  componentWillUnmount() {
    if (this.state.audio) {
      this.state.audio.pause()
    }
  }

  render() {
    const toggleBrokenBuilds = (newValue) => this.props.setBrokenBuildTimers(newValue)
    const toggleBrokenSounds = (newValue) => this.props.setBrokenBuildSounds(newValue)
    const setSoundFx = () => this.props.setBrokenBuildSoundFx(this.refs.soundFx.value)
    const testSoundFx = () => {
      const audio = new Audio(this.refs.soundFx.value)
      this.setState({audio, errors: []})
      audio.addEventListener('ended', () => this.setState({audio: null}))
      audio.play()
        .catch((e) => this.setState({errors: ['Unable to play audio file because of an error:', e.message]}))
    }

    return (

        <Container title='Display Settings'>
          <fieldset className='settings-list'>
            <ConfigOption name='Show broken build timers'
                          enabled={this.props.showBrokenBuildTimers}
                          onToggle={toggleBrokenBuilds}/>
          </fieldset>
        </Container>
        <Container title='Audio Settings'>
          <fieldset className='settings-list'>
            <ConfigOption name='Enable sound for broken builds'
                          enabled={this.props.showBrokenBuildSounds}
                          onToggle={toggleBrokenSounds}/>

              Broken build sound
              <input id='sound-fx'
                     ref='soundFx'
                     type='text'
                     placeholder='URL to an audio file'
                     defaultValue={this.props.brokenBuildSoundFx}
                     onBlur={setSoundFx}/>
              <button className='test' onClick={testSoundFx}>test</button>
              <Messages type='notification' messages={this.state.errors}/>

          </fieldset>
        </Container>

    )
  }
}

AudioVisual.propTypes = {
  showBrokenBuildTimers: PropTypes.bool.isRequired,
  showBrokenBuildSounds: PropTypes.bool.isRequired,
  brokenBuildSoundFx: PropTypes.string.isRequired,
  setBrokenBuildTimers: PropTypes.func.isRequired,
  setBrokenBuildSounds: PropTypes.func.isRequired,
  setBrokenBuildSoundFx: PropTypes.func.isRequired
}

export default AudioVisual
<|endoftext|>


<commit_before>function viz_nvd3(data_attribute) {
  var token_name = data_attribute['token'];
  var json_callback = data_attribute['json_endpoint'];

  function UTC(dttm){
    return v = new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(),  dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
  }
  var tickMultiFormat = d3.time.format.multi([
    [".%L", function(d) { return d.getMilliseconds(); }],
    [":%S", function(d) { return d.getSeconds(); }],
    ["%I:%M", function(d) { return d.getMinutes(); }],
    ["%I %p", function(d) { return d.getHours(); }],
    ["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
    ["%b %d", function(d) { return d.getDate() != 1; }],
    ["%B", function(d) { return d.getMonth(); }],
    ["%Y", function() { return true; }]
  ]);
  function formatDate(dttm) {
    var d = UTC(new Date(dttm));
    //d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
    return tickMultiFormat(d);
  }
  colors = [
    "#FF5A5F", "#007A87", "#7B0051", "#00D1C1", "#8CE071", "#FFB400",
    "#FFAA91", "#B4A76C", "#9CA299", "#565A5C"
  ];
  var token = d3.select('#' + token_name);
  var jtoken = $('#' + token_name);
  var loading = $('#' + token_name).find("img.loading");
  var chart = $('#' + token_name).find("div.chart");
  var refresh = function() {
    chart.hide();
    loading.show();
    $.getJSON(json_callback, function(payload) {
      var data = payload.data;
      var viz = payload;
      var viz_type = viz.form_data.viz_type;
      $("#query_container").html(data.query);
      nv.addGraph(function() {
        if (viz_type === 'line') {
          if (viz.form_data.show_brush) {
            var chart = nv.models.lineWithFocusChart()
          //chart.lines2.xScale( d3.time.scale.utc());
          chart.lines2.xScale(d3.time.scale.utc());
          chart.x2Axis
            .showMaxMin(true)
            .tickFormat(formatDate);
          } else {
            var chart = nv.models.lineChart()
          }
          // To alter the tooltip header
          // chart.interactiveLayer.tooltip.headerFormatter(function(){return '';});
          chart.xScale(d3.time.scale.utc());
          chart.interpolate(viz.form_data.line_interpolation);
          chart.xAxis
            .showMaxMin(true)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));
          if (viz.form_data.contribution || viz.form_data.num_period_compare) {
            chart.yAxis.tickFormat(d3.format('.3p'));
            if (chart.y2Axis != undefined) {
                chart.y2Axis.tickFormat(d3.format('.3p'));
            }
          }
        } else if (viz_type === 'bar') {
          var chart = nv.models.multiBarChart()
              .showControls(true)
              .groupSpacing(0.1);
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'dist_bar') {
          var chart = nv.models.multiBarChart()
            .showControls(true)   //Allow user to switch between 'Grouped' and 'Stacked' mode.
            .reduceXTicks(false)
            .rotateLabels(45)
            .groupSpacing(0.1);   //Distance between each group of bars.
          chart.xAxis
            .showMaxMin(false);
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'pie') {
          var chart = nv.models.pieChart()
          chart.showLegend(viz.form_data.show_legend);
          if (viz.form_data.donut) {
            chart.donut(true);
            chart.donutLabelsOutside(true);
          }
          chart.labelsOutside(true);
          chart.cornerRadius(true);

        } else if (viz_type === 'column') {
          var chart = nv.models.multiBarChart()
            .reduceXTicks(false)
            .rotateLabels(45) ;
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'compare') {
          var chart = nv.models.cumulativeLineChart();
          chart.xScale(d3.time.scale.utc());
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3p'));

        } else if (viz_type === 'bubble') {
          var chart = nv.models.scatterChart();
          chart.xAxis.tickFormat(d3.format('.3s'));
          chart.yAxis.tickFormat(d3.format('.3s'));
          chart.showLegend(viz.form_data.show_legend);
          chart.pointRange([5, 5000]);

        } else if (viz_type === 'area') {
          var chart = nv.models.stackedAreaChart();
          chart.xScale(d3.time.scale.utc());
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));
        }

        if ((viz_type === "line" || viz_type === "area") && viz.form_data.rich_tooltip) {
          chart.useInteractiveGuideline(true);
        }
        if (viz.form_data.y_axis_zero) {
          chart.forceY([0, 1]);
        }
        else if (viz.form_data.y_log_scale) {
          chart.yScale(d3.scale.log());
        }
        if (viz.form_data.x_log_scale) {
          chart.xScale(d3.scale.log());
        }
        if (viz.form_data.y_axis_format) {
          chart.yAxis.tickFormat(d3.format(viz.form_data.y_axis_format));
        }

        chart.duration(0);

        token.select('.chart').append("svg")
          .datum(data.chart_data)
          .transition().duration(500)
          .call(chart);

        return chart;
      });
      chart.show();
      loading.hide();
  }).fail(function(xhr) {
      var err = '
' + xhr.responseText  + '
';
      loading.hide();
      chart.show();
      chart.html(err);
    });
  };

  return {
    render: refresh,
    resize: refresh,
  };

}

[
  'area',
  'bar',
  'bubble',
  'column',
  'compare',
  'dist_bar',
  'line',
  'pie',
].forEach(function(name) {
  px.registerWidget(name, viz_nvd3);
});
<commit_msg>handle y2axis for y axis format<commit_after>function viz_nvd3(data_attribute) {
  var token_name = data_attribute['token'];
  var json_callback = data_attribute['json_endpoint'];

  function UTC(dttm){
    return v = new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(),  dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
  }
  var tickMultiFormat = d3.time.format.multi([
    [".%L", function(d) { return d.getMilliseconds(); }],
    [":%S", function(d) { return d.getSeconds(); }],
    ["%I:%M", function(d) { return d.getMinutes(); }],
    ["%I %p", function(d) { return d.getHours(); }],
    ["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
    ["%b %d", function(d) { return d.getDate() != 1; }],
    ["%B", function(d) { return d.getMonth(); }],
    ["%Y", function() { return true; }]
  ]);
  function formatDate(dttm) {
    var d = UTC(new Date(dttm));
    //d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
    return tickMultiFormat(d);
  }
  colors = [
    "#FF5A5F", "#007A87", "#7B0051", "#00D1C1", "#8CE071", "#FFB400",
    "#FFAA91", "#B4A76C", "#9CA299", "#565A5C"
  ];
  var token = d3.select('#' + token_name);
  var jtoken = $('#' + token_name);
  var loading = $('#' + token_name).find("img.loading");
  var chart = $('#' + token_name).find("div.chart");
  var refresh = function() {
    chart.hide();
    loading.show();
    $.getJSON(json_callback, function(payload) {
      var data = payload.data;
      var viz = payload;
      var viz_type = viz.form_data.viz_type;
      $("#query_container").html(data.query);
      nv.addGraph(function() {
        if (viz_type === 'line') {
          if (viz.form_data.show_brush) {
            var chart = nv.models.lineWithFocusChart()
          //chart.lines2.xScale( d3.time.scale.utc());
          chart.lines2.xScale(d3.time.scale.utc());
          chart.x2Axis
            .showMaxMin(true)
            .tickFormat(formatDate);
          } else {
            var chart = nv.models.lineChart()
          }
          // To alter the tooltip header
          // chart.interactiveLayer.tooltip.headerFormatter(function(){return '';});
          chart.xScale(d3.time.scale.utc());
          chart.interpolate(viz.form_data.line_interpolation);
          chart.xAxis
            .showMaxMin(true)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));
          if (viz.form_data.contribution || viz.form_data.num_period_compare) {
            chart.yAxis.tickFormat(d3.format('.3p'));
            if (chart.y2Axis != undefined) {
                chart.y2Axis.tickFormat(d3.format('.3p'));
            }
          }
        } else if (viz_type === 'bar') {
          var chart = nv.models.multiBarChart()
              .showControls(true)
              .groupSpacing(0.1);
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'dist_bar') {
          var chart = nv.models.multiBarChart()
            .showControls(true)   //Allow user to switch between 'Grouped' and 'Stacked' mode.
            .reduceXTicks(false)
            .rotateLabels(45)
            .groupSpacing(0.1);   //Distance between each group of bars.
          chart.xAxis
            .showMaxMin(false);
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'pie') {
          var chart = nv.models.pieChart()
          chart.showLegend(viz.form_data.show_legend);
          if (viz.form_data.donut) {
            chart.donut(true);
            chart.donutLabelsOutside(true);
          }
          chart.labelsOutside(true);
          chart.cornerRadius(true);

        } else if (viz_type === 'column') {
          var chart = nv.models.multiBarChart()
            .reduceXTicks(false)
            .rotateLabels(45) ;
          chart.yAxis.tickFormat(d3.format('.3s'));

        } else if (viz_type === 'compare') {
          var chart = nv.models.cumulativeLineChart();
          chart.xScale(d3.time.scale.utc());
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3p'));

        } else if (viz_type === 'bubble') {
          var chart = nv.models.scatterChart();
          chart.xAxis.tickFormat(d3.format('.3s'));
          chart.yAxis.tickFormat(d3.format('.3s'));
          chart.showLegend(viz.form_data.show_legend);
          chart.pointRange([5, 5000]);

        } else if (viz_type === 'area') {
          var chart = nv.models.stackedAreaChart();
          chart.xScale(d3.time.scale.utc());
          chart.xAxis
            .showMaxMin(false)
            .tickFormat(formatDate);
          chart.showLegend(viz.form_data.show_legend);
          chart.yAxis.tickFormat(d3.format('.3s'));
        }

        if ((viz_type === "line" || viz_type === "area") && viz.form_data.rich_tooltip) {
          chart.useInteractiveGuideline(true);
        }
        if (viz.form_data.y_axis_zero) {
          chart.forceY([0, 1]);
        }
        else if (viz.form_data.y_log_scale) {
          chart.yScale(d3.scale.log());
        }
        if (viz.form_data.x_log_scale) {
          chart.xScale(d3.scale.log());
        }
        if (viz.form_data.y_axis_format) {
          chart.yAxis.tickFormat(d3.format(viz.form_data.y_axis_format));

          if (chart.y2Axis != undefined) {
            chart.y2Axis.tickFormat(d3.format(viz.form_data.y_axis_format));
          }
        }

        chart.duration(0);

        token.select('.chart').append("svg")
          .datum(data.chart_data)
          .transition().duration(500)
          .call(chart);

        return chart;
      });
      chart.show();
      loading.hide();
  }).fail(function(xhr) {
      var err = '
' + xhr.responseText  + '
';
      loading.hide();
      chart.show();
      chart.html(err);
    });
  };

  return {
    render: refresh,
    resize: refresh,
  };

}

[
  'area',
  'bar',
  'bubble',
  'column',
  'compare',
  'dist_bar',
  'line',
  'pie',
].forEach(function(name) {
  px.registerWidget(name, viz_nvd3);
});
<|endoftext|>


<commit_before>'use strict';

let kCasterJobs = ['BLU', 'RDM', 'BLM', 'WHM', 'SCH', 'SMN', 'ACN', 'AST', 'CNJ', 'THM'];
let kTankJobs = ['GLA', 'PLD', 'MRD', 'WAR', 'DRK', 'GNB'];
let kHealerJobs = ['CNJ', 'WHM', 'SCH', 'AST'];
let kCraftingJobs = ['CRP', 'BSM', 'ARM', 'GSM', 'LTW', 'WVR', 'ALC', 'CUL'];
let kGatheringJobs = ['MIN', 'BTN', 'FSH'];

let kStunJobs = ['SAM', 'NIN', 'ROG', 'DRG', 'LNC', 'MNK', 'PGL', 'WAR', 'MRD', 'PLD', 'GLA', 'DRK', 'GNB'];
let kSilenceJobs = ['MCH', 'BRD', 'ARC', 'DNC', 'BLU', 'GNB', 'GLA', 'PLD', 'MRD', 'WAR', 'DRK', 'GNB'];
let kSleepJobs = ['BLM', 'WHM'];
let kFeintJobs = ['SAM', 'NIN', 'ROG', 'DRG', 'LNC', 'MNK', 'PGL'];
let kAddleJobs = ['BLU', 'RDM', 'BLM', 'SMN', 'ACN', 'CNJ', 'THM'];
let kCleanseJobs = ['AST', 'BRD', 'CNJ', 'SCH', 'WHM'];

let Util = {
  jobToRole: function(job) {
    let role;
    if (job.search(/^(WAR|DRK|PLD|GNB|MRD|GLA)$/) >= 0) {
      role = 'tank';
    } else if (job.search(/^(WHM|SCH|AST|CNJ)$/) >= 0) {
      role = 'healer';
    } else if (job.search(/^(MNK|NIN|DRG|SAM|ROG|LNC|PGL)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(BLU|BLM|SMN|RDM|THM|ACN)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(BRD|MCH|DNC|ARC)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(CRP|BSM|ARM|GSM|LTW|WVR|ALC|CUL)$/) >= 0) {
      role = 'crafter';
    } else if (job.search(/^(MIN|BTN|FSH)$/) >= 0) {
      role = 'gatherer';
    } else {
      role = '';
      console.log('Unknown job role');
    }
    return role;
  },

  isCasterJob: function(job) {
    return kCasterJobs.indexOf(job) >= 0;
  },

  isTankJob: function(job) {
    return kTankJobs.indexOf(job) >= 0;
  },

  isHealerJob: function(job) {
    return kHealerJobs.indexOf(job) >= 0;
  },

  isCraftingJob: function(job) {
    return kCraftingJobs.indexOf(job) >= 0;
  },

  isGatheringJob: function(job) {
    return kGatheringJobs.indexOf(job) >= 0;
  },

  isCombatJob: function(job) {
    return !this.isCraftingJob(job) && !this.isGatheringJob(job);
  },

  canStun: function(job) {
    return kStunJobs.indexOf(job) >= 0;
  },
  canSilence: function(job) {
    return kSilenceJobs.indexOf(job) >= 0;
  },
  canSleep: function(job) {
    return kSleepJobs.indexOf(job) >= 0;
  },
  canCleanse: function(job) {
    return kCleanseJobs.indexOf(job) >= 0;
  },
  canFeint: function(job) {
    return kFeintJobs.indexOf(job) >= 0;
  },
  canAddle: function(job) {
    return kAddleJobs.indexOf(job) >= 0;
  },
};

(function() {
  let wsUrl = /[\?&]OVERLAY_WS=([^&]+)/.exec(location.href);
  let ws = null;
  let queue = [];
  let rseqCounter = 0;
  let responsePromises = {};
  let subscribers = {};
  let sendMessage = null;

  if (wsUrl) {
    sendMessage = (obj) => {
      if (queue)
        queue.push(msg);
      else
        ws.send(JSON.stringify(obj));
    };

    let connectWs = function() {
      ws = new WebSocket(wsUrl[1]);

      ws.addEventListener('error', (e) => {
        console.error(e);
      });

      ws.addEventListener('open', () => {
        console.log('Connected!');

        let q = queue;
        queue = null;

        sendMessage({
          call: 'subscribe',
          events: Object.keys(subscribers),
        });

        for (let msg of q)
          sendMessage(msg);
      });

      ws.addEventListener('message', (msg) => {
        try {
          msg = JSON.parse(msg.data);
        } catch (e) {
          console.error('Invalid message received: ', msg);
          return;
        }

        if (msg.rseq !== undefined && responsePromises[msg.rseq]) {
          responsePromises[msg.rseq](msg);
          delete responsePromises[msg.rseq];
        } else {
          processEvent(msg);
        }
      });

      ws.addEventListener('close', () => {
        queue = [];

        console.log('Trying to reconnect...');
        // Don't spam the server with retries.
        setTimeout(() => {
          connectWs();
        }, 300);
      });
    };

    connectWs();
  } else {
    sendMessage = (obj, cb) => {
      if (queue)
        queue.push([obj, cb]);
      else
        OverlayPluginApi.callHandler(JSON.stringify(obj), cb);
    };

    let waitForApi = function() {
      if (!window.OverlayPluginApi || !window.OverlayPluginApi.ready) {
        setTimeout(waitForApi, 300);
        return;
      }

      let q = queue;
      queue = null;

      window.__OverlayCallback = processEvent;

      sendMessage({
        call: 'subscribe',
        events: Object.keys(subscribers),
      }, null);

      for (let [msg, resolve] of q)
        sendMessage(msg, resolve);
    };

    waitForApi();
  }

  function processEvent(msg) {
    if (subscribers[msg.type]) {
      for (let sub of subscribers[msg.type])
        sub(msg);
    }
  }

  window.dispatchOverlayEvent = processEvent;

  window.addOverlayListener = (event, cb) => {
    if (!subscribers[event]) {
      subscribers[event] = [];

      if (!queue) {
        sendMessage({
          call: 'subscribe',
          events: [event],
        });
      }
    }

    subscribers[event].push(cb);
  };

  window.removeOverlayListener = (event, cb) => {
    if (subscribers[event]) {
      let list = subscribers[event];
      let pos = list.indexOf(cb);

      if (pos > -1) list.splice(pos, 1);
    }
  };

  window.callOverlayHandler = (msg) => {
    let p;

    if (ws) {
      msg.rseq = rseqCounter++;
      p = new Promise((resolve, reject) => {
        responsePromises[msg.rseq] = resolve;
      });

      sendMessage(msg);
    } else {
      p = new Promise((resolve) => {
        sendMessage(msg, (data) => {
          resolve(data == null ? null : JSON.parse(data));
        });
      });
    }

    return p;
  };
})();
<commit_msg>Fix ACT ws typos<commit_after>'use strict';

let kCasterJobs = ['BLU', 'RDM', 'BLM', 'WHM', 'SCH', 'SMN', 'ACN', 'AST', 'CNJ', 'THM'];
let kTankJobs = ['GLA', 'PLD', 'MRD', 'WAR', 'DRK', 'GNB'];
let kHealerJobs = ['CNJ', 'WHM', 'SCH', 'AST'];
let kCraftingJobs = ['CRP', 'BSM', 'ARM', 'GSM', 'LTW', 'WVR', 'ALC', 'CUL'];
let kGatheringJobs = ['MIN', 'BTN', 'FSH'];

let kStunJobs = ['SAM', 'NIN', 'ROG', 'DRG', 'LNC', 'MNK', 'PGL', 'WAR', 'MRD', 'PLD', 'GLA', 'DRK', 'GNB'];
let kSilenceJobs = ['MCH', 'BRD', 'ARC', 'DNC', 'BLU', 'GNB', 'GLA', 'PLD', 'MRD', 'WAR', 'DRK', 'GNB'];
let kSleepJobs = ['BLM', 'WHM'];
let kFeintJobs = ['SAM', 'NIN', 'ROG', 'DRG', 'LNC', 'MNK', 'PGL'];
let kAddleJobs = ['BLU', 'RDM', 'BLM', 'SMN', 'ACN', 'CNJ', 'THM'];
let kCleanseJobs = ['AST', 'BRD', 'CNJ', 'SCH', 'WHM'];

let Util = {
  jobToRole: function(job) {
    let role;
    if (job.search(/^(WAR|DRK|PLD|GNB|MRD|GLA)$/) >= 0) {
      role = 'tank';
    } else if (job.search(/^(WHM|SCH|AST|CNJ)$/) >= 0) {
      role = 'healer';
    } else if (job.search(/^(MNK|NIN|DRG|SAM|ROG|LNC|PGL)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(BLU|BLM|SMN|RDM|THM|ACN)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(BRD|MCH|DNC|ARC)$/) >= 0) {
      role = 'dps';
    } else if (job.search(/^(CRP|BSM|ARM|GSM|LTW|WVR|ALC|CUL)$/) >= 0) {
      role = 'crafter';
    } else if (job.search(/^(MIN|BTN|FSH)$/) >= 0) {
      role = 'gatherer';
    } else {
      role = '';
      console.log('Unknown job role');
    }
    return role;
  },

  isCasterJob: function(job) {
    return kCasterJobs.indexOf(job) >= 0;
  },

  isTankJob: function(job) {
    return kTankJobs.indexOf(job) >= 0;
  },

  isHealerJob: function(job) {
    return kHealerJobs.indexOf(job) >= 0;
  },

  isCraftingJob: function(job) {
    return kCraftingJobs.indexOf(job) >= 0;
  },

  isGatheringJob: function(job) {
    return kGatheringJobs.indexOf(job) >= 0;
  },

  isCombatJob: function(job) {
    return !this.isCraftingJob(job) && !this.isGatheringJob(job);
  },

  canStun: function(job) {
    return kStunJobs.indexOf(job) >= 0;
  },
  canSilence: function(job) {
    return kSilenceJobs.indexOf(job) >= 0;
  },
  canSleep: function(job) {
    return kSleepJobs.indexOf(job) >= 0;
  },
  canCleanse: function(job) {
    return kCleanseJobs.indexOf(job) >= 0;
  },
  canFeint: function(job) {
    return kFeintJobs.indexOf(job) >= 0;
  },
  canAddle: function(job) {
    return kAddleJobs.indexOf(job) >= 0;
  },
};

(function() {
  let wsUrl = /[\?&]OVERLAY_WS=([^&]+)/.exec(location.href);
  let ws = null;
  let queue = [];
  let rseqCounter = 0;
  let responsePromises = {};
  let subscribers = {};
  let sendMessage = null;

  if (wsUrl) {
    sendMessage = (msg) => {
      if (queue)
        queue.push(msg);
      else
        ws.send(JSON.stringify(msg));
    };

    let connectWs = function() {
      ws = new WebSocket(wsUrl[1]);

      ws.addEventListener('error', (e) => {
        console.error(e);
      });

      ws.addEventListener('open', () => {
        console.log('Connected!');

        let q = queue;
        queue = null;

        sendMessage({
          call: 'subscribe',
          events: Object.keys(subscribers),
        });

        for (let msg of q)
          sendMessage(msg);
      });

      ws.addEventListener('message', (msg) => {
        try {
          msg = JSON.parse(msg.data);
        } catch (e) {
          console.error('Invalid message received: ', msg);
          return;
        }

        if (msg.rseq !== undefined && responsePromises[msg.rseq]) {
          responsePromises[msg.rseq](msg);
          delete responsePromises[msg.rseq];
        } else {
          processEvent(msg);
        }
      });

      ws.addEventListener('close', () => {
        queue = [];

        console.log('Trying to reconnect...');
        // Don't spam the server with retries.
        setTimeout(() => {
          connectWs();
        }, 300);
      });
    };

    connectWs();
  } else {
    sendMessage = (obj, cb) => {
      if (queue)
        queue.push([obj, cb]);
      else
        OverlayPluginApi.callHandler(JSON.stringify(obj), cb);
    };

    let waitForApi = function() {
      if (!window.OverlayPluginApi || !window.OverlayPluginApi.ready) {
        setTimeout(waitForApi, 300);
        return;
      }

      let q = queue;
      queue = null;

      window.__OverlayCallback = processEvent;

      sendMessage({
        call: 'subscribe',
        events: Object.keys(subscribers),
      }, null);

      for (let [msg, resolve] of q)
        sendMessage(msg, resolve);
    };

    waitForApi();
  }

  function processEvent(msg) {
    if (subscribers[msg.type]) {
      for (let sub of subscribers[msg.type])
        sub(msg);
    }
  }

  window.dispatchOverlayEvent = processEvent;

  window.addOverlayListener = (event, cb) => {
    if (!subscribers[event]) {
      subscribers[event] = [];

      if (!queue) {
        sendMessage({
          call: 'subscribe',
          events: [event],
        });
      }
    }

    subscribers[event].push(cb);
  };

  window.removeOverlayListener = (event, cb) => {
    if (subscribers[event]) {
      let list = subscribers[event];
      let pos = list.indexOf(cb);

      if (pos > -1) list.splice(pos, 1);
    }
  };

  window.callOverlayHandler = (msg) => {
    let p;

    if (ws) {
      msg.rseq = rseqCounter++;
      p = new Promise((resolve, reject) => {
        responsePromises[msg.rseq] = resolve;
      });

      sendMessage(msg);
    } else {
      p = new Promise((resolve) => {
        sendMessage(msg, (data) => {
          resolve(data == null ? null : JSON.parse(data));
        });
      });
    }

    return p;
  };
})();
<|endoftext|>